@canonical/pragma-cli 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/pragma +0 -0
- package/package.json +19 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +15 -6
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.test.ts +10 -1
- package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
- package/src/capabilities/capabilities/catalog.ts +20 -8
- package/src/capabilities/capabilities/hints.ts +21 -36
- package/src/capabilities/capabilities/types.ts +2 -0
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.render.test.ts +31 -4
- package/src/capabilities/colophon/colophon.render.ts +13 -4
- package/src/capabilities/colophon/colophon.test.ts +3 -3
- package/src/capabilities/colophon/colophon.verb.ts +11 -4
- package/src/capabilities/colophon/markdownTerminal.ts +33 -14
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +10 -108
- package/src/capabilities/config/set.test.ts +6 -6
- package/src/capabilities/config/set.verb.ts +12 -8
- package/src/capabilities/config/show.render.test.ts +137 -0
- package/src/capabilities/config/show.render.ts +57 -16
- package/src/capabilities/config/show.verb.ts +9 -8
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
- package/src/capabilities/create/byteEquality.test.ts +82 -101
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +192 -16
- package/src/capabilities/create/create.verb.ts +208 -79
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
- package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
- package/src/capabilities/doctor/doctor.render.test.ts +170 -0
- package/src/capabilities/doctor/doctor.render.ts +117 -34
- package/src/capabilities/doctor/doctor.test.ts +68 -9
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +33 -9
- package/src/capabilities/doctor/types.ts +16 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/index.ts +10 -3
- package/src/capabilities/ontology/ontology.test.ts +157 -5
- package/src/capabilities/ontology/verbs.ts +203 -104
- package/src/capabilities/prompt/prompt.render.test.ts +94 -0
- package/src/capabilities/prompt/prompt.render.ts +23 -6
- package/src/capabilities/prompt/prompt.test.ts +68 -1
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/reference.test.ts +32 -0
- package/src/capabilities/resources/provider.ts +58 -25
- package/src/capabilities/resources/resources.test.ts +176 -21
- package/src/capabilities/scopeTypeSync.test.ts +37 -0
- package/src/capabilities/setup/index.ts +7 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
- package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
- package/src/capabilities/setup/operations/setupLsp.ts +56 -12
- package/src/capabilities/setup/operations/setupMcp.ts +216 -39
- package/src/capabilities/setup/operations/setupSkills.ts +10 -4
- package/src/capabilities/setup/setup.render.test.ts +109 -0
- package/src/capabilities/setup/setup.render.ts +61 -14
- package/src/capabilities/setup/setup.test.ts +352 -1
- package/src/capabilities/setup/setup.verb.ts +68 -9
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/setup/types.ts +59 -2
- package/src/capabilities/shared/bands.ts +16 -0
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +69 -49
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +146 -98
- package/src/capabilities/sources/sources.test.ts +264 -84
- package/src/capabilities/sources/status.render.test.ts +94 -0
- package/src/capabilities/sources/status.render.ts +55 -33
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +65 -17
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +9 -7
- package/src/constants.ts +49 -23
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
- package/src/kernel/completion/complete.test.ts +3 -5
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +131 -113
- package/src/kernel/completion/model.test.ts +3 -3
- package/src/kernel/completion/model.ts +11 -1
- package/src/kernel/completion/parse.test.ts +2 -4
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +5 -5
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/renderError.ts +1 -1
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +60 -11
- package/src/kernel/project/cli/buildProgram.ts +11 -0
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/globalFlags.test.ts +12 -4
- package/src/kernel/project/cli/globalFlags.ts +14 -14
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +109 -107
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +12 -7
- package/src/kernel/project/mcp/mcpError.ts +52 -0
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
- package/src/kernel/project/mcp/prompts/provider.ts +51 -7
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/formatters.ts +5 -4
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +35 -1
- package/src/kernel/render/renderers.ts +25 -7
- package/src/kernel/render/style.test.ts +31 -0
- package/src/kernel/render/style.ts +74 -0
- package/src/kernel/runtime/graphpack/build.ts +57 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +32 -22
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +72 -45
- package/src/kernel/runtime/loadSession.ts +4 -12
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +141 -65
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +62 -0
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/types.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
- package/src/kernel/spec/emitReference.test.ts +196 -0
- package/src/kernel/spec/emitReference.ts +624 -0
- package/src/kernel/spec/emitSurface.ts +13 -7
- package/src/kernel/spec/index.ts +2 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
- package/src/testing/behavioral/completion.test.ts +21 -8
- package/src/testing/behavioral/disclosure.test.ts +12 -0
- package/src/testing/behavioral/docExamples.test.ts +283 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -73
- package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
- package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +10 -5
- package/src/testing/behavioral/staleCommands.test.ts +86 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
- package/src/testing/eval/cases/assembly.ts +16 -9
- package/src/testing/eval/cases/effectful.ts +31 -16
- package/src/testing/eval/cases/readNouns.ts +23 -18
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +201 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +179 -0
- package/src/testing/fixtures/graph/owlExport.ts +97 -0
- package/src/testing/helpers/extractSnippets.ts +29 -0
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/golden.ts +1 -1
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.verb.ts +0 -150
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -155
- package/src/capabilities/block/parity.test.ts +0 -180
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/config/field.test.ts +0 -188
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.ts +0 -34
- package/src/capabilities/tier/lookup.test.ts +0 -68
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -59
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The curated root `--help` page.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The kernel curates the nouns it ships — and only those. It names no domain:
|
|
5
|
+
* the nouns it does NOT curate lead the page untitled (the header one line up
|
|
6
|
+
* already carries the distribution's blurb) and describe themselves through
|
|
7
|
+
* their own verbs, so the front door follows the content instead of naming it.
|
|
8
|
+
* Every section is reconciled against the *live* nouns (from the registered
|
|
9
|
+
* verbs), so a noun that is not built yet is dropped and one the kernel has
|
|
10
|
+
* never heard of still surfaces.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import type { VerbSpec } from "../../spec/types.js";
|
|
@@ -24,88 +25,86 @@ interface NounSummary {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
interface HelpGroup {
|
|
27
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Absent for the leading uncurated group: its rows sit directly under the
|
|
30
|
+
* usage line, since repeating the header's blurb as a heading three lines
|
|
31
|
+
* below it says the same thing twice.
|
|
32
|
+
*/
|
|
33
|
+
readonly title?: string;
|
|
28
34
|
readonly nouns: readonly NounSummary[];
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
/**
|
|
32
|
-
*
|
|
33
|
-
* people reach for most
|
|
34
|
-
*
|
|
38
|
+
* The kernel's own nouns, grouped by task with curated summaries. Order is
|
|
39
|
+
* intentional: what people reach for most comes first; agent tooling comes last.
|
|
40
|
+
*
|
|
41
|
+
* @param programName - The CLI binary name (the distribution's `name`).
|
|
42
|
+
* @returns The task-oriented groups, in display order.
|
|
35
43
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
noun: "
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
noun
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
noun: "skill",
|
|
102
|
-
summary: "Browse agent skills from design-system packages",
|
|
103
|
-
},
|
|
104
|
-
{ noun: "prompt", summary: "Browse reusable prompt templates" },
|
|
105
|
-
{ noun: "mcp", summary: "Start the MCP server over stdio" },
|
|
106
|
-
],
|
|
107
|
-
},
|
|
108
|
-
];
|
|
44
|
+
function buildKernelGroups(programName: string): readonly HelpGroup[] {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
title: "Generate code",
|
|
48
|
+
nouns: [
|
|
49
|
+
{ noun: "create", summary: "Scaffold components, packages, and apps" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "Query & serve the graph",
|
|
54
|
+
nouns: [
|
|
55
|
+
{
|
|
56
|
+
noun: "graph",
|
|
57
|
+
summary: "Run SPARQL queries or inspect a URI directly",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
noun: "ontology",
|
|
61
|
+
summary: "Explore the loaded ontologies (classes, properties)",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: "Set up & maintain",
|
|
67
|
+
nouns: [
|
|
68
|
+
{ noun: "doctor", summary: "Check environment health" },
|
|
69
|
+
{
|
|
70
|
+
noun: "setup",
|
|
71
|
+
summary: "Configure MCP, skills, completions, and the LSP",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
noun: "sources",
|
|
75
|
+
summary: "Build and refresh the local store from packs",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
noun: "config",
|
|
79
|
+
summary: `Read and write ${programName} configuration`,
|
|
80
|
+
},
|
|
81
|
+
{ noun: "info", summary: "Show version, config, and update status" },
|
|
82
|
+
{
|
|
83
|
+
noun: "upgrade",
|
|
84
|
+
summary: `Upgrade the ${programName} CLI to the latest version`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
title: "For AI agents",
|
|
90
|
+
nouns: [
|
|
91
|
+
{
|
|
92
|
+
noun: "capabilities",
|
|
93
|
+
summary: "Discover conventions, tools, and the discovery sequence",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
noun: "colophon",
|
|
97
|
+
summary: `Read how ${programName} and the active domain are made`,
|
|
98
|
+
},
|
|
99
|
+
{ noun: "skill", summary: "Browse agent skills from the active packs" },
|
|
100
|
+
{ noun: "prompt", summary: "Browse reusable prompt templates" },
|
|
101
|
+
// The one noun with no verb to speak for it (the bin special-cases it),
|
|
102
|
+
// so its summary is hand-written rather than derived.
|
|
103
|
+
{ noun: "mcp", summary: "Start the MCP server over stdio" },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
}
|
|
109
108
|
|
|
110
109
|
/** All distinct, non-hidden top-level nouns present in the registered verbs. */
|
|
111
110
|
function nounsFrom(verbs: readonly VerbSpec[]): Set<string> {
|
|
@@ -117,16 +116,21 @@ function nounsFrom(verbs: readonly VerbSpec[]): Set<string> {
|
|
|
117
116
|
return nouns;
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Summarise an uncurated noun from its first live verb — which is the pack's own
|
|
121
|
+
* `description` whenever the pack ships a `list` (`compilePack` compiles that
|
|
122
|
+
* verb first). The terminal period a pack sentence carries is dropped so the
|
|
123
|
+
* column reads as one voice with the kernel's own fragments.
|
|
124
|
+
*/
|
|
125
|
+
function summarizeNoun(noun: string, verbs: readonly VerbSpec[]): string {
|
|
122
126
|
const first = verbs.find((v) => v.path[0] === noun && !v.hidden);
|
|
123
|
-
return first?.summary ?? `${noun} commands
|
|
127
|
+
return (first?.summary ?? `${noun} commands`).replace(/\.$/, "");
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
/**
|
|
127
131
|
* Build the curated root help string.
|
|
128
132
|
*
|
|
129
|
-
* @param programName - The CLI binary name (`
|
|
133
|
+
* @param programName - The CLI binary name (the distribution's `name`).
|
|
130
134
|
* @param description - The program description shown in the header.
|
|
131
135
|
* @param verbs - All registered verbs, used to derive the live noun set.
|
|
132
136
|
* @returns The formatted, colorized help text.
|
|
@@ -140,27 +144,23 @@ export function formatRootHelp(
|
|
|
140
144
|
// `mcp` is served by the bin's special-case, not a projected verb, but is
|
|
141
145
|
// always available — surface it so the front door is complete.
|
|
142
146
|
present.add("mcp");
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
);
|
|
147
|
+
const kernel = buildKernelGroups(programName);
|
|
148
|
+
const curated = new Set(kernel.flatMap((g) => g.nouns.map((n) => n.noun)));
|
|
146
149
|
|
|
147
|
-
const uncatalogued = [...present].filter((n) => !catalogued.has(n)).sort();
|
|
148
150
|
const groups: HelpGroup[] = [
|
|
149
|
-
|
|
151
|
+
// Everything the kernel's curated table does not claim — in the shipped
|
|
152
|
+
// binary the bundled domain packs, in a fork whatever it ships. Untitled
|
|
153
|
+
// and first, so the domain leads the page without the kernel naming it.
|
|
154
|
+
{
|
|
155
|
+
nouns: [...present]
|
|
156
|
+
.filter((n) => !curated.has(n))
|
|
157
|
+
.sort()
|
|
158
|
+
.map((noun) => ({ noun, summary: summarizeNoun(noun, verbs) })),
|
|
159
|
+
},
|
|
160
|
+
...kernel.map((g) => ({
|
|
150
161
|
...g,
|
|
151
162
|
nouns: g.nouns.filter((n) => present.has(n.noun)),
|
|
152
163
|
})),
|
|
153
|
-
...(uncatalogued.length > 0
|
|
154
|
-
? [
|
|
155
|
-
{
|
|
156
|
-
title: "Other",
|
|
157
|
-
nouns: uncatalogued.map((noun) => ({
|
|
158
|
-
noun,
|
|
159
|
-
summary: fallbackSummary(noun, verbs),
|
|
160
|
-
})),
|
|
161
|
-
},
|
|
162
|
-
]
|
|
163
|
-
: []),
|
|
164
164
|
].filter((g) => g.nouns.length > 0);
|
|
165
165
|
|
|
166
166
|
// One column width across ALL groups so the noun column aligns section to
|
|
@@ -180,7 +180,7 @@ export function formatRootHelp(
|
|
|
180
180
|
];
|
|
181
181
|
|
|
182
182
|
for (const group of groups) {
|
|
183
|
-
lines.push(helpHeading(group.title));
|
|
183
|
+
if (group.title) lines.push(helpHeading(group.title));
|
|
184
184
|
lines.push(
|
|
185
185
|
...helpColumns(
|
|
186
186
|
group.nouns.map((n) => [n.noun, n.summary] as const),
|
|
@@ -195,8 +195,10 @@ export function formatRootHelp(
|
|
|
195
195
|
// never these strings.
|
|
196
196
|
lines.push(helpHeading("Global flags"));
|
|
197
197
|
const flags: [string, string][] = [
|
|
198
|
-
[
|
|
199
|
-
|
|
198
|
+
[
|
|
199
|
+
"--format <plain|llm|json>",
|
|
200
|
+
"Select output format (llm = condensed Markdown for agents)",
|
|
201
|
+
],
|
|
200
202
|
[
|
|
201
203
|
"--detail <level>",
|
|
202
204
|
"Progressive-disclosure level (summary, standard, detailed)",
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Build the MCP server from the grammar.
|
|
3
3
|
*
|
|
4
|
-
* Constructs an {@link McpServer}
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Constructs an {@link McpServer} whose wire identity PROJECTS from the
|
|
5
|
+
* distribution: `serverInfo.name` is the declared distribution name (the same
|
|
6
|
+
* projection that names the CLI bin, so existing agent wiring resolves it) and
|
|
7
|
+
* `serverInfo.version` the package version — a fork's server introduces itself
|
|
8
|
+
* under the fork's own name with no code change. The covenant records the rule
|
|
9
|
+
* (`surface.v2.json` `mcpSurface.serverInfo`) and `identity.test.ts` pins it on
|
|
10
|
+
* the wire from a fork's config. Then registers every exposed verb as a tool
|
|
11
|
+
* and each module's optional `mcpResources` surface (the graph resource
|
|
12
|
+
* browser, wired below via `module.mcpResources?.register`). One runtime is
|
|
13
|
+
* booted for the server's lifetime and shared by every tool; the store stays
|
|
14
|
+
* lazy, booting only when a resource read needs it.
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* It carries the same content the retired `llm` tool front-loaded: the
|
|
6
6
|
* conventions + the discovery sequence. To guarantee it never diverges from the
|
|
7
|
-
* `capabilities` tool,
|
|
7
|
+
* `capabilities` tool, the WHOLE orientation — the opening line included —
|
|
8
|
+
* derives from the SAME source: `CONVENTIONS` and
|
|
8
9
|
* `buildDiscoverySequence` in `capabilities/capabilities/catalog.ts`. Live
|
|
9
10
|
* numbers (tier/channel/entity counts) are deliberately DROPPED (they would need
|
|
10
11
|
* a store boot at handshake); agents fetch those via `info`/`config show`/
|
|
@@ -15,8 +16,8 @@
|
|
|
15
16
|
import {
|
|
16
17
|
buildDiscoverySequence,
|
|
17
18
|
CONVENTIONS,
|
|
18
|
-
liveTools,
|
|
19
19
|
} from "../../../capabilities/capabilities/catalog.js";
|
|
20
|
+
import { emitSurface } from "../../spec/emitSurface.js";
|
|
20
21
|
import type { CapabilityModule } from "../../spec/types.js";
|
|
21
22
|
|
|
22
23
|
/** Hard ceiling on the instructions length (asserted by the protected test). */
|
|
@@ -31,18 +32,22 @@ export const INSTRUCTIONS_MAX_CHARS = 1500;
|
|
|
31
32
|
export function buildInstructions(
|
|
32
33
|
modules: readonly CapabilityModule[],
|
|
33
34
|
): string {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// Tools AND resource templates come from the one emitted surface: the
|
|
36
|
+
// `<scheme>:{+uri}` template is declared by the module that serves it and
|
|
37
|
+
// frozen in the covenant, so quoting it here rather than deriving it would be
|
|
38
|
+
// a second copy of a string the kernel does not own.
|
|
39
|
+
const { tools, resources } = emitSurface(modules).mcpSurface;
|
|
40
|
+
const steps = buildDiscoverySequence(tools)
|
|
36
41
|
.map((stage) => `${stage.stage}. ${stage.tool} — ${stage.purpose}`)
|
|
37
42
|
.join("\n");
|
|
43
|
+
const templates = resources.map((template) => `\`${template}\``).join(", ");
|
|
38
44
|
|
|
39
45
|
return [
|
|
40
|
-
|
|
41
|
-
`${CONVENTIONS.system} ${CONVENTIONS.model} ${CONVENTIONS.querying}`,
|
|
46
|
+
`${CONVENTIONS.system} ${CONVENTIONS.model} ${CONVENTIONS.querying} ${CONVENTIONS.mutations}`,
|
|
42
47
|
"",
|
|
43
48
|
"Discovery sequence:",
|
|
44
49
|
steps,
|
|
45
50
|
"",
|
|
46
|
-
|
|
51
|
+
`Call the \`capabilities\` tool for the full annotated tool catalog; read ${templates} resources (or \`graph_inspect\`) for entity detail.`,
|
|
47
52
|
].join("\n");
|
|
48
53
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project a {@link PragmaError} into an SDK `McpError` for the NATIVE MCP
|
|
3
|
+
* surfaces (the `prompts/*` provider, the `pragma:{+uri}` resource browser),
|
|
4
|
+
* which signal failure by throwing a JSON-RPC error rather than by returning the
|
|
5
|
+
* `{ ok, error }` tool envelope.
|
|
6
|
+
*
|
|
7
|
+
* A native read must not swallow a failure into plain-text "success" content: it
|
|
8
|
+
* carries the machine `code` AND the structured `recovery` into the JSON-RPC
|
|
9
|
+
* `data`, so an agent that hits a cold store over `prompts/list` or a resource
|
|
10
|
+
* read sees the SAME `STORE_UNAVAILABLE` + `sources_update` recovery the tool
|
|
11
|
+
* envelope carries — never a bare string with the recovery dropped.
|
|
12
|
+
*
|
|
13
|
+
* The SDK's `McpError`/`ErrorCode` are PASSED IN by the caller (which already
|
|
14
|
+
* dynamic-imported them in its `register`), so this module — reachable on the
|
|
15
|
+
* capabilities import graph — never statically pulls the SDK onto the
|
|
16
|
+
* `--help`/`__complete` fast path.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import type { PragmaError } from "../../error/PragmaError.js";
|
|
21
|
+
|
|
22
|
+
/** The SDK error primitives a provider dynamic-imports and hands to {@link mcpErrorFrom}. */
|
|
23
|
+
export interface McpErrorPrimitives {
|
|
24
|
+
readonly McpError: typeof McpError;
|
|
25
|
+
readonly ErrorCode: typeof ErrorCode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build an `McpError` from a {@link PragmaError}, mapping the machine code to a
|
|
30
|
+
* JSON-RPC code and preserving `code`/`recovery`/`suggestions`/`validOptions` in
|
|
31
|
+
* `data`.
|
|
32
|
+
*
|
|
33
|
+
* @param error - The structured pragma error to project.
|
|
34
|
+
* @param primitives - The SDK `McpError`/`ErrorCode` the caller imported.
|
|
35
|
+
* @returns An `McpError` carrying the diagnostic + recovery in its `data`.
|
|
36
|
+
*/
|
|
37
|
+
export function mcpErrorFrom(
|
|
38
|
+
error: PragmaError,
|
|
39
|
+
{ McpError: McpErrorCtor, ErrorCode: ErrorCodeEnum }: McpErrorPrimitives,
|
|
40
|
+
): McpError {
|
|
41
|
+
// A bad/missing entity or rejected input is the caller's fault (InvalidParams);
|
|
42
|
+
// an unreachable store or internal fault is the server's (InternalError).
|
|
43
|
+
const jsonRpcCode =
|
|
44
|
+
error.code === "ENTITY_NOT_FOUND" || error.code === "INVALID_INPUT"
|
|
45
|
+
? ErrorCodeEnum.InvalidParams
|
|
46
|
+
: ErrorCodeEnum.InternalError;
|
|
47
|
+
const data: Record<string, unknown> = { code: error.code };
|
|
48
|
+
if (error.recovery) data.recovery = error.recovery;
|
|
49
|
+
if (error.suggestions.length > 0) data.suggestions = error.suggestions;
|
|
50
|
+
if (error.validOptions) data.validOptions = error.validOptions;
|
|
51
|
+
return new McpErrorCtor(jsonRpcCode, error.message, data);
|
|
52
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdtempSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
2
4
|
import { describe, expect, it } from "vitest";
|
|
3
5
|
import {
|
|
4
6
|
fixtureEffectsModule,
|
|
7
|
+
fixturePreviewModule,
|
|
5
8
|
touchPath,
|
|
6
9
|
} from "../../../testing/fixtures/fixtureCapability.js";
|
|
7
10
|
import { projectMcp } from "../../../testing/helpers/projectMcp.js";
|
|
@@ -70,3 +73,38 @@ describe("MCP mutation plan-first / confirm (PROTECTED)", () => {
|
|
|
70
73
|
expect(existsSync(touchPath(name))).toBe(true);
|
|
71
74
|
});
|
|
72
75
|
});
|
|
76
|
+
|
|
77
|
+
describe("MCP plan-first is honest (PRA-104)", () => {
|
|
78
|
+
it("returns the ERROR a confirmed run would hit, not a confident plan", async () => {
|
|
79
|
+
// The CLI twin of this guard lives in cli/dispatch.test.ts: the same
|
|
80
|
+
// mutation, the same missing source, exiting nonzero instead of printing a
|
|
81
|
+
// plan. Both surfaces run one interpreter, so they cannot drift.
|
|
82
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-graft-mcp-"));
|
|
83
|
+
const missing = join(dir, "gone");
|
|
84
|
+
|
|
85
|
+
const mcp = await projectMcp([fixturePreviewModule], dir);
|
|
86
|
+
const planned = await mcp.callTool("probe_graft", { source: missing });
|
|
87
|
+
await mcp.cleanup();
|
|
88
|
+
|
|
89
|
+
expect(planned.ok).toBe(false);
|
|
90
|
+
expect(JSON.stringify(planned.error)).toContain(missing);
|
|
91
|
+
expect(existsSync(`${missing}.grafted`)).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("returns a plan, and writes nothing, when the read would succeed", async () => {
|
|
95
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-graft-mcp-"));
|
|
96
|
+
const source = join(dir, "present.txt");
|
|
97
|
+
writeFileSync(source, "content\n");
|
|
98
|
+
|
|
99
|
+
const mcp = await projectMcp([fixturePreviewModule], dir);
|
|
100
|
+
const planned = await mcp.callTool("probe_graft", { source });
|
|
101
|
+
await mcp.cleanup();
|
|
102
|
+
|
|
103
|
+
expect(planned.ok).toBe(true);
|
|
104
|
+
expect(planned.meta).toEqual({ planOnly: true, confirmRequired: true });
|
|
105
|
+
expect((planned.data as { plan: string[] }).plan).toContain(
|
|
106
|
+
`Write file: ${source}.grafted (8 bytes)`,
|
|
107
|
+
);
|
|
108
|
+
expect(readdirSync(dir)).toEqual(["present.txt"]);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { mkdtempSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
5
|
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { CONVENTIONS } from "../../../../capabilities/capabilities/catalog.js";
|
|
5
7
|
import { capabilities } from "../../../../capabilities/index.js";
|
|
6
8
|
import type { McpHarness } from "../../../../testing/helpers/projectMcp.js";
|
|
7
9
|
import { projectMcp } from "../../../../testing/helpers/projectMcp.js";
|
|
10
|
+
import type { PragmaRuntime } from "../../../runtime/types.js";
|
|
11
|
+
import { emitSurface } from "../../../spec/emitSurface.js";
|
|
8
12
|
import type { CapabilityModule } from "../../../spec/types.js";
|
|
9
13
|
import { buildInstructions, INSTRUCTIONS_MAX_CHARS } from "../instructions.js";
|
|
10
14
|
import { fillTemplate, promptProvider } from "./provider.js";
|
|
15
|
+
import { readPrompts } from "./source.js";
|
|
11
16
|
|
|
12
17
|
const freshCwd = (): string => mkdtempSync(join(tmpdir(), "pragma-prompts-"));
|
|
13
18
|
|
|
@@ -39,14 +44,33 @@ describe("instructions — handshake orientation (PROTECTED)", () => {
|
|
|
39
44
|
);
|
|
40
45
|
});
|
|
41
46
|
|
|
42
|
-
it("
|
|
43
|
-
// The
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
it("opens with the shared catalog's conventions, verbatim and once", () => {
|
|
48
|
+
// The conventions are authored once (capabilities/catalog.ts); instructions
|
|
49
|
+
// must OPEN with them, so a second hand-written preamble cannot creep back
|
|
50
|
+
// in and drift from the `capabilities` tool the same handshake carries.
|
|
51
|
+
expect(buildInstructions(capabilities).startsWith(CONVENTIONS.system)).toBe(
|
|
52
|
+
true,
|
|
47
53
|
);
|
|
48
54
|
});
|
|
49
55
|
|
|
56
|
+
it("quotes the resource templates the MCP surface actually advertises", () => {
|
|
57
|
+
// The `pragma:` scheme is covenant-frozen protocol identity
|
|
58
|
+
// (surface.v2.json). The orientation DERIVES it from the emitted surface
|
|
59
|
+
// instead of hand-copying it, so the two cannot disagree.
|
|
60
|
+
const { resources } = emitSurface(capabilities).mcpSurface;
|
|
61
|
+
expect(resources.length).toBeGreaterThan(0);
|
|
62
|
+
const text = buildInstructions(capabilities);
|
|
63
|
+
for (const template of resources) expect(text).toContain(template);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("states the plan-first/confirm convention in the orientation (D2)", () => {
|
|
67
|
+
// The confirm gate must be surfaced at handshake, not discovered by an agent
|
|
68
|
+
// tripping it. Same single source as the `capabilities` tool's conventions.
|
|
69
|
+
const text = buildInstructions(capabilities);
|
|
70
|
+
expect(text.toLowerCase()).toContain("plan-first");
|
|
71
|
+
expect(text).toContain("confirm: true");
|
|
72
|
+
});
|
|
73
|
+
|
|
50
74
|
it("orients a cold agent to check/build the store before any store read", () => {
|
|
51
75
|
// Store-blind guard: the handshake must point a cold agent at the store
|
|
52
76
|
// pre-check (sources_status → sources_update) BEFORE the sample/query steps,
|
|
@@ -83,6 +107,105 @@ describe("MCP handshake — capabilities advertised (PROTECTED)", () => {
|
|
|
83
107
|
});
|
|
84
108
|
});
|
|
85
109
|
|
|
110
|
+
/** A runtime whose SPARQL facade always fails with `message`. */
|
|
111
|
+
function createFailingRuntime(message: string): PragmaRuntime {
|
|
112
|
+
return {
|
|
113
|
+
query: {
|
|
114
|
+
sparql: () => Promise.reject(new Error(message)),
|
|
115
|
+
},
|
|
116
|
+
} as unknown as PragmaRuntime;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Every field at the default layer — the embedded pack answers, so the store IS available. */
|
|
120
|
+
const DEFAULT_ORIGINS = {
|
|
121
|
+
tier: "default",
|
|
122
|
+
channel: "default",
|
|
123
|
+
detail: "default",
|
|
124
|
+
packs: "default",
|
|
125
|
+
stories: "default",
|
|
126
|
+
prefixes: "default",
|
|
127
|
+
} as const;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The same failing facade, wearing the shape `promptProvider.register` needs:
|
|
131
|
+
* a cwd whose store is AVAILABLE (so `guardStore` passes and the read is what
|
|
132
|
+
* fails) plus a `loadConfig` the readiness check can resolve.
|
|
133
|
+
*/
|
|
134
|
+
function createFailingProviderRuntime(
|
|
135
|
+
cwd: string,
|
|
136
|
+
message: string,
|
|
137
|
+
): PragmaRuntime {
|
|
138
|
+
return {
|
|
139
|
+
cwd,
|
|
140
|
+
loadConfig: async () => ({
|
|
141
|
+
config: { channel: "normal" as const },
|
|
142
|
+
origins: DEFAULT_ORIGINS,
|
|
143
|
+
global: { path: "/nonexistent", exists: false },
|
|
144
|
+
project: { exists: false },
|
|
145
|
+
}),
|
|
146
|
+
query: { sparql: () => Promise.reject(new Error(message)) },
|
|
147
|
+
} as unknown as PragmaRuntime;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
describe("native prompts/get — a failed read reaches the agent as one", () => {
|
|
151
|
+
it("carries the machine code and recovery, not a bare Error", async () => {
|
|
152
|
+
// The native surface signals failure by THROWING, so a read that fails must
|
|
153
|
+
// be projected the way the resource browser projects its own — with `code`
|
|
154
|
+
// and `recovery` in the JSON-RPC `data`. Otherwise the store diagnosis is
|
|
155
|
+
// lost between `prompt_lookup` (which envelopes it) and `prompts/get`.
|
|
156
|
+
const handlers = new Map<unknown, (request: unknown) => Promise<unknown>>();
|
|
157
|
+
const stubServer = {
|
|
158
|
+
server: {
|
|
159
|
+
registerCapabilities: () => {},
|
|
160
|
+
setRequestHandler: (
|
|
161
|
+
schema: unknown,
|
|
162
|
+
handler: (request: unknown) => Promise<unknown>,
|
|
163
|
+
) => {
|
|
164
|
+
handlers.set(schema, handler);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
await promptProvider.register(
|
|
169
|
+
stubServer as never,
|
|
170
|
+
createFailingProviderRuntime(freshCwd(), "Prefix not found: ds"),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const get = handlers.get(GetPromptRequestSchema);
|
|
174
|
+
expect(get).toBeDefined();
|
|
175
|
+
await expect(
|
|
176
|
+
get?.({ params: { name: "build-a-block" } }),
|
|
177
|
+
).rejects.toMatchObject({
|
|
178
|
+
data: {
|
|
179
|
+
code: "STORE_UNAVAILABLE",
|
|
180
|
+
recovery: { mcp: { tool: "sources_update" } },
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("readPrompts — a failed read is never an empty graph", () => {
|
|
187
|
+
it("reports an unbound prefix as a store that cannot answer, not as no prompts", async () => {
|
|
188
|
+
// A pack whose store does not know the declared prompt namespace cannot
|
|
189
|
+
// answer, and the actionable form of that is STORE_UNAVAILABLE with the
|
|
190
|
+
// build command — not a silent zero-prompt listing.
|
|
191
|
+
await expect(
|
|
192
|
+
readPrompts(createFailingRuntime("Prefix not found: ds")),
|
|
193
|
+
).rejects.toMatchObject({
|
|
194
|
+
code: "STORE_UNAVAILABLE",
|
|
195
|
+
recovery: { mcp: { tool: "sources_update" } },
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("propagates a query failure instead of returning no prompts", async () => {
|
|
200
|
+
// The exact defect a bare `catch {}` here caused: a malformed declaration
|
|
201
|
+
// becomes a parse error, the parse error becomes `[]`, and the user is told
|
|
202
|
+
// the distribution ships no prompts. It must surface as a failure.
|
|
203
|
+
await expect(
|
|
204
|
+
readPrompts(createFailingRuntime("SPARQL syntax error at line 2")),
|
|
205
|
+
).rejects.toThrow(/syntax error/);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
86
209
|
describe("fillTemplate — argument substitution", () => {
|
|
87
210
|
it("replaces {{arg}} placeholders and leaves unknowns intact", () => {
|
|
88
211
|
expect(
|