@canonical/pragma-cli 0.33.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 (250) hide show
  1. package/README.md +7 -7
  2. package/dist/pragma +0 -0
  3. package/package.json +18 -15
  4. package/pragma.conf.ts +664 -0
  5. package/src/bin.ts +13 -4
  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.verb.ts +31 -13
  9. package/src/capabilities/capabilities/catalog.ts +18 -10
  10. package/src/capabilities/capabilities/hints.ts +13 -19
  11. package/src/capabilities/colophon/collectColophon.ts +34 -19
  12. package/src/capabilities/colophon/colophon.verb.ts +8 -4
  13. package/src/capabilities/colophon/types.ts +16 -7
  14. package/src/capabilities/config/collectConfigShow.test.ts +57 -0
  15. package/src/capabilities/config/collectConfigShow.ts +41 -4
  16. package/src/capabilities/config/fields.ts +0 -27
  17. package/src/capabilities/config/set.verb.ts +7 -3
  18. package/src/capabilities/config/show.render.test.ts +77 -5
  19. package/src/capabilities/config/show.render.ts +7 -12
  20. package/src/capabilities/config/show.verb.ts +4 -3
  21. package/src/capabilities/config/types.ts +8 -0
  22. package/src/capabilities/create/byteEquality.test.ts +78 -100
  23. package/src/capabilities/create/compiledCreate.subprocess.test.ts +52 -15
  24. package/src/capabilities/create/constants.ts +75 -0
  25. package/src/capabilities/create/create.mcp.test.ts +2 -2
  26. package/src/capabilities/create/create.test.ts +162 -15
  27. package/src/capabilities/create/create.verb.ts +140 -69
  28. package/src/capabilities/create/index.ts +2 -10
  29. package/src/capabilities/create/pickGenerator.ts +34 -28
  30. package/src/capabilities/create/templates.embedded.generated.ts +4 -29
  31. package/src/capabilities/create/types.ts +10 -6
  32. package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
  33. package/src/capabilities/distribution.test.ts +120 -0
  34. package/src/capabilities/distribution.ts +59 -0
  35. package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
  36. package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
  37. package/src/capabilities/doctor/checks/checkMcpConfigured.ts +7 -6
  38. package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
  39. package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
  40. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
  41. package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
  42. package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
  43. package/src/capabilities/doctor/doctor.render.test.ts +6 -6
  44. package/src/capabilities/doctor/doctor.render.ts +2 -1
  45. package/src/capabilities/doctor/doctor.test.ts +37 -8
  46. package/src/capabilities/doctor/doctor.verb.ts +6 -2
  47. package/src/capabilities/doctor/runChecks.ts +5 -5
  48. package/src/capabilities/doctor/types.ts +1 -1
  49. package/src/capabilities/emptyRecovery.test.ts +32 -15
  50. package/src/capabilities/graph/inspect.verb.ts +3 -2
  51. package/src/capabilities/graph/query.verb.ts +3 -2
  52. package/src/capabilities/graph/runQuery.ts +8 -5
  53. package/src/capabilities/index.ts +22 -18
  54. package/src/capabilities/info/collectInfo.ts +10 -6
  55. package/src/capabilities/info/info.render.ts +3 -2
  56. package/src/capabilities/info/info.verb.ts +3 -2
  57. package/src/capabilities/lazy.test.ts +93 -2
  58. package/src/capabilities/ontology/verbs.ts +21 -21
  59. package/src/capabilities/prompt/prompt.test.ts +12 -6
  60. package/src/capabilities/prompt/verbs.ts +24 -11
  61. package/src/capabilities/resources/provider.ts +30 -11
  62. package/src/capabilities/resources/resources.test.ts +85 -29
  63. package/src/capabilities/setup/operations/setupGenerator.ts +16 -9
  64. package/src/capabilities/setup/operations/setupMcp.ts +2 -2
  65. package/src/capabilities/setup/operations/setupSkills.ts +3 -3
  66. package/src/capabilities/setup/setup.verb.ts +12 -5
  67. package/src/capabilities/setup/shell.ts +29 -3
  68. package/src/capabilities/skill/discover.ts +11 -5
  69. package/src/capabilities/skill/render.ts +4 -5
  70. package/src/capabilities/skill/verbs.ts +7 -7
  71. package/src/capabilities/sources/collectStatus.ts +55 -50
  72. package/src/capabilities/sources/index.ts +1 -1
  73. package/src/capabilities/sources/installSkills.ts +2 -2
  74. package/src/capabilities/sources/runUpdate.ts +113 -122
  75. package/src/capabilities/sources/sources.test.ts +111 -117
  76. package/src/capabilities/sources/status.render.test.ts +53 -24
  77. package/src/capabilities/sources/status.render.ts +24 -29
  78. package/src/capabilities/sources/status.verb.ts +15 -8
  79. package/src/capabilities/sources/types.ts +18 -25
  80. package/src/capabilities/sources/update.render.ts +9 -2
  81. package/src/capabilities/sources/update.verb.ts +12 -23
  82. package/src/capabilities/surface.test.ts +33 -10
  83. package/src/capabilities/tier.parity.test.ts +144 -0
  84. package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
  85. package/src/constants.test.ts +8 -6
  86. package/src/constants.ts +47 -21
  87. package/src/identity.test.ts +333 -0
  88. package/src/index.ts +2 -2
  89. package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +5 -2
  90. package/src/kernel/completion/emitScripts.test.ts +1 -1
  91. package/src/kernel/completion/emitScripts.ts +4 -2
  92. package/src/kernel/completion/entitySource.test.ts +185 -41
  93. package/src/kernel/completion/entitySource.ts +117 -107
  94. package/src/kernel/completion/model.test.ts +1 -1
  95. package/src/kernel/completion/model.ts +11 -0
  96. package/src/kernel/completion/safety.test.ts +82 -36
  97. package/src/kernel/completion/shellDrive.test.ts +592 -0
  98. package/src/kernel/completion/templates/fish.ts +28 -6
  99. package/src/kernel/completion/types.ts +3 -3
  100. package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
  101. package/src/kernel/config/defaults.test.ts +85 -0
  102. package/src/kernel/config/defaults.ts +12 -23
  103. package/src/kernel/config/evaluateProjectConfig.ts +2 -3
  104. package/src/kernel/config/findProjectConfig.ts +4 -3
  105. package/src/kernel/config/firstRun.ts +12 -5
  106. package/src/kernel/config/globalConfig.ts +2 -2
  107. package/src/kernel/config/index.ts +4 -5
  108. package/src/kernel/config/paths.ts +17 -17
  109. package/src/kernel/config/readConfig.test.ts +242 -6
  110. package/src/kernel/config/readConfig.ts +8 -8
  111. package/src/kernel/config/schema.test.ts +50 -0
  112. package/src/kernel/config/schema.ts +97 -10
  113. package/src/kernel/config/types.ts +73 -19
  114. package/src/kernel/copy.test.ts +453 -0
  115. package/src/kernel/error/PragmaError.test.ts +1 -1
  116. package/src/kernel/error/constants.ts +3 -2
  117. package/src/kernel/error/index.ts +1 -1
  118. package/src/kernel/error/recovery.test.ts +22 -45
  119. package/src/kernel/error/recovery.ts +22 -27
  120. package/src/kernel/error/types.ts +5 -4
  121. package/src/kernel/packs/collect.test.ts +195 -11
  122. package/src/kernel/packs/collect.ts +213 -58
  123. package/src/kernel/packs/compile.test.ts +163 -7
  124. package/src/kernel/packs/compile.ts +15 -12
  125. package/src/kernel/packs/disclosure.test.ts +5 -2
  126. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
  127. package/src/kernel/packs/graphqlSource.test.ts +6 -3
  128. package/src/kernel/packs/iri.ts +5 -7
  129. package/src/kernel/packs/renderPack.ts +5 -3
  130. package/src/kernel/packs/resolveEntity.ts +21 -17
  131. package/src/kernel/packs/runBodies.ts +15 -14
  132. package/src/kernel/packs/schema.ts +96 -10
  133. package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
  134. package/src/kernel/packs/sparql/runSelect.ts +73 -34
  135. package/src/kernel/packs/toolDescription.test.ts +10 -2
  136. package/src/kernel/packs/types.ts +39 -4
  137. package/src/kernel/packs/uniqueness.ts +4 -4
  138. package/src/kernel/project/cli/buildProgram.test.ts +9 -4
  139. package/src/kernel/project/cli/dispatch.test.ts +67 -0
  140. package/src/kernel/project/cli/dispatch.ts +34 -12
  141. package/src/kernel/project/cli/helpFormat.ts +1 -1
  142. package/src/kernel/project/cli/rootHelp.ts +105 -105
  143. package/src/kernel/project/mcp/buildServer.ts +11 -6
  144. package/src/kernel/project/mcp/instructions.ts +11 -6
  145. package/src/kernel/project/mcp/parity.test.ts +39 -1
  146. package/src/kernel/project/mcp/prompts/prompts.test.ts +120 -5
  147. package/src/kernel/project/mcp/prompts/provider.ts +22 -8
  148. package/src/kernel/project/mcp/prompts/source.ts +68 -67
  149. package/src/kernel/project/mcp/registerVerb.ts +25 -6
  150. package/src/kernel/project/mcp/serve.ts +11 -3
  151. package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
  152. package/src/kernel/render/disclosure.ts +4 -2
  153. package/src/kernel/render/prefixes.test.ts +36 -0
  154. package/src/kernel/render/prefixes.ts +40 -13
  155. package/src/kernel/render/renderers.test.ts +1 -1
  156. package/src/kernel/runtime/graphpack/build.ts +29 -3
  157. package/src/kernel/runtime/graphpack/buildIndex.test.ts +46 -0
  158. package/src/kernel/runtime/graphpack/buildIndex.ts +52 -17
  159. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -5
  160. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -3
  161. package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
  162. package/src/kernel/runtime/graphpack/embedded.ts +37 -11
  163. package/src/kernel/runtime/graphpack/graphpack.test.ts +205 -5
  164. package/src/kernel/runtime/graphpack/hash.ts +3 -2
  165. package/src/kernel/runtime/graphpack/index.ts +1 -1
  166. package/src/kernel/runtime/graphpack/manifest.ts +114 -12
  167. package/src/kernel/runtime/graphpack/read.ts +4 -9
  168. package/src/kernel/runtime/graphpack/schemas.ts +74 -0
  169. package/src/kernel/runtime/graphpack/stories.ts +84 -0
  170. package/src/kernel/runtime/graphpack/types.ts +61 -48
  171. package/src/kernel/runtime/paths.ts +64 -15
  172. package/src/kernel/runtime/probe.test.ts +8 -4
  173. package/src/kernel/runtime/readEntity.ts +6 -5
  174. package/src/kernel/runtime/refs/gitOps.ts +1 -19
  175. package/src/kernel/runtime/refs/parseRef.ts +7 -7
  176. package/src/kernel/runtime/refs/resolve.ts +78 -62
  177. package/src/kernel/runtime/resolveSources.test.ts +63 -24
  178. package/src/kernel/runtime/resolveSources.ts +36 -25
  179. package/src/kernel/runtime/storeReadiness.ts +9 -8
  180. package/src/kernel/runtime/storeless.test.ts +1 -1
  181. package/src/kernel/runtime/wasmEmbed.test.ts +22 -19
  182. package/src/kernel/spec/emitReference.test.ts +3 -3
  183. package/src/kernel/spec/emitReference.ts +144 -11
  184. package/src/kernel/spec/emitSurface.ts +9 -5
  185. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  186. package/src/kernel/spec/surfaceConformance.ts +8 -0
  187. package/src/kernel/spec/types.ts +29 -9
  188. package/src/kernel/spec/validate.ts +2 -1
  189. package/src/kernel/vocabulary.test.ts +164 -0
  190. package/src/kernel/vocabulary.ts +146 -0
  191. package/src/testing/PARITY_GAPS.ts +8 -2
  192. package/src/testing/behavioral/README.md +3 -3
  193. package/src/testing/behavioral/completion.test.ts +19 -6
  194. package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
  195. package/src/testing/behavioral/errorMatrix.storeless.test.ts +1 -1
  196. package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
  197. package/src/testing/behavioral/harness.foundation.test.ts +14 -5
  198. package/src/testing/behavioral/journeys.cli.test.ts +116 -75
  199. package/src/testing/behavioral/journeys.defaultPack.test.ts +82 -176
  200. package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
  201. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +9 -6
  202. package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
  203. package/src/testing/behavioral/liveReadSurface.ts +4 -3
  204. package/src/testing/behavioral/mcpSurface.test.ts +9 -4
  205. package/src/testing/behavioral/staleCommands.test.ts +10 -0
  206. package/src/testing/eval/__snapshots__/eval.test.ts.snap +7 -7
  207. package/src/testing/eval/cases/assembly.ts +13 -6
  208. package/src/testing/eval/cases/effectful.ts +25 -13
  209. package/src/testing/eval/cases/readNouns.ts +22 -20
  210. package/src/testing/eval/cases/stable.ts +5 -1
  211. package/src/testing/eval/eval.test.ts +7 -1
  212. package/src/testing/fixtures/fixtureCapability.ts +45 -1
  213. package/src/testing/fixtures/graph/canonical.ts +6 -3
  214. package/src/testing/fixtures/graph/defaultPack.ts +6 -5
  215. package/src/testing/fixtures/graph/owlExport.ts +7 -5
  216. package/src/testing/helpers/fixtureGraph.ts +33 -16
  217. package/src/testing/helpers/packRuntime.ts +2 -3
  218. package/src/testing/helpers/projectCli.ts +2 -1
  219. package/src/testing/helpers/projectMcp.ts +6 -0
  220. package/src/testing/perf/budgets.test.ts +4 -0
  221. package/src/testing/perf/budgets.ts +22 -7
  222. package/src/testing/perf/globalSetup.ts +121 -6
  223. package/src/types.ts +2 -2
  224. package/src/capabilities/block/blockList.test.ts +0 -79
  225. package/src/capabilities/block/blockList.verb.ts +0 -163
  226. package/src/capabilities/block/index.ts +0 -25
  227. package/src/capabilities/block/pack.ts +0 -166
  228. package/src/capabilities/block/parity.test.ts +0 -187
  229. package/src/capabilities/block/tierChain.ts +0 -78
  230. package/src/capabilities/colophon/pragmaColophon.ts +0 -66
  231. package/src/capabilities/modifier/index.ts +0 -15
  232. package/src/capabilities/modifier/pack.ts +0 -64
  233. package/src/capabilities/standard/index.ts +0 -18
  234. package/src/capabilities/standard/pack.ts +0 -128
  235. package/src/capabilities/tier/index.ts +0 -19
  236. package/src/capabilities/tier/lookup.render.test.ts +0 -72
  237. package/src/capabilities/tier/lookup.render.ts +0 -59
  238. package/src/capabilities/tier/lookup.test.ts +0 -83
  239. package/src/capabilities/tier/lookup.verb.ts +0 -44
  240. package/src/capabilities/tier/pack.ts +0 -28
  241. package/src/capabilities/tier/runLookup.ts +0 -87
  242. package/src/capabilities/token/addConfig.render.ts +0 -27
  243. package/src/capabilities/token/addConfig.test.ts +0 -62
  244. package/src/capabilities/token/addConfig.verb.ts +0 -40
  245. package/src/capabilities/token/index.ts +0 -19
  246. package/src/capabilities/token/pack.ts +0 -58
  247. package/src/capabilities/token/runAddConfig.ts +0 -95
  248. package/src/kernel/completion/bashDrive.test.ts +0 -73
  249. package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
  250. package/src/kernel/runtime/lock.ts +0 -90
@@ -1,128 +0,0 @@
1
- /**
2
- * The bundled `standard` pack — code standards as data.
3
- *
4
- * `standard list|lookup|categories|sample` compile from this definition through
5
- * the one pack kernel. Normalized for v2: the old `digest` level is renamed to
6
- * the canonical `standard`, so disclosure gates by the canonical index; the
7
- * default is `summary` (base fields by name), `--detail standard` adds the `dos`
8
- * examples, `--detail detailed` adds `donts`. `cs:extends` stays the raw IRI in
9
- * JSON (renderers compact it at display time).
10
- */
11
-
12
- import type { PackDefinition } from "../../kernel/packs/types.js";
13
-
14
- export const standardPack: PackDefinition = {
15
- noun: "standard",
16
- description: "List all code standards.",
17
- toolDescription:
18
- "List code standards. Optionally filter by category or search term.",
19
- list: {
20
- query: [
21
- "SELECT ?uri ?name ?category ?description",
22
- "WHERE {",
23
- " ?uri a cs:CodeStandard ;",
24
- " cs:description ?description .",
25
- " OPTIONAL { ?uri cs:name ?n . }",
26
- ' BIND(COALESCE(?n, REPLACE(STRAFTER(STR(?uri), "#"), "\\\\.", "/")) AS ?name)',
27
- " OPTIONAL {",
28
- " ?uri cs:hasCategory ?cat .",
29
- " ?cat cs:slug ?category .",
30
- " }",
31
- "}",
32
- "ORDER BY ?name",
33
- ].join("\n"),
34
- columns: [
35
- { field: "uri", label: "IRI" },
36
- { field: "name", label: "Name" },
37
- { field: "category", label: "Category" },
38
- { field: "description", label: "Description" },
39
- ],
40
- filters: [
41
- {
42
- param: "category",
43
- variable: "category",
44
- description: "Filter by category name.",
45
- },
46
- ],
47
- search: {
48
- variables: ["name", "description"],
49
- description: "Search in name and description.",
50
- },
51
- },
52
- verbs: [
53
- {
54
- verb: "categories",
55
- description: "List all standard categories with counts.",
56
- toolDescription: "List all code standard categories.",
57
- query: [
58
- "SELECT ?name (COUNT(?standard) AS ?count)",
59
- "WHERE {",
60
- " ?cat a cs:Category ;",
61
- " cs:slug ?name .",
62
- " OPTIONAL {",
63
- " ?standard a cs:CodeStandard ;",
64
- " cs:hasCategory ?cat .",
65
- " }",
66
- "}",
67
- "GROUP BY ?name",
68
- "ORDER BY ?name",
69
- ].join("\n"),
70
- columns: [
71
- { field: "name", label: "Category" },
72
- { field: "count", label: "Standards" },
73
- ],
74
- },
75
- ],
76
- lookup: {
77
- source: "sparql",
78
- by: "cs:name",
79
- type: "cs:CodeStandard",
80
- description:
81
- "Look up detailed information for a standard by name, IRI, or glob.",
82
- toolDescription:
83
- "Get detailed information about one or more code standards including dos and donts with code examples. Address standards by name, prefixed name (cs:…), absolute IRI, or glob pattern (react/component/*).",
84
- fields: [
85
- {
86
- name: "category",
87
- property: "cs:hasCategory/cs:slug",
88
- label: "Category",
89
- },
90
- { name: "description", property: "cs:description", label: "Description" },
91
- { name: "extends", property: "cs:extends", label: "Extends" },
92
- ],
93
- expand: [
94
- {
95
- name: "dos",
96
- heading: "Do",
97
- relation: "cs:do",
98
- select: [
99
- { name: "caption", property: "cs:description" },
100
- { name: "language", property: "cs:language" },
101
- { name: "code", property: "cs:code" },
102
- ],
103
- level: "standard",
104
- },
105
- {
106
- name: "donts",
107
- heading: "Don't",
108
- relation: "cs:dont",
109
- select: [
110
- { name: "caption", property: "cs:description" },
111
- { name: "language", property: "cs:language" },
112
- { name: "code", property: "cs:code" },
113
- ],
114
- level: "detailed",
115
- },
116
- ],
117
- disclosure: {
118
- levels: ["summary", "standard", "detailed"],
119
- default: "summary",
120
- },
121
- sample: {
122
- description:
123
- "Return randomly selected complete standard instances as exemplars for shape discovery.",
124
- toolDescription:
125
- "Return 1–5 randomly selected complete code standard instances as exemplars. Use BEFORE writing queries to see actual data shapes, property names, and value formats. Each call returns different instances.",
126
- },
127
- },
128
- };
@@ -1,19 +0,0 @@
1
- /**
2
- * The `tier` capability module — the bundled flat-list pack compiled to `list`.
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 { tierLookupVerb } from "./lookup.verb.js";
9
- import { tierPack } from "./pack.js";
10
-
11
- /** The `tier` capability module — the bundled flat-list pack plus the bespoke
12
- * single-name `lookup` (a pack lookup would emit the variadic `<name...>`). */
13
- export const tierModule: CapabilityModule = {
14
- name: "tier",
15
- verbs: [
16
- ...compilePack(tierPack, "bundled:tier", DEFAULT_PREFIX_MAP),
17
- tierLookupVerb,
18
- ],
19
- };
@@ -1,72 +0,0 @@
1
- import chalk from "chalk";
2
- import { afterEach, describe, expect, it } from "vitest";
3
- import { type TierLookupData, tierLookupFormatters } from "./lookup.render.js";
4
-
5
- const TIER: TierLookupData = {
6
- uri: "cs:apps/lxd-panel",
7
- name: "apps/lxd-panel",
8
- blocks: ["LXD Panel", "Status Bar"],
9
- };
10
-
11
- /** Run `body` with stdout's `isTTY` forced to `value`, then restore it. */
12
- function withStdoutTty(value: boolean | undefined, body: () => void): void {
13
- const stream = process.stdout as { isTTY?: boolean };
14
- const saved = stream.isTTY;
15
- stream.isTTY = value;
16
- try {
17
- body();
18
- } finally {
19
- stream.isTTY = saved;
20
- }
21
- }
22
-
23
- describe("tierLookupFormatters", () => {
24
- it("underlines the plain title with an ═ rule (B7)", () => {
25
- const [title, rule, blank, blocks] = tierLookupFormatters
26
- .plain(TIER)
27
- .split("\n");
28
- expect(title).toBe("apps/lxd-panel (cs:apps/lxd-panel)");
29
- expect(rule).toBe("═".repeat(Math.max((title as string).length, 24)));
30
- expect(blank).toBe("");
31
- expect(blocks).toBe(" blocks: LXD Panel, Status Bar");
32
- });
33
-
34
- it("floors the plain rule at 24 columns for a short title", () => {
35
- const out = tierLookupFormatters.plain({ ...TIER, uri: "x", name: "a" });
36
- expect(out.split("\n").at(1)).toBe("═".repeat(24));
37
- });
38
-
39
- it("heads the llm entity read at H2, never H1 (B2)", () => {
40
- const out = tierLookupFormatters.llm(TIER);
41
- expect(out.startsWith("## apps/lxd-panel\n")).toBe(true);
42
- expect(out).not.toMatch(/^# /m);
43
- });
44
- });
45
-
46
- describe("tierLookupFormatters — shared style seam (B7)", () => {
47
- const prevLevel = chalk.level;
48
- afterEach(() => {
49
- chalk.level = prevLevel;
50
- });
51
-
52
- it("styles the plain title on a color-capable TTY (single-sourced frame)", () => {
53
- chalk.level = 1;
54
- withStdoutTty(true, () => {
55
- // Delegating to `renderLookupPlain` now carries the same TTY tint block and
56
- // skill lookups get — previously the tier title stayed unstyled.
57
- // biome-ignore lint/suspicious/noControlCharactersInRegex: asserting the ESC byte is the point
58
- expect(tierLookupFormatters.plain(TIER)).toMatch(/\x1b\[/);
59
- });
60
- });
61
-
62
- it("keeps piped plain output byte-stable off a TTY", () => {
63
- chalk.level = 3;
64
- withStdoutTty(undefined, () => {
65
- const out = tierLookupFormatters.plain(TIER);
66
- // biome-ignore lint/suspicious/noControlCharactersInRegex: asserting NO ESC byte survives
67
- expect(out).not.toMatch(/\x1b\[/);
68
- expect(out.split("\n").at(0)).toBe("apps/lxd-panel (cs:apps/lxd-panel)");
69
- expect(out.split("\n").at(3)).toBe(" blocks: LXD Panel, Status Bar");
70
- });
71
- });
72
- });
@@ -1,59 +0,0 @@
1
- /**
2
- * Formatters + data shape for `tier lookup <name>`.
3
- */
4
-
5
- import type { RenderLookupOptions } from "../../kernel/render/contracts.js";
6
- import { renderLookupPlain } from "../../kernel/render/renderers.js";
7
- import type { Formatters } from "../../kernel/spec/types.js";
8
-
9
- /** The looked-up tier: its IRI, name, and the blocks scoped directly to it. */
10
- export interface TierLookupData {
11
- readonly uri: string;
12
- readonly name: string;
13
- readonly blocks: readonly string[];
14
- }
15
-
16
- /**
17
- * The shared plain-lookup layout for a tier: a `name (uri)` title over an ═ rule,
18
- * then the directly-scoped blocks as one inline field. Delegating to
19
- * {@link renderLookupPlain} single-sources the frame AND the TTY styling (bold
20
- * title, dim rule, cyan label) that block/skill lookups already earn — so a tier
21
- * title no longer stays unstyled on a terminal while theirs are bold (B7).
22
- */
23
- const tierPlainOptions: RenderLookupOptions<TierLookupData> = {
24
- title: (tier) => `${tier.name} (${tier.uri})`,
25
- fields: [
26
- {
27
- label: "blocks",
28
- // Pre-joined so the empty case still renders (the shared field renderer
29
- // skips an empty array). Blocks are `ds:name` display strings, never IRIs,
30
- // so the shared renderer's URI compaction is a no-op here — byte-stable.
31
- value: (tier) =>
32
- tier.blocks.length > 0
33
- ? tier.blocks.join(", ")
34
- : "(none scoped directly to this tier)",
35
- },
36
- ],
37
- sections: [],
38
- };
39
-
40
- export const tierLookupFormatters: Formatters<TierLookupData> = {
41
- plain(data) {
42
- return renderLookupPlain(data, tierPlainOptions);
43
- },
44
- llm(data) {
45
- // A byte-frozen agent contract that DIVERGES from the generic
46
- // `renderLookupLlm`: an H2 `name` title (not `name (uri)`) with the IRI and
47
- // blocks backtick-wrapped (`\`cs:…\``) — a shape the shared renderer does not
48
- // emit. The llm path is never styled, so it has no TTY drift to single-source;
49
- // kept inline so the frozen bytes hold (B2).
50
- const lines = [`## ${data.name}`, `- IRI: \`${data.uri}\``];
51
- if (data.blocks.length > 0) {
52
- lines.push(`- Blocks: ${data.blocks.map((b) => `\`${b}\``).join(", ")}`);
53
- }
54
- return lines.join("\n");
55
- },
56
- json(data) {
57
- return JSON.stringify(data);
58
- },
59
- };
@@ -1,83 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
- import type { PragmaRuntime } from "../../kernel/runtime/types.js";
3
- import { CANONICAL_TTL } from "../../testing/fixtures/graph/canonical.js";
4
- import {
5
- bootFixtureRuntime,
6
- type FixtureGraph,
7
- } from "../../testing/helpers/fixtureGraph.js";
8
- import type { McpHarness } from "../../testing/helpers/projectMcp.js";
9
- import { projectMcp } from "../../testing/helpers/projectMcp.js";
10
- import { capabilities } from "../index.js";
11
- import { runTierLookup } from "./runLookup.js";
12
-
13
- let fixture: FixtureGraph;
14
- let mcp: McpHarness;
15
-
16
- beforeAll(async () => {
17
- fixture = await bootFixtureRuntime({ ttl: CANONICAL_TTL });
18
- mcp = await projectMcp(capabilities, fixture.cwd);
19
- });
20
-
21
- afterAll(async () => {
22
- await mcp.cleanup();
23
- await fixture.dispose();
24
- });
25
-
26
- describe("tier_lookup — bespoke single-name lookup (PROTECTED)", () => {
27
- it("resolves a tier and the blocks scoped directly to it", async () => {
28
- const result = await mcp.callTool("tier_lookup", { name: "apps/lxd" });
29
- expect(result.ok).toBe(true);
30
- const data = result.data as { name: string; blocks: string[] };
31
- expect(data.name).toBe("apps/lxd");
32
- // The canonical fixture scopes "LXD Panel" to apps/lxd.
33
- expect(data.blocks).toContain("LXD Panel");
34
- });
35
-
36
- it("errors on an unknown tier name with a tier_list recovery", async () => {
37
- const result = await mcp.callTool("tier_lookup", { name: "not-a-tier" });
38
- expect(result.ok).toBe(false);
39
- const error = result.error as { code: string };
40
- expect(error.code).toBe("ENTITY_NOT_FOUND");
41
- });
42
-
43
- it("suggests a near-miss tier name from the full list (did-you-mean parity)", async () => {
44
- // B6: tier lookup was the only entity lookup that omitted suggestions,
45
- // though — like the others — it can read the full tier list on a miss.
46
- let caught: unknown;
47
- try {
48
- await runTierLookup(fixture.runtime, "apps/lxdd");
49
- } catch (error) {
50
- caught = error;
51
- }
52
- expect(caught).toMatchObject({ code: "ENTITY_NOT_FOUND" });
53
- expect((caught as { suggestions: string[] }).suggestions).toContain(
54
- "apps/lxd",
55
- );
56
- });
57
- });
58
-
59
- describe("tier_lookup — cold/unseeded store (ROOT A)", () => {
60
- it("remaps a 'Prefix not found' to STORE_UNAVAILABLE, not INTERNAL_ERROR", async () => {
61
- // The bespoke lookup used to call `rt.query.sparql` directly, bypassing the
62
- // `runSelect` remap — so a cold store's raw "Prefix not found" collapsed to
63
- // INTERNAL_ERROR. It now routes through `runSelect`, inheriting the remap.
64
- const coldRuntime = {
65
- query: {
66
- sparql: async () => {
67
- throw new Error("Prefix not found: ds");
68
- },
69
- },
70
- } as unknown as PragmaRuntime;
71
-
72
- let caught: unknown;
73
- try {
74
- await runTierLookup(coldRuntime, "apps/lxd");
75
- } catch (error) {
76
- caught = error;
77
- }
78
- expect(caught).toMatchObject({ code: "STORE_UNAVAILABLE" });
79
- expect(
80
- (caught as { recovery?: { mcp?: { tool: string } } }).recovery?.mcp,
81
- ).toMatchObject({ tool: "sources_update" });
82
- });
83
- });
@@ -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,87 +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 { suggestNames } from "../../kernel/project/cli/suggestNames.js";
13
- import { compactUri } from "../../kernel/render/compactUri.js";
14
- import { DEFAULT_PREFIX_MAP } from "../../kernel/render/prefixes.js";
15
- import type { PragmaRuntime } from "../../kernel/runtime/types.js";
16
- import type { TierLookupData } from "./lookup.render.js";
17
-
18
- /**
19
- * Read every tier name in the store (its `ds:name`), ordered.
20
- *
21
- * The bespoke lookup already holds the machinery to reach the store, so a
22
- * name-not-found can rank "did you mean?" candidates the same way every other
23
- * entity lookup does — the tier list is a single cheap SELECT away.
24
- *
25
- * @param rt - The runtime (its store is booted by the projector for needsStore).
26
- * @returns The tier names, ordered.
27
- * @note Impure — queries the store through `runSelect`.
28
- */
29
- async function selectTierNames(rt: PragmaRuntime): Promise<string[]> {
30
- const rows = await runSelect(
31
- rt,
32
- "SELECT ?name WHERE { ?tier a ds:Tier ; ds:name ?name } ORDER BY ?name",
33
- "tier",
34
- );
35
- return rows
36
- .map((row) => row.name as string | undefined)
37
- .filter((name): name is string => Boolean(name));
38
- }
39
-
40
- /**
41
- * Look up one tier by name.
42
- *
43
- * @param rt - The runtime (its store is booted by the projector for needsStore).
44
- * @param name - The tier name (its `ds:name`, e.g. `apps/lxd`).
45
- * @returns The tier's IRI, name, and directly-scoped block names.
46
- * @throws PragmaError ENTITY_NOT_FOUND when no tier has that name;
47
- * STORE_UNAVAILABLE (exit 3) when the store is unseeded for this project.
48
- */
49
- export async function runTierLookup(
50
- rt: PragmaRuntime,
51
- name: string,
52
- ): Promise<TierLookupData> {
53
- const query = [
54
- "SELECT ?uri ?blockName WHERE {",
55
- " ?uri a ds:Tier ; ds:name ?tierName .",
56
- ` FILTER(STR(?tierName) = ${JSON.stringify(name)})`,
57
- " OPTIONAL { ?block ds:tier ?uri ; ds:name ?blockName }",
58
- "}",
59
- "ORDER BY ?blockName",
60
- ].join("\n");
61
-
62
- // Route through `runSelect` (not `rt.query.sparql` directly) so a cold/unseeded
63
- // store — a generated `ds:` query hitting an unknown prefix — is remapped to an
64
- // actionable STORE_UNAVAILABLE with `pragma sources update` recovery, instead of
65
- // a raw "Prefix not found" collapsing to INTERNAL_ERROR at the boundary.
66
- const rows = await runSelect(rt, query, "tier");
67
- const uri = rows[0]?.uri;
68
- if (!uri) {
69
- // Rank "did you mean?" candidates from the full tier list — the only entity
70
- // lookup that used to omit suggestions, though (like the others) it has the
71
- // names in hand. The suggestion query runs only on this cold, not-found path.
72
- const suggestions = suggestNames(name, await selectTierNames(rt));
73
- throw PragmaError.notFound("tier", name, {
74
- suggestions,
75
- recovery: {
76
- message: "List available tiers.",
77
- cli: "pragma tier list",
78
- mcp: { tool: "tier_list" },
79
- },
80
- });
81
- }
82
- const blocks = rows
83
- // `blockName` rides an OPTIONAL, so the key is absent for a memberless tier.
84
- .map((row) => row.blockName as string | undefined)
85
- .filter((block): block is string => Boolean(block));
86
- return { uri: compactUri(uri, DEFAULT_PREFIX_MAP), name, blocks };
87
- }
@@ -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
- };