@canonical/pragma-cli 0.29.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 (410) hide show
  1. package/README.md +128 -47
  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/defaultConfig.test.ts +33 -0
  12. package/src/config/defaultConfig.ts +59 -0
  13. package/src/config/defaults.json +17 -0
  14. package/src/config/findProjectConfigPath.test.ts +53 -0
  15. package/src/config/findProjectConfigPath.ts +36 -0
  16. package/src/config/index.ts +17 -2
  17. package/src/config/mergeConfigFileUpdate.ts +58 -0
  18. package/src/config/parseConfigValues.ts +217 -0
  19. package/src/config/readConfig.test.ts +104 -13
  20. package/src/config/readConfig.ts +12 -153
  21. package/src/config/readConfigLayers.test.ts +109 -0
  22. package/src/config/readConfigLayers.ts +118 -0
  23. package/src/config/resolveGlobalConfigPath.ts +13 -0
  24. package/src/config/resolveWriteConfigPath.test.ts +53 -0
  25. package/src/config/resolveWriteConfigPath.ts +31 -0
  26. package/src/config/types.ts +83 -1
  27. package/src/config/writeConfig.ts +25 -55
  28. package/src/constants.ts +3 -1
  29. package/src/domains/block/commands/list.ts +5 -90
  30. package/src/domains/block/commands/lookup.ts +8 -167
  31. package/src/domains/block/commands/sample.ts +2 -1
  32. package/src/domains/block/formatters/list.test.ts +3 -3
  33. package/src/domains/block/formatters/lookup.test.ts +5 -5
  34. package/src/domains/block/mcp/specs.ts +16 -196
  35. package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
  36. package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
  37. package/src/domains/block/stories.test.ts +28 -0
  38. package/src/domains/block/stories.ts +260 -0
  39. package/src/domains/config/commands/channel.test.ts +30 -3
  40. package/src/domains/config/commands/channel.ts +25 -8
  41. package/src/domains/config/commands/configScope.ts +46 -0
  42. package/src/domains/config/commands/framework.test.ts +32 -1
  43. package/src/domains/config/commands/framework.ts +26 -8
  44. package/src/domains/config/commands/show.ts +5 -45
  45. package/src/domains/config/commands/tier.test.ts +20 -2
  46. package/src/domains/config/commands/tier.ts +25 -8
  47. package/src/domains/config/commands/trace.test.ts +19 -2
  48. package/src/domains/config/commands/trace.ts +6 -3
  49. package/src/domains/config/formatters/channel.test.ts +20 -6
  50. package/src/domains/config/formatters/channel.ts +14 -11
  51. package/src/domains/config/formatters/framework.ts +14 -11
  52. package/src/domains/config/formatters/show.test.ts +27 -0
  53. package/src/domains/config/formatters/show.ts +27 -4
  54. package/src/domains/config/formatters/tier.test.ts +26 -10
  55. package/src/domains/config/formatters/tier.ts +19 -11
  56. package/src/domains/config/formatters/trace.ts +7 -7
  57. package/src/domains/config/mcp/specs.ts +33 -53
  58. package/src/domains/config/operations/show.test.ts +18 -0
  59. package/src/domains/config/operations/show.ts +7 -1
  60. package/src/domains/config/operations/types.ts +7 -0
  61. package/src/domains/config/operations/validateTier.test.ts +9 -0
  62. package/src/domains/config/operations/validateTier.ts +10 -2
  63. package/src/domains/config/stories.ts +42 -0
  64. package/src/domains/config/tasks/index.ts +1 -0
  65. package/src/domains/config/tasks/setChannel.ts +13 -28
  66. package/src/domains/config/tasks/setFramework.ts +11 -30
  67. package/src/domains/config/tasks/setTier.ts +10 -29
  68. package/src/domains/config/tasks/setTrace.ts +10 -28
  69. package/src/domains/config/tasks/writeConfigField.ts +41 -0
  70. package/src/domains/create/generatorPack/bundled.ts +49 -0
  71. package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
  72. package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
  73. package/src/domains/create/index.ts +13 -7
  74. package/src/domains/create/mcp/specs.ts +6 -161
  75. package/src/domains/create/renderGeneratorUi.test.ts +103 -0
  76. package/src/domains/create/renderGeneratorUi.ts +65 -0
  77. package/src/domains/doctor/formatters/doctor.test.ts +50 -2
  78. package/src/domains/doctor/formatters/doctor.ts +90 -38
  79. package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
  80. package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
  81. package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
  82. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
  83. package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
  84. package/src/domains/doctor/operations/checks/index.ts +1 -0
  85. package/src/domains/doctor/operations/checks.test.ts +160 -7
  86. package/src/domains/doctor/operations/runChecks.test.ts +15 -6
  87. package/src/domains/doctor/operations/runChecks.ts +2 -0
  88. package/src/domains/doctor/operations/types.ts +23 -1
  89. package/src/domains/graph/commands/inspect.test.ts +2 -2
  90. package/src/domains/graph/commands/query.test.ts +5 -3
  91. package/src/domains/graph/formatters/inspect.ts +7 -7
  92. package/src/domains/graph/formatters/query.test.ts +87 -0
  93. package/src/domains/graph/formatters/query.ts +42 -5
  94. package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
  95. package/src/domains/graph/helpers/resolveUri.ts +28 -21
  96. package/src/domains/graph/operations/inspectUri.test.ts +5 -5
  97. package/src/domains/graphql/commands/build.test.ts +5 -3
  98. package/src/domains/graphql/commands/build.ts +8 -16
  99. package/src/domains/graphql/commands/check.test.ts +5 -3
  100. package/src/domains/graphql/commands/check.ts +7 -13
  101. package/src/domains/graphql/commands/index.ts +1 -0
  102. package/src/domains/graphql/commands/serve.test.ts +46 -0
  103. package/src/domains/graphql/commands/serve.ts +141 -0
  104. package/src/domains/graphql/constants.ts +10 -0
  105. package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
  106. package/src/domains/graphql/helpers/gatherSources.ts +58 -0
  107. package/src/domains/graphql/helpers/index.ts +2 -0
  108. package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
  109. package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
  110. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
  111. package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
  112. package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
  113. package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
  114. package/src/domains/graphql/index.ts +9 -6
  115. package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
  116. package/src/domains/graphql/operations/compileSchema.ts +23 -25
  117. package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
  118. package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
  119. package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
  120. package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
  121. package/src/domains/graphql/operations/index.ts +10 -0
  122. package/src/domains/info/formatters/upgrade.test.ts +7 -4
  123. package/src/domains/info/formatters/upgrade.ts +3 -1
  124. package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
  125. package/src/domains/info/operations/collectEntityCounts.ts +123 -0
  126. package/src/domains/info/operations/collectInfo.test.ts +14 -3
  127. package/src/domains/info/operations/collectInfo.ts +12 -15
  128. package/src/domains/info/operations/index.ts +1 -0
  129. package/src/domains/info/types.ts +13 -0
  130. package/src/domains/llm/capabilities.test.ts +24 -4
  131. package/src/domains/llm/commands/capabilities.ts +12 -3
  132. package/src/domains/llm/commands/llm.ts +5 -1
  133. package/src/domains/llm/data/commandReference.ts +20 -11
  134. package/src/domains/llm/data/decisionTrees.ts +10 -5
  135. package/src/domains/llm/data/toolCatalog.ts +55 -18
  136. package/src/domains/llm/formatters/orientation.ts +5 -1
  137. package/src/domains/llm/llmCommand.test.ts +11 -3
  138. package/src/domains/llm/mcp/specs.ts +3 -4
  139. package/src/domains/llm/operations/collectContext.test.ts +64 -0
  140. package/src/domains/llm/operations/collectContext.ts +7 -17
  141. package/src/domains/modifier/commands/list.ts +5 -38
  142. package/src/domains/modifier/commands/lookup.ts +8 -79
  143. package/src/domains/modifier/commands/sample.ts +2 -1
  144. package/src/domains/modifier/mcp/specs.ts +15 -94
  145. package/src/domains/modifier/operations/lookup.ts +5 -3
  146. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
  147. package/src/domains/modifier/stories.test.ts +20 -0
  148. package/src/domains/modifier/stories.ts +86 -0
  149. package/src/domains/ontology/commands/list.test.ts +5 -3
  150. package/src/domains/ontology/commands/list.ts +5 -58
  151. package/src/domains/ontology/commands/show.test.ts +85 -9
  152. package/src/domains/ontology/commands/show.ts +6 -58
  153. package/src/domains/ontology/formatters/index.ts +1 -0
  154. package/src/domains/ontology/formatters/list.ts +37 -12
  155. package/src/domains/ontology/formatters/show.ts +361 -64
  156. package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
  157. package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
  158. package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
  159. package/src/domains/ontology/helpers/index.ts +14 -0
  160. package/src/domains/ontology/helpers/queryClasses.ts +32 -16
  161. package/src/domains/ontology/helpers/queryProperties.ts +39 -6
  162. package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
  163. package/src/domains/ontology/mcp/specs.ts +7 -84
  164. package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
  165. package/src/domains/ontology/operations/listOntologies.ts +126 -30
  166. package/src/domains/ontology/operations/showOntology.test.ts +131 -12
  167. package/src/domains/ontology/operations/showOntology.ts +289 -7
  168. package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
  169. package/src/domains/ontology/stories.test.ts +36 -0
  170. package/src/domains/ontology/stories.ts +153 -0
  171. package/src/domains/refs/operations/updateRefs.ts +2 -38
  172. package/src/domains/setup/commands/index.ts +1 -0
  173. package/src/domains/setup/commands/setup.ts +70 -0
  174. package/src/domains/setup/commands/skills.test.ts +6 -2
  175. package/src/domains/setup/commands/skills.ts +6 -2
  176. package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
  177. package/src/domains/setup/helpers/runSetupTask.ts +62 -70
  178. package/src/domains/setup/index.ts +7 -3
  179. package/src/domains/setup/operations/setupLsp.test.ts +3 -2
  180. package/src/domains/setup/operations/setupLsp.ts +7 -2
  181. package/src/domains/setup/operations/setupMcp.test.ts +6 -1
  182. package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
  183. package/src/domains/shared/bootStore.test.ts +82 -0
  184. package/src/domains/shared/bootStore.ts +57 -18
  185. package/src/domains/shared/compactUri.test.ts +11 -0
  186. package/src/domains/shared/compactUri.ts +10 -5
  187. package/src/domains/shared/context.ts +2 -2
  188. package/src/domains/shared/contracts.ts +11 -0
  189. package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
  190. package/src/domains/shared/createInteractivePromptSession.ts +236 -0
  191. package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
  192. package/src/domains/shared/extractLocalName.test.ts +3 -3
  193. package/src/domains/shared/formatters.ts +23 -2
  194. package/src/domains/shared/listTiers.ts +40 -0
  195. package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
  196. package/src/domains/shared/loaders/bundledLoader.ts +158 -12
  197. package/src/domains/shared/loaders/gitLoader.ts +1 -2
  198. package/src/domains/shared/loaders/index.ts +4 -1
  199. package/src/domains/shared/loaders/localLoader.ts +2 -4
  200. package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
  201. package/src/domains/shared/loaders/readPackageDir.ts +82 -9
  202. package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
  203. package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
  204. package/src/domains/shared/lookupMany.test.ts +135 -0
  205. package/src/domains/shared/lookupMany.ts +50 -2
  206. package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
  207. package/src/domains/shared/mergeAndParseRefs.ts +44 -0
  208. package/src/domains/shared/namespaces.ts +13 -5
  209. package/src/domains/shared/packages.ts +10 -17
  210. package/src/domains/shared/parseSampleCount.test.ts +47 -0
  211. package/src/domains/shared/parseSampleCount.ts +45 -0
  212. package/src/domains/shared/prefixes.test.ts +211 -0
  213. package/src/domains/shared/prefixes.ts +182 -15
  214. package/src/domains/shared/renderers.ts +5 -5
  215. package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
  216. package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
  217. package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
  218. package/src/domains/shared/resolveLookupExitCode.ts +42 -0
  219. package/src/domains/shared/runtime.ts +4 -63
  220. package/src/domains/shared/semanticPackage.test.ts +89 -0
  221. package/src/domains/shared/semanticPackage.ts +34 -3
  222. package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
  223. package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
  224. package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
  225. package/src/domains/shared/stories/compileLookupTool.ts +96 -0
  226. package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
  227. package/src/domains/shared/stories/compileReadCommand.ts +48 -0
  228. package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
  229. package/src/domains/shared/stories/compileReadTool.ts +38 -0
  230. package/src/domains/shared/stories/condense.test.ts +18 -0
  231. package/src/domains/shared/stories/condense.ts +15 -0
  232. package/src/domains/shared/stories/index.ts +20 -0
  233. package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
  234. package/src/domains/shared/stories/normalizeNames.ts +22 -0
  235. package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
  236. package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
  237. package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
  238. package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
  239. package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
  240. package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
  241. package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
  242. package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
  243. package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
  244. package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
  245. package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
  246. package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
  247. package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
  248. package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
  249. package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
  250. package/src/domains/shared/stories/pack/index.ts +42 -0
  251. package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
  252. package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
  253. package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
  254. package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
  255. package/src/domains/shared/stories/pack/types.ts +224 -0
  256. package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
  257. package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
  258. package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
  259. package/src/domains/shared/stories/projectCliParameters.ts +57 -0
  260. package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
  261. package/src/domains/shared/stories/projectToolParams.ts +32 -0
  262. package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
  263. package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
  264. package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
  265. package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
  266. package/src/domains/shared/stories/types.ts +179 -0
  267. package/src/domains/shared/suggestions/expandGlob.ts +1 -0
  268. package/src/domains/shared/suggestions/suggestNames.ts +43 -10
  269. package/src/domains/shared/types/index.ts +5 -9
  270. package/src/domains/shared/types/ontology.ts +119 -24
  271. package/src/domains/shared/types/standard.ts +9 -52
  272. package/src/domains/skill/commands/index.ts +3 -1
  273. package/src/domains/skill/commands/list.test.ts +6 -2
  274. package/src/domains/skill/commands/list.ts +5 -63
  275. package/src/domains/skill/commands/lookup.test.ts +118 -0
  276. package/src/domains/skill/commands/lookup.ts +14 -0
  277. package/src/domains/skill/formatters/index.ts +1 -0
  278. package/src/domains/skill/formatters/lookup.test.ts +46 -0
  279. package/src/domains/skill/formatters/lookup.ts +57 -0
  280. package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
  281. package/src/domains/skill/index.ts +7 -3
  282. package/src/domains/skill/mcp/specs.ts +10 -29
  283. package/src/domains/skill/operations/discover.test.ts +61 -30
  284. package/src/domains/skill/operations/discover.ts +28 -37
  285. package/src/domains/skill/operations/index.ts +2 -1
  286. package/src/domains/skill/operations/list.test.ts +15 -7
  287. package/src/domains/skill/operations/lookup.test.ts +91 -0
  288. package/src/domains/skill/operations/lookup.ts +70 -0
  289. package/src/domains/skill/stories.test.ts +43 -0
  290. package/src/domains/skill/stories.ts +103 -0
  291. package/src/domains/skill/types.ts +8 -0
  292. package/src/domains/token/commands/lookup.test.ts +8 -0
  293. package/src/domains/token/commands/lookup.ts +12 -0
  294. package/src/domains/token/commands/sample.ts +2 -1
  295. package/src/domains/token/featureFlag.ts +17 -0
  296. package/src/domains/token/index.ts +5 -0
  297. package/src/domains/token/mcp/specs.ts +18 -2
  298. package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
  299. package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
  300. package/src/domains/trace/commands/show.ts +30 -4
  301. package/src/domains/trace/tracePlugin.test.ts +1 -1
  302. package/src/index.ts +1 -13
  303. package/src/mcp/registerResources.test.ts +382 -74
  304. package/src/mcp/registerResources.ts +5 -348
  305. package/src/mcp/resources/buildGraphIndex.ts +167 -0
  306. package/src/mcp/resources/buildResourceList.test.ts +97 -0
  307. package/src/mcp/resources/buildResourceList.ts +176 -0
  308. package/src/mcp/resources/classifyEntity.test.ts +40 -0
  309. package/src/mcp/resources/classifyEntity.ts +27 -0
  310. package/src/mcp/resources/constants.ts +105 -0
  311. package/src/mcp/resources/displayPredicates.ts +62 -0
  312. package/src/mcp/resources/pickFirstValue.test.ts +29 -0
  313. package/src/mcp/resources/pickFirstValue.ts +26 -0
  314. package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
  315. package/src/mcp/resources/pickPrimaryType.ts +26 -0
  316. package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
  317. package/src/mcp/resources/rankUriCompletions.ts +64 -0
  318. package/src/mcp/resources/readEntity.ts +169 -0
  319. package/src/mcp/resources/registerResources.ts +126 -0
  320. package/src/mcp/resources/types.ts +128 -0
  321. package/src/mcp/tools/index.ts +22 -5
  322. package/src/mcp/tools/registerTools.test.ts +167 -34
  323. package/src/mcp/types.ts +4 -0
  324. package/src/pipeline/collectCommands.test.ts +151 -1
  325. package/src/pipeline/collectCommands.ts +35 -13
  326. package/src/pipeline/createProgram.test.ts +5 -1
  327. package/src/pipeline/createProgram.ts +2 -2
  328. package/src/pipeline/firstRun.test.ts +80 -0
  329. package/src/pipeline/firstRun.ts +99 -0
  330. package/src/pipeline/parseGlobalFlags.test.ts +102 -1
  331. package/src/pipeline/parseGlobalFlags.ts +98 -9
  332. package/src/pipeline/resolveCommandKind.test.ts +12 -0
  333. package/src/pipeline/resolveCommandKind.ts +4 -0
  334. package/src/pipeline/rootHelp.test.ts +87 -0
  335. package/src/pipeline/rootHelp.ts +265 -0
  336. package/src/pipeline/runCli.orchestration.test.ts +28 -12
  337. package/src/pipeline/runCli.test.ts +17 -4
  338. package/src/pipeline/runCli.ts +202 -25
  339. package/src/testing/cli.ts +30 -2
  340. package/src/testing/createTestMcpClient.ts +4 -1
  341. package/src/testing/dsFixtures.ts +21 -4
  342. package/src/testing/index.ts +7 -0
  343. package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
  344. package/src/testing/integration/cli-user-stories.test.ts +63 -0
  345. package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
  346. package/src/testing/integration/edge-cases.test.ts +62 -50
  347. package/src/testing/integration/executor-execution.test.ts +103 -0
  348. package/src/testing/integration/executor-golden.test.ts +145 -0
  349. package/src/testing/integration/mcp-surface.test.ts +40 -15
  350. package/src/testing/integration/parity.test.ts +136 -82
  351. package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
  352. package/src/testing/setupXdgIsolation.ts +26 -0
  353. package/src/testing/standardParityFixtures.ts +35 -0
  354. package/src/testing/store.ts +4 -4
  355. package/src/testing/storyFixtures.ts +63 -0
  356. package/src/domains/create/commands/component.test.ts +0 -166
  357. package/src/domains/create/commands/component.ts +0 -123
  358. package/src/domains/create/commands/index.ts +0 -3
  359. package/src/domains/create/commands/package.test.ts +0 -35
  360. package/src/domains/create/commands/package.ts +0 -21
  361. package/src/domains/create/generators.ts +0 -8
  362. package/src/domains/standard/commands/categories.test.ts +0 -70
  363. package/src/domains/standard/commands/categories.ts +0 -48
  364. package/src/domains/standard/commands/index.ts +0 -6
  365. package/src/domains/standard/commands/list.test.ts +0 -105
  366. package/src/domains/standard/commands/list.ts +0 -90
  367. package/src/domains/standard/commands/lookup.test.ts +0 -135
  368. package/src/domains/standard/commands/lookup.ts +0 -99
  369. package/src/domains/standard/commands/sample.ts +0 -57
  370. package/src/domains/standard/formatters/categories.test.ts +0 -44
  371. package/src/domains/standard/formatters/categories.ts +0 -38
  372. package/src/domains/standard/formatters/index.ts +0 -10
  373. package/src/domains/standard/formatters/list.test.ts +0 -169
  374. package/src/domains/standard/formatters/list.ts +0 -184
  375. package/src/domains/standard/formatters/lookup.test.ts +0 -95
  376. package/src/domains/standard/formatters/lookup.ts +0 -101
  377. package/src/domains/standard/formatters/sample.ts +0 -15
  378. package/src/domains/standard/formatters/types.ts +0 -31
  379. package/src/domains/standard/index.ts +0 -40
  380. package/src/domains/standard/mcp/index.ts +0 -2
  381. package/src/domains/standard/mcp/specs.ts +0 -244
  382. package/src/domains/standard/operations/categories.test.ts +0 -61
  383. package/src/domains/standard/operations/categories.ts +0 -38
  384. package/src/domains/standard/operations/index.ts +0 -6
  385. package/src/domains/standard/operations/list.test.ts +0 -75
  386. package/src/domains/standard/operations/list.ts +0 -64
  387. package/src/domains/standard/operations/lookup.test.ts +0 -58
  388. package/src/domains/standard/operations/lookup.ts +0 -151
  389. package/src/domains/standard/operations/sample.ts +0 -35
  390. package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
  391. package/src/domains/standard/orchestration/index.ts +0 -4
  392. package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
  393. package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
  394. package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
  395. package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
  396. package/src/domains/standard/standardConfig.ts +0 -42
  397. package/src/domains/standard/types.ts +0 -15
  398. package/src/domains/tier/commands/index.ts +0 -3
  399. package/src/domains/tier/commands/list.test.ts +0 -67
  400. package/src/domains/tier/commands/list.ts +0 -59
  401. package/src/domains/tier/formatters/index.ts +0 -3
  402. package/src/domains/tier/formatters/list.test.ts +0 -33
  403. package/src/domains/tier/formatters/list.ts +0 -45
  404. package/src/domains/tier/index.ts +0 -17
  405. package/src/domains/tier/mcp/index.ts +0 -2
  406. package/src/domains/tier/mcp/specs.ts +0 -38
  407. package/src/domains/tier/operations/index.ts +0 -3
  408. package/src/domains/tier/operations/list.test.ts +0 -32
  409. package/src/domains/tier/operations/list.ts +0 -33
  410. package/src/pipeline/runInteractiveCommand.ts +0 -198
@@ -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(29);
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
 
@@ -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
  // ---- Shared utilities ----
13
+ import { readConfigLayers } from "#config";
13
14
  import { VERSION } from "#constants";
14
15
  import { detectInstallSource } from "#package-manager";
15
16
  // ---- Formatters ----
@@ -41,20 +42,10 @@ import {
41
42
  } from "../../domains/ontology/operations/index.js";
42
43
  import { CHANNEL_RELEASES } from "../../domains/shared/filters/buildChannelFilter.js";
43
44
  import { resolveTierChain } from "../../domains/shared/filters/buildTierFilter.js";
45
+ import { listTiers } from "../../domains/shared/listTiers.js";
44
46
  import type { PragmaRuntime } from "../../domains/shared/runtime.js";
45
- import {
46
- type StandardListOutput,
47
- categoriesFormatters as standardCatFmt,
48
- listFormatters as standardListFmt,
49
- lookupFormatters as standardLookupFmt,
50
- } from "../../domains/standard/formatters/index.js";
51
- import {
52
- listCategories,
53
- listStandards,
54
- lookupStandard,
55
- } from "../../domains/standard/operations/index.js";
56
- import { listFormatters as tierListFmt } from "../../domains/tier/formatters/index.js";
57
- import { listTiers } from "../../domains/tier/operations/index.js";
47
+ import { listDomainNames } from "../../domains/shared/suggestions/index.js";
48
+ import { TOKEN_READ_SURFACE_ENABLED } from "../../domains/token/featureFlag.js";
58
49
  import {
59
50
  createLookupFormatters as createTokenLookupFmt,
60
51
  listFormatters as tokenListFmt,
@@ -175,58 +166,83 @@ describe("block parity", () => {
175
166
  // Standard parity
176
167
  // ---------------------------------------------------------------------------
177
168
 
178
- describe("standard parity", () => {
179
- it("standard_list: count and names match", async () => {
180
- const opResult = await listStandards(rt.store);
169
+ // `standard *` is now served by the bundled `standard` story pack (the
170
+ // hand-written domain was deleted). Pack rows/entities are uniform
171
+ // `Record<string,string>` projections with expand arrays, and rendering is
172
+ // the generic pack renderer — so these tests assert *semantic* parity
173
+ // against the store (every standard reachable, by the same names, with the
174
+ // same category/description/dos/donts values) rather than byte equality
175
+ // with the removed bespoke formatters. See PARITY_GAPS in #testing for the
176
+ // pinned, accepted divergences.
177
+ describe("standard parity (bundled pack)", () => {
178
+ it("standard_list returns one {uri, name, category, description} row per standard", async () => {
179
+ const allNames = await listDomainNames(rt.store, "standard");
180
+ expect(allNames.length).toBeGreaterThan(0);
181
+
181
182
  const mcpRes = await client.callTool({
182
183
  name: "standard_list",
183
184
  arguments: {},
184
185
  });
185
- assertParity(opResult, mcpRes);
186
+ const body = parseEnvelope(mcpRes);
187
+ expect(body.ok).toBe(true);
188
+ const rows = body.data as {
189
+ uri?: string;
190
+ name?: string;
191
+ category?: string;
192
+ description?: string;
193
+ }[];
194
+
195
+ // Same standards, same names — nothing dropped or invented.
196
+ const listed = new Set(rows.map((row) => row.name));
197
+ for (const name of allNames) {
198
+ expect(listed.has(name), `"${name}" missing from standard_list`).toBe(
199
+ true,
200
+ );
201
+ }
202
+ for (const row of rows) {
203
+ expect(row.uri).toBeTruthy();
204
+ expect(row.description).toBeTruthy();
205
+ }
186
206
  });
187
207
 
188
- it("standard_list condensed: matches llm formatter", async () => {
189
- const opResult = await listStandards(rt.store);
190
- const output: StandardListOutput = {
191
- items: opResult,
192
- details: undefined,
193
- disclosure: { level: "summary" },
194
- };
195
- const expectedText = standardListFmt.llm(output);
196
-
208
+ it("standard_list condensed lists every standard under the pack heading", async () => {
209
+ const allNames = await listDomainNames(rt.store, "standard");
197
210
  const mcpRes = await client.callTool({
198
211
  name: "standard_list",
199
212
  arguments: { condensed: true },
200
213
  });
201
214
  const body = parseCondensed(mcpRes);
202
- expect(body.text).toBe(expectedText);
215
+ expect(body.text).toContain("## Standard");
216
+ for (const name of allNames) {
217
+ expect(body.text).toContain(name);
218
+ }
203
219
  });
204
220
 
205
- it("standard_lookup: detailed fields match", async () => {
206
- const opResult = await lookupStandard(
207
- rt.store,
208
- "react/component/folder-structure",
209
- );
221
+ it("standard_lookup returns full detail by default (MCP full-data contract)", async () => {
210
222
  const mcpRes = await client.callTool({
211
223
  name: "standard_lookup",
212
224
  arguments: { names: ["react/component/folder-structure"] },
213
225
  });
214
226
  const body = parseEnvelope(mcpRes);
215
- const data = body.data as { results: unknown[]; errors: unknown[] };
216
- expect(data.results[0]).toEqual(opResult);
227
+ const data = body.data as {
228
+ results: {
229
+ name: string;
230
+ category: string;
231
+ dos: { code?: string; language?: string }[];
232
+ donts: { code?: string }[];
233
+ }[];
234
+ errors: unknown[];
235
+ };
217
236
  expect(data.errors).toEqual([]);
237
+ const entity = data.results[0];
238
+ expect(entity?.name).toBe("react/component/folder-structure");
239
+ expect(entity?.category).toBe("react");
240
+ expect(entity?.dos.length).toBeGreaterThan(0);
241
+ expect(entity?.dos.every((example) => example.code)).toBe(true);
242
+ expect(entity?.donts.length).toBeGreaterThan(0);
218
243
  });
219
244
 
220
- it("standard_lookup condensed: matches llm formatter", async () => {
221
- const opResult = await lookupStandard(
222
- rt.store,
223
- "react/component/folder-structure",
224
- );
225
- const expectedText = standardLookupFmt.llm({
226
- standard: opResult,
227
- detailed: true,
228
- });
229
-
245
+ it("standard_lookup condensed renders the entity with its code examples", async () => {
230
246
  const mcpRes = await client.callTool({
231
247
  name: "standard_lookup",
232
248
  arguments: {
@@ -235,28 +251,37 @@ describe("standard parity", () => {
235
251
  },
236
252
  });
237
253
  const body = parseCondensed(mcpRes);
238
- expect(body.text).toBe(expectedText);
254
+ expect(body.text).toContain("## react/component/folder-structure");
255
+ expect(body.text).toContain("Do");
256
+ expect(body.text).toContain("src/lib/Button/Button.tsx");
239
257
  });
240
258
 
241
- it("standard_categories: data matches", async () => {
242
- const opResult = await listCategories(rt.store);
243
- const mcpRes = await client.callTool({
244
- name: "standard_categories",
259
+ it("standard_categories counts agree with the standard list", async () => {
260
+ const listRes = await client.callTool({
261
+ name: "standard_list",
245
262
  arguments: {},
246
263
  });
247
- assertParity(opResult, mcpRes);
248
- });
249
-
250
- it("standard_categories condensed: matches llm formatter", async () => {
251
- const opResult = await listCategories(rt.store);
252
- const expectedText = standardCatFmt.llm(opResult);
264
+ const rows = parseEnvelope(listRes).data as { category?: string }[];
265
+ const expected = new Map<string, number>();
266
+ for (const row of rows) {
267
+ if (row.category) {
268
+ expected.set(row.category, (expected.get(row.category) ?? 0) + 1);
269
+ }
270
+ }
253
271
 
254
272
  const mcpRes = await client.callTool({
255
273
  name: "standard_categories",
256
- arguments: { condensed: true },
274
+ arguments: {},
257
275
  });
258
- const body = parseCondensed(mcpRes);
259
- expect(body.text).toBe(expectedText);
276
+ const body = parseEnvelope(mcpRes);
277
+ expect(body.ok).toBe(true);
278
+ const categories = body.data as { name: string; count: string }[];
279
+ expect(categories.length).toBeGreaterThan(0);
280
+ for (const [category, count] of expected) {
281
+ const row = categories.find((entry) => entry.name === category);
282
+ expect(row, `category "${category}" missing`).toBeDefined();
283
+ expect(Number(row?.count)).toBe(count);
284
+ }
260
285
  });
261
286
  });
262
287
 
@@ -264,26 +289,51 @@ describe("standard parity", () => {
264
289
  // Tier parity
265
290
  // ---------------------------------------------------------------------------
266
291
 
267
- describe("tier parity", () => {
268
- it("tier_list: paths and depths match", async () => {
292
+ // `tier list` / `tier_list` is now served by the bundled `tier` story pack
293
+ // (the hand-written domain was deleted). Pack rows are `Record<string,string>`
294
+ // projected from the SELECT, so the shape is intentionally uniform: `{uri,
295
+ // name}` with no always-zero `depth` field, and the llm renderer is the
296
+ // generic pack renderer. These tests assert *semantic* parity — every tier the
297
+ // shared operation sees is present, by the same names — rather than byte
298
+ // equality with the removed bespoke formatter.
299
+ describe("tier parity (bundled pack)", () => {
300
+ it("tier_list returns one {uri, name} row per ontology tier", async () => {
269
301
  const opResult = await listTiers(rt.store);
270
- const mcpRes = await client.callTool({
271
- name: "tier_list",
272
- arguments: {},
273
- });
274
- assertParity(opResult, mcpRes);
302
+ const mcpRes = await client.callTool({ name: "tier_list", arguments: {} });
303
+
304
+ const content = mcpRes.content as { type: string; text: string }[];
305
+ const envelope = JSON.parse(content[0].text) as {
306
+ ok: boolean;
307
+ data: Array<{ uri?: string; name?: string }>;
308
+ };
309
+ expect(envelope.ok).toBe(true);
310
+
311
+ // Same tiers, same names — no tier dropped or invented by the cutover.
312
+ // The shared op names the field `path`; the pack names it `name` (the
313
+ // honest `ds:name` mapping) — the values are identical.
314
+ expect(new Set(envelope.data.map((r) => r.name))).toEqual(
315
+ new Set(opResult.map((t) => t.path)),
316
+ );
317
+ // Uniform pack shape: uri + name, no bespoke `depth` field.
318
+ for (const row of envelope.data) {
319
+ expect(row.uri).toBeTruthy();
320
+ expect(row).not.toHaveProperty("depth");
321
+ }
275
322
  });
276
323
 
277
- it("tier_list condensed: matches llm formatter", async () => {
324
+ it("tier_list condensed lists every tier under a markdown heading", async () => {
278
325
  const opResult = await listTiers(rt.store);
279
- const expectedText = tierListFmt.llm(opResult);
280
-
281
326
  const mcpRes = await client.callTool({
282
327
  name: "tier_list",
283
328
  arguments: { condensed: true },
284
329
  });
285
330
  const body = parseCondensed(mcpRes);
286
- expect(body.text).toBe(expectedText);
331
+
332
+ // The generic pack llm renderer headings by the capitalized noun: `## Tier`.
333
+ expect(body.text).toContain("## Tier");
334
+ for (const tier of opResult) {
335
+ expect(body.text).toContain(tier.path);
336
+ }
287
337
  });
288
338
  });
289
339
 
@@ -341,7 +391,7 @@ describe("modifier parity", () => {
341
391
  // Token parity
342
392
  // ---------------------------------------------------------------------------
343
393
 
344
- describe("token parity", () => {
394
+ describe.skipIf(!TOKEN_READ_SURFACE_ENABLED)("token parity", () => {
345
395
  it("token_list: count matches", async () => {
346
396
  const opResult = await listTokens(rt.store);
347
397
  const mcpRes = await client.callTool({
@@ -440,14 +490,25 @@ describe("ontology parity", () => {
440
490
  // ---------------------------------------------------------------------------
441
491
 
442
492
  describe("config parity", () => {
443
- it("config_show: data matches resolveConfigShow", async () => {
493
+ // Both surfaces resolve from the filesystem via the shared config show
494
+ // story, so parity is asserted against the real config path/existence
495
+ // (the MCP tool previously hardcoded "pragma.config.json" / true).
496
+ function resolveExpectedConfigShow() {
497
+ const layers = readConfigLayers(rt.cwd);
444
498
  const install = detectInstallSource();
445
- const opResult = resolveConfigShow(rt.config, {
499
+ return resolveConfigShow(layers.config, {
446
500
  packageManager: install.packageManager,
447
501
  installSource: install.label,
448
- configFilePath: "pragma.config.json",
449
- configFileExists: true,
502
+ configFilePath: layers.project.path,
503
+ configFileExists: layers.project.exists,
504
+ globalConfigPath: layers.global.path,
505
+ globalConfigExists: layers.global.exists,
506
+ origins: layers.origins,
450
507
  });
508
+ }
509
+
510
+ it("config_show: data matches resolveConfigShow", async () => {
511
+ const opResult = resolveExpectedConfigShow();
451
512
 
452
513
  const mcpRes = await client.callTool({
453
514
  name: "config_show",
@@ -458,14 +519,7 @@ describe("config parity", () => {
458
519
  });
459
520
 
460
521
  it("config_show condensed: matches llm formatter", async () => {
461
- const install = detectInstallSource();
462
- const opResult = resolveConfigShow(rt.config, {
463
- packageManager: install.packageManager,
464
- installSource: install.label,
465
- configFilePath: "pragma.config.json",
466
- configFileExists: true,
467
- });
468
- const expectedText = configShowFmt.llm(opResult);
522
+ const expectedText = configShowFmt.llm(resolveExpectedConfigShow());
469
523
 
470
524
  const mcpRes = await client.callTool({
471
525
  name: "config_show",
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Regression: skill discovery returned zero skills on real installs.
3
+ *
4
+ * `readPackageDir` emits one `SkillEntry.dir` per skill folder
5
+ * (`<package>/skills/<name>`), but `discoverSkills` treated each source
6
+ * directory as a *parent* of skill folders and scanned it for
7
+ * subdirectories containing SKILL.md. Production sources therefore never
8
+ * matched — `pragma skill list` errored with "No SKILL.md files found"
9
+ * and MCP `skill_list` returned an empty list, despite `doctor` counting
10
+ * skills in the very same packages. This test pins the granularity
11
+ * contract between the loader and discovery using both real
12
+ * implementations end-to-end.
13
+ */
14
+
15
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
19
+ import readPackageDir from "../../domains/shared/loaders/readPackageDir.js";
20
+ import type { SemanticPackage } from "../../domains/shared/semanticPackage.js";
21
+ import { resolveSkillSources } from "../../domains/skill/helpers/index.js";
22
+ import discoverSkills from "../../domains/skill/operations/discover.js";
23
+
24
+ const TMP_ROOT = join(tmpdir(), `pragma-regression-0001-${Date.now()}`);
25
+ const PKG_DIR = join(TMP_ROOT, "node_modules/@canonical/design-system");
26
+
27
+ beforeAll(() => {
28
+ const skillDir = join(PKG_DIR, "skills/design-auditor");
29
+ mkdirSync(skillDir, { recursive: true });
30
+ writeFileSync(
31
+ join(PKG_DIR, "package.json"),
32
+ JSON.stringify({ name: "@canonical/design-system", version: "0.1.2" }),
33
+ );
34
+ writeFileSync(
35
+ join(skillDir, "SKILL.md"),
36
+ `---
37
+ name: design-auditor
38
+ description: Audit design system coverage, consistency, and quality
39
+ ---
40
+
41
+ # Design Auditor
42
+ `,
43
+ );
44
+ });
45
+
46
+ afterAll(() => {
47
+ rmSync(TMP_ROOT, { recursive: true, force: true });
48
+ });
49
+
50
+ describe("skill discovery granularity (loader -> discovery contract)", () => {
51
+ it("discovers skills from sources produced by readPackageDir", async () => {
52
+ const contents = readPackageDir(PKG_DIR);
53
+ expect(contents.skills).toHaveLength(1);
54
+
55
+ const pkg: SemanticPackage = {
56
+ name: "@canonical/design-system",
57
+ version: contents.version,
58
+ source: "local",
59
+ graphs: contents.graphs,
60
+ skills: contents.skills,
61
+ };
62
+
63
+ const sources = resolveSkillSources([pkg]);
64
+ const skills = await discoverSkills(TMP_ROOT, sources);
65
+
66
+ expect(skills).toHaveLength(1);
67
+ expect(skills.at(0)?.name).toBe("design-auditor");
68
+ expect(skills.at(0)?.folderName).toBe("design-auditor");
69
+ expect(skills.at(0)?.sourcePackage).toBe("@canonical/design-system");
70
+ });
71
+ });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Global test setup: isolate the XDG config and data layers.
3
+ *
4
+ * The layered config reader consults `$XDG_CONFIG_HOME/pragma/config.json`
5
+ * on every read, and global-first writes create it; the schema artifact
6
+ * writes to `$XDG_DATA_HOME/pragma/schema.graphql`. Point both at
7
+ * per-run temp directories so tests neither observe nor pollute the
8
+ * developer's real global state. Individual tests that exercise XDG
9
+ * behavior override these values themselves.
10
+ *
11
+ * The boot-time schema artifact emission is disabled globally: it would
12
+ * add a full ke-graphql compile to every test that drives the real CLI
13
+ * pipeline and print an "Updated ..." notice on first boot. Its behavior
14
+ * is covered directly by ensureSchemaArtifact.test.ts; tests exercising
15
+ * the boot hook re-enable it explicitly.
16
+ */
17
+
18
+ import { mkdtempSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+
22
+ process.env.XDG_CONFIG_HOME = mkdtempSync(join(tmpdir(), "pragma-test-xdg-"));
23
+ process.env.XDG_DATA_HOME = mkdtempSync(
24
+ join(tmpdir(), "pragma-test-xdg-data-"),
25
+ );
26
+ process.env.PRAGMA_SCHEMA_ARTIFACT = "off";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Standard-noun cutover parity contract.
3
+ *
4
+ * The hand-written `standard` domain was deleted; the noun is served by
5
+ * the bundled pack-v1 definition (`pack/bundled/standardPack.ts`), and
6
+ * `standardParity.test.ts` asserts SEMANTIC parity against the real
7
+ * @canonical/code-standards graphs: same entities and values reachable,
8
+ * uniform pack row/entity shapes, generic renderers.
9
+ *
10
+ * {@link PARITY_GAPS} is the honest record of the differences that
11
+ * remain BY DESIGN after the cutover. Each entry names a concrete,
12
+ * accepted divergence from the deleted built-in — remove an entry only
13
+ * when the pack format gains the capability and the parity test asserts
14
+ * it.
15
+ */
16
+
17
+ /**
18
+ * Accepted post-cutover divergences from the deleted built-in standard
19
+ * domain. These are doctrine (semantic parity, not byte parity) plus the
20
+ * capabilities pack v1 deliberately does not carry.
21
+ */
22
+ export const PARITY_GAPS: readonly string[] = [
23
+ "list disclosure: pack lists are single-level — the old `standard list --digest/--detailed` row enrichment (extends/example/dos/donts merged into every row, disclosure meta on the MCP envelope) is gone; the same data is reachable per-entity via `standard lookup --detail digest|detailed` and `standard sample`",
24
+ "list empty guard: the old list raised EMPTY_RESULTS with an install-hint recovery when no standards load or a --category matched nothing; the pack format has no emptyError hook, so an empty or fully-filtered list renders zero rows successfully",
25
+ "lookup digest rendering: the old list digest truncated the first do-example to 120 chars; the pack `digest` level fetches the full `dos` expand instead (richer, not byte-equal)",
26
+ "lookup data compaction: the old domain compacted cs:extends in resolved data (JSON showed `cs:ComponentFolderStructure`); pack entities keep the raw IRI in JSON — plain/llm output still compacts at display time",
27
+ "lookup recovery copy: cross-domain hints (detectCrossDomain) and the curated `List available standards.` message are not authorable; pack misses carry ranked suggestions with the generic `List available standard entries.` copy",
28
+ 'typed shapes: pack rows/entities are all-string records — category counts are strings, and dos/donts entries are `{caption?, language?, code?}` string records rather than typed CodeBlock objects (no `language: "typescript"` default, absent captions are omitted)',
29
+ "render templates: plain/llm output uses the generic pack renderers (aligned columns, `## Standard (n)` headings, `- key: value` collection entries), not the old bespoke standard templates",
30
+ "sample count flag: the pack sample takes a positional count (`standard sample 3`) instead of the old `--count 3` flag",
31
+ "lookup IRI display: plain/llm lookup output does not print the entity IRI (the pack renderer titles by name and projects no uri field); the IRI stays reachable via the list's IRI column, `--format json`, and the non-condensed MCP envelope",
32
+ "categories JSON shape: the old CategorySummary carried a numeric `standardCount`; pack category rows carry the key `count` with a string value (all-string pack rows, until pack v2 typed columns)",
33
+ "sample population: totalCount counts name-addressable standards (the set the sampler can actually resolve) rather than all list rows; the old op counted every list row but failed outright when it drew a standard without cs:name",
34
+ "ink renderers: the pack format declares no renderInk TUI views",
35
+ ];
@@ -2,12 +2,12 @@
2
2
  * Test store bridge — createTestStore, PragmaTestStoreOptions.
3
3
  *
4
4
  * Wraps `@canonical/ke/testing` with pragma's default prefix map
5
- * pre-configured so test authors do not need to repeat PREFIX_MAP.
5
+ * pre-configured so test authors do not need to repeat DEFAULT_PREFIX_MAP.
6
6
  */
7
7
 
8
8
  import type { TestStoreOptions, TestStoreResult } from "@canonical/ke/testing";
9
9
  import { createTestStore as keCreateTestStore } from "@canonical/ke/testing";
10
- import { PREFIX_MAP } from "../domains/shared/prefixes.js";
10
+ import { DEFAULT_PREFIX_MAP } from "../domains/shared/prefixes.js";
11
11
 
12
12
  /** Options for creating a test store, with pragma prefixes pre-applied. */
13
13
  interface PragmaTestStoreOptions extends Omit<TestStoreOptions, "prefixes"> {
@@ -18,7 +18,7 @@ interface PragmaTestStoreOptions extends Omit<TestStoreOptions, "prefixes"> {
18
18
  * Create a test ke store with pragma's default prefix map.
19
19
  *
20
20
  * @param options - Optional overrides; additional prefixes are merged
21
- * on top of the default PREFIX_MAP.
21
+ * on top of the default DEFAULT_PREFIX_MAP.
22
22
  * @returns The test store result from ke/testing.
23
23
  *
24
24
  * @note Impure
@@ -29,7 +29,7 @@ async function createTestStore(
29
29
  return keCreateTestStore({
30
30
  ...options,
31
31
  prefixes: {
32
- ...PREFIX_MAP,
32
+ ...DEFAULT_PREFIX_MAP,
33
33
  ...options.prefixes,
34
34
  },
35
35
  });
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Story-pack test fixtures — a foreign (non-DS) recipe ontology plus a
3
+ * matching pack definition.
4
+ *
5
+ * The `ex:` namespace is deliberately outside PREFIX_MAP: tests register
6
+ * it via config `prefixes`, proving the DS-agnostic path end to end.
7
+ */
8
+
9
+ import type { StoryPackDefinition } from "../domains/shared/stories/pack/types.js";
10
+
11
+ /** Namespace of the foreign recipe ontology. */
12
+ export const RECIPE_NAMESPACE = "http://example.org/recipes/";
13
+
14
+ /** Prefix map entry tests merge into config `prefixes`. */
15
+ export const RECIPE_PREFIXES = { ex: RECIPE_NAMESPACE } as const;
16
+
17
+ /** A tiny foreign ontology with two recipe instances. */
18
+ export const RECIPE_TTL = `
19
+ @prefix ex: <${RECIPE_NAMESPACE}> .
20
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
21
+
22
+ ex:Recipe a rdfs:Class .
23
+
24
+ ex:pancakes a ex:Recipe ;
25
+ ex:name "Pancakes" ;
26
+ ex:category "breakfast" ;
27
+ ex:instructions "Mix, fry, flip." .
28
+
29
+ ex:gazpacho a ex:Recipe ;
30
+ ex:name "Gazpacho" ;
31
+ ex:category "soup" ;
32
+ ex:instructions "Blend everything cold." .
33
+ `;
34
+
35
+ /** A valid pack definition for the recipe ontology. */
36
+ export const RECIPE_STORY: StoryPackDefinition = {
37
+ noun: "recipe",
38
+ description: "List recipes",
39
+ list: {
40
+ query:
41
+ "SELECT ?uri ?name ?category WHERE { ?uri a ex:Recipe ; ex:name ?name ; ex:category ?category } ORDER BY ?name",
42
+ columns: [
43
+ { field: "name" },
44
+ { field: "category" },
45
+ { field: "uri", label: "IRI" },
46
+ ],
47
+ filters: [
48
+ {
49
+ param: "category",
50
+ variable: "category",
51
+ values: ["breakfast", "soup"],
52
+ },
53
+ ],
54
+ },
55
+ lookup: {
56
+ type: "ex:Recipe",
57
+ by: "ex:name",
58
+ fields: [{ name: "category", property: "ex:category" }],
59
+ sections: [
60
+ { name: "instructions", property: "ex:instructions", kind: "field" },
61
+ ],
62
+ },
63
+ };