
Completion.pudu
Pudu52 lines2.3 KB
1/** @App.Completion.Handler — argument completion for templates and prompts */2module PuduLangMcp.App.Completion34import Std.Json as Json5import Std.List as List6import Std.Option as Option7import PuduLangMcp.App.Context as Context8import PuduLangMcp.App.Resources as Resources9import PuduLangMcp.Domain.Docs.Chapter as Chapter10import PuduLangMcp.Domain.Rpc.Reply as Reply11import PuduLangMcp.Errors.RpcError as RpcError12import PuduLangMcp.Services.Reference as Reference13import PuduLangMcp.Utils.JsonAccess as Access141516const MAX_VALUES: Int = 100171819export fn complete(context: &Context.Context, params: &Json.Json) -> Result[Json.Json, RpcError.ProtocolError] {20 let reference = Option.unwrapOr(Access.member(params, "ref"), Json.Null)21 let kind = match Access.text(&reference, "type") {22 case Some(given) => given23 case None => { return Err(RpcError.InvalidParams("ref.type must be a string")) }24 }25 let argument = Option.unwrapOr(Access.member(params, "argument"), Json.Null)26 let name = match Access.text(&argument, "name") {27 case Some(given) => given28 case None => { return Err(RpcError.InvalidParams("argument.name must be a string")) }29 }30 let value = match Access.text(&argument, "value") {31 case Some(given) => given.toLower()32 case None => { return Err(RpcError.InvalidParams("argument.value must be a string")) }33 }34 let template = Resources.REFERENCE_PREFIX + "\{module\}"35 let candidates = if kind == "ref/resource" && Access.text(&reference, "uri") == Some(template) && name == "module" {36 context.moduleFiles.map(|held: Reference.ModuleFile| held.moduleName)37 } else if kind == "ref/prompt" && Access.text(&reference, "name") == Some("pudu_explain") && name == "topic" {38 Chapter.inGroup(&context.chapters, "docs").map(|held: Chapter.Chapter| held.slug)39 } else {40 []41 }42 let matching = candidates.filter(|candidate: Str| candidate.toLower().startsWith(value))43 let shown = List.take(&matching, MAX_VALUES)44 Ok(Reply.complete(&[45 ("completion", Json.object(&[46 ("values", Access.texts(&shown)),47 ("total", Json.Number(matching.length())),48 ("hasMore", Json.Boolean(matching.length() > shown.length()))49 ]))50 ], &Reply.NoCache))51}52