@canonical/pragma-cli 0.27.1-experimental.0 → 0.30.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 (344) hide show
  1. package/README.md +67 -15
  2. package/dist/pragma +0 -0
  3. package/package.json +16 -17
  4. package/src/config/defaultConfig.test.ts +33 -0
  5. package/src/config/defaultConfig.ts +59 -0
  6. package/src/config/defaults.json +17 -0
  7. package/src/config/findProjectConfigPath.test.ts +53 -0
  8. package/src/config/findProjectConfigPath.ts +36 -0
  9. package/src/config/index.ts +17 -2
  10. package/src/config/mergeConfigFileUpdate.ts +58 -0
  11. package/src/config/parseConfigValues.ts +215 -0
  12. package/src/config/readConfig.test.ts +104 -13
  13. package/src/config/readConfig.ts +12 -117
  14. package/src/config/readConfigLayers.test.ts +107 -0
  15. package/src/config/readConfigLayers.ts +115 -0
  16. package/src/config/resolveGlobalConfigPath.ts +13 -0
  17. package/src/config/resolveWriteConfigPath.test.ts +53 -0
  18. package/src/config/resolveWriteConfigPath.ts +31 -0
  19. package/src/config/types.ts +88 -2
  20. package/src/config/writeConfig.ts +25 -43
  21. package/src/constants.ts +12 -1
  22. package/src/domains/block/commands/index.ts +2 -1
  23. package/src/domains/block/commands/list.ts +5 -90
  24. package/src/domains/block/commands/lookup.ts +8 -167
  25. package/src/domains/block/commands/sample.ts +55 -0
  26. package/src/domains/block/formatters/index.ts +2 -1
  27. package/src/domains/block/formatters/sample.ts +21 -0
  28. package/src/domains/block/index.ts +3 -3
  29. package/src/domains/block/mcp/specs.ts +36 -168
  30. package/src/domains/block/operations/index.ts +2 -1
  31. package/src/domains/block/operations/lookup.ts +9 -0
  32. package/src/domains/block/operations/sample.ts +41 -0
  33. package/src/domains/block/orchestration/blockEmptyError.test.ts +26 -0
  34. package/src/domains/block/orchestration/resolveBlockLookup.ts +11 -3
  35. package/src/domains/block/stories.test.ts +28 -0
  36. package/src/domains/block/stories.ts +254 -0
  37. package/src/domains/config/commands/channel.test.ts +14 -3
  38. package/src/domains/config/commands/channel.ts +11 -7
  39. package/src/domains/config/commands/configScope.ts +46 -0
  40. package/src/domains/config/commands/framework.test.ts +105 -0
  41. package/src/domains/config/commands/framework.ts +91 -0
  42. package/src/domains/config/commands/index.ts +2 -0
  43. package/src/domains/config/commands/show.ts +5 -45
  44. package/src/domains/config/commands/tier.test.ts +13 -2
  45. package/src/domains/config/commands/tier.ts +11 -7
  46. package/src/domains/config/commands/trace.test.ts +165 -0
  47. package/src/domains/config/commands/trace.ts +83 -0
  48. package/src/domains/config/formatters/channel.test.ts +20 -6
  49. package/src/domains/config/formatters/channel.ts +14 -11
  50. package/src/domains/config/formatters/framework.ts +37 -0
  51. package/src/domains/config/formatters/index.ts +2 -0
  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 +28 -0
  57. package/src/domains/config/index.ts +15 -2
  58. package/src/domains/config/mcp/specs.ts +33 -53
  59. package/src/domains/config/operations/index.ts +1 -0
  60. package/src/domains/config/operations/show.test.ts +18 -0
  61. package/src/domains/config/operations/show.ts +7 -1
  62. package/src/domains/config/operations/types.ts +7 -0
  63. package/src/domains/config/operations/validateFramework.ts +24 -0
  64. package/src/domains/config/stories.ts +42 -0
  65. package/src/domains/config/tasks/index.ts +3 -0
  66. package/src/domains/config/tasks/setChannel.ts +13 -22
  67. package/src/domains/config/tasks/setFramework.ts +21 -0
  68. package/src/domains/config/tasks/setTier.ts +10 -23
  69. package/src/domains/config/tasks/setTrace.ts +20 -0
  70. package/src/domains/config/tasks/writeConfigField.ts +41 -0
  71. package/src/domains/create/commands/component.ts +5 -5
  72. package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
  73. package/src/domains/doctor/operations/checks/checkKeStore.ts +5 -5
  74. package/src/domains/doctor/operations/checks/checkMcpCommands.ts +130 -0
  75. package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
  76. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -48
  77. package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
  78. package/src/domains/doctor/operations/checks/index.ts +1 -0
  79. package/src/domains/doctor/operations/checks.test.ts +162 -8
  80. package/src/domains/doctor/operations/runChecks.test.ts +85 -4
  81. package/src/domains/doctor/operations/runChecks.ts +50 -12
  82. package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
  83. package/src/domains/graph/helpers/resolveUri.ts +28 -21
  84. package/src/domains/graphql/commands/build.test.ts +196 -0
  85. package/src/domains/graphql/commands/build.ts +117 -0
  86. package/src/domains/graphql/commands/check.test.ts +141 -0
  87. package/src/domains/graphql/commands/check.ts +81 -0
  88. package/src/domains/graphql/commands/index.ts +5 -0
  89. package/src/domains/graphql/commands/serve.test.ts +40 -0
  90. package/src/domains/graphql/commands/serve.ts +131 -0
  91. package/src/domains/graphql/constants.ts +10 -0
  92. package/src/domains/graphql/formatters/index.ts +3 -0
  93. package/src/domains/graphql/formatters/report.ts +78 -0
  94. package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
  95. package/src/domains/graphql/helpers/gatherSources.ts +58 -0
  96. package/src/domains/graphql/helpers/index.ts +6 -0
  97. package/src/domains/graphql/helpers/parsePrefixes.test.ts +44 -0
  98. package/src/domains/graphql/helpers/parsePrefixes.ts +38 -0
  99. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +56 -0
  100. package/src/domains/graphql/helpers/resolveSourceFiles.ts +45 -0
  101. package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
  102. package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
  103. package/src/domains/graphql/index.ts +24 -0
  104. package/src/domains/graphql/operations/compileSchema.test.ts +95 -0
  105. package/src/domains/graphql/operations/compileSchema.ts +107 -0
  106. package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
  107. package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
  108. package/src/domains/graphql/operations/index.ts +12 -0
  109. package/src/domains/graphql/types.ts +29 -0
  110. package/src/domains/info/operations/collectInfo.test.ts +16 -5
  111. package/src/domains/info/operations/collectInfo.ts +16 -19
  112. package/src/domains/llm/capabilities.test.ts +91 -0
  113. package/src/domains/llm/commands/capabilities.ts +28 -0
  114. package/src/domains/llm/commands/index.ts +1 -0
  115. package/src/domains/llm/data/commandReference.ts +90 -18
  116. package/src/domains/llm/data/decisionTrees.ts +15 -13
  117. package/src/domains/llm/data/index.ts +2 -0
  118. package/src/domains/llm/data/toolCatalog.ts +190 -0
  119. package/src/domains/llm/formatters/capabilities.ts +50 -0
  120. package/src/domains/llm/formatters/index.ts +1 -0
  121. package/src/domains/llm/index.ts +3 -2
  122. package/src/domains/llm/mcp/index.ts +1 -1
  123. package/src/domains/llm/mcp/specs.ts +59 -52
  124. package/src/domains/llm/types.ts +40 -0
  125. package/src/domains/modifier/commands/index.ts +2 -1
  126. package/src/domains/modifier/commands/list.ts +5 -38
  127. package/src/domains/modifier/commands/lookup.ts +8 -79
  128. package/src/domains/modifier/commands/sample.ts +53 -0
  129. package/src/domains/modifier/formatters/index.ts +2 -1
  130. package/src/domains/modifier/formatters/sample.ts +15 -0
  131. package/src/domains/modifier/index.ts +7 -3
  132. package/src/domains/modifier/mcp/specs.ts +38 -70
  133. package/src/domains/modifier/operations/index.ts +2 -1
  134. package/src/domains/modifier/operations/lookup.ts +9 -0
  135. package/src/domains/modifier/operations/sample.ts +32 -0
  136. package/src/domains/modifier/orchestration/modifierEmptyError.test.ts +26 -0
  137. package/src/domains/modifier/orchestration/modifierEmptyError.ts +7 -2
  138. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +15 -2
  139. package/src/domains/modifier/stories.test.ts +20 -0
  140. package/src/domains/modifier/stories.ts +77 -0
  141. package/src/domains/ontology/commands/list.ts +5 -58
  142. package/src/domains/ontology/commands/show.ts +6 -58
  143. package/src/domains/ontology/formatters/show.ts +7 -1
  144. package/src/domains/ontology/mcp/specs.ts +7 -84
  145. package/src/domains/ontology/stories.test.ts +36 -0
  146. package/src/domains/ontology/stories.ts +108 -0
  147. package/src/domains/refs/operations/index.ts +1 -1
  148. package/src/domains/refs/operations/paths.ts +29 -1
  149. package/src/domains/refs/operations/updateRefs.test.ts +6 -3
  150. package/src/domains/refs/operations/updateRefs.ts +2 -36
  151. package/src/domains/setup/commands/skills.test.ts +6 -2
  152. package/src/domains/setup/commands/skills.ts +6 -2
  153. package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
  154. package/src/domains/setup/helpers/runSetupTask.ts +62 -70
  155. package/src/domains/setup/operations/setupMcp.test.ts +6 -1
  156. package/src/domains/shared/bootStore.ts +92 -50
  157. package/src/domains/shared/compactUri.test.ts +11 -0
  158. package/src/domains/shared/compactUri.ts +10 -5
  159. package/src/domains/shared/context.ts +2 -2
  160. package/src/domains/shared/contracts.ts +12 -0
  161. package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
  162. package/src/domains/shared/createInteractivePromptSession.ts +236 -0
  163. package/src/domains/shared/emptyErrorRecovery.test.ts +109 -0
  164. package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
  165. package/src/domains/shared/loaders/bundledLoader.ts +206 -0
  166. package/src/domains/shared/loaders/gitLoader.ts +31 -0
  167. package/src/domains/shared/loaders/index.ts +9 -0
  168. package/src/domains/shared/loaders/localLoader.ts +87 -0
  169. package/src/domains/shared/loaders/readPackageDir.ts +147 -0
  170. package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
  171. package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
  172. package/src/domains/shared/lookupMany.test.ts +120 -0
  173. package/src/domains/shared/lookupMany.ts +43 -3
  174. package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
  175. package/src/domains/shared/mergeAndParseRefs.ts +44 -0
  176. package/src/domains/shared/packages.ts +14 -120
  177. package/src/domains/shared/pickRandom.ts +22 -0
  178. package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
  179. package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
  180. package/src/domains/shared/runtime.ts +7 -63
  181. package/src/domains/shared/sampleFormatters.ts +47 -0
  182. package/src/domains/shared/semanticPackage.ts +112 -0
  183. package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
  184. package/src/domains/shared/stories/compileLookupCommand.ts +100 -0
  185. package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
  186. package/src/domains/shared/stories/compileLookupTool.ts +96 -0
  187. package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
  188. package/src/domains/shared/stories/compileReadCommand.ts +48 -0
  189. package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
  190. package/src/domains/shared/stories/compileReadTool.ts +38 -0
  191. package/src/domains/shared/stories/condense.test.ts +18 -0
  192. package/src/domains/shared/stories/condense.ts +15 -0
  193. package/src/domains/shared/stories/index.ts +20 -0
  194. package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
  195. package/src/domains/shared/stories/normalizeNames.ts +22 -0
  196. package/src/domains/shared/stories/pack/applyPackFilters.test.ts +102 -0
  197. package/src/domains/shared/stories/pack/applyPackFilters.ts +76 -0
  198. package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +51 -0
  199. package/src/domains/shared/stories/pack/buildLookupQuery.ts +80 -0
  200. package/src/domains/shared/stories/pack/collectPackStories.test.ts +108 -0
  201. package/src/domains/shared/stories/pack/collectPackStories.ts +76 -0
  202. package/src/domains/shared/stories/pack/compilePackCommands.ts +40 -0
  203. package/src/domains/shared/stories/pack/compilePackStories.test.ts +146 -0
  204. package/src/domains/shared/stories/pack/compilePackStories.ts +253 -0
  205. package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +43 -0
  206. package/src/domains/shared/stories/pack/index.ts +28 -0
  207. package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
  208. package/src/domains/shared/stories/pack/standardParity.test.ts +308 -0
  209. package/src/domains/shared/stories/pack/types.ts +104 -0
  210. package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +240 -0
  211. package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +408 -0
  212. package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
  213. package/src/domains/shared/stories/projectCliParameters.ts +57 -0
  214. package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
  215. package/src/domains/shared/stories/projectToolParams.ts +32 -0
  216. package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
  217. package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
  218. package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
  219. package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
  220. package/src/domains/shared/stories/types.ts +171 -0
  221. package/src/domains/shared/suggestions/detectCrossDomain.test.ts +43 -0
  222. package/src/domains/shared/suggestions/detectCrossDomain.ts +116 -0
  223. package/src/domains/shared/suggestions/expandGlob.test.ts +92 -0
  224. package/src/domains/shared/suggestions/expandGlob.ts +52 -0
  225. package/src/domains/shared/suggestions/expandLookupQueries.ts +57 -0
  226. package/src/domains/shared/suggestions/index.ts +7 -0
  227. package/src/domains/shared/suggestions/listDomainNames.ts +61 -0
  228. package/src/domains/shared/suggestions/suggestNames.test.ts +70 -0
  229. package/src/domains/shared/suggestions/suggestNames.ts +130 -0
  230. package/src/domains/shared/types/index.ts +1 -0
  231. package/src/domains/shared/types/runtime.ts +4 -1
  232. package/src/domains/shared/types/sample.ts +17 -0
  233. package/src/domains/skill/commands/index.ts +3 -1
  234. package/src/domains/skill/commands/list.test.ts +6 -2
  235. package/src/domains/skill/commands/list.ts +5 -63
  236. package/src/domains/skill/commands/lookup.test.ts +118 -0
  237. package/src/domains/skill/commands/lookup.ts +14 -0
  238. package/src/domains/skill/formatters/index.ts +1 -0
  239. package/src/domains/skill/formatters/lookup.test.ts +46 -0
  240. package/src/domains/skill/formatters/lookup.ts +57 -0
  241. package/src/domains/skill/helpers/resolveSkillSources.ts +48 -16
  242. package/src/domains/skill/index.ts +7 -3
  243. package/src/domains/skill/mcp/specs.ts +10 -29
  244. package/src/domains/skill/operations/discover.test.ts +61 -30
  245. package/src/domains/skill/operations/discover.ts +28 -37
  246. package/src/domains/skill/operations/index.ts +2 -1
  247. package/src/domains/skill/operations/list.test.ts +15 -7
  248. package/src/domains/skill/operations/lookup.test.ts +91 -0
  249. package/src/domains/skill/operations/lookup.ts +70 -0
  250. package/src/domains/skill/stories.test.ts +43 -0
  251. package/src/domains/skill/stories.ts +103 -0
  252. package/src/domains/skill/types.ts +8 -0
  253. package/src/domains/standard/commands/index.ts +2 -1
  254. package/src/domains/standard/commands/list.ts +5 -84
  255. package/src/domains/standard/commands/lookup.ts +8 -93
  256. package/src/domains/standard/commands/sample.ts +57 -0
  257. package/src/domains/standard/formatters/index.ts +2 -1
  258. package/src/domains/standard/formatters/sample.ts +15 -0
  259. package/src/domains/standard/index.ts +8 -1
  260. package/src/domains/standard/mcp/specs.ts +44 -152
  261. package/src/domains/standard/operations/index.ts +2 -1
  262. package/src/domains/standard/operations/lookup.ts +9 -0
  263. package/src/domains/standard/operations/sample.ts +35 -0
  264. package/src/domains/standard/orchestration/resolveStandardLookup.ts +15 -2
  265. package/src/domains/standard/orchestration/standardEmptyError.test.ts +33 -0
  266. package/src/domains/standard/orchestration/standardEmptyError.ts +16 -6
  267. package/src/domains/standard/stories.test.ts +24 -0
  268. package/src/domains/standard/stories.ts +151 -0
  269. package/src/domains/tier/commands/list.ts +5 -55
  270. package/src/domains/tier/mcp/specs.ts +5 -31
  271. package/src/domains/tier/stories.test.ts +22 -0
  272. package/src/domains/tier/stories.ts +52 -0
  273. package/src/domains/token/commands/index.ts +2 -1
  274. package/src/domains/token/commands/sample.ts +53 -0
  275. package/src/domains/token/formatters/index.ts +2 -1
  276. package/src/domains/token/formatters/sample.ts +16 -0
  277. package/src/domains/token/index.ts +8 -1
  278. package/src/domains/token/mcp/specs.ts +52 -2
  279. package/src/domains/token/operations/index.ts +2 -1
  280. package/src/domains/token/operations/lookup.ts +9 -0
  281. package/src/domains/token/operations/sample.ts +32 -0
  282. package/src/domains/token/orchestration/resolveTokenLookup.ts +15 -2
  283. package/src/domains/token/orchestration/tokenEmptyError.test.ts +33 -0
  284. package/src/domains/token/orchestration/tokenEmptyError.ts +5 -2
  285. package/src/domains/trace/commands/clear.ts +71 -0
  286. package/src/domains/trace/commands/index.ts +5 -0
  287. package/src/domains/trace/commands/sessions.ts +32 -0
  288. package/src/domains/trace/commands/show.ts +145 -0
  289. package/src/domains/trace/formatters/index.ts +9 -0
  290. package/src/domains/trace/formatters/sessions.ts +80 -0
  291. package/src/domains/trace/formatters/show.ts +122 -0
  292. package/src/domains/trace/index.ts +17 -0
  293. package/src/domains/trace/operations/index.ts +8 -0
  294. package/src/domains/trace/operations/listSessions.test.ts +55 -0
  295. package/src/domains/trace/operations/listSessions.ts +52 -0
  296. package/src/domains/trace/operations/readTraceLog.test.ts +100 -0
  297. package/src/domains/trace/operations/readTraceLog.ts +104 -0
  298. package/src/domains/trace/tracePlugin.test.ts +171 -0
  299. package/src/domains/trace/tracePlugin.ts +159 -0
  300. package/src/domains/trace/types.ts +65 -0
  301. package/src/error/PragmaError.ts +15 -2
  302. package/src/error/types.ts +15 -1
  303. package/src/mcp/error/serializeErrorPayload.ts +7 -0
  304. package/src/mcp/error/types.ts +8 -0
  305. package/src/mcp/registerResources.test.ts +405 -75
  306. package/src/mcp/registerResources.ts +5 -299
  307. package/src/mcp/resources/buildGraphIndex.ts +167 -0
  308. package/src/mcp/resources/buildResourceList.test.ts +97 -0
  309. package/src/mcp/resources/buildResourceList.ts +176 -0
  310. package/src/mcp/resources/classifyEntity.test.ts +40 -0
  311. package/src/mcp/resources/classifyEntity.ts +27 -0
  312. package/src/mcp/resources/constants.ts +105 -0
  313. package/src/mcp/resources/displayPredicates.ts +62 -0
  314. package/src/mcp/resources/pickFirstValue.test.ts +29 -0
  315. package/src/mcp/resources/pickFirstValue.ts +26 -0
  316. package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
  317. package/src/mcp/resources/pickPrimaryType.ts +26 -0
  318. package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
  319. package/src/mcp/resources/rankUriCompletions.ts +64 -0
  320. package/src/mcp/resources/readEntity.ts +169 -0
  321. package/src/mcp/resources/registerResources.ts +126 -0
  322. package/src/mcp/resources/types.ts +128 -0
  323. package/src/mcp/tools/index.ts +9 -0
  324. package/src/mcp/tools/registerTools.test.ts +84 -12
  325. package/src/mcp/types.ts +4 -0
  326. package/src/mcp/utils/wrapTool.test.ts +1 -0
  327. package/src/pipeline/collectCommands.ts +15 -2
  328. package/src/pipeline/renderError.ts +15 -0
  329. package/src/pipeline/resolveCommandKind.ts +8 -1
  330. package/src/pipeline/runCli.ts +51 -6
  331. package/src/testing/createTestMcpClient.ts +5 -1
  332. package/src/testing/graphqlFixtures.ts +72 -0
  333. package/src/testing/index.ts +17 -0
  334. package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
  335. package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
  336. package/src/testing/integration/executor-execution.test.ts +101 -0
  337. package/src/testing/integration/executor-golden.test.ts +145 -0
  338. package/src/testing/integration/mcp-surface.test.ts +11 -5
  339. package/src/testing/integration/parity.test.ts +17 -12
  340. package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
  341. package/src/testing/setupXdgIsolation.ts +15 -0
  342. package/src/testing/standardParityFixtures.ts +92 -0
  343. package/src/testing/storyFixtures.ts +63 -0
  344. package/src/pipeline/runInteractiveCommand.ts +0 -198
package/README.md CHANGED
@@ -22,7 +22,7 @@ See [docs/getting-started.md](docs/getting-started.md) for a full walkthrough.
22
22
 
23
23
  ## CLI Commands
24
24
 
25
- pragma organizes commands into 14 domains. Every command supports three output modes: plain text (default), `--llm` (condensed Markdown), and `--format json` (structured JSON).
25
+ pragma organizes commands into 17 domains — counting every directory under `src/domains/` except `shared/`, which holds cross-domain infrastructure rather than commands. The 14 day-to-day domains are documented below; the remaining three are specialised: `graphql` (compile TTL ontologies into GraphQL schema artifacts), `refs` (`pragma update-refs`, package reference data), and `trace` (query access tracing). Every command supports three output modes: plain text (default), `--llm` (condensed Markdown), and `--format json` (structured JSON).
26
26
 
27
27
  ### Block
28
28
 
@@ -87,6 +87,7 @@ pragma organizes commands into 14 domains. Every command supports three output m
87
87
  | Command | Description |
88
88
  |---------|-------------|
89
89
  | `pragma skill list` | List discovered agent skills from installed packages |
90
+ | `pragma skill lookup <name...>` | Show full SKILL.md instructions for skills by name |
90
91
 
91
92
  ### LLM
92
93
 
@@ -173,11 +174,11 @@ Or add manually to your `.mcp.json`:
173
174
  }
174
175
  ```
175
176
 
176
- ### MCP Tools (29)
177
+ ### MCP Tools (30)
177
178
 
178
179
  All tools return a consistent envelope: `{ ok: true, data, meta }` for success, `{ ok: true, condensed: true, text, tokens }` for condensed mode, or `{ ok: false, error }` with structured recovery on failure.
179
180
 
180
- #### Read Tools (20)
181
+ #### Read Tools (21)
181
182
 
182
183
  | Tool | Description |
183
184
  |------|-------------|
@@ -201,6 +202,7 @@ All tools return a consistent envelope: `{ ok: true, data, meta }` for success,
201
202
  | `graph_query` | Execute raw SPARQL against the ke store |
202
203
  | `graph_inspect` | Inspect all triples for a URI |
203
204
  | `skill_list` | List discovered agent skills |
205
+ | `skill_lookup` | Get full SKILL.md content for skills by name |
204
206
 
205
207
  #### Write Tools (5)
206
208
 
@@ -295,14 +297,64 @@ Every subject URI in the ke graph is exposed as a discoverable MCP resource via
295
297
 
296
298
  ## Configuration
297
299
 
298
- Create a `pragma.config.toml` in your project root:
300
+ Configuration is layered: built-in defaults, then the global file at
301
+ `$XDG_CONFIG_HOME/pragma/config.json` (default `~/.config/pragma/config.json`),
302
+ then the nearest `pragma.config.json` at or above the current directory —
303
+ each field won by the most specific layer that sets it. Install pragma
304
+ globally, configure once, and override per project:
299
305
 
300
- ```toml
301
- tier = "apps/lxd"
302
- channel = "normal" # normal | experimental | prerelease
306
+ ```json
307
+ {
308
+ "tier": "apps/lxd",
309
+ "channel": "normal"
310
+ }
311
+ ```
312
+
313
+ `pragma config tier|channel|framework|trace` writes to the nearest existing
314
+ project file, or to the global file when no project is configured; pass
315
+ `--local` or `--global` to pick the layer explicitly. Every write echoes the
316
+ file it wrote, and `pragma config show` reports which layer supplied each
317
+ value.
318
+
319
+ ### Custom read stories (experimental)
320
+
321
+ A config (or a semantic package, via `stories/*.json`) can declare
322
+ declarative read stories — any ontology loaded through `packages` gets
323
+ `pragma <noun> list` / `<noun> lookup` on both the CLI and MCP:
324
+
325
+ ```json
326
+ {
327
+ "prefixes": { "ex": "http://example.org/recipes/" },
328
+ "packages": [{ "name": "my-recipes", "source": "file:///data/recipes" }],
329
+ "stories": [
330
+ {
331
+ "noun": "recipe",
332
+ "description": "List recipes",
333
+ "list": {
334
+ "query": "SELECT ?uri ?name ?category WHERE { ?uri a ex:Recipe ; ex:name ?name ; ex:category ?category } ORDER BY ?name",
335
+ "columns": [{ "field": "name" }, { "field": "category" }],
336
+ "filters": [
337
+ { "param": "category", "variable": "category", "values": ["breakfast", "soup"] }
338
+ ]
339
+ },
340
+ "lookup": {
341
+ "type": "ex:Recipe",
342
+ "by": "ex:name",
343
+ "fields": [{ "name": "category", "property": "ex:category" }],
344
+ "sections": [{ "name": "instructions", "property": "ex:instructions" }]
345
+ }
346
+ }
347
+ ]
348
+ }
303
349
  ```
304
350
 
305
- When no config file is present, pragma defaults to no tier and `normal` channel.
351
+ The lookup query is generated (user input is escaped, never interpolated by
352
+ the pack), the store stays read-only, and config-declared stories override
353
+ package-shipped ones on noun collisions. Declared `filters` become
354
+ `pragma recipe list --category soup` on the CLI and an enum parameter on the
355
+ MCP tool; they are row predicates applied after the query runs, so filter
356
+ input can never inject SPARQL and the query's ordering is preserved. The
357
+ format is experimental and may change.
306
358
 
307
359
  ## Error Handling
308
360
 
@@ -336,7 +388,7 @@ With `--format json`, errors are returned as structured JSON with error code, su
336
388
 
337
389
  pragma is structured as a domain-driven application using the federation pattern from `@canonical/cli-core`:
338
390
 
339
- 1. **Domains** — 14 domain modules (block, standard, modifier, token, tier, ontology, graph, config, skill, llm, setup, create, doctor, info), each exporting `CommandDefinition[]`
391
+ 1. **Domains** — 17 domain modules (block, config, create, doctor, graph, graphql, info, llm, modifier, ontology, refs, setup, skill, standard, tier, token, trace — every directory under `src/domains/` except the `shared/` infrastructure folder), each contributing `CommandDefinition`s
340
392
  2. **Operations** — data retrieval functions that query the ke triple store via SPARQL
341
393
  3. **Formatters** — three-mode output adapters (plain/llm/json) for each operation
342
394
  4. **Commands** — thin wiring layer connecting Commander.js parameters to operations + formatters
@@ -364,13 +416,13 @@ bun build --compile --minify src/bin.ts --outfile dist/pragma
364
416
 
365
417
  ## Dependencies
366
418
 
367
- - [`@canonical/ke`](../runtime/ke/) — triple store runtime (Oxigraph WASM)
419
+ - [`@canonical/ke`](../../runtime/ke/) — triple store runtime (Oxigraph WASM)
368
420
  - [`@canonical/cli-core`](../core/) — shared CLI machinery (Commander.js registration, help formatting)
369
- - [`@canonical/task`](../../lib/task/) — reversible task trees for setup/create with undo support
370
- - [`@canonical/harnesses`](../../lib/harnesses/) — AI harness detection (Claude Code, Cursor, Windsurf)
371
- - [`@canonical/summon-core`](../../lib/summon-core/) — code generation runtime
372
- - [`@canonical/summon-component`](../../lib/summon-component/) — component generator
373
- - [`@canonical/summon-package`](../../lib/summon-package/) — package generator
421
+ - [`@canonical/task`](../../runtime/task/) — reversible task trees for setup/create with undo support
422
+ - [`@canonical/harnesses`](../../runtime/harnesses/) — AI harness detection (Claude Code, Cursor, Windsurf)
423
+ - [`@canonical/summon-core`](../../summon/core/) — code generation runtime
424
+ - [`@canonical/summon-component`](../../summon/component/) — component generator
425
+ - [`@canonical/summon-package`](../../summon/package/) — package generator
374
426
 
375
427
  ## License
376
428
 
package/dist/pragma CHANGED
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@canonical/pragma-cli",
3
3
  "description": "CLI and MCP server for Canonical's design system.",
4
- "version": "0.27.1-experimental.0",
4
+ "version": "0.30.0",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
7
7
  "types": "src/index.ts",
@@ -44,7 +44,7 @@
44
44
  "access": "public"
45
45
  },
46
46
  "scripts": {
47
- "build": "bun build --compile --minify --target=bun-linux-x64 src/bin.ts --outfile dist/pragma",
47
+ "build": "bun run scripts/build.ts",
48
48
  "build:compile": "bun build --compile --minify --target=bun-linux-x64 src/bin.ts --outfile dist/pragma",
49
49
  "prepublishOnly": "bun run build",
50
50
  "check": "bun run check:biome && bun run check:ts && bun run check:webarchitect",
@@ -60,9 +60,9 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@biomejs/biome": "2.4.9",
63
- "@canonical/biome-config": "^0.27.1-experimental.0",
64
- "@canonical/typescript-config": "^0.27.1-experimental.0",
65
- "@canonical/webarchitect": "^0.27.1-experimental.0",
63
+ "@canonical/biome-config": "^0.30.0",
64
+ "@canonical/typescript-config": "^0.30.0",
65
+ "@canonical/webarchitect": "^0.30.0",
66
66
  "@types/bun": "^1.3.10",
67
67
  "@types/node": "^24.12.0",
68
68
  "@types/react": "^19.2.14",
@@ -70,19 +70,18 @@
70
70
  "typescript": "^5.9.3",
71
71
  "vitest": "^4.0.18"
72
72
  },
73
- "optionalDependencies": {
73
+ "dependencies": {
74
74
  "@canonical/anatomy-dsl": "^0.2.2",
75
+ "@canonical/cli-core": "^0.30.0",
75
76
  "@canonical/code-standards": "^0.1.2",
76
- "@canonical/design-system": "^0.1.2"
77
- },
78
- "dependencies": {
79
- "@canonical/cli-core": "^0.27.1-experimental.0",
80
- "@canonical/harnesses": "^0.27.1-experimental.0",
81
- "@canonical/ke": "^0.27.1-experimental.0",
82
- "@canonical/summon-component": "^0.27.1-experimental.0",
83
- "@canonical/summon-core": "^0.27.1-experimental.0",
84
- "@canonical/summon-package": "^0.27.1-experimental.0",
85
- "@canonical/task": "^0.27.1-experimental.0",
77
+ "@canonical/design-system": "^0.1.2",
78
+ "@canonical/harnesses": "^0.30.0",
79
+ "@canonical/ke": "^0.30.0",
80
+ "@canonical/ke-graphql": "^0.30.0",
81
+ "@canonical/summon-component": "^0.30.0",
82
+ "@canonical/summon-core": "^0.30.0",
83
+ "@canonical/summon-package": "^0.30.0",
84
+ "@canonical/task": "^0.30.0",
86
85
  "@modelcontextprotocol/sdk": "^1.12.0",
87
86
  "chalk": "^5.6.2",
88
87
  "commander": "^14.0.3",
@@ -91,5 +90,5 @@
91
90
  "react": "^19.2.4",
92
91
  "zod": "^3.25.0"
93
92
  },
94
- "gitHead": "d3f5b9aab7bec07fce40a12b45097d28d3cf0201"
93
+ "gitHead": "40fe86ac955473536ac14f074812a9821f5b65c0"
95
94
  }
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { VALID_CHANNELS } from "../constants.js";
3
+ import { parsePackageEntry } from "../domains/refs/operations/parseRef.js";
4
+ import { DEFAULT_CONFIG } from "./defaultConfig.js";
5
+
6
+ describe("DEFAULT_CONFIG", () => {
7
+ it("declares a valid channel", () => {
8
+ expect(VALID_CHANNELS).toContain(DEFAULT_CONFIG.channel);
9
+ });
10
+
11
+ it("declares at least one package, all parseable", () => {
12
+ expect(DEFAULT_CONFIG.packages.length).toBeGreaterThan(0);
13
+ for (const entry of DEFAULT_CONFIG.packages) {
14
+ const ref = parsePackageEntry(entry);
15
+ expect(ref.pkg.length).toBeGreaterThan(0);
16
+ }
17
+ });
18
+
19
+ it("carries the canonical design-system package by default", () => {
20
+ const names = DEFAULT_CONFIG.packages.map((entry) =>
21
+ typeof entry === "string" ? entry : entry.name,
22
+ );
23
+ expect(names).toContain("@canonical/design-system");
24
+ });
25
+
26
+ it("does not declare fields the document must not layer", () => {
27
+ // tier/trace/framework defaults are semantic absences, and packages
28
+ // must reach the merge via DEFAULT_PACKAGES (see defaultConfig.ts).
29
+ expect(DEFAULT_CONFIG.tier).toBeUndefined();
30
+ expect(DEFAULT_CONFIG.trace).toBeUndefined();
31
+ expect(DEFAULT_CONFIG.framework).toBeUndefined();
32
+ });
33
+ });
@@ -0,0 +1,59 @@
1
+ import type { Channel } from "../constants.js";
2
+ import type { RawPackageEntry } from "../domains/refs/operations/parseRef.js";
3
+ import { PragmaError } from "../error/PragmaError.js";
4
+ import defaultsDocument from "./defaults.json" with { type: "json" };
5
+ import parseConfigValues from "./parseConfigValues.js";
6
+ import type { ConfigFileValues } from "./types.js";
7
+
8
+ /** The built-in defaults with the fields every install relies on present. */
9
+ interface DefaultConfig extends ConfigFileValues {
10
+ /** Release channel active when no config file sets one. */
11
+ readonly channel: Channel;
12
+ /** Semantic packages loaded when no config or global refs declare any. */
13
+ readonly packages: ReadonlyArray<RawPackageEntry>;
14
+ }
15
+
16
+ /**
17
+ * Validate the embedded defaults document into the built-in config layer.
18
+ *
19
+ * The document goes through the same parser as user config files, so an
20
+ * invalid edit to `defaults.json` fails loudly at first import (and in
21
+ * tests) instead of silently changing behavior.
22
+ *
23
+ * @throws PragmaError with code `CONFIG_ERROR` when the document is
24
+ * invalid or misses a required default.
25
+ */
26
+ function parseDefaultsDocument(): DefaultConfig {
27
+ const values = parseConfigValues(
28
+ JSON.stringify(defaultsDocument),
29
+ "built-in defaults (src/config/defaults.json)",
30
+ );
31
+ if (values.channel === undefined) {
32
+ throw PragmaError.configError("Built-in defaults must declare a channel.", {
33
+ recovery: { message: "Restore `channel` in src/config/defaults.json." },
34
+ });
35
+ }
36
+ if (values.packages === undefined || values.packages.length === 0) {
37
+ throw PragmaError.configError(
38
+ "Built-in defaults must declare at least one semantic package.",
39
+ {
40
+ recovery: {
41
+ message: "Restore `packages` in src/config/defaults.json.",
42
+ },
43
+ },
44
+ );
45
+ }
46
+ return { ...values, channel: values.channel, packages: values.packages };
47
+ }
48
+
49
+ /**
50
+ * The built-in configuration layer, sourced from the embedded declarative
51
+ * `defaults.json` document rather than hardcoded values.
52
+ *
53
+ * `packages` is deliberately NOT surfaced through the layered `pick()` in
54
+ * `readConfigLayers` — it feeds `DEFAULT_PACKAGES` (and from there the
55
+ * ref merge), where global `refs.json` entries still merge over it by
56
+ * package name. Sourcing it as an ordinary config layer would make it
57
+ * look user-declared and shut global refs out.
58
+ */
59
+ export const DEFAULT_CONFIG: DefaultConfig = parseDefaultsDocument();
@@ -0,0 +1,17 @@
1
+ {
2
+ "channel": "normal",
3
+ "packages": [
4
+ {
5
+ "name": "@canonical/design-system",
6
+ "source": "git+https://github.com/canonical/design-system.git#main"
7
+ },
8
+ {
9
+ "name": "@canonical/anatomy-dsl",
10
+ "source": "git+https://github.com/canonical/anatomy-dsl.git#main"
11
+ },
12
+ {
13
+ "name": "@canonical/code-standards",
14
+ "source": "git+https://github.com/canonical/web-code-standards.git#main"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,53 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import findProjectConfigPath from "./findProjectConfigPath.js";
6
+
7
+ describe("findProjectConfigPath", () => {
8
+ let root: string;
9
+
10
+ beforeEach(() => {
11
+ root = mkdtempSync(join(tmpdir(), "pragma-find-config-"));
12
+ mkdirSync(join(root, ".git"));
13
+ mkdirSync(join(root, "packages/app/src"), { recursive: true });
14
+ });
15
+
16
+ afterEach(() => {
17
+ rmSync(root, { recursive: true, force: true });
18
+ });
19
+
20
+ it("finds the config in the starting directory", () => {
21
+ writeFileSync(join(root, "packages/app/pragma.config.json"), "{}");
22
+ expect(findProjectConfigPath(join(root, "packages/app"))).toBe(
23
+ join(root, "packages/app/pragma.config.json"),
24
+ );
25
+ });
26
+
27
+ it("walks up to the nearest config", () => {
28
+ writeFileSync(join(root, "pragma.config.json"), "{}");
29
+ expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
30
+ join(root, "pragma.config.json"),
31
+ );
32
+ });
33
+
34
+ it("prefers the nearest config over a higher one", () => {
35
+ writeFileSync(join(root, "pragma.config.json"), "{}");
36
+ writeFileSync(join(root, "packages/app/pragma.config.json"), "{}");
37
+ expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
38
+ join(root, "packages/app/pragma.config.json"),
39
+ );
40
+ });
41
+
42
+ it("stops at the repository root (.git) without escaping", () => {
43
+ // The fixture root has .git and no config; nothing above it may leak in.
44
+ expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
45
+ undefined,
46
+ );
47
+ });
48
+
49
+ it("checks the .git directory's own level before stopping", () => {
50
+ writeFileSync(join(root, "pragma.config.json"), "{}");
51
+ expect(findProjectConfigPath(root)).toBe(join(root, "pragma.config.json"));
52
+ });
53
+ });
@@ -0,0 +1,36 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+
5
+ /**
6
+ * Find the nearest `pragma.config.json` at or above `cwd`.
7
+ *
8
+ * Walks up the directory tree so pragma works from anywhere inside a
9
+ * project. The walk is bounded: it stops after checking the first
10
+ * directory that contains `.git` (the repository root), after checking
11
+ * the home directory, and at the filesystem root — a config outside the
12
+ * repository or home never leaks in.
13
+ *
14
+ * @param cwd - Directory to start from.
15
+ * @returns Absolute path of the nearest config file, or `undefined`.
16
+ * @note Impure — probes the filesystem while walking up.
17
+ */
18
+ export default function findProjectConfigPath(cwd: string): string | undefined {
19
+ const home = homedir();
20
+ let dir = resolve(cwd);
21
+
22
+ while (true) {
23
+ const candidate = join(dir, "pragma.config.json");
24
+ if (existsSync(candidate)) {
25
+ return candidate;
26
+ }
27
+ if (existsSync(join(dir, ".git")) || dir === home) {
28
+ return undefined;
29
+ }
30
+ const parent = dirname(dir);
31
+ if (parent === dir) {
32
+ return undefined;
33
+ }
34
+ dir = parent;
35
+ }
36
+ }
@@ -1,6 +1,21 @@
1
- /** @module Configuration file reading, writing, and path resolution. */
1
+ /** @module Configuration reading, writing, layered resolution, and paths. */
2
2
  export { default as configExists } from "./configExists.js";
3
+ export { default as findProjectConfigPath } from "./findProjectConfigPath.js";
4
+ export { default as mergeConfigFileUpdate } from "./mergeConfigFileUpdate.js";
5
+ export { default as parseConfigValues } from "./parseConfigValues.js";
3
6
  export { default as readConfig } from "./readConfig.js";
7
+ export { default as readConfigLayers } from "./readConfigLayers.js";
4
8
  export { default as resolveConfigPath } from "./resolveConfigPath.js";
5
- export type { ConfigUpdate, PragmaConfig } from "./types.js";
9
+ export { default as resolveGlobalConfigPath } from "./resolveGlobalConfigPath.js";
10
+ export { default as resolveWriteConfigPath } from "./resolveWriteConfigPath.js";
11
+ export type {
12
+ ConfigFileValues,
13
+ ConfigLayer,
14
+ ConfigLayers,
15
+ ConfigOrigin,
16
+ ConfigOrigins,
17
+ ConfigScope,
18
+ ConfigUpdate,
19
+ PragmaConfig,
20
+ } from "./types.js";
6
21
  export { default as writeConfig } from "./writeConfig.js";
@@ -0,0 +1,58 @@
1
+ import { readFileSync } from "node:fs";
2
+ import type { ConfigUpdate } from "./types.js";
3
+
4
+ /**
5
+ * Compute a config file's new contents after applying an update.
6
+ *
7
+ * Reads the target file itself (not the merged layers — a write must
8
+ * never copy values from another layer into this file), applies the
9
+ * update per field (`undefined` with the key present removes the field),
10
+ * and returns the serialized JSON. Unknown keys already in the file are
11
+ * preserved verbatim.
12
+ *
13
+ * @param configPath - Absolute path of the config file to update.
14
+ * @param update - Fields to set or remove.
15
+ * @returns The file's new contents, newline-terminated.
16
+ * @throws The raw filesystem/parse error when the existing file is
17
+ * present but unreadable or not valid JSON — never silently clobbers.
18
+ * @note Impure — reads the current file contents.
19
+ */
20
+ export default function mergeConfigFileUpdate(
21
+ configPath: string,
22
+ update: ConfigUpdate,
23
+ ): string {
24
+ let existing: Record<string, unknown> = {};
25
+ try {
26
+ const raw = readFileSync(configPath, "utf-8");
27
+ existing = JSON.parse(raw) as Record<string, unknown>;
28
+ } catch (err: unknown) {
29
+ // Only swallow "file not found" — surface parse or permission errors.
30
+ const isNotFound =
31
+ err instanceof Error &&
32
+ "code" in err &&
33
+ (err as NodeJS.ErrnoException).code === "ENOENT";
34
+ if (!isNotFound) {
35
+ throw err;
36
+ }
37
+ }
38
+
39
+ applyField(existing, update, "tier");
40
+ applyField(existing, update, "channel");
41
+ applyField(existing, update, "packages");
42
+ applyField(existing, update, "trace");
43
+ applyField(existing, update, "framework");
44
+
45
+ return `${JSON.stringify(existing, null, 2)}\n`;
46
+ }
47
+
48
+ function applyField(
49
+ existing: Record<string, unknown>,
50
+ update: ConfigUpdate,
51
+ field: keyof ConfigUpdate,
52
+ ): void {
53
+ if (update[field] !== undefined) {
54
+ existing[field] = update[field];
55
+ } else if (field in update) {
56
+ delete existing[field];
57
+ }
58
+ }