@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,17 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `ontology list` and `ontology
|
|
2
|
+
* `ontology list` and `ontology lookup <prefix>` — schema (TBox) inspection.
|
|
3
3
|
*
|
|
4
|
-
* `list` groups loaded namespaces by prefix with class/property counts; `
|
|
4
|
+
* `list` groups loaded namespaces by prefix with class/property counts; `lookup`
|
|
5
5
|
* renders a namespace's class hierarchy (with per-class instance counts read
|
|
6
|
-
* from the pack index) and, with `--properties
|
|
7
|
-
* properties. `--full-uris` shows absolute IRIs;
|
|
8
|
-
* class and the properties whose domain is that
|
|
6
|
+
* from the pack index) and, with `--properties`, a `--class` focus, or a
|
|
7
|
+
* `--detail` of `standard`+, its properties. `--full-uris` shows absolute IRIs;
|
|
8
|
+
* `--class <name>` narrows to one class and the properties whose domain is that
|
|
9
|
+
* class.
|
|
10
|
+
*
|
|
11
|
+
* `ontology show` is the DEPRECATED alias of `ontology lookup` (AV-228 B1): the
|
|
12
|
+
* verb was renamed to `lookup` for consistency with block/standard/tier/etc.
|
|
13
|
+
* (the ratified by-name read name), and `show` is kept callable — both share the
|
|
14
|
+
* same params, disclosure, and handler, and both stay blessed in the covenant.
|
|
15
|
+
*
|
|
16
|
+
* Disclosure (B5): rather than reinvent its own scheme, the by-name read declares
|
|
17
|
+
* the canonical disclosure ladder and honours the `--detail`/config `detail` the
|
|
18
|
+
* rest of the CLI reads AND the injected `detail` tool param the MCP projector
|
|
19
|
+
* derives from that declaration — a two-behaviour fold where `summary` is
|
|
20
|
+
* classes-only and `standard`/`detailed` add the properties section. The frozen
|
|
21
|
+
* `--properties` flag stays as an explicit override (it forces the section at
|
|
22
|
+
* any level), so the covenant is untouched.
|
|
9
23
|
*/
|
|
10
24
|
|
|
25
|
+
import { BIN_NAME } from "../../constants.js";
|
|
11
26
|
import { PragmaError } from "../../kernel/error/PragmaError.js";
|
|
27
|
+
import { cliRecovery } from "../../kernel/error/recovery.js";
|
|
28
|
+
import { resolvePackDetail } from "../../kernel/packs/disclosure.js";
|
|
12
29
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
13
30
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
14
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
DisclosureSpec,
|
|
33
|
+
ParamSpec,
|
|
34
|
+
VerbSpec,
|
|
35
|
+
} from "../../kernel/spec/types.js";
|
|
15
36
|
import {
|
|
16
37
|
listNamespaces,
|
|
17
38
|
localName,
|
|
@@ -32,22 +53,22 @@ function resolvePrefix(
|
|
|
32
53
|
if (entry) return { prefix: entry[0], namespace: entry[1] };
|
|
33
54
|
throw PragmaError.invalidInput("namespace", input, {
|
|
34
55
|
validOptions: Object.values(prefixes),
|
|
35
|
-
recovery:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
recovery: cliRecovery(
|
|
57
|
+
"ontology list",
|
|
58
|
+
"List loaded ontology namespaces.",
|
|
59
|
+
{
|
|
60
|
+
tool: "ontology_list",
|
|
61
|
+
},
|
|
62
|
+
),
|
|
40
63
|
});
|
|
41
64
|
}
|
|
42
65
|
const namespace = prefixes[input];
|
|
43
66
|
if (namespace === undefined) {
|
|
44
67
|
throw PragmaError.invalidInput("prefix", input, {
|
|
45
68
|
validOptions: Object.keys(prefixes),
|
|
46
|
-
recovery: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
mcp: { tool: "ontology_list" },
|
|
50
|
-
},
|
|
69
|
+
recovery: cliRecovery("ontology list", "List loaded ontologies.", {
|
|
70
|
+
tool: "ontology_list",
|
|
71
|
+
}),
|
|
51
72
|
});
|
|
52
73
|
}
|
|
53
74
|
return { prefix: input, namespace };
|
|
@@ -58,7 +79,7 @@ const listVerb: VerbSpec<Record<string, unknown>, OntologySummary[]> = {
|
|
|
58
79
|
summary: "List loaded ontology namespaces with class and property counts.",
|
|
59
80
|
params: [],
|
|
60
81
|
output: { formatters: ontologyListFormatters },
|
|
61
|
-
examples: [{ cmd:
|
|
82
|
+
examples: [{ cmd: `${BIN_NAME} ontology list` }],
|
|
62
83
|
capability: {
|
|
63
84
|
needsStore: true,
|
|
64
85
|
mutates: false,
|
|
@@ -73,98 +94,176 @@ const listVerb: VerbSpec<Record<string, unknown>, OntologySummary[]> = {
|
|
|
73
94
|
},
|
|
74
95
|
};
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
name: "class",
|
|
101
|
-
doc: "Focus on one class and its properties.",
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
output: { formatters: ontologyShowFormatters },
|
|
105
|
-
examples: [
|
|
106
|
-
{ cmd: "pragma ontology show ds" },
|
|
107
|
-
{ cmd: "pragma ontology show ds --properties" },
|
|
108
|
-
{ cmd: "pragma ontology show ds --class Component" },
|
|
109
|
-
],
|
|
110
|
-
capability: {
|
|
111
|
-
needsStore: true,
|
|
112
|
-
mutates: false,
|
|
113
|
-
mcp: {
|
|
114
|
-
expose: true,
|
|
115
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
116
|
-
},
|
|
97
|
+
/**
|
|
98
|
+
* The canonical disclosure the by-name read folds onto (B5). Declared on both
|
|
99
|
+
* the primary {@link ontologyLookupVerb} and its deprecated {@link
|
|
100
|
+
* ontologyShowVerb} alias — so the MCP projector injects the `detail` enum tool
|
|
101
|
+
* param and its per-call `withDetail` seeding light up (symmetric with
|
|
102
|
+
* `block`/`standard`, which an inline resolve left dark over MCP) — AND read by
|
|
103
|
+
* `run` to resolve the effective level, so the advertised param and the fetched
|
|
104
|
+
* level share ONE source. The levels are the full canonical ladder; the fold is
|
|
105
|
+
* two-behaviour (`summary` = classes only, `standard`/`detailed` add properties).
|
|
106
|
+
*/
|
|
107
|
+
const BY_NAME_DISCLOSURE: DisclosureSpec = {
|
|
108
|
+
levels: ["summary", "standard", "detailed"],
|
|
109
|
+
default: "summary",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** The params shared by `ontology lookup` and its deprecated `ontology show` alias. */
|
|
113
|
+
const BY_NAME_PARAMS: ParamSpec[] = [
|
|
114
|
+
{
|
|
115
|
+
kind: "string",
|
|
116
|
+
name: "prefix",
|
|
117
|
+
doc: "The namespace prefix (ds) or full URI.",
|
|
118
|
+
positional: true,
|
|
119
|
+
required: true,
|
|
120
|
+
complete: { kind: "names", source: { from: "prefixes" } },
|
|
117
121
|
},
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
{
|
|
123
|
+
kind: "boolean",
|
|
124
|
+
name: "properties",
|
|
125
|
+
doc: "Include the properties section (also implied by --detail standard or higher).",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
kind: "boolean",
|
|
129
|
+
name: "fullUris",
|
|
130
|
+
doc: "Show full IRIs instead of prefixed.",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
kind: "string",
|
|
134
|
+
name: "class",
|
|
135
|
+
doc: "Focus on one class and its properties.",
|
|
136
|
+
},
|
|
137
|
+
];
|
|
126
138
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
/** The by-name read capability — a storeless-free, read-only MCP tool. */
|
|
140
|
+
const BY_NAME_CAPABILITY = {
|
|
141
|
+
needsStore: true,
|
|
142
|
+
mutates: false,
|
|
143
|
+
mcp: {
|
|
144
|
+
expose: true as const,
|
|
145
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The by-name read handler shared by `ontology lookup` and `ontology show`.
|
|
151
|
+
*
|
|
152
|
+
* @param params - The coerced param bag (`prefix`, `properties`, `fullUris`, `class`).
|
|
153
|
+
* @param rt - The runtime (store + disclosure precedence).
|
|
154
|
+
* @returns The namespace's classes (and, per disclosure, properties) payload.
|
|
155
|
+
*/
|
|
156
|
+
async function runByName(
|
|
157
|
+
params: Record<string, unknown>,
|
|
158
|
+
rt: PragmaRuntime,
|
|
159
|
+
): Promise<OntologyShowData> {
|
|
160
|
+
const session = await rt.store.get();
|
|
161
|
+
const { prefix, namespace } = resolvePrefix(
|
|
162
|
+
String(params.prefix),
|
|
163
|
+
session.prefixes,
|
|
164
|
+
);
|
|
165
|
+
const focus = typeof params.class === "string" ? params.class : undefined;
|
|
166
|
+
// Fold ontology's bespoke disclosure onto the canonical `--detail` (B5),
|
|
167
|
+
// resolving through the SAME {@link BY_NAME_DISCLOSURE} the verb declares (so
|
|
168
|
+
// the MCP `detail` param and its withDetail seeding resolve identically, not
|
|
169
|
+
// just the CLI `--detail` flag) and the SAME precedence packs use (flag >
|
|
170
|
+
// explicit config > default). The frozen `--properties` flag and a `--class`
|
|
171
|
+
// focus still force the section, so honouring the level is covenant-safe.
|
|
172
|
+
const level = await resolvePackDetail(rt, BY_NAME_DISCLOSURE);
|
|
173
|
+
const wantProperties =
|
|
174
|
+
params.properties === true || focus !== undefined || level !== "summary";
|
|
175
|
+
|
|
176
|
+
let classes = await queryClasses(
|
|
177
|
+
rt,
|
|
178
|
+
namespace,
|
|
179
|
+
session.index.instanceCountByType,
|
|
180
|
+
);
|
|
181
|
+
let properties = wantProperties ? await queryProperties(rt, namespace) : [];
|
|
182
|
+
|
|
183
|
+
if (focus !== undefined) {
|
|
184
|
+
const needle = focus.toLowerCase();
|
|
185
|
+
classes = classes.filter(
|
|
186
|
+
(c) =>
|
|
187
|
+
localName(c.uri).toLowerCase() === needle ||
|
|
188
|
+
c.label.toLowerCase() === needle,
|
|
189
|
+
);
|
|
190
|
+
const focusUris = new Set(classes.map((c) => c.uri));
|
|
191
|
+
properties = properties.filter(
|
|
192
|
+
(p) => p.domain !== undefined && focusUris.has(p.domain),
|
|
131
193
|
);
|
|
132
|
-
|
|
194
|
+
}
|
|
133
195
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const focusUris = new Set(classes.map((c) => c.uri));
|
|
142
|
-
properties = properties.filter(
|
|
143
|
-
(p) => p.domain !== undefined && focusUris.has(p.domain),
|
|
144
|
-
);
|
|
145
|
-
}
|
|
196
|
+
if (classes.length === 0 && properties.length === 0) {
|
|
197
|
+
throw PragmaError.notFound("ontology", String(params.prefix), {
|
|
198
|
+
recovery: cliRecovery("ontology list", "List loaded ontologies.", {
|
|
199
|
+
tool: "ontology_list",
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
146
203
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
204
|
+
return {
|
|
205
|
+
prefix,
|
|
206
|
+
namespace,
|
|
207
|
+
classes,
|
|
208
|
+
properties,
|
|
209
|
+
fullUris: params.fullUris === true,
|
|
210
|
+
...(focus !== undefined ? { focus } : {}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
156
213
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Build one by-name read verb (`lookup` primary or `show` deprecated alias) —
|
|
216
|
+
* both share {@link BY_NAME_PARAMS}, {@link BY_NAME_DISCLOSURE}, the formatters,
|
|
217
|
+
* and {@link runByName}; only the path label, summary, doc, and examples differ.
|
|
218
|
+
*/
|
|
219
|
+
function byNameVerb(
|
|
220
|
+
verb: "lookup" | "show",
|
|
221
|
+
summary: string,
|
|
222
|
+
examples: VerbSpec["examples"],
|
|
223
|
+
doc?: string,
|
|
224
|
+
): VerbSpec<Record<string, unknown>, OntologyShowData> {
|
|
225
|
+
return {
|
|
226
|
+
path: ["ontology", verb],
|
|
227
|
+
summary,
|
|
228
|
+
...(doc !== undefined ? { doc } : {}),
|
|
229
|
+
params: BY_NAME_PARAMS,
|
|
230
|
+
output: { formatters: ontologyShowFormatters },
|
|
231
|
+
examples,
|
|
232
|
+
disclosure: BY_NAME_DISCLOSURE,
|
|
233
|
+
capability: BY_NAME_CAPABILITY,
|
|
234
|
+
run: runByName,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
167
237
|
|
|
168
|
-
/** The `ontology` verbs (`list`, `show`). */
|
|
238
|
+
/** The `ontology` verbs (`list`, `lookup`, and the deprecated `show` alias). */
|
|
169
239
|
export const ontologyListVerb = asVerb(listVerb);
|
|
170
|
-
|
|
240
|
+
|
|
241
|
+
/** `ontology lookup <prefix>` — the primary by-name schema read. */
|
|
242
|
+
export const ontologyLookupVerb = asVerb(
|
|
243
|
+
byNameVerb(
|
|
244
|
+
"lookup",
|
|
245
|
+
"Look up a namespace's classes (hierarchy + counts) and properties.",
|
|
246
|
+
[
|
|
247
|
+
{ cmd: `${BIN_NAME} ontology lookup ds` },
|
|
248
|
+
{ cmd: `${BIN_NAME} ontology lookup ds --properties` },
|
|
249
|
+
{ cmd: `${BIN_NAME} ontology lookup ds --class Component` },
|
|
250
|
+
],
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* `ontology show <prefix>` — DEPRECATED alias of {@link ontologyLookupVerb}
|
|
256
|
+
* (AV-228 B1). Kept callable and blessed in the covenant; shares the same
|
|
257
|
+
* handler, so it behaves identically. New callers should use `ontology lookup`.
|
|
258
|
+
*/
|
|
259
|
+
export const ontologyShowVerb = asVerb(
|
|
260
|
+
byNameVerb(
|
|
261
|
+
"show",
|
|
262
|
+
"(deprecated: use `ontology lookup`) Show a namespace's classes (hierarchy + counts) and properties.",
|
|
263
|
+
[
|
|
264
|
+
{ cmd: `${BIN_NAME} ontology lookup ds`, note: "prefer `lookup`" },
|
|
265
|
+
{ cmd: `${BIN_NAME} ontology show ds`, note: "deprecated alias" },
|
|
266
|
+
],
|
|
267
|
+
"Deprecated alias of `ontology lookup` — retained for compatibility. Prefer `ontology lookup <prefix>`.",
|
|
268
|
+
),
|
|
269
|
+
);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
promptListFormatters,
|
|
5
|
+
promptLookupFormatters,
|
|
6
|
+
} from "./prompt.render.js";
|
|
7
|
+
import type { PromptListData, PromptLookupData } from "./types.js";
|
|
8
|
+
|
|
9
|
+
/** Run `body` with stdout's `isTTY` forced to `value`, then restore it. */
|
|
10
|
+
function withStdoutTty(value: boolean | undefined, body: () => void): void {
|
|
11
|
+
const stream = process.stdout as { isTTY?: boolean };
|
|
12
|
+
const saved = stream.isTTY;
|
|
13
|
+
stream.isTTY = value;
|
|
14
|
+
try {
|
|
15
|
+
body();
|
|
16
|
+
} finally {
|
|
17
|
+
stream.isTTY = saved;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const LOOKUP: PromptLookupData = {
|
|
22
|
+
name: "build-a-block",
|
|
23
|
+
description: "Scaffold a block",
|
|
24
|
+
arguments: [
|
|
25
|
+
{ name: "blockName", required: true, description: "The block name" },
|
|
26
|
+
{ name: "tier", required: false },
|
|
27
|
+
],
|
|
28
|
+
body: "Create {{blockName}} in {{tier}}.",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const LIST: PromptListData = {
|
|
32
|
+
prompts: [
|
|
33
|
+
{ name: "build-a-block", description: "Scaffold a block", arguments: [] },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe("promptLookupFormatters", () => {
|
|
38
|
+
it("underlines the plain name with an ═ rule (B7)", () => {
|
|
39
|
+
const lines = promptLookupFormatters.plain(LOOKUP).split("\n");
|
|
40
|
+
expect(lines.at(0)).toBe("build-a-block");
|
|
41
|
+
expect(lines.at(1)).toBe("═".repeat(24));
|
|
42
|
+
expect(lines.at(2)).toBe("");
|
|
43
|
+
expect(lines).toContain(" args: blockName (required), tier");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("heads the llm entity at H2 with H3 sub-sections (B2)", () => {
|
|
47
|
+
const lines = promptLookupFormatters.llm(LOOKUP).split("\n");
|
|
48
|
+
expect(lines.at(0)).toBe("## build-a-block");
|
|
49
|
+
expect(lines).toContain("### Arguments");
|
|
50
|
+
expect(lines).toContain("### Template");
|
|
51
|
+
// The old H1 title / H2 sub-sections are gone.
|
|
52
|
+
expect(lines).not.toContain("# build-a-block");
|
|
53
|
+
expect(lines).not.toContain("## Arguments");
|
|
54
|
+
expect(lines).not.toContain("## Template");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("promptListFormatters", () => {
|
|
59
|
+
it("heads the llm list at H2, never H1 (B2)", () => {
|
|
60
|
+
const out = promptListFormatters.llm(LIST);
|
|
61
|
+
expect(out.startsWith("## Prompts")).toBe(true);
|
|
62
|
+
expect(out).not.toMatch(/^# Prompts/m);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("promptLookupFormatters — shared style seam (B7)", () => {
|
|
67
|
+
const prevLevel = chalk.level;
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
chalk.level = prevLevel;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("styles the plain title on a color-capable TTY", () => {
|
|
73
|
+
chalk.level = 1;
|
|
74
|
+
withStdoutTty(true, () => {
|
|
75
|
+
// The title/rule/label now route through the same style seam block and
|
|
76
|
+
// skill lookups use — previously a prompt title stayed unstyled on a TTY.
|
|
77
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: asserting the ESC byte is the point
|
|
78
|
+
expect(promptLookupFormatters.plain(LOOKUP)).toMatch(/\x1b\[/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("keeps piped plain output byte-stable off a TTY", () => {
|
|
83
|
+
chalk.level = 3;
|
|
84
|
+
withStdoutTty(undefined, () => {
|
|
85
|
+
const out = promptLookupFormatters.plain(LOOKUP);
|
|
86
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: asserting NO ESC byte survives
|
|
87
|
+
expect(out).not.toMatch(/\x1b\[/);
|
|
88
|
+
const lines = out.split("\n");
|
|
89
|
+
expect(lines.at(0)).toBe("build-a-block");
|
|
90
|
+
expect(lines.at(1)).toBe("═".repeat(24));
|
|
91
|
+
expect(lines).toContain(" args: blockName (required), tier");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Formatters for the `prompt` content noun — plain, llm, json.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { defaultStyle } from "../../kernel/render/style.js";
|
|
5
6
|
import type { Formatters } from "../../kernel/spec/types.js";
|
|
6
7
|
import type {
|
|
7
8
|
PromptArgument,
|
|
@@ -29,7 +30,8 @@ export const promptListFormatters: Formatters<PromptListData> = {
|
|
|
29
30
|
},
|
|
30
31
|
llm(data) {
|
|
31
32
|
if (data.prompts.length === 0) return "_No prompts in the store._";
|
|
32
|
-
|
|
33
|
+
// H2 for a content read — consistent with the shared list renderer (B2).
|
|
34
|
+
const lines = ["## Prompts"];
|
|
33
35
|
for (const prompt of data.prompts) {
|
|
34
36
|
lines.push(
|
|
35
37
|
`- \`${prompt.name}\` — ${prompt.description ?? ""}`.trimEnd(),
|
|
@@ -45,23 +47,38 @@ export const promptListFormatters: Formatters<PromptListData> = {
|
|
|
45
47
|
|
|
46
48
|
export const promptLookupFormatters: Formatters<PromptLookupData> = {
|
|
47
49
|
plain(data) {
|
|
48
|
-
|
|
50
|
+
// Full delegation to `renderLookupPlain` is infeasible: the description line
|
|
51
|
+
// carries no field label and the template body is appended raw, whereas the
|
|
52
|
+
// shared renderer only emits `label: value` fields + indented sections. So
|
|
53
|
+
// route the TITLE + rule + field label through the SAME style seam it uses
|
|
54
|
+
// (bold title, dim rule, cyan label) — closing the TTY drift where a prompt
|
|
55
|
+
// title stayed unstyled — and keep the bespoke body inline. Off a TTY the
|
|
56
|
+
// styler is inert, so piped output stays byte-stable (B7).
|
|
57
|
+
const style = defaultStyle();
|
|
58
|
+
const rule = "═".repeat(Math.max(data.name.length, 24));
|
|
59
|
+
const lines = [style.bold(data.name), style.dim(rule), ""];
|
|
49
60
|
if (data.description) lines.push(` ${data.description}`);
|
|
50
|
-
lines.push(
|
|
61
|
+
lines.push(
|
|
62
|
+
` ${style.cyan("args")}: ${argTokens(data.arguments)}`,
|
|
63
|
+
"",
|
|
64
|
+
data.body,
|
|
65
|
+
);
|
|
51
66
|
return lines.join("\n");
|
|
52
67
|
},
|
|
53
68
|
llm(data) {
|
|
54
|
-
|
|
69
|
+
// H2 entity title with H3 sub-sections — the hierarchy the shared
|
|
70
|
+
// `renderLookupLlm` uses (`## title` / `### section`) (B2).
|
|
71
|
+
const lines = [`## ${data.name}`];
|
|
55
72
|
if (data.description) lines.push(data.description);
|
|
56
73
|
if (data.arguments.length > 0) {
|
|
57
|
-
lines.push("", "
|
|
74
|
+
lines.push("", "### Arguments");
|
|
58
75
|
for (const arg of data.arguments) {
|
|
59
76
|
lines.push(
|
|
60
77
|
`- \`${arg.name}\`${arg.required ? " (required)" : ""}${arg.description ? ` — ${arg.description}` : ""}`,
|
|
61
78
|
);
|
|
62
79
|
}
|
|
63
80
|
}
|
|
64
|
-
lines.push("", "
|
|
81
|
+
lines.push("", "### Template", data.body);
|
|
65
82
|
return lines.join("\n");
|
|
66
83
|
},
|
|
67
84
|
json(data) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
1
4
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
2
5
|
import { listPromptSummaries } from "../../kernel/project/mcp/prompts/source.js";
|
|
3
6
|
import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
7
|
+
import { resolveSources } from "../../kernel/runtime/resolveSources.js";
|
|
4
8
|
import type { GlobalFlags } from "../../kernel/spec/types.js";
|
|
5
9
|
import { CANONICAL_TTL } from "../../testing/fixtures/graph/canonical.js";
|
|
6
10
|
import {
|
|
@@ -112,8 +116,71 @@ describe("prompt — native MCP prompts surface (PROTECTED)", () => {
|
|
|
112
116
|
describe("prompt — storeless native listing (PROTECTED)", () => {
|
|
113
117
|
it("listing the prompts does NOT boot the store", async () => {
|
|
114
118
|
const runtime = bootRuntime(FLAGS, fixture.cwd);
|
|
115
|
-
const summaries = listPromptSummaries(
|
|
119
|
+
const summaries = listPromptSummaries(
|
|
120
|
+
resolveSources(await runtime.loadConfig(), runtime.cwd),
|
|
121
|
+
);
|
|
116
122
|
expect(summaries.map((s) => s.name).sort()).toEqual(EXPECTED);
|
|
117
123
|
expect(runtime.store.booted).toBe(false);
|
|
118
124
|
});
|
|
119
125
|
});
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* D1/D4 — cold-store convergence. The `prompt_list` tool is store-backed
|
|
129
|
+
* (`needsStore`), so a cold store throws STORE_UNAVAILABLE through its pre-check;
|
|
130
|
+
* native `prompts/list`/`prompts/get` must present the SAME diagnostic rather
|
|
131
|
+
* than silently listing a stale index (D1) or reporting a bogus "not found"
|
|
132
|
+
* (D4). A project that declares its own packs and has never built them is the
|
|
133
|
+
* cold "configured but unbuilt" store — the row of the boot decision table
|
|
134
|
+
* that must never be served the distribution's embedded graph.
|
|
135
|
+
*/
|
|
136
|
+
describe("prompt — cold-store convergence, tool + native agree (D1/D4)", () => {
|
|
137
|
+
let cold: McpHarness;
|
|
138
|
+
|
|
139
|
+
beforeAll(async () => {
|
|
140
|
+
const cwd = mkdtempSync(join(tmpdir(), "pragma-prompt-cold-"));
|
|
141
|
+
writeFileSync(
|
|
142
|
+
join(cwd, "pragma.config.ts"),
|
|
143
|
+
`export default { packs: [{ name: "unbuilt", source: "file:///pragma-never-built" }] };\n`,
|
|
144
|
+
);
|
|
145
|
+
cold = await projectMcp(capabilities, cwd);
|
|
146
|
+
});
|
|
147
|
+
afterAll(async () => {
|
|
148
|
+
await cold.cleanup();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("prompt_list (tool) and prompts/list (native) BOTH surface STORE_UNAVAILABLE", async () => {
|
|
152
|
+
const tool = await cold.callTool("prompt_list");
|
|
153
|
+
expect(tool.ok).toBe(false);
|
|
154
|
+
expect((tool.error as { code: string }).code).toBe("STORE_UNAVAILABLE");
|
|
155
|
+
// The native list no longer diverges into a silent [] / stale index listing.
|
|
156
|
+
await expect(cold.listPrompts()).rejects.toThrow(/store unavailable/i);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("both surfaces carry the same `sources_update` recovery cold", async () => {
|
|
160
|
+
const tool = await cold.callTool("prompt_list");
|
|
161
|
+
const toolRecovery = (
|
|
162
|
+
tool.error as { recovery?: { mcp?: { tool?: string } } }
|
|
163
|
+
).recovery;
|
|
164
|
+
expect(toolRecovery?.mcp?.tool).toBe("sources_update");
|
|
165
|
+
|
|
166
|
+
// Native carries the recovery in the JSON-RPC error `data` — never dropped.
|
|
167
|
+
const nativeErr = await cold.listPrompts().then(
|
|
168
|
+
() => undefined,
|
|
169
|
+
(error: unknown) => error,
|
|
170
|
+
);
|
|
171
|
+
const data = (
|
|
172
|
+
nativeErr as { data?: { recovery?: { mcp?: { tool?: string } } } }
|
|
173
|
+
).data;
|
|
174
|
+
expect(data?.recovery?.mcp?.tool).toBe("sources_update");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("native prompts/get reports STORE_UNAVAILABLE cold, not a bogus 'not found' (D4)", async () => {
|
|
178
|
+
const error = await cold.getPrompt("build-a-block").then(
|
|
179
|
+
() => undefined,
|
|
180
|
+
(caught: unknown) => caught as Error,
|
|
181
|
+
);
|
|
182
|
+
expect(error?.message).toMatch(/store unavailable/i);
|
|
183
|
+
// The genuine-miss message must NOT mask the cold store.
|
|
184
|
+
expect(error?.message).not.toMatch(/not found/i);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
* formatters).
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import { BIN_NAME } from "../../constants.js";
|
|
15
16
|
import { PragmaError } from "../../kernel/error/PragmaError.js";
|
|
17
|
+
import { cliRecovery } from "../../kernel/error/recovery.js";
|
|
16
18
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
17
19
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
18
20
|
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
21
|
+
import { VOCABULARY } from "../../kernel/vocabulary.js";
|
|
19
22
|
import {
|
|
20
23
|
promptListFormatters,
|
|
21
24
|
promptLookupFormatters,
|
|
@@ -34,10 +37,10 @@ const READ_CAPABILITY = {
|
|
|
34
37
|
const listVerb: VerbSpec<Record<string, unknown>, PromptListData> = {
|
|
35
38
|
path: ["prompt", "list"],
|
|
36
39
|
summary: "List the workflow prompt templates the design system offers.",
|
|
37
|
-
doc:
|
|
40
|
+
doc: `Browse the prompt entities the active graph declares (${VOCABULARY.prompt.type} in this distribution) — name, description, and argument names. This distribution's graph carries none today. The same prompts are offered natively over MCP prompts/list; use prompt_lookup for the full template body.`,
|
|
38
41
|
params: [],
|
|
39
42
|
output: { formatters: promptListFormatters },
|
|
40
|
-
examples: [{ cmd:
|
|
43
|
+
examples: [{ cmd: `${BIN_NAME} prompt list` }],
|
|
41
44
|
capability: READ_CAPABILITY,
|
|
42
45
|
run: (_params: Record<string, unknown>, rt: PragmaRuntime) =>
|
|
43
46
|
import("../../kernel/project/mcp/prompts/source.js").then(async (m) => {
|
|
@@ -55,19 +58,31 @@ const listVerb: VerbSpec<Record<string, unknown>, PromptListData> = {
|
|
|
55
58
|
const lookupVerb: VerbSpec<Record<string, unknown>, PromptLookupData> = {
|
|
56
59
|
path: ["prompt", "lookup"],
|
|
57
60
|
summary: "Show one workflow prompt template's body and arguments by name.",
|
|
58
|
-
doc: "Fetch a single
|
|
61
|
+
doc: "Fetch a single prompt entity's full template body (with {{arg}} placeholders) and its declared arguments. A prompt is addressed by its label; prompt_list names the ones the active graph carries.",
|
|
59
62
|
params: [
|
|
60
63
|
{
|
|
61
64
|
kind: "string",
|
|
62
65
|
name: "name",
|
|
63
|
-
doc: "The prompt name
|
|
66
|
+
doc: "The prompt name, as `prompt list` reports it.",
|
|
64
67
|
positional: true,
|
|
65
68
|
required: true,
|
|
66
|
-
|
|
69
|
+
// A prompt is addressed by its `rdfs:label`, which the index carries as
|
|
70
|
+
// `label`, and the index reader offers no substitute for a prompt that
|
|
71
|
+
// carries none. Not a total guarantee: `label` is the first of several
|
|
72
|
+
// label predicates the index accepts, so an entity labelled ONLY by
|
|
73
|
+
// `skos:prefLabel` would still complete without resolving. That gap is
|
|
74
|
+
// the index's label preference order, not this ref.
|
|
75
|
+
complete: {
|
|
76
|
+
kind: "names",
|
|
77
|
+
source: { from: "index", type: VOCABULARY.prompt.type, field: "label" },
|
|
78
|
+
},
|
|
67
79
|
},
|
|
68
80
|
],
|
|
69
81
|
output: { formatters: promptLookupFormatters },
|
|
70
|
-
|
|
82
|
+
// No example command: this distribution's graph declares no prompt, so any
|
|
83
|
+
// name quoted here exits 1 with ENTITY_NOT_FOUND — and this is the GENERATED
|
|
84
|
+
// reference, published as machine-derived truth.
|
|
85
|
+
examples: [],
|
|
71
86
|
capability: READ_CAPABILITY,
|
|
72
87
|
run: (params: Record<string, unknown>, rt: PragmaRuntime) =>
|
|
73
88
|
import("../../kernel/project/mcp/prompts/source.js").then(async (m) => {
|
|
@@ -77,11 +92,9 @@ const lookupVerb: VerbSpec<Record<string, unknown>, PromptLookupData> = {
|
|
|
77
92
|
const available = await m.readPrompts(rt);
|
|
78
93
|
throw PragmaError.notFound("prompt", name, {
|
|
79
94
|
suggestions: available.map((prompt) => prompt.name),
|
|
80
|
-
recovery: {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
mcp: { tool: "prompt_list" },
|
|
84
|
-
},
|
|
95
|
+
recovery: cliRecovery("prompt list", "List available prompts.", {
|
|
96
|
+
tool: "prompt_list",
|
|
97
|
+
}),
|
|
85
98
|
});
|
|
86
99
|
}
|
|
87
100
|
return entry;
|