@canonical/pragma-cli 0.30.0 → 0.31.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 (254) hide show
  1. package/README.md +64 -35
  2. package/dist/pragma +0 -0
  3. package/package.json +15 -13
  4. package/src/completions/constants.ts +7 -0
  5. package/src/completions/handleQuery.test.ts +32 -0
  6. package/src/completions/handleQuery.ts +20 -1
  7. package/src/completions/queryCompletions.test.ts +5 -0
  8. package/src/completions/queryCompletions.ts +17 -3
  9. package/src/completions/querySocket.test.ts +31 -0
  10. package/src/completions/querySocket.ts +36 -9
  11. package/src/config/parseConfigValues.ts +2 -0
  12. package/src/config/readConfigLayers.test.ts +2 -0
  13. package/src/config/readConfigLayers.ts +3 -0
  14. package/src/config/types.ts +7 -0
  15. package/src/constants.ts +3 -1
  16. package/src/domains/block/commands/sample.ts +2 -1
  17. package/src/domains/block/formatters/list.test.ts +3 -3
  18. package/src/domains/block/formatters/lookup.test.ts +5 -5
  19. package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
  20. package/src/domains/block/stories.ts +9 -3
  21. package/src/domains/config/commands/channel.test.ts +16 -0
  22. package/src/domains/config/commands/channel.ts +14 -1
  23. package/src/domains/config/commands/framework.test.ts +14 -0
  24. package/src/domains/config/commands/framework.ts +13 -1
  25. package/src/domains/config/commands/tier.test.ts +7 -0
  26. package/src/domains/config/commands/tier.ts +14 -1
  27. package/src/domains/config/operations/validateTier.test.ts +9 -0
  28. package/src/domains/config/operations/validateTier.ts +10 -2
  29. package/src/domains/create/generatorPack/bundled.ts +49 -0
  30. package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
  31. package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
  32. package/src/domains/create/index.ts +13 -7
  33. package/src/domains/create/mcp/specs.ts +6 -161
  34. package/src/domains/create/renderGeneratorUi.test.ts +103 -0
  35. package/src/domains/create/renderGeneratorUi.ts +65 -0
  36. package/src/domains/doctor/formatters/doctor.test.ts +50 -2
  37. package/src/domains/doctor/formatters/doctor.ts +90 -38
  38. package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
  39. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
  40. package/src/domains/doctor/operations/checks.test.ts +6 -4
  41. package/src/domains/doctor/operations/types.ts +23 -1
  42. package/src/domains/graph/commands/inspect.test.ts +2 -2
  43. package/src/domains/graph/commands/query.test.ts +5 -3
  44. package/src/domains/graph/formatters/inspect.ts +7 -7
  45. package/src/domains/graph/formatters/query.test.ts +87 -0
  46. package/src/domains/graph/formatters/query.ts +42 -5
  47. package/src/domains/graph/operations/inspectUri.test.ts +5 -5
  48. package/src/domains/graphql/commands/serve.test.ts +12 -6
  49. package/src/domains/graphql/commands/serve.ts +16 -6
  50. package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
  51. package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
  52. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
  53. package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
  54. package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
  55. package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
  56. package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
  57. package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
  58. package/src/domains/graphql/operations/index.ts +5 -0
  59. package/src/domains/info/formatters/upgrade.test.ts +7 -4
  60. package/src/domains/info/formatters/upgrade.ts +3 -1
  61. package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
  62. package/src/domains/info/operations/collectEntityCounts.ts +123 -0
  63. package/src/domains/info/operations/index.ts +1 -0
  64. package/src/domains/info/types.ts +13 -0
  65. package/src/domains/llm/capabilities.test.ts +23 -3
  66. package/src/domains/llm/commands/capabilities.ts +12 -3
  67. package/src/domains/llm/commands/llm.ts +5 -1
  68. package/src/domains/llm/data/commandReference.ts +20 -11
  69. package/src/domains/llm/data/decisionTrees.ts +10 -5
  70. package/src/domains/llm/data/toolCatalog.ts +49 -18
  71. package/src/domains/llm/formatters/orientation.ts +5 -1
  72. package/src/domains/llm/llmCommand.test.ts +11 -3
  73. package/src/domains/llm/mcp/specs.ts +3 -4
  74. package/src/domains/llm/operations/collectContext.test.ts +64 -0
  75. package/src/domains/llm/operations/collectContext.ts +7 -17
  76. package/src/domains/modifier/commands/sample.ts +2 -1
  77. package/src/domains/modifier/operations/lookup.ts +5 -3
  78. package/src/domains/modifier/stories.ts +9 -0
  79. package/src/domains/ontology/commands/list.test.ts +5 -3
  80. package/src/domains/ontology/commands/show.test.ts +85 -9
  81. package/src/domains/ontology/formatters/index.ts +1 -0
  82. package/src/domains/ontology/formatters/list.ts +37 -12
  83. package/src/domains/ontology/formatters/show.ts +358 -67
  84. package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
  85. package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
  86. package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
  87. package/src/domains/ontology/helpers/index.ts +14 -0
  88. package/src/domains/ontology/helpers/queryClasses.ts +32 -16
  89. package/src/domains/ontology/helpers/queryProperties.ts +39 -6
  90. package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
  91. package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
  92. package/src/domains/ontology/operations/listOntologies.ts +126 -30
  93. package/src/domains/ontology/operations/showOntology.test.ts +131 -12
  94. package/src/domains/ontology/operations/showOntology.ts +289 -7
  95. package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
  96. package/src/domains/ontology/stories.ts +53 -8
  97. package/src/domains/refs/operations/updateRefs.ts +0 -1
  98. package/src/domains/setup/commands/index.ts +1 -0
  99. package/src/domains/setup/commands/setup.ts +70 -0
  100. package/src/domains/setup/index.ts +7 -3
  101. package/src/domains/setup/operations/setupLsp.test.ts +3 -2
  102. package/src/domains/setup/operations/setupLsp.ts +7 -2
  103. package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
  104. package/src/domains/shared/bootStore.test.ts +82 -0
  105. package/src/domains/shared/bootStore.ts +56 -20
  106. package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
  107. package/src/domains/shared/extractLocalName.test.ts +3 -3
  108. package/src/domains/shared/formatters.ts +23 -2
  109. package/src/domains/shared/listTiers.ts +40 -0
  110. package/src/domains/shared/loaders/bundledLoader.ts +48 -0
  111. package/src/domains/shared/loaders/gitLoader.ts +1 -2
  112. package/src/domains/shared/loaders/localLoader.ts +2 -18
  113. package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
  114. package/src/domains/shared/loaders/readPackageDir.ts +46 -8
  115. package/src/domains/shared/lookupMany.test.ts +15 -0
  116. package/src/domains/shared/lookupMany.ts +22 -6
  117. package/src/domains/shared/namespaces.ts +13 -5
  118. package/src/domains/shared/parseSampleCount.test.ts +47 -0
  119. package/src/domains/shared/parseSampleCount.ts +45 -0
  120. package/src/domains/shared/prefixes.test.ts +211 -0
  121. package/src/domains/shared/prefixes.ts +182 -15
  122. package/src/domains/shared/renderers.ts +5 -5
  123. package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
  124. package/src/domains/shared/resolveLookupExitCode.ts +42 -0
  125. package/src/domains/shared/semanticPackage.test.ts +89 -0
  126. package/src/domains/shared/semanticPackage.ts +17 -2
  127. package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
  128. package/src/domains/shared/stories/compileLookupTool.ts +1 -1
  129. package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
  130. package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
  131. package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
  132. package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
  133. package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
  134. package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
  135. package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
  136. package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
  137. package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
  138. package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
  139. package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
  140. package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
  141. package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
  142. package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
  143. package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
  144. package/src/domains/shared/stories/pack/index.ts +14 -0
  145. package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
  146. package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
  147. package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
  148. package/src/domains/shared/stories/pack/types.ts +123 -3
  149. package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
  150. package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
  151. package/src/domains/shared/stories/types.ts +9 -1
  152. package/src/domains/shared/types/index.ts +5 -9
  153. package/src/domains/shared/types/ontology.ts +119 -24
  154. package/src/domains/shared/types/standard.ts +9 -52
  155. package/src/domains/token/commands/lookup.test.ts +8 -0
  156. package/src/domains/token/commands/lookup.ts +12 -0
  157. package/src/domains/token/commands/sample.ts +2 -1
  158. package/src/domains/token/featureFlag.ts +17 -0
  159. package/src/domains/token/index.ts +5 -0
  160. package/src/domains/token/mcp/specs.ts +13 -1
  161. package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
  162. package/src/domains/trace/commands/show.ts +30 -4
  163. package/src/index.ts +1 -13
  164. package/src/mcp/registerResources.test.ts +2 -2
  165. package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
  166. package/src/mcp/tools/index.ts +18 -10
  167. package/src/mcp/tools/registerTools.test.ts +106 -30
  168. package/src/pipeline/collectCommands.test.ts +151 -1
  169. package/src/pipeline/collectCommands.ts +34 -19
  170. package/src/pipeline/createProgram.test.ts +5 -1
  171. package/src/pipeline/createProgram.ts +2 -2
  172. package/src/pipeline/firstRun.test.ts +80 -0
  173. package/src/pipeline/firstRun.ts +99 -0
  174. package/src/pipeline/parseGlobalFlags.test.ts +102 -1
  175. package/src/pipeline/parseGlobalFlags.ts +98 -9
  176. package/src/pipeline/resolveCommandKind.test.ts +12 -0
  177. package/src/pipeline/resolveCommandKind.ts +4 -0
  178. package/src/pipeline/rootHelp.test.ts +87 -0
  179. package/src/pipeline/rootHelp.ts +265 -0
  180. package/src/pipeline/runCli.orchestration.test.ts +28 -12
  181. package/src/pipeline/runCli.test.ts +17 -4
  182. package/src/pipeline/runCli.ts +162 -19
  183. package/src/testing/cli.ts +30 -2
  184. package/src/testing/dsFixtures.ts +21 -4
  185. package/src/testing/index.ts +1 -4
  186. package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
  187. package/src/testing/integration/cli-user-stories.test.ts +63 -0
  188. package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
  189. package/src/testing/integration/edge-cases.test.ts +62 -50
  190. package/src/testing/integration/executor-execution.test.ts +6 -4
  191. package/src/testing/integration/executor-golden.test.ts +2 -2
  192. package/src/testing/integration/mcp-surface.test.ts +40 -15
  193. package/src/testing/integration/parity.test.ts +119 -70
  194. package/src/testing/setupXdgIsolation.ts +16 -5
  195. package/src/testing/standardParityFixtures.ts +26 -83
  196. package/src/testing/store.ts +4 -4
  197. package/src/domains/create/commands/component.test.ts +0 -166
  198. package/src/domains/create/commands/component.ts +0 -123
  199. package/src/domains/create/commands/index.ts +0 -3
  200. package/src/domains/create/commands/package.test.ts +0 -35
  201. package/src/domains/create/commands/package.ts +0 -21
  202. package/src/domains/create/generators.ts +0 -8
  203. package/src/domains/standard/commands/categories.test.ts +0 -70
  204. package/src/domains/standard/commands/categories.ts +0 -48
  205. package/src/domains/standard/commands/index.ts +0 -6
  206. package/src/domains/standard/commands/list.test.ts +0 -105
  207. package/src/domains/standard/commands/list.ts +0 -11
  208. package/src/domains/standard/commands/lookup.test.ts +0 -135
  209. package/src/domains/standard/commands/lookup.ts +0 -14
  210. package/src/domains/standard/commands/sample.ts +0 -57
  211. package/src/domains/standard/formatters/categories.test.ts +0 -44
  212. package/src/domains/standard/formatters/categories.ts +0 -38
  213. package/src/domains/standard/formatters/index.ts +0 -10
  214. package/src/domains/standard/formatters/list.test.ts +0 -169
  215. package/src/domains/standard/formatters/list.ts +0 -184
  216. package/src/domains/standard/formatters/lookup.test.ts +0 -95
  217. package/src/domains/standard/formatters/lookup.ts +0 -101
  218. package/src/domains/standard/formatters/sample.ts +0 -15
  219. package/src/domains/standard/formatters/types.ts +0 -31
  220. package/src/domains/standard/index.ts +0 -40
  221. package/src/domains/standard/mcp/index.ts +0 -2
  222. package/src/domains/standard/mcp/specs.ts +0 -89
  223. package/src/domains/standard/operations/categories.test.ts +0 -61
  224. package/src/domains/standard/operations/categories.ts +0 -38
  225. package/src/domains/standard/operations/index.ts +0 -6
  226. package/src/domains/standard/operations/list.test.ts +0 -75
  227. package/src/domains/standard/operations/list.ts +0 -64
  228. package/src/domains/standard/operations/lookup.test.ts +0 -58
  229. package/src/domains/standard/operations/lookup.ts +0 -151
  230. package/src/domains/standard/operations/sample.ts +0 -35
  231. package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
  232. package/src/domains/standard/orchestration/index.ts +0 -4
  233. package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
  234. package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
  235. package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
  236. package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
  237. package/src/domains/standard/standardConfig.ts +0 -42
  238. package/src/domains/standard/stories.test.ts +0 -24
  239. package/src/domains/standard/stories.ts +0 -151
  240. package/src/domains/standard/types.ts +0 -15
  241. package/src/domains/tier/commands/index.ts +0 -3
  242. package/src/domains/tier/commands/list.test.ts +0 -67
  243. package/src/domains/tier/commands/list.ts +0 -9
  244. package/src/domains/tier/formatters/index.ts +0 -3
  245. package/src/domains/tier/formatters/list.test.ts +0 -33
  246. package/src/domains/tier/formatters/list.ts +0 -45
  247. package/src/domains/tier/index.ts +0 -17
  248. package/src/domains/tier/mcp/index.ts +0 -2
  249. package/src/domains/tier/mcp/specs.ts +0 -12
  250. package/src/domains/tier/operations/index.ts +0 -3
  251. package/src/domains/tier/operations/list.test.ts +0 -32
  252. package/src/domains/tier/operations/list.ts +0 -33
  253. package/src/domains/tier/stories.test.ts +0 -22
  254. package/src/domains/tier/stories.ts +0 -52
@@ -5,16 +5,19 @@
5
5
  * that D3 operations query against.
6
6
  *
7
7
  * All @prefix declarations derived from {@link TTL_PREFIXES} so that
8
- * a namespace rename in PREFIX_MAP propagates here automatically.
8
+ * a namespace rename in DEFAULT_PREFIX_MAP propagates here automatically.
9
9
  */
10
10
 
11
- import { PREFIX_MAP, TTL_PREFIXES } from "../domains/shared/prefixes.js";
11
+ import {
12
+ DEFAULT_PREFIX_MAP,
13
+ TTL_PREFIXES,
14
+ } from "../domains/shared/prefixes.js";
12
15
 
13
16
  /** Turtle @prefix block for ds: only (most data fixtures). */
14
- const dsPrefix = `@prefix ds: <${PREFIX_MAP.ds}> .`;
17
+ const dsPrefix = `@prefix ds: <${DEFAULT_PREFIX_MAP.ds}> .`;
15
18
 
16
19
  /** Turtle @prefix block for cs: only (standards fixtures). */
17
- const csPrefix = `@prefix cs: <${PREFIX_MAP.cs}> .`;
20
+ const csPrefix = `@prefix cs: <${DEFAULT_PREFIX_MAP.cs}> .`;
18
21
 
19
22
  // =============================================================================
20
23
  // Ontology (TBox) — class and property definitions
@@ -225,6 +228,20 @@ cs:code a owl:DatatypeProperty ;
225
228
  cs:extends a owl:ObjectProperty ;
226
229
  rdfs:domain cs:CodeStandard ;
227
230
  rdfs:range cs:CodeStandard .
231
+
232
+ # ---- TBox extras (header, comments, functional typing, SHACL) ----
233
+
234
+ ds:ontology a owl:Ontology ;
235
+ rdfs:label "Design System Ontology" ;
236
+ owl:versionInfo "0.1.0-test" .
237
+
238
+ ds:Component rdfs:comment "A reusable UI building block." .
239
+
240
+ ds:tier a owl:FunctionalProperty .
241
+
242
+ ds:ComponentShape a <http://www.w3.org/ns/shacl#NodeShape> ;
243
+ <http://www.w3.org/ns/shacl#targetClass> ds:Component ;
244
+ <http://www.w3.org/ns/shacl#property> ds:shapeName, ds:shapeTier .
228
245
  `;
229
246
 
230
247
  // =============================================================================
@@ -10,10 +10,7 @@ export {
10
10
  GRAPHQL_ERROR_TTL,
11
11
  GRAPHQL_FATAL_TTL,
12
12
  } from "./graphqlFixtures.js";
13
- export {
14
- PARITY_GAPS,
15
- STANDARD_PACK_STORY,
16
- } from "./standardParityFixtures.js";
13
+ export { PARITY_GAPS } from "./standardParityFixtures.js";
17
14
  export type { PragmaTestStoreOptions } from "./store.js";
18
15
  export { createTestStore } from "./store.js";
19
16
  export {
@@ -1182,9 +1182,9 @@ No changes were made."
1182
1182
  exports[`golden: runSetupTask — setups > lsp — dry-run 1`] = `
1183
1183
  "Dry run — the following operations would be performed:
1184
1184
 
1185
- ├─ Info Installing Terrazzo LSP extension for VS Code...
1185
+ ├─ Info Ensuring the Terrazzo LSP VS Code extension is installed...
1186
1186
  ├─ Execute bunx @canonical/terrazzo-lsp-extension
1187
- └─ Info ✓ Terrazzo LSP extension installed.
1187
+ └─ Info ✓ Terrazzo LSP extension is installed (up to date).
1188
1188
 
1189
1189
  No changes were made."
1190
1190
  `;
@@ -1193,14 +1193,14 @@ exports[`golden: runSetupTask — setups > lsp — dry-run json 1`] = `
1193
1193
  "[
1194
1194
  {
1195
1195
  "action": "Log",
1196
- "message": "Installing Terrazzo LSP extension for VS Code..."
1196
+ "message": "Ensuring the Terrazzo LSP VS Code extension is installed..."
1197
1197
  },
1198
1198
  {
1199
1199
  "action": "Exec"
1200
1200
  },
1201
1201
  {
1202
1202
  "action": "Log",
1203
- "message": "✓ Terrazzo LSP extension installed."
1203
+ "message": "✓ Terrazzo LSP extension is installed (up to date)."
1204
1204
  }
1205
1205
  ]
1206
1206
  "
@@ -52,6 +52,30 @@ function parseJson<T>(result: CommandResult): T {
52
52
  }
53
53
 
54
54
  describe("CLI user stories", () => {
55
+ it("story: a first-time user is greeted once and gets a global config", () => {
56
+ const workspace = createWorkspace();
57
+ const freshXdg = mkdtempSync(join(tmpdir(), "pragma-firstrun-e2e-"));
58
+ tempDirs.add(freshXdg);
59
+ const env = { XDG_CONFIG_HOME: freshXdg };
60
+
61
+ // First invocation: greeting on stderr, config created, stdout untouched.
62
+ const first = runCommand(["info", "--format", "json"], workspace, env);
63
+ expect(first.exitCode).toBe(0);
64
+ expect(first.stderr).toContain("pre-release pragma CLI");
65
+ expect(first.stderr).toContain(join(freshXdg, "pragma", "config.json"));
66
+ expect(() => JSON.parse(first.stdout)).not.toThrow();
67
+ expect(
68
+ JSON.parse(
69
+ readFileSync(join(freshXdg, "pragma", "config.json"), "utf-8"),
70
+ ),
71
+ ).toEqual({});
72
+
73
+ // Second invocation: no greeting — first-run happens once.
74
+ const second = runCommand(["info", "--format", "json"], workspace, env);
75
+ expect(second.exitCode).toBe(0);
76
+ expect(second.stderr.trim()).toBe("");
77
+ }, 40_000);
78
+
55
79
  it("story: a new contributor verifies the local installation with info", () => {
56
80
  const workspace = createWorkspace();
57
81
 
@@ -195,4 +219,43 @@ describe("CLI user stories", () => {
195
219
  expect(existsSync(indexFile)).toBe(true);
196
220
  expect(readFileSync(indexFile, "utf-8")).toContain("./MySth");
197
221
  }, 20_000);
222
+
223
+ it("story: --version works at the root, noun, and verb levels", () => {
224
+ const workspace = createWorkspace();
225
+ const root = runCommand(["--version"], workspace);
226
+ const verb = runCommand(["block", "list", "--version"], workspace);
227
+
228
+ expectSuccess(root);
229
+ expectSuccess(verb);
230
+ expect(root.stdout.trim()).toBe(verb.stdout.trim());
231
+ expect(root.stdout.trim()).toMatch(/^\d+\.\d+\.\d+/);
232
+ }, 20_000);
233
+
234
+ it("story: an unknown verb under a known noun lists that noun's verbs", () => {
235
+ const workspace = createWorkspace();
236
+
237
+ const result = runCommand(["standard", "frobnicate"], workspace);
238
+
239
+ expect(result.exitCode).not.toBe(0);
240
+ // Errors, then defers to the category that exists.
241
+ expect(result.stderr).toContain("unknown command 'frobnicate'");
242
+ expect(result.stderr).toContain("pragma standard <verb>");
243
+ expect(result.stderr).toContain("list");
244
+ expect(result.stderr).toContain("lookup");
245
+ }, 20_000);
246
+
247
+ it("story: a global flag before the noun still lists that noun's verbs", () => {
248
+ const workspace = createWorkspace();
249
+
250
+ // `--format json` sits before the noun; the flag *value* (`json`) must not
251
+ // be mistaken for the noun, or no verbs would be suggested.
252
+ const result = runCommand(
253
+ ["--format", "json", "standard", "frobnicate"],
254
+ workspace,
255
+ );
256
+
257
+ expect(result.exitCode).not.toBe(0);
258
+ expect(result.stderr).toContain("pragma standard <verb>");
259
+ expect(result.stderr).toContain("lookup");
260
+ }, 20_000);
198
261
  });
@@ -27,10 +27,10 @@ import {
27
27
  executeGenerator,
28
28
  runGeneratorTask,
29
29
  } from "@canonical/cli-core";
30
+ import { generators as componentGenerators } from "@canonical/summon-component";
30
31
  import type { GeneratorDefinition } from "@canonical/summon-core";
31
32
  import { generators as packageGenerators } from "@canonical/summon-package";
32
33
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
33
- import { COMPONENT_GENERATORS } from "../../domains/create/generators.js";
34
34
 
35
35
  interface Case {
36
36
  readonly name: string;
@@ -41,7 +41,7 @@ interface Case {
41
41
  const cases: Case[] = [
42
42
  {
43
43
  name: "component/react",
44
- gen: COMPONENT_GENERATORS.react as GeneratorDefinition,
44
+ gen: componentGenerators["component/react"] as GeneratorDefinition,
45
45
  answers: { componentPath: "src/components/Button" },
46
46
  },
47
47
  {
@@ -10,6 +10,7 @@
10
10
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
11
11
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
12
12
  import type { PragmaRuntime } from "../../domains/shared/runtime.js";
13
+ import { TOKEN_READ_SURFACE_ENABLED } from "../../domains/token/featureFlag.js";
13
14
  import createTestMcpClient from "../helpers/createTestMcpClient.js";
14
15
  import createTestRuntime from "../helpers/createTestRuntime.js";
15
16
 
@@ -122,21 +123,24 @@ describe("all-fail batch lookups", () => {
122
123
  expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
123
124
  });
124
125
 
125
- it("token_lookup: unknown token → error", async () => {
126
- const res = await client.callTool({
127
- name: "token_lookup",
128
- arguments: { names: ["color.nonexistent"] },
129
- });
130
- const body = parseEnvelope(res);
131
- expect(body.ok).toBe(true);
132
- const data = body.data as {
133
- results: unknown[];
134
- errors: { query: string; code: string }[];
135
- };
136
- expect(data.results).toHaveLength(0);
137
- expect(data.errors).toHaveLength(1);
138
- expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
139
- });
126
+ it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
127
+ "token_lookup: unknown token → error",
128
+ async () => {
129
+ const res = await client.callTool({
130
+ name: "token_lookup",
131
+ arguments: { names: ["color.nonexistent"] },
132
+ });
133
+ const body = parseEnvelope(res);
134
+ expect(body.ok).toBe(true);
135
+ const data = body.data as {
136
+ results: unknown[];
137
+ errors: { query: string; code: string }[];
138
+ };
139
+ expect(data.results).toHaveLength(0);
140
+ expect(data.errors).toHaveLength(1);
141
+ expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
142
+ },
143
+ );
140
144
  });
141
145
 
142
146
  // ---------------------------------------------------------------------------
@@ -144,33 +148,38 @@ describe("all-fail batch lookups", () => {
144
148
  // ---------------------------------------------------------------------------
145
149
 
146
150
  describe("empty result sets", () => {
147
- it("standard_list with nonexistent category → error envelope", async () => {
151
+ it("standard_list with nonexistent category → empty rows", async () => {
148
152
  const res = await client.callTool({
149
153
  name: "standard_list",
150
154
  arguments: { category: "nonexistent_category" },
151
155
  });
152
156
  const body = parseEnvelope(res);
153
- // resolveStandardList throws when no results found
154
- expect(body.ok).toBe(false);
155
- const error = body.error as { code: string };
156
- expect(error.code).toBeDefined();
157
+ // The bundled pack's value-free category filter returns zero rows for
158
+ // an unmatched value; the old EMPTY_RESULTS error is a pinned
159
+ // PARITY_GAP of the standard cutover (packs have no emptyError hook).
160
+ expect(body.ok).toBe(true);
161
+ expect(body.data).toEqual([]);
162
+ expect((body.meta as { count: number }).count).toBe(0);
157
163
  });
158
164
 
159
- it("token_list with nonexistent category → empty or error", async () => {
160
- const res = await client.callTool({
161
- name: "token_list",
162
- arguments: { category: "NonexistentType" },
163
- });
164
- const body = parseEnvelope(res);
165
- if (body.ok) {
166
- const data = body.data as unknown[];
167
- expect(data).toHaveLength(0);
168
- } else {
169
- // Token list may throw on empty results
170
- const error = body.error as { code: string };
171
- expect(error.code).toBeDefined();
172
- }
173
- });
165
+ it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
166
+ "token_list with nonexistent category → empty or error",
167
+ async () => {
168
+ const res = await client.callTool({
169
+ name: "token_list",
170
+ arguments: { category: "NonexistentType" },
171
+ });
172
+ const body = parseEnvelope(res);
173
+ if (body.ok) {
174
+ const data = body.data as unknown[];
175
+ expect(data).toHaveLength(0);
176
+ } else {
177
+ // Token list may throw on empty results
178
+ const error = body.error as { code: string };
179
+ expect(error.code).toBeDefined();
180
+ }
181
+ },
182
+ );
174
183
  });
175
184
 
176
185
  // ---------------------------------------------------------------------------
@@ -245,19 +254,22 @@ describe("condensed mode error rendering", () => {
245
254
  expect(text).toContain("nonexistent");
246
255
  });
247
256
 
248
- it("token_lookup condensed: includes error for unknown token", async () => {
249
- const res = await client.callTool({
250
- name: "token_lookup",
251
- arguments: {
252
- names: ["color.primary", "color.nonexistent"],
253
- condensed: true,
254
- },
255
- });
256
- const body = parseEnvelope(res);
257
- expect(body.condensed).toBe(true);
258
- const text = body.text as string;
259
- expect(text).toContain("color.primary");
260
- expect(text).toContain("Errors");
261
- expect(text).toContain("color.nonexistent");
262
- });
257
+ it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
258
+ "token_lookup condensed: includes error for unknown token",
259
+ async () => {
260
+ const res = await client.callTool({
261
+ name: "token_lookup",
262
+ arguments: {
263
+ names: ["color.primary", "color.nonexistent"],
264
+ condensed: true,
265
+ },
266
+ });
267
+ const body = parseEnvelope(res);
268
+ expect(body.condensed).toBe(true);
269
+ const text = body.text as string;
270
+ expect(text).toContain("color.primary");
271
+ expect(text).toContain("Errors");
272
+ expect(text).toContain("color.nonexistent");
273
+ },
274
+ );
263
275
  });
@@ -20,10 +20,10 @@ import {
20
20
  import { tmpdir } from "node:os";
21
21
  import { join } from "node:path";
22
22
  import { runGeneratorTask } from "@canonical/cli-core";
23
+ import { generators as componentGenerators } from "@canonical/summon-component";
23
24
  import { generators as packageGenerators } from "@canonical/summon-package";
24
25
  import type { Task } from "@canonical/task";
25
26
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
26
- import { COMPONENT_GENERATORS } from "../../domains/create/generators.js";
27
27
  import setupMcp from "../../domains/setup/operations/setupMcp.js";
28
28
 
29
29
  interface Case {
@@ -37,15 +37,17 @@ const packageParams = { name: "@canonical/example", type: "library" };
37
37
  const cases: Case[] = [
38
38
  {
39
39
  name: "component/react",
40
- build: () => COMPONENT_GENERATORS.react.generate(componentParams),
40
+ build: () =>
41
+ componentGenerators["component/react"].generate(componentParams),
41
42
  },
42
43
  {
43
44
  name: "component/svelte",
44
- build: () => COMPONENT_GENERATORS.svelte.generate(componentParams),
45
+ build: () =>
46
+ componentGenerators["component/svelte"].generate(componentParams),
45
47
  },
46
48
  {
47
49
  name: "component/lit",
48
- build: () => COMPONENT_GENERATORS.lit.generate(componentParams),
50
+ build: () => componentGenerators["component/lit"].generate(componentParams),
49
51
  },
50
52
  {
51
53
  name: "package/library",
@@ -16,9 +16,9 @@ import { dirname, resolve } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
17
  import type { CommandContext, CommandResult } from "@canonical/cli-core";
18
18
  import { executeGenerator } from "@canonical/cli-core";
19
+ import { generators as componentGenerators } from "@canonical/summon-component";
19
20
  import { generators as packageGenerators } from "@canonical/summon-package";
20
21
  import { describe, expect, it } from "vitest";
21
- import { COMPONENT_GENERATORS } from "../../domains/create/generators.js";
22
22
  import runSetupTask from "../../domains/setup/helpers/runSetupTask.js";
23
23
  import setupCompletions from "../../domains/setup/operations/setupCompletions.js";
24
24
  import setupLsp from "../../domains/setup/operations/setupLsp.js";
@@ -65,7 +65,7 @@ const packageParams = { name: "@canonical/example", type: "library" };
65
65
 
66
66
  describe("golden: executeGenerator — components", () => {
67
67
  for (const framework of ["react", "svelte", "lit"] as const) {
68
- const gen = COMPONENT_GENERATORS[framework];
68
+ const gen = componentGenerators[`component/${framework}`];
69
69
 
70
70
  it(`${framework} — dry-run plan`, async () => {
71
71
  const result = await executeGenerator(
@@ -9,6 +9,7 @@
9
9
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
10
10
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
11
11
  import type { PragmaRuntime } from "../../domains/shared/runtime.js";
12
+ import { TOKEN_READ_SURFACE_ENABLED } from "../../domains/token/featureFlag.js";
12
13
  import createTestMcpClient from "../helpers/createTestMcpClient.js";
13
14
  import createTestRuntime from "../helpers/createTestRuntime.js";
14
15
 
@@ -55,7 +56,7 @@ describe("capabilities", () => {
55
56
  expect(toolNames).toContain("block_list");
56
57
  expect(toolNames).toContain("capabilities");
57
58
  expect(data.tools.every((t) => t.use_when.length > 0)).toBe(true);
58
- expect(data.counts.total).toBe(30);
59
+ expect(data.counts.total).toBe(TOKEN_READ_SURFACE_ENABLED ? 34 : 31);
59
60
  expect(data.conventions).toBeDefined();
60
61
  expect(data.version).toBeDefined();
61
62
  });
@@ -190,7 +191,11 @@ describe("standard_lookup", () => {
190
191
  results: { name: string; extends?: string }[];
191
192
  };
192
193
  expect(data.results[0]?.name).toBe("react/component/props");
193
- expect(data.results[0]?.extends).toBe("cs:react_folder");
194
+ // Pack entities keep cs:extends as the raw IRI in data (compaction is
195
+ // display-time only) — a pinned PARITY_GAP of the standard cutover.
196
+ expect(data.results[0]?.extends).toBe(
197
+ "http://pragma.canonical.com/codestandards#react_folder",
198
+ );
194
199
  });
195
200
  });
196
201
 
@@ -240,7 +245,7 @@ describe("modifier_lookup", () => {
240
245
  // Token
241
246
  // ---------------------------------------------------------------------------
242
247
 
243
- describe("token_list", () => {
248
+ describe.skipIf(!TOKEN_READ_SURFACE_ENABLED)("token_list", () => {
244
249
  it("returns tokens", async () => {
245
250
  const res = await client.callTool({
246
251
  name: "token_list",
@@ -252,7 +257,7 @@ describe("token_list", () => {
252
257
  });
253
258
  });
254
259
 
255
- describe("token_lookup", () => {
260
+ describe.skipIf(!TOKEN_READ_SURFACE_ENABLED)("token_lookup", () => {
256
261
  it("returns token with values", async () => {
257
262
  const res = await client.callTool({
258
263
  name: "token_lookup",
@@ -273,15 +278,16 @@ describe("token_lookup", () => {
273
278
  // ---------------------------------------------------------------------------
274
279
 
275
280
  describe("tier_list", () => {
276
- it("returns tiers with hierarchy", async () => {
281
+ it("returns the tiers (bundled pack)", async () => {
277
282
  const res = await client.callTool({
278
283
  name: "tier_list",
279
284
  arguments: {},
280
285
  });
281
286
  const body = parseEnvelope(res);
282
287
  expect(body.ok).toBe(true);
283
- const data = body.data as { path: string }[];
284
- expect(data.some((t) => t.path === "global")).toBe(true);
288
+ // Bundled tier pack: uniform `{uri, name}` rows (no bespoke hierarchy).
289
+ const data = body.data as { name: string }[];
290
+ expect(data.some((t) => t.name === "global")).toBe(true);
285
291
  });
286
292
  });
287
293
 
@@ -480,15 +486,34 @@ describe("disclosure", () => {
480
486
  expect(meta.disclosure).toBe("digest");
481
487
  });
482
488
 
483
- it("standard_list with detailed returns enriched data", async () => {
484
- const res = await client.callTool({
485
- name: "standard_list",
486
- arguments: { detailed: true },
489
+ // `standard list --detailed` was retired with the standard cutover (a
490
+ // pinned PARITY_GAP: pack lists are single-level); per-entity disclosure
491
+ // now lives on the lookup's `detail` parameter and its legacy aliases.
492
+ it("standard_lookup honors the detail parameter", async () => {
493
+ const summary = await client.callTool({
494
+ name: "standard_lookup",
495
+ arguments: {
496
+ names: ["react/component/folder-structure"],
497
+ detail: "summary",
498
+ },
487
499
  });
488
- const body = parseEnvelope(res);
489
- expect(body.ok).toBe(true);
490
- const meta = body.meta as { disclosure?: string };
491
- expect(meta.disclosure).toBe("detailed");
500
+ const summaryData = parseEnvelope(summary).data as {
501
+ results: Record<string, unknown>[];
502
+ };
503
+ expect(summaryData.results[0]).not.toHaveProperty("dos");
504
+
505
+ const detailed = await client.callTool({
506
+ name: "standard_lookup",
507
+ arguments: {
508
+ names: ["react/component/folder-structure"],
509
+ detail: "detailed",
510
+ },
511
+ });
512
+ const detailedData = parseEnvelope(detailed).data as {
513
+ results: Record<string, unknown>[];
514
+ };
515
+ expect(detailedData.results[0]).toHaveProperty("dos");
516
+ expect(detailedData.results[0]).toHaveProperty("donts");
492
517
  });
493
518
  });
494
519