@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,44 +0,0 @@
1
- /**
2
- * `tier lookup <name>` — the covenant `tier_lookup` tool.
3
- *
4
- * Bespoke store-backed lookup with a SINGLE `<name>` positional (a pack lookup
5
- * would emit the variadic `<name...>`), so it emits
6
- * `{ v: "lookup", args: ["<name>"], needsStore: true, mcp: "tier_lookup" }`.
7
- */
8
-
9
- import { asVerb } from "../../kernel/spec/asVerb.js";
10
- import type { VerbSpec } from "../../kernel/spec/types.js";
11
- import { type TierLookupData, tierLookupFormatters } from "./lookup.render.js";
12
-
13
- const tierLookupSpec: VerbSpec<Record<string, unknown>, TierLookupData> = {
14
- path: ["tier", "lookup"],
15
- summary: "Show one tier by name, with the blocks scoped to it.",
16
- doc: "Look up a single tier by its name (e.g. apps/lxd) and list the blocks scoped directly to it.",
17
- params: [
18
- {
19
- kind: "string",
20
- name: "name",
21
- doc: "The tier name (e.g. apps/lxd).",
22
- positional: true,
23
- required: true,
24
- complete: { kind: "names", source: { from: "tiers" } },
25
- },
26
- ],
27
- output: { formatters: tierLookupFormatters },
28
- examples: [{ cmd: "pragma tier lookup apps/lxd" }],
29
- capability: {
30
- needsStore: true,
31
- mutates: false,
32
- mcp: {
33
- expose: true,
34
- annotations: { readOnlyHint: true, openWorldHint: false },
35
- },
36
- },
37
- run: (params, rt) =>
38
- import("./runLookup.js").then((m) =>
39
- m.runTierLookup(rt, String(params.name)),
40
- ),
41
- };
42
-
43
- /** The `tier lookup` verb, widened for registry composition. */
44
- export const tierLookupVerb = asVerb(tierLookupSpec);
@@ -1,28 +0,0 @@
1
- /**
2
- * The bundled `tier` pack — a flat, name-ordered list story.
3
- *
4
- * Tier hierarchy is encoded in the slash-separated path string (`apps/lxd`), not
5
- * in graph edges, so `tier list` is a flat list; the ordered-inheritance logic
6
- * lives in the block list's tier chain. SPARQL-sourced (a flat-ordered list).
7
- */
8
-
9
- import type { PackDefinition } from "../../kernel/packs/types.js";
10
-
11
- export const tierPack: PackDefinition = {
12
- noun: "tier",
13
- description: "List all tiers in the design system ontology.",
14
- toolDescription:
15
- "List all tiers in the design-system ontology. Use when understanding the tier hierarchy before setting a tier filter. Example: tier_list {}.",
16
- list: {
17
- query: [
18
- "SELECT ?uri ?name WHERE {",
19
- " ?uri a ds:Tier ;",
20
- " ds:name ?name .",
21
- "} ORDER BY ?name",
22
- ].join("\n"),
23
- columns: [
24
- { field: "uri", label: "IRI" },
25
- { field: "name", label: "Name" },
26
- ],
27
- },
28
- };
@@ -1,59 +0,0 @@
1
- /**
2
- * The `tier lookup` run body (lazily imported, off the fast path).
3
- *
4
- * Bespoke (not a pack lookup): the covenant freezes `tier_lookup` with a SINGLE
5
- * `<name>` positional, whereas a pack lookup emits the variadic `<name...>`.
6
- * Resolves the tier by its `ds:name` and returns the blocks scoped directly to
7
- * it (a joined OPTIONAL, so a tier with no direct members still resolves).
8
- */
9
-
10
- import { PragmaError } from "../../kernel/error/PragmaError.js";
11
- import { runSelect } from "../../kernel/packs/sparql/runSelect.js";
12
- import { compactUri } from "../../kernel/render/compactUri.js";
13
- import { DEFAULT_PREFIX_MAP } from "../../kernel/render/prefixes.js";
14
- import type { PragmaRuntime } from "../../kernel/runtime/types.js";
15
- import type { TierLookupData } from "./lookup.render.js";
16
-
17
- /**
18
- * Look up one tier by name.
19
- *
20
- * @param rt - The runtime (its store is booted by the projector for needsStore).
21
- * @param name - The tier name (its `ds:name`, e.g. `apps/lxd`).
22
- * @returns The tier's IRI, name, and directly-scoped block names.
23
- * @throws PragmaError ENTITY_NOT_FOUND when no tier has that name;
24
- * STORE_UNAVAILABLE (exit 3) when the store is unseeded for this project.
25
- */
26
- export async function runTierLookup(
27
- rt: PragmaRuntime,
28
- name: string,
29
- ): Promise<TierLookupData> {
30
- const query = [
31
- "SELECT ?uri ?blockName WHERE {",
32
- " ?uri a ds:Tier ; ds:name ?tierName .",
33
- ` FILTER(STR(?tierName) = ${JSON.stringify(name)})`,
34
- " OPTIONAL { ?block ds:tier ?uri ; ds:name ?blockName }",
35
- "}",
36
- "ORDER BY ?blockName",
37
- ].join("\n");
38
-
39
- // Route through `runSelect` (not `rt.query.sparql` directly) so a cold/unseeded
40
- // store — a generated `ds:` query hitting an unknown prefix — is remapped to an
41
- // actionable STORE_UNAVAILABLE with `pragma sources update` recovery, instead of
42
- // a raw "Prefix not found" collapsing to INTERNAL_ERROR at the boundary.
43
- const rows = await runSelect(rt, query, "tier");
44
- const uri = rows[0]?.uri;
45
- if (!uri) {
46
- throw PragmaError.notFound("tier", name, {
47
- recovery: {
48
- message: "List available tiers.",
49
- cli: "pragma tier list",
50
- mcp: { tool: "tier_list" },
51
- },
52
- });
53
- }
54
- const blocks = rows
55
- // `blockName` rides an OPTIONAL, so the key is absent for a memberless tier.
56
- .map((row) => row.blockName as string | undefined)
57
- .filter((block): block is string => Boolean(block));
58
- return { uri: compactUri(uri, DEFAULT_PREFIX_MAP), name, blocks };
59
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Formatters for `token add-config` — plain, llm, json.
3
- */
4
-
5
- import type { Formatters } from "../../kernel/spec/types.js";
6
- import type { AddConfigResult } from "./runAddConfig.js";
7
-
8
- export const addConfigFormatters: Formatters<AddConfigResult> = {
9
- plain(data) {
10
- const lines = [
11
- `${data.alreadyExisted ? "Updated" : "Wrote"} ${data.path}`,
12
- ` ${data.tokenCount} token${data.tokenCount === 1 ? "" : "s"} in the active graph`,
13
- ` sources: ${data.sources.join(", ")}`,
14
- ];
15
- return lines.join("\n");
16
- },
17
- llm(data) {
18
- return [
19
- `${data.alreadyExisted ? "Updated" : "Wrote"} \`${data.path}\` for the terrazzo token pipeline.`,
20
- `- ${data.tokenCount} token(s) in the active graph`,
21
- `- sources: ${data.sources.map((source) => `\`${source}\``).join(", ")}`,
22
- ].join("\n");
23
- },
24
- json(data) {
25
- return JSON.stringify(data);
26
- },
27
- };
@@ -1,62 +0,0 @@
1
- import { existsSync, readFileSync, rmSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
4
- import { CANONICAL_TTL } from "../../testing/fixtures/graph/canonical.js";
5
- import {
6
- bootFixtureRuntime,
7
- type FixtureGraph,
8
- } from "../../testing/helpers/fixtureGraph.js";
9
- import type { McpHarness } from "../../testing/helpers/projectMcp.js";
10
- import { projectMcp } from "../../testing/helpers/projectMcp.js";
11
- import { capabilities } from "../index.js";
12
-
13
- let fixture: FixtureGraph;
14
- let mcp: McpHarness;
15
- let configPath: string;
16
-
17
- beforeAll(async () => {
18
- fixture = await bootFixtureRuntime({ ttl: CANONICAL_TTL });
19
- mcp = await projectMcp(capabilities, fixture.cwd);
20
- configPath = join(fixture.cwd, "tokens.config.mjs");
21
- });
22
-
23
- afterAll(async () => {
24
- await mcp.cleanup();
25
- await fixture.dispose();
26
- });
27
-
28
- beforeEach(() => {
29
- rmSync(configPath, { force: true });
30
- });
31
-
32
- describe("token_add-config — plan-first mutation (PROTECTED)", () => {
33
- it("without confirm, returns a plan and writes NOTHING", async () => {
34
- const result = await mcp.callTool("token_add-config");
35
- expect(result.ok).toBe(true);
36
- const meta = result.meta as {
37
- planOnly?: boolean;
38
- confirmRequired?: boolean;
39
- };
40
- expect(meta.planOnly).toBe(true);
41
- expect(meta.confirmRequired).toBe(true);
42
- const data = result.data as { plan: string[] };
43
- expect(data.plan.join("\n")).toMatch(/tokens\.config\.mjs/);
44
- expect(existsSync(configPath)).toBe(false);
45
- });
46
-
47
- it("with confirm, writes a terrazzo config reflecting the store token count", async () => {
48
- const result = await mcp.callTool("token_add-config", { confirm: true });
49
- expect(result.ok).toBe(true);
50
- const data = result.data as {
51
- path: string;
52
- tokenCount: number;
53
- sources: string[];
54
- };
55
- expect(existsSync(configPath)).toBe(true);
56
- // The canonical fixture holds 2 ds:Token entities.
57
- expect(data.tokenCount).toBe(2);
58
- const written = readFileSync(configPath, "utf-8");
59
- expect(written).toContain("defineConfig");
60
- expect(written).toContain("@canonical/terrazzo");
61
- });
62
- });
@@ -1,40 +0,0 @@
1
- /**
2
- * `token add-config` — generate a terrazzo `tokens.config.mjs` (the one `token`
3
- * mutation). Store-backed (needsStore, per covenant) so it can report the token
4
- * count; plan-first through the uniform mutation seam (a tool call without
5
- * `confirm` returns the write plan; `--dry-run`/`--yes` on the CLI). No params —
6
- * the covenant emits `{ v: "add-config", mutates: true, needsStore: true }`.
7
- */
8
-
9
- import type { Task } from "@canonical/task";
10
- import { asVerb } from "../../kernel/spec/asVerb.js";
11
- import type { VerbSpec } from "../../kernel/spec/types.js";
12
- import { addConfigFormatters } from "./addConfig.render.js";
13
- import type { AddConfigResult } from "./runAddConfig.js";
14
-
15
- const addConfigVerb: VerbSpec<Record<string, unknown>, AddConfigResult> = {
16
- path: ["token", "add-config"],
17
- summary: "Generate a tokens.config.mjs for the terrazzo token pipeline.",
18
- doc: "Writes a terrazzo `defineConfig` at the project root, sourcing token JSON from the configured design-system packages. Store-backed so it reports how many tokens the active graph holds. Plan-first: returns the write plan until you confirm.",
19
- params: [],
20
- output: { formatters: addConfigFormatters },
21
- examples: [
22
- { cmd: "pragma token add-config --dry-run", note: "preview the write" },
23
- { cmd: "pragma token add-config --yes", note: "write the config" },
24
- ],
25
- capability: {
26
- needsStore: true,
27
- mutates: true,
28
- mcp: {
29
- expose: true,
30
- annotations: { readOnlyHint: false, openWorldHint: false },
31
- },
32
- },
33
- run: (_params, rt) =>
34
- import("./runAddConfig.js").then((m) =>
35
- m.runAddConfig(rt),
36
- ) as unknown as Task<AddConfigResult>,
37
- };
38
-
39
- /** The `token add-config` verb, widened for registry composition. */
40
- export const tokenAddConfigVerb = asVerb(addConfigVerb);
@@ -1,19 +0,0 @@
1
- /**
2
- * The `token` capability module — the bundled SPARQL pack compiled to verbs.
3
- */
4
-
5
- import { compilePack } from "../../kernel/packs/compile.js";
6
- import { DEFAULT_PREFIX_MAP } from "../../kernel/render/prefixes.js";
7
- import type { CapabilityModule } from "../../kernel/spec/types.js";
8
- import { tokenAddConfigVerb } from "./addConfig.verb.js";
9
- import { tokenPack } from "./pack.js";
10
-
11
- /** The `token` capability module — the bundled read pack plus the `add-config`
12
- * mutation (hand-written; the pack compiler emits reads only). */
13
- export const tokenModule: CapabilityModule = {
14
- name: "token",
15
- verbs: [
16
- ...compilePack(tokenPack, "bundled:token", DEFAULT_PREFIX_MAP),
17
- tokenAddConfigVerb,
18
- ],
19
- };
@@ -1,58 +0,0 @@
1
- /**
2
- * The bundled `token` pack — design tokens as data.
3
- *
4
- * SPARQL-sourced on both verbs: there is no `ds:Token` GraphQL type to project
5
- * against when the graph ships no tokens, and the lookup reads a property path
6
- * (`ds:tokenType/rdfs:label`) only SPARQL can express. `token add-config` (a
7
- * mutation) is out of PR3; the `emptyRecovery` install hint is the story users
8
- * see on an empty store.
9
- */
10
-
11
- import type { PackDefinition } from "../../kernel/packs/types.js";
12
-
13
- export const tokenPack: PackDefinition = {
14
- noun: "token",
15
- description: "List all design tokens.",
16
- toolDescription:
17
- "List all design tokens with their type. Use when browsing which tokens exist under the active scope. Example: token_list {}.",
18
- list: {
19
- query: [
20
- "SELECT ?uri ?name ?category WHERE {",
21
- " ?uri a ds:Token ;",
22
- " ds:tokenId ?name .",
23
- " OPTIONAL {",
24
- " ?uri ds:tokenType ?type .",
25
- " ?type rdfs:label ?category .",
26
- " }",
27
- "}",
28
- "ORDER BY ?name",
29
- ].join("\n"),
30
- columns: [
31
- { field: "uri", label: "IRI" },
32
- { field: "name", label: "Name" },
33
- { field: "category", label: "Type" },
34
- ],
35
- emptyRecovery: {
36
- message:
37
- "No tokens in the store. Build it from the configured design-system packages.",
38
- cli: "pragma sources update",
39
- },
40
- },
41
- lookup: {
42
- source: "sparql",
43
- by: "ds:tokenId",
44
- type: "ds:Token",
45
- toolDescription:
46
- 'Get type and theme values for one or more design tokens by name. Use when resolving specific tokens\' light/dark values. Example: token_lookup { names: ["color.primary"] }.',
47
- fields: [
48
- { name: "category", property: "ds:tokenType/rdfs:label", label: "Type" },
49
- { name: "valueLight", property: "ds:valueLight", label: "Light value" },
50
- { name: "valueDark", property: "ds:valueDark", label: "Dark value" },
51
- ],
52
- sample: {
53
- fixedCount: true,
54
- toolDescription:
55
- "Return randomly selected complete design tokens (with theme values) as exemplars. Use BEFORE writing queries to see actual data shapes.",
56
- },
57
- },
58
- };
@@ -1,95 +0,0 @@
1
- /**
2
- * The `token add-config` write body (lazily imported, off the fast path).
3
- *
4
- * A store-backed mutation: it counts the design tokens in the active graph (so
5
- * the plan reports how many tokens the config will cover) and derives the
6
- * terrazzo token-source globs from the configured design-system packages, then
7
- * returns a Task that writes `tokens.config.mjs`. The store read happens in the
8
- * async setup; the file write is the Task's single `WriteFile` effect, so a
9
- * plan-only preview (`mutation.preview`) describes the write without performing
10
- * it. Ported from the old shell's `token add-config` (terrazzo `defineConfig`).
11
- */
12
-
13
- import { resolve } from "node:path";
14
- import { $, exists, gen, type Task, writeFile } from "@canonical/task";
15
- import type { PragmaRuntime } from "../../kernel/runtime/types.js";
16
-
17
- /** The default token source when no design-system packages are configured. */
18
- const DEFAULT_TOKEN_SOURCE =
19
- "node_modules/@canonical/ds-global/tokens/**/*.json";
20
-
21
- /** The result of generating a terrazzo tokens config. */
22
- export interface AddConfigResult {
23
- /** The absolute path written. */
24
- readonly path: string;
25
- /** How many `ds:Token` entities the active graph holds. */
26
- readonly tokenCount: number;
27
- /** The token-source globs written into the config. */
28
- readonly sources: readonly string[];
29
- /** Whether a `tokens.config.mjs` already existed (it is overwritten). */
30
- readonly alreadyExisted: boolean;
31
- }
32
-
33
- /** Count the `ds:Token` entities in the booted store (best-effort). */
34
- async function countTokens(rt: PragmaRuntime): Promise<number> {
35
- try {
36
- const result = (await rt.query.sparql(
37
- "SELECT (COUNT(DISTINCT ?token) AS ?count) WHERE { ?token a ds:Token }",
38
- )) as { type?: string; bindings?: { count?: string }[] };
39
- const raw = result.bindings?.[0]?.count;
40
- return raw ? Number(raw) : 0;
41
- } catch {
42
- return 0;
43
- }
44
- }
45
-
46
- /** Derive the terrazzo token-source globs from the configured packages. */
47
- async function resolveSources(rt: PragmaRuntime): Promise<string[]> {
48
- const { config } = await rt.loadConfig();
49
- const packages = config.packages ?? [];
50
- if (packages.length === 0) return [DEFAULT_TOKEN_SOURCE];
51
- return packages.map((pkg) => {
52
- const name = typeof pkg === "string" ? pkg : pkg.name;
53
- return `node_modules/${name}/tokens/**/*.json`;
54
- });
55
- }
56
-
57
- /** Render the terrazzo `tokens.config.mjs` content. */
58
- function generateConfig(sources: readonly string[]): string {
59
- const sourceList = sources
60
- .map((source) => ` ${JSON.stringify(source)}`)
61
- .join(",\n");
62
- return `// tokens.config.mjs — generated by \`pragma token add-config\`
63
- import { defineConfig } from "@canonical/terrazzo";
64
-
65
- export default defineConfig({
66
- tokens: [
67
- ${sourceList},
68
- ],
69
- outDir: "./tokens/",
70
- plugins: [],
71
- });
72
- `;
73
- }
74
-
75
- /**
76
- * Build the Task that writes `tokens.config.mjs` for the terrazzo pipeline.
77
- *
78
- * @param rt - The runtime (its store is booted by the projector for needsStore).
79
- * @returns A Task yielding the write outcome.
80
- * @note Impure — reads the store + config; the write is the Task's effect.
81
- */
82
- export async function runAddConfig(
83
- rt: PragmaRuntime,
84
- ): Promise<Task<AddConfigResult>> {
85
- const tokenCount = await countTokens(rt);
86
- const sources = await resolveSources(rt);
87
- const path = resolve(rt.cwd, "tokens.config.mjs");
88
- const content = generateConfig(sources);
89
-
90
- return gen(function* () {
91
- const alreadyExisted = yield* $(exists(path));
92
- yield* $(writeFile(path, content));
93
- return { path, tokenCount, sources, alreadyExisted };
94
- });
95
- }
@@ -1,73 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { mkdtempSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { describe, expect, it } from "vitest";
6
- import { completionFixture } from "../../testing/fixtures/completionFixture.js";
7
- import { emitScripts } from "./emitScripts.js";
8
-
9
- /**
10
- * M1 regression: the generated bash script must complete the `--flag=value`
11
- * wordbreak form. With `=` in COMP_WORDBREAKS, bash splits `--format=` into
12
- * `--format` `=` `` (or `` after `=`); without the normalization at the top of
13
- * `_pragma`, the value slot falls through to the noun/positional arm and
14
- * offers the WRONG candidates. These tests drive the REAL bash to prove the
15
- * fix, so the static tier answers `--format=<TAB>` as a flag value with zero
16
- * exec (the resolver is only reached for `{kind:"entity"}` contexts).
17
- */
18
-
19
- /** Whether a usable bash is on PATH (the package pins os:["linux"], so it is). */
20
- const bashOk =
21
- spawnSync("bash", ["-c", "printf ok"], { encoding: "utf-8" }).stdout === "ok";
22
-
23
- /** The bash completion script for the fixture grammar (has the global --format). */
24
- const script = emitScripts([completionFixture]).bash;
25
-
26
- /**
27
- * Source the generated script in a real bash, place the cursor per COMP_WORDS
28
- * / COMP_CWORD, invoke `_pragma`, and return COMPREPLY line-by-line.
29
- */
30
- function driveBash(words: readonly string[], cword: number): string[] {
31
- const dir = mkdtempSync(join(tmpdir(), "pragma-bashdrive-"));
32
- const file = join(dir, "completion.bash");
33
- writeFileSync(file, script);
34
- const wordsLiteral = words
35
- .map((word) => `'${word.replace(/'/g, "'\\''")}'`)
36
- .join(" ");
37
- const driver = [
38
- `source '${file}'`,
39
- `COMP_WORDS=(${wordsLiteral})`,
40
- `COMP_CWORD=${cword}`,
41
- "_pragma",
42
- `printf '%s\\n' "\${COMPREPLY[@]}"`,
43
- ].join("\n");
44
- const result = spawnSync("bash", ["--norc", "--noprofile", "-c", driver], {
45
- encoding: "utf-8",
46
- });
47
- return result.stdout.split("\n").filter((line) => line.length > 0);
48
- }
49
-
50
- describe.skipIf(!bashOk)("generated bash — --flag=value wordbreak (M1)", () => {
51
- it("routes --format=<TAB> to the format values (not nouns/positionals)", () => {
52
- // Default COMP_WORDBREAKS split: `--format` `=` `` (empty current word).
53
- const reply = driveBash(["pragma", "--format", "=", ""], 3);
54
- expect(reply.sort()).toEqual(["json", "plain"]);
55
- });
56
-
57
- it("filters the value by the partial typed after =", () => {
58
- const reply = driveBash(["pragma", "--format", "=", "j"], 3);
59
- expect(reply).toEqual(["json"]);
60
- });
61
-
62
- it("routes a verb-scoped --format=<TAB> the same way", () => {
63
- // After a noun/verb the value must still route as a flag value, never as
64
- // block's positional (which would offer entity/enum candidates instead).
65
- const reply = driveBash(["pragma", "block", "get", "--format", "=", ""], 5);
66
- expect(reply.sort()).toEqual(["json", "plain"]);
67
- });
68
-
69
- it("still completes the space form --format <TAB> (unbroken)", () => {
70
- const reply = driveBash(["pragma", "--format", ""], 2);
71
- expect(reply.sort()).toEqual(["json", "plain"]);
72
- });
73
- });
@@ -1,39 +0,0 @@
1
- # Embedded fallback pack source — a small, self-contained sample ontology.
2
- #
3
- # Compiled by `scripts/genEmbedded.ts` into `pack.generated.ts`. It exists so a
4
- # fresh install (no configured packages, no lock) can answer store-backed reads
5
- # without a network round-trip. Keep it tiny: it ships inside the binary.
6
-
7
- @prefix ex: <https://pragma.canonical.com/sample#> .
8
- @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
9
- @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
10
- @prefix owl: <http://www.w3.org/2002/07/owl#> .
11
- @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
12
-
13
- ex:Component a owl:Class ;
14
- rdfs:label "Component" ;
15
- rdfs:comment "A reusable design-system building block." .
16
-
17
- ex:componentName a owl:DatatypeProperty ;
18
- rdfs:label "component name" ;
19
- rdfs:domain ex:Component ;
20
- rdfs:range xsd:string .
21
-
22
- ex:dependsOn a owl:ObjectProperty ;
23
- rdfs:label "depends on" ;
24
- rdfs:domain ex:Component ;
25
- rdfs:range ex:Component .
26
-
27
- ex:Button a ex:Component ;
28
- rdfs:label "Button" ;
29
- ex:componentName "Button" .
30
-
31
- ex:Card a ex:Component ;
32
- rdfs:label "Card" ;
33
- ex:componentName "Card" ;
34
- ex:dependsOn ex:Button .
35
-
36
- ex:Dialog a ex:Component ;
37
- rdfs:label "Dialog" ;
38
- ex:componentName "Dialog" ;
39
- ex:dependsOn ex:Button , ex:Card .
@@ -1,90 +0,0 @@
1
- /**
2
- * The project lock — `pragma.lock.json`, committed at the project root.
3
- *
4
- * It pins the single combined pack the store boots from: `contentHash` names
5
- * the pack's cache directory, and `packs[]` records the resolved provenance of
6
- * every configured source (so `sources status` reports it and a `--frozen`
7
- * update re-resolves to exactly these commits). Serialization is canonical —
8
- * fixed field order, packs sorted by name, one trailing newline — so a
9
- * `--frozen` update that changes nothing rewrites a byte-identical file.
10
- *
11
- * Pure: zod + fs only, no store code, so both the storeless `sources status`
12
- * verb and the store-boot decision table read it without booting oxigraph.
13
- */
14
-
15
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
16
- import { z } from "zod";
17
- import { lockPath } from "./paths.js";
18
-
19
- /** One resolved source's provenance in the lock. */
20
- export const lockPackageSchema = z.object({
21
- /** Package name (config `packages` entry name / npm name). */
22
- name: z.string(),
23
- /** The config `packages` source ref, verbatim (e.g. `git+https://…#main`). */
24
- source: z.string(),
25
- /** The resolved commit / version / absolute path this ref pinned to. */
26
- resolved: z.string(),
27
- /** ISO timestamp the ref was resolved. */
28
- resolvedAt: z.string(),
29
- });
30
-
31
- /** The lock document. */
32
- export const pragmaLockSchema = z.object({
33
- version: z.literal(1),
34
- /** The combined pack's content hash — names its cache directory. */
35
- contentHash: z.string(),
36
- packs: z.array(lockPackageSchema),
37
- });
38
-
39
- /** One resolved source's provenance in the lock. */
40
- export type PragmaLockPackage = z.infer<typeof lockPackageSchema>;
41
- /** The parsed lock document. */
42
- export type PragmaLock = z.infer<typeof pragmaLockSchema>;
43
-
44
- /**
45
- * Read and validate the lock for a working directory.
46
- *
47
- * @param cwd - The project directory.
48
- * @returns The parsed lock, or `undefined` when absent or invalid.
49
- * @note Impure — reads from disk.
50
- */
51
- export function readLock(cwd: string): PragmaLock | undefined {
52
- const path = lockPath(cwd);
53
- if (!existsSync(path)) return undefined;
54
- try {
55
- return pragmaLockSchema.parse(JSON.parse(readFileSync(path, "utf-8")));
56
- } catch {
57
- return undefined;
58
- }
59
- }
60
-
61
- /** Serialize a lock canonically — stable field order, sorted packs, newline. */
62
- export function serializeLock(lock: PragmaLock): string {
63
- const packs = [...lock.packs]
64
- .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
65
- .map((pack) => ({
66
- name: pack.name,
67
- source: pack.source,
68
- resolved: pack.resolved,
69
- resolvedAt: pack.resolvedAt,
70
- }));
71
- return `${JSON.stringify(
72
- { version: lock.version, contentHash: lock.contentHash, packs },
73
- null,
74
- 2,
75
- )}\n`;
76
- }
77
-
78
- /**
79
- * Write the lock for a working directory.
80
- *
81
- * @param cwd - The project directory.
82
- * @param lock - The lock to persist.
83
- * @returns The exact bytes written (for the write effect / assertions).
84
- * @note Impure — writes `pragma.lock.json`.
85
- */
86
- export function writeLock(cwd: string, lock: PragmaLock): string {
87
- const content = serializeLock(lock);
88
- writeFileSync(lockPath(cwd), content);
89
- return content;
90
- }