@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.
Files changed (295) hide show
  1. package/README.md +72 -0
  2. package/dist/pragma +0 -0
  3. package/package.json +19 -15
  4. package/pragma.conf.ts +664 -0
  5. package/src/bin.ts +15 -6
  6. package/src/capabilities/block.parity.test.ts +250 -0
  7. package/src/capabilities/capabilities/capabilities.render.ts +3 -2
  8. package/src/capabilities/capabilities/capabilities.test.ts +10 -1
  9. package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
  10. package/src/capabilities/capabilities/catalog.ts +20 -8
  11. package/src/capabilities/capabilities/hints.ts +21 -36
  12. package/src/capabilities/capabilities/types.ts +2 -0
  13. package/src/capabilities/colophon/collectColophon.ts +34 -19
  14. package/src/capabilities/colophon/colophon.render.test.ts +31 -4
  15. package/src/capabilities/colophon/colophon.render.ts +13 -4
  16. package/src/capabilities/colophon/colophon.test.ts +3 -3
  17. package/src/capabilities/colophon/colophon.verb.ts +11 -4
  18. package/src/capabilities/colophon/markdownTerminal.ts +33 -14
  19. package/src/capabilities/colophon/types.ts +16 -7
  20. package/src/capabilities/config/collectConfigShow.test.ts +57 -0
  21. package/src/capabilities/config/collectConfigShow.ts +41 -4
  22. package/src/capabilities/config/fields.ts +10 -108
  23. package/src/capabilities/config/set.test.ts +6 -6
  24. package/src/capabilities/config/set.verb.ts +12 -8
  25. package/src/capabilities/config/show.render.test.ts +137 -0
  26. package/src/capabilities/config/show.render.ts +57 -16
  27. package/src/capabilities/config/show.verb.ts +9 -8
  28. package/src/capabilities/config/types.ts +8 -0
  29. package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
  30. package/src/capabilities/create/byteEquality.test.ts +82 -101
  31. package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
  32. package/src/capabilities/create/constants.ts +75 -0
  33. package/src/capabilities/create/create.mcp.test.ts +2 -2
  34. package/src/capabilities/create/create.test.ts +192 -16
  35. package/src/capabilities/create/create.verb.ts +208 -79
  36. package/src/capabilities/create/index.ts +2 -10
  37. package/src/capabilities/create/pickGenerator.ts +34 -28
  38. package/src/capabilities/create/templates.embedded.generated.ts +4 -29
  39. package/src/capabilities/create/types.ts +10 -6
  40. package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
  41. package/src/capabilities/distribution.test.ts +120 -0
  42. package/src/capabilities/distribution.ts +59 -0
  43. package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
  44. package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
  45. package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
  46. package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
  47. package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
  48. package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
  49. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
  50. package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
  51. package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
  52. package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
  53. package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
  54. package/src/capabilities/doctor/doctor.render.test.ts +170 -0
  55. package/src/capabilities/doctor/doctor.render.ts +117 -34
  56. package/src/capabilities/doctor/doctor.test.ts +68 -9
  57. package/src/capabilities/doctor/doctor.verb.ts +6 -2
  58. package/src/capabilities/doctor/runChecks.ts +33 -9
  59. package/src/capabilities/doctor/types.ts +16 -1
  60. package/src/capabilities/emptyRecovery.test.ts +32 -15
  61. package/src/capabilities/graph/inspect.verb.ts +3 -2
  62. package/src/capabilities/graph/query.verb.ts +3 -2
  63. package/src/capabilities/graph/runQuery.ts +8 -5
  64. package/src/capabilities/index.ts +22 -18
  65. package/src/capabilities/info/collectInfo.ts +10 -6
  66. package/src/capabilities/info/info.render.ts +3 -2
  67. package/src/capabilities/info/info.verb.ts +3 -2
  68. package/src/capabilities/lazy.test.ts +93 -2
  69. package/src/capabilities/ontology/index.ts +10 -3
  70. package/src/capabilities/ontology/ontology.test.ts +157 -5
  71. package/src/capabilities/ontology/verbs.ts +203 -104
  72. package/src/capabilities/prompt/prompt.render.test.ts +94 -0
  73. package/src/capabilities/prompt/prompt.render.ts +23 -6
  74. package/src/capabilities/prompt/prompt.test.ts +68 -1
  75. package/src/capabilities/prompt/verbs.ts +24 -11
  76. package/src/capabilities/reference.test.ts +32 -0
  77. package/src/capabilities/resources/provider.ts +58 -25
  78. package/src/capabilities/resources/resources.test.ts +176 -21
  79. package/src/capabilities/scopeTypeSync.test.ts +37 -0
  80. package/src/capabilities/setup/index.ts +7 -1
  81. package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
  82. package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
  83. package/src/capabilities/setup/operations/setupLsp.ts +56 -12
  84. package/src/capabilities/setup/operations/setupMcp.ts +216 -39
  85. package/src/capabilities/setup/operations/setupSkills.ts +10 -4
  86. package/src/capabilities/setup/setup.render.test.ts +109 -0
  87. package/src/capabilities/setup/setup.render.ts +61 -14
  88. package/src/capabilities/setup/setup.test.ts +352 -1
  89. package/src/capabilities/setup/setup.verb.ts +68 -9
  90. package/src/capabilities/setup/shell.ts +29 -3
  91. package/src/capabilities/setup/types.ts +59 -2
  92. package/src/capabilities/shared/bands.ts +16 -0
  93. package/src/capabilities/skill/discover.ts +11 -5
  94. package/src/capabilities/skill/render.ts +4 -5
  95. package/src/capabilities/skill/verbs.ts +7 -7
  96. package/src/capabilities/sources/collectStatus.ts +69 -49
  97. package/src/capabilities/sources/index.ts +1 -1
  98. package/src/capabilities/sources/installSkills.ts +2 -2
  99. package/src/capabilities/sources/runUpdate.ts +146 -98
  100. package/src/capabilities/sources/sources.test.ts +264 -84
  101. package/src/capabilities/sources/status.render.test.ts +94 -0
  102. package/src/capabilities/sources/status.render.ts +55 -33
  103. package/src/capabilities/sources/status.verb.ts +15 -8
  104. package/src/capabilities/sources/types.ts +18 -25
  105. package/src/capabilities/sources/update.render.ts +9 -2
  106. package/src/capabilities/sources/update.verb.ts +12 -23
  107. package/src/capabilities/surface.test.ts +65 -17
  108. package/src/capabilities/tier.parity.test.ts +144 -0
  109. package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
  110. package/src/constants.test.ts +9 -7
  111. package/src/constants.ts +49 -23
  112. package/src/identity.test.ts +333 -0
  113. package/src/index.ts +2 -2
  114. package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
  115. package/src/kernel/completion/complete.test.ts +3 -5
  116. package/src/kernel/completion/emitScripts.test.ts +1 -1
  117. package/src/kernel/completion/emitScripts.ts +4 -2
  118. package/src/kernel/completion/entitySource.test.ts +185 -41
  119. package/src/kernel/completion/entitySource.ts +131 -113
  120. package/src/kernel/completion/model.test.ts +3 -3
  121. package/src/kernel/completion/model.ts +11 -1
  122. package/src/kernel/completion/parse.test.ts +2 -4
  123. package/src/kernel/completion/safety.test.ts +82 -36
  124. package/src/kernel/completion/shellDrive.test.ts +592 -0
  125. package/src/kernel/completion/templates/fish.ts +28 -6
  126. package/src/kernel/completion/types.ts +3 -3
  127. package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
  128. package/src/kernel/config/defaults.test.ts +85 -0
  129. package/src/kernel/config/defaults.ts +12 -23
  130. package/src/kernel/config/evaluateProjectConfig.ts +2 -3
  131. package/src/kernel/config/findProjectConfig.ts +4 -3
  132. package/src/kernel/config/firstRun.ts +12 -5
  133. package/src/kernel/config/globalConfig.ts +2 -2
  134. package/src/kernel/config/index.ts +4 -5
  135. package/src/kernel/config/paths.ts +17 -17
  136. package/src/kernel/config/readConfig.test.ts +242 -6
  137. package/src/kernel/config/readConfig.ts +8 -8
  138. package/src/kernel/config/schema.test.ts +50 -0
  139. package/src/kernel/config/schema.ts +97 -10
  140. package/src/kernel/config/types.ts +73 -19
  141. package/src/kernel/copy.test.ts +453 -0
  142. package/src/kernel/error/PragmaError.test.ts +5 -5
  143. package/src/kernel/error/constants.ts +3 -2
  144. package/src/kernel/error/index.ts +1 -1
  145. package/src/kernel/error/recovery.test.ts +22 -45
  146. package/src/kernel/error/recovery.ts +22 -27
  147. package/src/kernel/error/renderError.ts +1 -1
  148. package/src/kernel/error/types.ts +5 -4
  149. package/src/kernel/packs/collect.test.ts +195 -11
  150. package/src/kernel/packs/collect.ts +213 -58
  151. package/src/kernel/packs/compile.test.ts +163 -7
  152. package/src/kernel/packs/compile.ts +15 -12
  153. package/src/kernel/packs/disclosure.test.ts +5 -2
  154. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
  155. package/src/kernel/packs/graphqlSource.test.ts +6 -3
  156. package/src/kernel/packs/iri.ts +5 -7
  157. package/src/kernel/packs/renderPack.ts +5 -3
  158. package/src/kernel/packs/resolveEntity.ts +21 -17
  159. package/src/kernel/packs/runBodies.ts +15 -14
  160. package/src/kernel/packs/schema.ts +96 -10
  161. package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
  162. package/src/kernel/packs/sparql/runSelect.ts +73 -34
  163. package/src/kernel/packs/toolDescription.test.ts +10 -2
  164. package/src/kernel/packs/types.ts +39 -4
  165. package/src/kernel/packs/uniqueness.ts +4 -4
  166. package/src/kernel/project/cli/buildProgram.test.ts +60 -11
  167. package/src/kernel/project/cli/buildProgram.ts +11 -0
  168. package/src/kernel/project/cli/dispatch.test.ts +67 -0
  169. package/src/kernel/project/cli/dispatch.ts +34 -12
  170. package/src/kernel/project/cli/globalFlags.test.ts +12 -4
  171. package/src/kernel/project/cli/globalFlags.ts +14 -14
  172. package/src/kernel/project/cli/helpFormat.ts +1 -1
  173. package/src/kernel/project/cli/rootHelp.ts +109 -107
  174. package/src/kernel/project/mcp/buildServer.ts +11 -6
  175. package/src/kernel/project/mcp/instructions.ts +12 -7
  176. package/src/kernel/project/mcp/mcpError.ts +52 -0
  177. package/src/kernel/project/mcp/parity.test.ts +39 -1
  178. package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
  179. package/src/kernel/project/mcp/prompts/provider.ts +51 -7
  180. package/src/kernel/project/mcp/prompts/source.ts +68 -67
  181. package/src/kernel/project/mcp/registerVerb.ts +25 -6
  182. package/src/kernel/project/mcp/serve.ts +11 -3
  183. package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
  184. package/src/kernel/render/disclosure.ts +4 -2
  185. package/src/kernel/render/formatters.ts +5 -4
  186. package/src/kernel/render/prefixes.test.ts +36 -0
  187. package/src/kernel/render/prefixes.ts +40 -13
  188. package/src/kernel/render/renderers.test.ts +35 -1
  189. package/src/kernel/render/renderers.ts +25 -7
  190. package/src/kernel/render/style.test.ts +31 -0
  191. package/src/kernel/render/style.ts +74 -0
  192. package/src/kernel/runtime/graphpack/build.ts +57 -3
  193. package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
  194. package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
  195. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
  196. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
  197. package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
  198. package/src/kernel/runtime/graphpack/embedded.ts +37 -11
  199. package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
  200. package/src/kernel/runtime/graphpack/hash.ts +3 -2
  201. package/src/kernel/runtime/graphpack/index.ts +1 -1
  202. package/src/kernel/runtime/graphpack/manifest.ts +114 -12
  203. package/src/kernel/runtime/graphpack/read.ts +32 -22
  204. package/src/kernel/runtime/graphpack/schemas.ts +74 -0
  205. package/src/kernel/runtime/graphpack/stories.ts +84 -0
  206. package/src/kernel/runtime/graphpack/types.ts +72 -45
  207. package/src/kernel/runtime/loadSession.ts +4 -12
  208. package/src/kernel/runtime/paths.ts +64 -15
  209. package/src/kernel/runtime/probe.test.ts +8 -4
  210. package/src/kernel/runtime/readEntity.ts +6 -5
  211. package/src/kernel/runtime/refs/gitOps.ts +1 -19
  212. package/src/kernel/runtime/refs/parseRef.ts +7 -7
  213. package/src/kernel/runtime/refs/resolve.ts +141 -65
  214. package/src/kernel/runtime/resolveSources.test.ts +63 -24
  215. package/src/kernel/runtime/resolveSources.ts +36 -25
  216. package/src/kernel/runtime/storeReadiness.ts +62 -0
  217. package/src/kernel/runtime/storeless.test.ts +1 -1
  218. package/src/kernel/runtime/types.ts +1 -1
  219. package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
  220. package/src/kernel/spec/emitReference.test.ts +196 -0
  221. package/src/kernel/spec/emitReference.ts +624 -0
  222. package/src/kernel/spec/emitSurface.ts +13 -7
  223. package/src/kernel/spec/index.ts +2 -0
  224. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  225. package/src/kernel/spec/surfaceConformance.ts +8 -0
  226. package/src/kernel/spec/types.ts +29 -9
  227. package/src/kernel/spec/validate.ts +2 -1
  228. package/src/kernel/vocabulary.test.ts +164 -0
  229. package/src/kernel/vocabulary.ts +146 -0
  230. package/src/testing/PARITY_GAPS.ts +9 -3
  231. package/src/testing/behavioral/README.md +3 -3
  232. package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
  233. package/src/testing/behavioral/completion.test.ts +21 -8
  234. package/src/testing/behavioral/disclosure.test.ts +12 -0
  235. package/src/testing/behavioral/docExamples.test.ts +283 -0
  236. package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
  237. package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
  238. package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
  239. package/src/testing/behavioral/harness.foundation.test.ts +14 -5
  240. package/src/testing/behavioral/journeys.cli.test.ts +116 -73
  241. package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
  242. package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
  243. package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
  244. package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
  245. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
  246. package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
  247. package/src/testing/behavioral/liveReadSurface.ts +4 -3
  248. package/src/testing/behavioral/mcpSurface.test.ts +10 -5
  249. package/src/testing/behavioral/staleCommands.test.ts +86 -0
  250. package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
  251. package/src/testing/eval/cases/assembly.ts +16 -9
  252. package/src/testing/eval/cases/effectful.ts +31 -16
  253. package/src/testing/eval/cases/readNouns.ts +23 -18
  254. package/src/testing/eval/cases/stable.ts +5 -1
  255. package/src/testing/eval/eval.test.ts +7 -1
  256. package/src/testing/fixtures/fixtureCapability.ts +201 -1
  257. package/src/testing/fixtures/graph/canonical.ts +6 -3
  258. package/src/testing/fixtures/graph/defaultPack.ts +179 -0
  259. package/src/testing/fixtures/graph/owlExport.ts +97 -0
  260. package/src/testing/helpers/extractSnippets.ts +29 -0
  261. package/src/testing/helpers/fixtureGraph.ts +33 -16
  262. package/src/testing/helpers/golden.ts +1 -1
  263. package/src/testing/helpers/packRuntime.ts +2 -3
  264. package/src/testing/helpers/projectCli.ts +2 -1
  265. package/src/testing/helpers/projectMcp.ts +6 -0
  266. package/src/testing/perf/budgets.test.ts +4 -0
  267. package/src/testing/perf/budgets.ts +22 -7
  268. package/src/testing/perf/globalSetup.ts +121 -6
  269. package/src/types.ts +2 -2
  270. package/src/capabilities/block/blockList.verb.ts +0 -150
  271. package/src/capabilities/block/index.ts +0 -25
  272. package/src/capabilities/block/pack.ts +0 -155
  273. package/src/capabilities/block/parity.test.ts +0 -180
  274. package/src/capabilities/block/tierChain.ts +0 -78
  275. package/src/capabilities/colophon/pragmaColophon.ts +0 -66
  276. package/src/capabilities/config/field.test.ts +0 -188
  277. package/src/capabilities/modifier/index.ts +0 -15
  278. package/src/capabilities/modifier/pack.ts +0 -64
  279. package/src/capabilities/standard/index.ts +0 -18
  280. package/src/capabilities/standard/pack.ts +0 -128
  281. package/src/capabilities/tier/index.ts +0 -19
  282. package/src/capabilities/tier/lookup.render.ts +0 -34
  283. package/src/capabilities/tier/lookup.test.ts +0 -68
  284. package/src/capabilities/tier/lookup.verb.ts +0 -44
  285. package/src/capabilities/tier/pack.ts +0 -28
  286. package/src/capabilities/tier/runLookup.ts +0 -59
  287. package/src/capabilities/token/addConfig.render.ts +0 -27
  288. package/src/capabilities/token/addConfig.test.ts +0 -62
  289. package/src/capabilities/token/addConfig.verb.ts +0 -40
  290. package/src/capabilities/token/index.ts +0 -19
  291. package/src/capabilities/token/pack.ts +0 -58
  292. package/src/capabilities/token/runAddConfig.ts +0 -95
  293. package/src/kernel/completion/bashDrive.test.ts +0 -73
  294. package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
  295. package/src/kernel/runtime/lock.ts +0 -90
@@ -1,17 +1,38 @@
1
1
  /**
2
- * `ontology list` and `ontology show <prefix>` — schema (TBox) inspection.
2
+ * `ontology list` and `ontology lookup <prefix>` — schema (TBox) inspection.
3
3
  *
4
- * `list` groups loaded namespaces by prefix with class/property counts; `show`
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` or a `--class` focus, its
7
- * properties. `--full-uris` shows absolute IRIs; `--class <name>` narrows to one
8
- * class and the properties whose domain is that class.
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 { VerbSpec } from "../../kernel/spec/types.js";
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
- message: "List loaded ontology namespaces.",
37
- cli: "pragma ontology list",
38
- mcp: { tool: "ontology_list" },
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
- message: "List loaded ontologies.",
48
- cli: "pragma ontology list",
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: "pragma ontology list" }],
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
- const showVerb: VerbSpec<Record<string, unknown>, OntologyShowData> = {
77
- path: ["ontology", "show"],
78
- summary: "Show a namespace's classes (hierarchy + counts) and properties.",
79
- params: [
80
- {
81
- kind: "string",
82
- name: "prefix",
83
- doc: "The namespace prefix (ds) or full URI.",
84
- positional: true,
85
- required: true,
86
- complete: { kind: "names", source: { from: "prefixes" } },
87
- },
88
- {
89
- kind: "boolean",
90
- name: "properties",
91
- doc: "Include the properties section.",
92
- },
93
- {
94
- kind: "boolean",
95
- name: "fullUris",
96
- doc: "Show full IRIs instead of prefixed.",
97
- },
98
- {
99
- kind: "string",
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
- run: async (params: Record<string, unknown>, rt: PragmaRuntime) => {
119
- const session = await rt.store.get();
120
- const { prefix, namespace } = resolvePrefix(
121
- String(params.prefix),
122
- session.prefixes,
123
- );
124
- const focus = typeof params.class === "string" ? params.class : undefined;
125
- const wantProperties = params.properties === true || focus !== undefined;
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
- let classes = await queryClasses(
128
- rt,
129
- namespace,
130
- session.index.instanceCountByType,
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
- let properties = wantProperties ? await queryProperties(rt, namespace) : [];
194
+ }
133
195
 
134
- if (focus !== undefined) {
135
- const needle = focus.toLowerCase();
136
- classes = classes.filter(
137
- (c) =>
138
- localName(c.uri).toLowerCase() === needle ||
139
- c.label.toLowerCase() === needle,
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
- if (classes.length === 0 && properties.length === 0) {
148
- throw PragmaError.notFound("ontology", String(params.prefix), {
149
- recovery: {
150
- message: "List loaded ontologies.",
151
- cli: "pragma ontology list",
152
- mcp: { tool: "ontology_list" },
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
- return {
158
- prefix,
159
- namespace,
160
- classes,
161
- properties,
162
- fullUris: params.fullUris === true,
163
- ...(focus !== undefined ? { focus } : {}),
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
- export const ontologyShowVerb = asVerb(showVerb);
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
- const lines = ["# Prompts"];
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
- const lines = [data.name];
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(` args: ${argTokens(data.arguments)}`, "", data.body);
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
- const lines = [`# ${data.name}`];
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("", "## Arguments");
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("", "## Template", data.body);
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(runtime);
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: "Browse the ds:Prompt entities in the active graph — name, description, and argument names. The same prompts are offered natively over MCP prompts/list; use prompt_lookup for the full template body.",
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: "pragma prompt list" }],
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 ds:Prompt entity's full template body (with {{arg}} placeholders) and its declared arguments.",
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 (e.g. build-a-block).",
66
+ doc: "The prompt name, as `prompt list` reports it.",
64
67
  positional: true,
65
68
  required: true,
66
- complete: { kind: "names", source: { from: "prompts" } },
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
- examples: [{ cmd: "pragma prompt lookup build-a-block" }],
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
- message: "List available prompts.",
82
- cli: "pragma prompt list",
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;