@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
@@ -1,126 +1,20 @@
1
1
  /**
2
- * Design system package registry and resolution.
2
+ * Default semantic graph package registry.
3
3
  *
4
- * Resolves semantic packages from three sources (in priority order):
5
- * 1. file:// local path (development)
6
- * 2. git ref cache (shared/CI)
7
- * 3. require.resolve from node_modules (npm fallback)
4
+ * Sourced from the declarative built-in defaults document
5
+ * (`src/config/defaults.json`) — pragma core carries no hardcoded
6
+ * knowledge of specific design-system packages. These form the base
7
+ * package set whenever a config layer does not declare a non-empty
8
+ * `packages` list; global `refs.json` entries then merge over them by
9
+ * package name, and a non-empty config list replaces them entirely
10
+ * (see `mergeAndParseRefs`).
8
11
  *
9
- * The package list itself is configurable via pragma.config.json `packages`
10
- * field. When absent, the hardcoded DEFAULT_PACKAGES are used.
12
+ * Resolution is handled by the loader chain in `loaders/`.
11
13
  */
12
14
 
13
- import { existsSync } from "node:fs";
14
- import { createRequire } from "node:module";
15
- import { dirname } from "node:path";
16
- import type { PackageRef } from "../refs/operations/parseRef.js";
17
- import { gitCacheDir } from "../refs/operations/paths.js";
15
+ import { DEFAULT_CONFIG } from "../../config/defaultConfig.js";
16
+ import type { RawPackageEntry } from "../refs/operations/parseRef.js";
18
17
 
19
- const require = createRequire(import.meta.url);
20
-
21
- // ---------------------------------------------------------------------------
22
- // Default package registry (used when config.packages is absent)
23
- // ---------------------------------------------------------------------------
24
-
25
- /** Default semantic packages loaded when no config overrides are present. */
26
- export const DEFAULT_PACKAGES: readonly string[] = [
27
- "@canonical/design-system",
28
- "@canonical/anatomy-dsl",
29
- "@canonical/code-standards",
30
- "@canonical/pragma-cli",
31
- ];
32
-
33
- // ---------------------------------------------------------------------------
34
- // Types
35
- // ---------------------------------------------------------------------------
36
-
37
- export interface ResolvedPackage {
38
- /** npm package name. */
39
- readonly pkg: string;
40
- /** Absolute path to the package root. */
41
- readonly dir: string;
42
- }
43
-
44
- // ---------------------------------------------------------------------------
45
- // Resolution
46
- // ---------------------------------------------------------------------------
47
-
48
- /**
49
- * Resolve packages to filesystem directories.
50
- *
51
- * When `refs` is provided, each PackageRef is resolved according to its kind.
52
- * When `refs` is omitted, the DEFAULT_PACKAGES are resolved via require.resolve.
53
- *
54
- * @param refs - Parsed package references (from config merge). Omit for defaults.
55
- * @returns Array of resolved packages with absolute directory paths.
56
- */
57
- export function resolvePackages(
58
- refs?: ReadonlyArray<PackageRef>,
59
- ): ResolvedPackage[] {
60
- if (!refs) {
61
- return resolveNpmPackages(DEFAULT_PACKAGES);
62
- }
63
-
64
- const resolved: ResolvedPackage[] = [];
65
-
66
- for (const ref of refs) {
67
- switch (ref.kind) {
68
- case "file": {
69
- if (!existsSync(ref.path)) {
70
- console.warn(
71
- `pragma: package "${ref.pkg}" points to ${ref.path} which does not exist. Check the path or remove the file:// source.`,
72
- );
73
- break;
74
- }
75
- resolved.push({ pkg: ref.pkg, dir: ref.path });
76
- break;
77
- }
78
-
79
- case "git": {
80
- const cacheDir = gitCacheDir(ref.pkg, ref.ref);
81
- if (existsSync(cacheDir)) {
82
- resolved.push({ pkg: ref.pkg, dir: cacheDir });
83
- break;
84
- }
85
- // Cache miss — warn and fall through to npm
86
- console.warn(
87
- `pragma: git ref for "${ref.pkg}" not cached. Run "pragma update-refs" to fetch it.`,
88
- );
89
- resolveNpmFallback(ref.pkg, resolved);
90
- break;
91
- }
92
-
93
- case "npm": {
94
- resolveNpmFallback(ref.pkg, resolved);
95
- break;
96
- }
97
- }
98
- }
99
-
100
- return resolved;
101
- }
102
-
103
- // ---------------------------------------------------------------------------
104
- // Helpers
105
- // ---------------------------------------------------------------------------
106
-
107
- /** Resolve a list of package names via require.resolve. */
108
- function resolveNpmPackages(pkgs: readonly string[]): ResolvedPackage[] {
109
- const resolved: ResolvedPackage[] = [];
110
- for (const pkg of pkgs) {
111
- resolveNpmFallback(pkg, resolved);
112
- }
113
- return resolved;
114
- }
115
-
116
- /** Try to resolve a single package via require.resolve, push if found. */
117
- function resolveNpmFallback(pkg: string, resolved: ResolvedPackage[]): void {
118
- try {
119
- resolved.push({
120
- pkg,
121
- dir: dirname(require.resolve(`${pkg}/package.json`)),
122
- });
123
- } catch {
124
- // package not installed — skip
125
- }
126
- }
18
+ /** Default semantic graph packages loaded when no config overrides are present. */
19
+ export const DEFAULT_PACKAGES: readonly RawPackageEntry[] =
20
+ DEFAULT_CONFIG.packages;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pick `count` random items from an array without replacement.
3
+ *
4
+ * Uses Fisher-Yates partial shuffle for uniform distribution.
5
+ * Returns at most `arr.length` items if count exceeds the array size.
6
+ */
7
+ export default function pickRandom<T>(
8
+ arr: readonly T[],
9
+ count: number,
10
+ ): readonly T[] {
11
+ const n = Math.min(count, arr.length);
12
+ if (n === 0) return [];
13
+
14
+ const copy = [...arr];
15
+ for (let i = 0; i < n; i++) {
16
+ const j = i + Math.floor(Math.random() * (copy.length - i));
17
+ const tmp = copy[i];
18
+ copy[i] = copy[j] as T;
19
+ copy[j] = tmp as T;
20
+ }
21
+ return copy.slice(0, n);
22
+ }
@@ -0,0 +1,27 @@
1
+ import { mkdtempSync, rmSync } 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 resolveConfiguredGraphs from "./resolveConfiguredGraphs.js";
6
+
7
+ describe("resolveConfiguredGraphs", () => {
8
+ let dir: string;
9
+
10
+ beforeEach(() => {
11
+ dir = mkdtempSync(join(tmpdir(), "pragma-rcg-"));
12
+ });
13
+
14
+ afterEach(() => {
15
+ rmSync(dir, { recursive: true, force: true });
16
+ });
17
+
18
+ it("resolves the default semantic packages' TTL graphs", async () => {
19
+ // No pragma.config.json in the temp dir → mergeAndParseRefs falls back to
20
+ // the default packages, which resolve from node_modules in the workspace.
21
+ const graphs = await resolveConfiguredGraphs(dir);
22
+
23
+ expect(graphs.length).toBeGreaterThan(0);
24
+ expect(graphs[0]).toMatchObject({ format: "turtle" });
25
+ expect(typeof graphs[0]?.content).toBe("string");
26
+ });
27
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Resolve every TTL graph from the semantically-configured packages.
3
+ *
4
+ * Reads `pragma.config.json`, merges its `packages` with the global refs
5
+ * and the built-in defaults (a non-empty `packages` list replaces the set
6
+ * entirely; an empty or absent list falls back to global refs merged over
7
+ * the defaults), resolves each package through the loader chain
8
+ * (local > git > bundled), and returns the union of their definitions/ and
9
+ * data/ graph content — the same discovery the runtime store performs at
10
+ * boot, minus the store.
11
+ *
12
+ * @note Impure — reads config and package files from disk.
13
+ *
14
+ * @param cwd - Working directory used to locate `pragma.config.json`.
15
+ * @returns The TTL graph content of every resolved package.
16
+ */
17
+
18
+ import { readConfig } from "#config";
19
+ import {
20
+ createBundledLoader,
21
+ createGitLoader,
22
+ createLocalLoader,
23
+ } from "./loaders/index.js";
24
+ import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
25
+ import {
26
+ type GraphContent,
27
+ resolveSemanticPackages,
28
+ } from "./semanticPackage.js";
29
+
30
+ export default async function resolveConfiguredGraphs(
31
+ cwd: string,
32
+ ): Promise<GraphContent[]> {
33
+ const config = readConfig(cwd);
34
+ const refs = mergeAndParseRefs(config.packages);
35
+ const packages = await resolveSemanticPackages(refs, [
36
+ createLocalLoader(),
37
+ createGitLoader(),
38
+ createBundledLoader(),
39
+ ]);
40
+ return packages.flatMap((pkg) => pkg.graphs);
41
+ }
@@ -10,14 +10,8 @@
10
10
 
11
11
  import type { SourceSpec } from "@canonical/ke";
12
12
  import { readConfig } from "#config";
13
- import {
14
- type PackageRef,
15
- parsePackageEntry,
16
- type RawPackageEntry,
17
- } from "../refs/operations/parseRef.js";
18
- import readGlobalRefs from "../refs/operations/readGlobalRefs.js";
19
13
  import { bootStore } from "./bootStore.js";
20
- import { DEFAULT_PACKAGES } from "./packages.js";
14
+ import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
21
15
  import type { PragmaRuntime } from "./types/index.js";
22
16
 
23
17
  export type { PragmaRuntime } from "./types/index.js";
@@ -43,75 +37,25 @@ export async function bootPragma(options?: {
43
37
  const cwd = options?.cwd ?? process.cwd();
44
38
  const config = readConfig(cwd);
45
39
 
46
- // Merge package refs: project overrides global, both override defaults.
40
+ // Merge package refs: a non-empty config list replaces global refs and
41
+ // defaults; otherwise global refs merge over the defaults.
47
42
  const refs = options?.sources
48
43
  ? undefined
49
44
  : mergeAndParseRefs(config.packages);
50
45
 
51
- const store = await bootStore({
46
+ const { store, packages } = await bootStore({
52
47
  cwd,
53
48
  sources: options?.sources,
54
49
  refs,
50
+ trace: config.trace,
51
+ prefixes: config.prefixes,
55
52
  });
56
53
 
57
54
  return {
58
55
  store,
59
56
  config,
60
57
  cwd,
58
+ packages,
61
59
  dispose: () => store.dispose(),
62
60
  };
63
61
  }
64
-
65
- // ---------------------------------------------------------------------------
66
- // Ref merging
67
- // ---------------------------------------------------------------------------
68
-
69
- /**
70
- * Merge global refs, project config packages, and defaults into a final
71
- * parsed PackageRef array.
72
- *
73
- * Priority: project packages > global refs > hardcoded defaults.
74
- * Merging is by package name — a project entry for "@canonical/foo"
75
- * overrides the global entry for the same package.
76
- */
77
- function mergeAndParseRefs(
78
- projectPackages?: ReadonlyArray<RawPackageEntry>,
79
- ): PackageRef[] {
80
- const globalEntries = readGlobalRefs();
81
-
82
- // If neither project nor global defines packages, return undefined
83
- // so resolvePackages() uses hardcoded defaults.
84
- if (
85
- (!projectPackages || projectPackages.length === 0) &&
86
- globalEntries.length === 0
87
- ) {
88
- return DEFAULT_PACKAGES.map((pkg) => parsePackageEntry(pkg));
89
- }
90
-
91
- // Build a name → entry map, global first, project overrides.
92
- const merged = new Map<string, RawPackageEntry>();
93
-
94
- // Start with defaults (lowest priority)
95
- for (const pkg of DEFAULT_PACKAGES) {
96
- merged.set(pkg, pkg);
97
- }
98
-
99
- // Global overrides defaults
100
- for (const entry of globalEntries) {
101
- const name = typeof entry === "string" ? entry : entry.name;
102
- merged.set(name, entry);
103
- }
104
-
105
- // Project overrides global
106
- if (projectPackages) {
107
- // When project explicitly declares packages, it's a full replacement
108
- // of the package list — clear defaults and global, use project only.
109
- merged.clear();
110
- for (const entry of projectPackages) {
111
- const name = typeof entry === "string" ? entry : entry.name;
112
- merged.set(name, entry);
113
- }
114
- }
115
-
116
- return [...merged.values()].map(parsePackageEntry);
117
- }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Factory for sample command formatters.
3
+ *
4
+ * Every domain's sample formatter follows the same pattern: a count
5
+ * header, then each item rendered via the domain's lookup formatter.
6
+ * This factory eliminates the duplication.
7
+ */
8
+
9
+ import type { Formatters } from "./formatters.js";
10
+ import type { SampleOutput } from "./types/index.js";
11
+
12
+ /**
13
+ * Create a three-mode formatter for `pragma <domain> sample`.
14
+ *
15
+ * @param domain - plural domain label for the header (e.g. "standards")
16
+ * @param renderItem - renders one item; receives the item and the mode
17
+ */
18
+ export default function createSampleFormatters<T>(
19
+ domain: string,
20
+ renderItem: (item: T, mode: "plain" | "llm") => string,
21
+ ): Formatters<SampleOutput<T>> {
22
+ return {
23
+ plain(output) {
24
+ const header = `Showing ${output.samples.length} of ${output.totalCount} ${domain}\n`;
25
+ const items = output.samples.map((item) => renderItem(item, "plain"));
26
+ return header + items.join("\n\n");
27
+ },
28
+
29
+ llm(output) {
30
+ const header = `Showing ${output.samples.length} of ${output.totalCount} ${domain}\n`;
31
+ const items = output.samples.map((item) => renderItem(item, "llm"));
32
+ return header + items.join("\n\n");
33
+ },
34
+
35
+ json(output) {
36
+ return JSON.stringify(
37
+ {
38
+ samples: output.samples,
39
+ totalCount: output.totalCount,
40
+ nextSteps: output.nextSteps,
41
+ },
42
+ null,
43
+ 2,
44
+ );
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Semantic package contracts and resolution orchestrator.
3
+ *
4
+ * A semantic package is a resolved graph dependency containing TTL
5
+ * definitions, instance data, and skills. Three loaders produce the
6
+ * same `SemanticPackage` shape — local (file/npm), git cache, and
7
+ * bundled (compiled binary). The orchestrator tries each loader in
8
+ * precedence order: local > git > bundled.
9
+ */
10
+
11
+ import type { PackageRef } from "../refs/operations/parseRef.js";
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Contracts
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /** A single TTL file's content with provenance annotation. */
18
+ export interface GraphContent {
19
+ /** Original file path or virtual identifier (for provenance/debugging). */
20
+ readonly path: string;
21
+ /** TTL content string. */
22
+ readonly content: string;
23
+ /** RDF serialization format. */
24
+ readonly format: "turtle";
25
+ }
26
+
27
+ /** A discovered story-pack JSON file with its content. */
28
+ export interface StoryFileEntry {
29
+ /**
30
+ * Original file path, or a virtual identifier for embedded sources
31
+ * (e.g. `(bundled)/…`) — for provenance/debugging, not guaranteed to
32
+ * exist on disk.
33
+ */
34
+ readonly path: string;
35
+ /** The file's parsed JSON — validated by the story-pack compiler. */
36
+ readonly definition: unknown;
37
+ }
38
+
39
+ export interface SkillEntry {
40
+ /** Absolute path to the skill directory (must exist on disk for symlinking). */
41
+ readonly dir: string;
42
+ /** Skill folder name (e.g. "anatomy-author"). */
43
+ readonly folderName: string;
44
+ }
45
+
46
+ /** A resolved graph package. All loaders produce this uniform shape. */
47
+ export interface SemanticPackage {
48
+ /** npm package name (e.g. "@canonical/design-system"). */
49
+ readonly name: string;
50
+ /** Version string from package.json. */
51
+ readonly version: string;
52
+ /** How this package was resolved. */
53
+ readonly source: "local" | "git" | "bundled";
54
+ /** TTL graph content to load into the ke store. */
55
+ readonly graphs: GraphContent[];
56
+ /** Skills available in this package. */
57
+ readonly skills: SkillEntry[];
58
+ /**
59
+ * Raw story-pack files shipped by this package (`stories/*.json`).
60
+ * Parsed JSON, validated at compile time so one bad file cannot break boot.
61
+ */
62
+ readonly stories: StoryFileEntry[];
63
+ }
64
+
65
+ /**
66
+ * Resolves a package reference to a SemanticPackage.
67
+ * Returns `undefined` if this loader cannot resolve the given ref.
68
+ * May be async (e.g. BundledLoader reads blob content asynchronously).
69
+ */
70
+ export interface PackageLoader {
71
+ /** Loader identity for diagnostics. */
72
+ readonly name: "local" | "git" | "bundled";
73
+ /** Attempt to resolve a package reference. */
74
+ resolve(
75
+ ref: PackageRef,
76
+ ): SemanticPackage | undefined | Promise<SemanticPackage | undefined>;
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Orchestrator
81
+ // ---------------------------------------------------------------------------
82
+
83
+ /**
84
+ * Resolve package references to semantic packages using a loader chain.
85
+ *
86
+ * Tries each loader in array order for each ref. The first loader that
87
+ * returns a result wins — subsequent loaders are skipped for that ref.
88
+ * Refs that no loader can resolve are silently dropped.
89
+ *
90
+ * @param refs - Parsed package references from config merge.
91
+ * @param loaders - Loaders in precedence order (local > git > bundled).
92
+ * @returns Array of resolved semantic packages.
93
+ */
94
+ export async function resolveSemanticPackages(
95
+ refs: ReadonlyArray<PackageRef>,
96
+ loaders: ReadonlyArray<PackageLoader>,
97
+ ): Promise<SemanticPackage[]> {
98
+ const packages: SemanticPackage[] = [];
99
+
100
+ for (const ref of refs) {
101
+ let resolved: SemanticPackage | undefined;
102
+ for (const loader of loaders) {
103
+ resolved = await loader.resolve(ref);
104
+ if (resolved) break;
105
+ }
106
+ if (resolved) {
107
+ packages.push(resolved);
108
+ }
109
+ }
110
+
111
+ return packages;
112
+ }
@@ -0,0 +1,145 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { PragmaError } from "#error";
3
+ import type { PragmaContext } from "../context.js";
4
+ import type { LookupResult } from "../contracts.js";
5
+ import compileLookupCommand from "./compileLookupCommand.js";
6
+ import type { LookupStory } from "./types.js";
7
+
8
+ interface FakeEntity {
9
+ readonly name: string;
10
+ readonly detail: string;
11
+ }
12
+
13
+ function makeStory(
14
+ overrides: Partial<LookupStory<FakeEntity, string>> = {},
15
+ ): LookupStory<FakeEntity, string> {
16
+ return {
17
+ noun: "thing",
18
+ description: "Look up things",
19
+ toolDescription: "Look up things over MCP.",
20
+ namesDescription: "Thing names",
21
+ detailedParam: { description: "Show details" },
22
+ examples: ["pragma thing lookup a"],
23
+ resolve: async (_rt, names) => {
24
+ const results = names
25
+ .filter((name) => name !== "missing")
26
+ .map((name) => ({ name, detail: `detail-${name}` }));
27
+ const errors: LookupResult<FakeEntity>["errors"] = names
28
+ .filter((name) => name === "missing")
29
+ .map((name) => ({
30
+ query: name,
31
+ code: "ENTITY_NOT_FOUND",
32
+ message: `thing "${name}" not found.`,
33
+ }));
34
+ return { results, errors };
35
+ },
36
+ toFmtInput: (entity, view) =>
37
+ view.detailed ? `${entity.name}:${entity.detail}` : entity.name,
38
+ formatters: {
39
+ plain: (text) => `plain:${text}`,
40
+ llm: (text) => `llm:${text}`,
41
+ json: (text) => JSON.stringify(text),
42
+ },
43
+ ...overrides,
44
+ };
45
+ }
46
+
47
+ function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
48
+ return {
49
+ cwd: "/tmp",
50
+ globalFlags: { llm: false, format: "text" as const, verbose: false },
51
+ ...overrides,
52
+ } as PragmaContext;
53
+ }
54
+
55
+ async function executeText(
56
+ story: LookupStory<FakeEntity, string>,
57
+ params: Record<string, unknown>,
58
+ ctx: PragmaContext,
59
+ ): Promise<string> {
60
+ const cmd = compileLookupCommand(ctx, story);
61
+ const result = await cmd.execute(params, ctx);
62
+ if (result.tag !== "output") throw new Error("expected output result");
63
+ return result.render.plain(result.value);
64
+ }
65
+
66
+ describe("compileLookupCommand", () => {
67
+ it("projects names, detailed, and extra parameters", () => {
68
+ const story = makeStory({
69
+ params: [
70
+ { name: "extra", type: "boolean", description: "extra flag" },
71
+ { name: "mcpOnly", type: "boolean", description: "x", surfaces: "mcp" },
72
+ ],
73
+ parameterGroups: { Extras: ["extra"] },
74
+ });
75
+ const cmd = compileLookupCommand(makeCtx(), story);
76
+ expect(cmd.path).toEqual(["thing", "lookup"]);
77
+ expect(cmd.parameters.map((p) => p.name)).toEqual([
78
+ "names",
79
+ "detailed",
80
+ "extra",
81
+ ]);
82
+ const names = cmd.parameters.at(0);
83
+ expect(names?.type).toBe("multiselect");
84
+ expect(names?.positional).toBe(true);
85
+ expect(names?.required).toBe(true);
86
+ expect(cmd.parameterGroups).toEqual({ Extras: ["extra"] });
87
+ });
88
+
89
+ it("omits the detailed parameter when the story has none", () => {
90
+ const cmd = compileLookupCommand(
91
+ makeCtx(),
92
+ makeStory({ detailedParam: undefined }),
93
+ );
94
+ expect(cmd.parameters.map((p) => p.name)).toEqual(["names"]);
95
+ });
96
+
97
+ it("renders each result and appends an errors section", async () => {
98
+ const text = await executeText(
99
+ makeStory(),
100
+ { names: ["a", "missing"] },
101
+ makeCtx(),
102
+ );
103
+ expect(text).toContain("plain:a");
104
+ expect(text).toContain("Errors:");
105
+ expect(text).toContain('thing "missing" not found.');
106
+ });
107
+
108
+ it("is summary by default and detailed with the flag", async () => {
109
+ const summary = await executeText(makeStory(), { names: ["a"] }, makeCtx());
110
+ expect(summary).toBe("plain:a");
111
+
112
+ const detailed = await executeText(
113
+ makeStory(),
114
+ { names: ["a"], detailed: true },
115
+ makeCtx(),
116
+ );
117
+ expect(detailed).toBe("plain:a:detail-a");
118
+ });
119
+
120
+ it("accepts the legacy single name parameter", async () => {
121
+ const text = await executeText(makeStory(), { name: "a" }, makeCtx());
122
+ expect(text).toBe("plain:a");
123
+ });
124
+
125
+ it("throws the story's empty-names error when no names are given", async () => {
126
+ const story = makeStory({
127
+ emptyNamesError: () => PragmaError.invalidInput("names", "(empty)"),
128
+ });
129
+ const cmd = compileLookupCommand(makeCtx(), story);
130
+ await expect(cmd.execute({ names: [] }, makeCtx())).rejects.toThrow(
131
+ PragmaError,
132
+ );
133
+ });
134
+
135
+ it("exposes an ink renderer only when the story defines one", async () => {
136
+ const story = makeStory({
137
+ renderInk: (result, view) =>
138
+ `ink:${result.results.length}:${view.detailed}`,
139
+ });
140
+ const cmd = compileLookupCommand(makeCtx(), story);
141
+ const result = await cmd.execute({ names: ["a"] }, makeCtx());
142
+ if (result.tag !== "output") throw new Error("expected output result");
143
+ expect(result.render.ink?.(result.value)).toBe("ink:1:false");
144
+ });
145
+ });