
Prompts.pudu
Pudu86 lines5.5 KB
1/** @Domain.Catalog.Prompts.Registry — prompt templates and their rendering */2module PuduLangMcp.Domain.Catalog.Prompts34import Std.Json as Json5import Std.List as List6import Std.Text as Text7import PuduLangMcp.Errors.RpcError as RpcError8import PuduLangMcp.Utils.JsonAccess as Access910/** @Domain.Catalog.Prompts.PromptArgument — one named prompt input */11export type PromptArgument = { name: Str, description: Str, required: Bool }1213/** @Domain.Catalog.Prompts.PromptSpec — one prompt described once */14export type PromptSpec = { name: Str, title: Str, description: Str, arguments: Array[PromptArgument] }151617export const CONVENTIONS: Str = "Pudu rules that are easy to get wrong:\n- A brace inside a string literal starts interpolation: \"\{name\}\" inserts name. Write \\\{ and \\\} for literal braces.\n- Option and Result helpers are module functions: Option.unwrapOr(value, fallback), never value.unwrapOr(fallback).\n- items.get(i) answers the element and stops the program when i is out of range; List.get(&items, i) answers an Option.\n- Module scope holds only const. Lookup tables are const tables built with mapOf([...]) or setOf([...]).\n- scope, module, and where are keywords and cannot name bindings or fields.\n- Every import is qualified and aliased: import Std.Json as Json.\n\nBefore answering with Pudu code, check it with pudu_check, format it with pudu_format, and lint it with pudu_lint. Look up library functions with pudu_reference_search and pudu_module_reference rather than guessing their names."181920export fn all() -> Array[PromptSpec] {21 [22 PromptSpec{name: "pudu_write_code", title: "Write Pudu code", description: "Write Pudu code for a task, following the language's conventions and verifying it with the compiler.", arguments: [23 PromptArgument{name: "task", description: "What the code should do.", required: true}24 ] },25 PromptSpec{name: "pudu_fix_diagnostics", title: "Fix Pudu diagnostics", description: "Fix the compiler diagnostics in a piece of Pudu code.", arguments: [26 PromptArgument{name: "source", description: "The Pudu code.", required: true},27 PromptArgument{name: "diagnostics", description: "The diagnostics to fix; pudu_check answers them when omitted.", required: false}28 ] },29 PromptSpec{name: "pudu_review", title: "Review Pudu code", description: "Review Pudu code for correctness, idiom, and failure handling.", arguments: [30 PromptArgument{name: "source", description: "The Pudu code to review.", required: true}31 ] },32 PromptSpec{name: "pudu_explain", title: "Explain a Pudu topic", description: "Explain a Pudu language topic from its documentation, with an example that compiles.", arguments: [33 PromptArgument{name: "topic", description: "The topic, such as 'ownership' or 'traits'.", required: true}34 ] }35 ]36}373839export fn toJson(spec: &PromptSpec) -> Json.Json {40 Json.object(&[41 ("name", Json.Text(spec.name)),42 ("title", Json.Text(spec.title)),43 ("description", Json.Text(spec.description)),44 ("arguments", Json.list(&spec.arguments.map(|held: PromptArgument| Json.object(&[45 ("name", Json.Text(held.name)),46 ("description", Json.Text(held.description)),47 ("required", Json.Boolean(held.required))48 ]))))49 ])50}515253export fn render(name: Str, arguments: &Json.Json) -> Result[(Str, Str), RpcError.ProtocolError] {54 let spec = match List.find(&all(), |held: PromptSpec| held.name == name) {55 case Some(found) => found56 case None => { return Err(RpcError.InvalidParams("unknown prompt: " + name)) }57 }58 var values: Array[(Str, Str)] = []59 for held in spec.arguments {60 match Access.member(arguments, held.name) {61 case Some(Json.Text(text)) => {62 if held.required && Text.isBlank(text) { return Err(RpcError.InvalidParams("prompt argument '" + held.name + "' must not be empty")) }63 values = values.push((held.name, text))64 }65 case Some(_) => { return Err(RpcError.InvalidParams("prompt argument '" + held.name + "' must be a string")) }66 case None => {67 if held.required { return Err(RpcError.InvalidParams("missing prompt argument '" + held.name + "'")) }68 values = values.push((held.name, ""))69 }70 }71 }72 let value = |key: Str| match List.lookup(&values, key) { case Some(text) => text case None => "" }73 let body = if name == "pudu_write_code" {74 "Write Pudu code for this task:\n\n" + value("task") + "\n\nSearch the documentation with pudu_docs_search when a construct is unfamiliar."75 } else if name == "pudu_fix_diagnostics" {76 let given = value("diagnostics")77 let reported = if given.isEmpty() { "Run pudu_check on it to get the diagnostics." } else { "The diagnostics are:\n\n```\n" + given + "\n```" }78 "Fix this Pudu code so it compiles without diagnostics:\n\n```pudu\n" + value("source") + "\n```\n\n" + reported + "\n\nExplain each change in one sentence."79 } else if name == "pudu_review" {80 "Review this Pudu code. Report correctness problems first, then failure handling (Result and Option use), then idiom, each with the line and a concrete change:\n\n```pudu\n" + value("source") + "\n```"81 } else {82 "Explain the Pudu topic '" + value("topic") + "'. Read the relevant documentation with pudu_docs_search and pudu_docs_read first, and end with a short example checked with pudu_check."83 }84 Ok((spec.description, body + "\n\n" + CONVENTIONS))85}86