@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
@@ -8,19 +8,28 @@ import type { ConfigShowData } from "../operations/types.js";
8
8
  * - **llm** renders a markdown section with bullet points.
9
9
  * - **json** serializes the raw ConfigShowData.
10
10
  */
11
+ /** Render a `[layer]` marker for values a config file supplied. */
12
+ function originMarker(origin: "default" | "global" | "project"): string {
13
+ return origin === "default" ? "" : ` [${origin}]`;
14
+ }
15
+
11
16
  const formatters: Formatters<ConfigShowData> = {
12
17
  plain(data) {
13
18
  const lines: string[] = [];
14
19
 
15
20
  if (data.tier !== undefined) {
16
21
  const chain = data.tierChain.join(" → ");
17
- lines.push(`tier: ${data.tier} (${chain})`);
22
+ lines.push(
23
+ `tier: ${data.tier} (${chain})${originMarker(data.origins.tier)}`,
24
+ );
18
25
  } else {
19
26
  lines.push("tier: (none — all tiers visible)");
20
27
  }
21
28
 
22
29
  const releases = data.includedReleases.join(" + ");
23
- lines.push(`channel: ${data.channel} (${releases})`);
30
+ lines.push(
31
+ `channel: ${data.channel} (${releases})${originMarker(data.origins.channel)}`,
32
+ );
24
33
  lines.push(`installed via: ${data.installSource}`);
25
34
 
26
35
  if (data.configFileExists) {
@@ -28,6 +37,11 @@ const formatters: Formatters<ConfigShowData> = {
28
37
  } else {
29
38
  lines.push("config file: (not found)");
30
39
  }
40
+ if (data.globalConfigExists) {
41
+ lines.push(`global config: ${data.globalConfigPath}`);
42
+ } else {
43
+ lines.push("global config: (not found)");
44
+ }
31
45
 
32
46
  return lines.join("\n");
33
47
  },
@@ -40,13 +54,17 @@ const formatters: Formatters<ConfigShowData> = {
40
54
 
41
55
  if (data.tier !== undefined) {
42
56
  const chain = data.tierChain.join(" > ");
43
- lines.push(`- **Tier:** ${data.tier} (${chain})`);
57
+ lines.push(
58
+ `- **Tier:** ${data.tier} (${chain})${originMarker(data.origins.tier)}`,
59
+ );
44
60
  } else {
45
61
  lines.push("- **Tier:** none (all tiers visible)");
46
62
  }
47
63
 
48
64
  const releases = data.includedReleases.join(", ");
49
- lines.push(`- **Channel:** ${data.channel} (${releases})`);
65
+ lines.push(
66
+ `- **Channel:** ${data.channel} (${releases})${originMarker(data.origins.channel)}`,
67
+ );
50
68
  lines.push(`- **Installed via:** ${data.installSource}`);
51
69
 
52
70
  if (data.configFileExists) {
@@ -54,6 +72,11 @@ const formatters: Formatters<ConfigShowData> = {
54
72
  } else {
55
73
  lines.push("- **Config file:** not found");
56
74
  }
75
+ if (data.globalConfigExists) {
76
+ lines.push(`- **Global config:** \`${data.globalConfigPath}\``);
77
+ } else {
78
+ lines.push("- **Global config:** not found");
79
+ }
57
80
 
58
81
  return lines.join("\n");
59
82
  },
@@ -2,36 +2,52 @@ import { describe, expect, it } from "vitest";
2
2
  import formatters from "./tier.js";
3
3
 
4
4
  describe("tierFormatters.set", () => {
5
- const data = { field: "tier", value: "apps/lxd" };
5
+ const data = {
6
+ field: "tier",
7
+ value: "apps/lxd",
8
+ path: "/repo/pragma.config.json",
9
+ };
6
10
 
7
- it("plain renders set message", () => {
8
- expect(formatters.set.plain(data)).toBe('Set tier to "apps/lxd".');
11
+ it("plain renders set message with the written path", () => {
12
+ expect(formatters.set.plain(data)).toBe(
13
+ 'Set tier to "apps/lxd".\nWrote /repo/pragma.config.json',
14
+ );
9
15
  });
10
16
 
11
- it("llm renders set message", () => {
12
- expect(formatters.set.llm(data)).toBe('Set tier to "apps/lxd".');
17
+ it("llm renders set message with the written path", () => {
18
+ expect(formatters.set.llm(data)).toBe(
19
+ 'Set tier to "apps/lxd".\nWrote /repo/pragma.config.json',
20
+ );
13
21
  });
14
22
 
15
23
  it("json returns valid JSON", () => {
16
24
  const parsed = JSON.parse(formatters.set.json(data));
17
25
  expect(parsed.field).toBe("tier");
18
26
  expect(parsed.value).toBe("apps/lxd");
27
+ expect(parsed.path).toBe("/repo/pragma.config.json");
19
28
  });
20
29
  });
21
30
 
22
31
  describe("tierFormatters.reset", () => {
23
- it("plain renders reset message", () => {
24
- expect(formatters.reset.plain("")).toBe("Reset tier to default.");
32
+ const data = { field: "tier", path: "/repo/pragma.config.json" };
33
+
34
+ it("plain renders reset message with the written path", () => {
35
+ expect(formatters.reset.plain(data)).toBe(
36
+ "Reset tier to default.\nWrote /repo/pragma.config.json",
37
+ );
25
38
  });
26
39
 
27
- it("llm renders reset message", () => {
28
- expect(formatters.reset.llm("")).toBe("Reset tier to default.");
40
+ it("llm renders reset message with the written path", () => {
41
+ expect(formatters.reset.llm(data)).toBe(
42
+ "Reset tier to default.\nWrote /repo/pragma.config.json",
43
+ );
29
44
  });
30
45
 
31
46
  it("json returns valid JSON", () => {
32
- const parsed = JSON.parse(formatters.reset.json(""));
47
+ const parsed = JSON.parse(formatters.reset.json(data));
33
48
  expect(parsed.field).toBe("tier");
34
49
  expect(parsed.reset).toBe(true);
50
+ expect(parsed.path).toBe("/repo/pragma.config.json");
35
51
  });
36
52
  });
37
53
 
@@ -1,5 +1,12 @@
1
1
  import type { Formatters } from "../../shared/formatters.js";
2
2
 
3
+ /** Payload for a persisted config write, including the file written. */
4
+ interface TierWrite {
5
+ readonly field: string;
6
+ readonly value: string;
7
+ readonly path: string;
8
+ }
9
+
3
10
  /**
4
11
  * Formatter sets for `pragma config tier` output, grouped by branch:
5
12
  *
@@ -9,19 +16,20 @@ import type { Formatters } from "../../shared/formatters.js";
9
16
  */
10
17
  const tierFormatters = {
11
18
  set: {
12
- plain: (d: { field: string; value: string }) =>
13
- `Set ${d.field} to "${d.value}".`,
14
- llm: (d: { field: string; value: string }) =>
15
- `Set ${d.field} to "${d.value}".`,
16
- json: (d: { field: string; value: string }) =>
17
- JSON.stringify({ field: d.field, value: d.value }),
18
- } satisfies Formatters<{ field: string; value: string }>,
19
+ plain: (d: TierWrite) => `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
20
+ llm: (d: TierWrite) => `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
21
+ json: (d: TierWrite) =>
22
+ JSON.stringify({ field: d.field, value: d.value, path: d.path }),
23
+ } satisfies Formatters<TierWrite>,
19
24
 
20
25
  reset: {
21
- plain: () => "Reset tier to default.",
22
- llm: () => "Reset tier to default.",
23
- json: () => JSON.stringify({ field: "tier", reset: true }),
24
- } satisfies Formatters<string>,
26
+ plain: (d: { field: string; path: string }) =>
27
+ `Reset tier to default.\nWrote ${d.path}`,
28
+ llm: (d: { field: string; path: string }) =>
29
+ `Reset tier to default.\nWrote ${d.path}`,
30
+ json: (d: { field: string; path: string }) =>
31
+ JSON.stringify({ field: "tier", reset: true, path: d.path }),
32
+ } satisfies Formatters<{ field: string; path: string }>,
25
33
 
26
34
  query: {
27
35
  plain: (tier: string | undefined) =>
@@ -8,13 +8,13 @@ import type { Formatters } from "../../shared/formatters.js";
8
8
  */
9
9
  const traceFormatters = {
10
10
  set: {
11
- plain: (d: { field: string; value: boolean }) =>
12
- d.value ? "Tracing enabled." : "Tracing disabled.",
13
- llm: (d: { field: string; value: boolean }) =>
14
- d.value ? "Tracing enabled." : "Tracing disabled.",
15
- json: (d: { field: string; value: boolean }) =>
16
- JSON.stringify({ field: d.field, value: d.value }),
17
- } satisfies Formatters<{ field: string; value: boolean }>,
11
+ plain: (d: { field: string; value: boolean; path: string }) =>
12
+ `${d.value ? "Tracing enabled." : "Tracing disabled."}\nWrote ${d.path}`,
13
+ llm: (d: { field: string; value: boolean; path: string }) =>
14
+ `${d.value ? "Tracing enabled." : "Tracing disabled."}\nWrote ${d.path}`,
15
+ json: (d: { field: string; value: boolean; path: string }) =>
16
+ JSON.stringify({ field: d.field, value: d.value, path: d.path }),
17
+ } satisfies Formatters<{ field: string; value: boolean; path: string }>,
18
18
 
19
19
  query: {
20
20
  plain: (status: string) => status,
@@ -1,54 +1,20 @@
1
1
  /**
2
- * MCP tool specs for the config domain.
2
+ * MCP tool specs for the config domain — config_show, config_tier,
3
+ * config_channel.
3
4
  *
4
- * config_show uses the shared resolveConfigShow() operation so that
5
- * MCP data matches what the CLI produces (tierChain, includedReleases, etc.).
5
+ * config_show is compiled from the config read story in `../stories.ts`
6
+ * so both surfaces share resolution and formatters; the mutating tier and
7
+ * channel tools are spec'd by hand.
6
8
  */
7
9
 
8
10
  import { readConfig, writeConfig } from "#config";
9
- import { detectInstallSource } from "#package-manager";
11
+ import { compileReadTool } from "../../shared/stories/index.js";
10
12
  import type { ToolSpec } from "../../shared/ToolSpec.js";
11
- import { showFormatters } from "../formatters/index.js";
12
- import {
13
- resolveConfigShow,
14
- validateChannel,
15
- validateTier,
16
- } from "../operations/index.js";
13
+ import { validateChannel, validateTier } from "../operations/index.js";
14
+ import { configShowStory } from "../stories.js";
17
15
 
18
16
  const specs: readonly ToolSpec[] = [
19
- {
20
- name: "config_show",
21
- description:
22
- "Show current pragma configuration (tier and channel settings).",
23
- params: {
24
- condensed: {
25
- type: "boolean",
26
- description: "Token-optimized output",
27
- optional: true,
28
- },
29
- },
30
- readOnly: true,
31
- async execute(rt, { condensed }) {
32
- const install = detectInstallSource();
33
- const data = resolveConfigShow(rt.config, {
34
- packageManager: install.packageManager,
35
- installSource: install.label,
36
- configFilePath: "pragma.config.json",
37
- configFileExists: true,
38
- });
39
-
40
- if (condensed) {
41
- const text = showFormatters.llm(data);
42
- return {
43
- condensed: true,
44
- text,
45
- tokens: `~${Math.ceil(text.length / 4)}`,
46
- };
47
- }
48
-
49
- return { data };
50
- },
51
- },
17
+ compileReadTool(configShowStory),
52
18
  {
53
19
  name: "config_tier",
54
20
  description:
@@ -64,18 +30,25 @@ const specs: readonly ToolSpec[] = [
64
30
  description: "Reset tier to default",
65
31
  optional: true,
66
32
  },
33
+ global: {
34
+ type: "boolean",
35
+ description: "Write to the global config instead of the project file",
36
+ optional: true,
37
+ },
67
38
  },
68
39
  readOnly: false,
69
- async execute(rt, { path, reset }) {
40
+ async execute(rt, { path, reset, global: globalScope }) {
41
+ const scope = globalScope === true ? "global" : undefined;
42
+
70
43
  if (reset) {
71
- writeConfig(rt.cwd, { tier: undefined });
72
- return { data: { tier: null, action: "reset" } };
44
+ const written = writeConfig(rt.cwd, { tier: undefined }, scope);
45
+ return { data: { tier: null, action: "reset", path: written } };
73
46
  }
74
47
 
75
48
  if (path) {
76
49
  await validateTier(rt.store, path as string);
77
- writeConfig(rt.cwd, { tier: path as string });
78
- return { data: { tier: path, action: "set" } };
50
+ const written = writeConfig(rt.cwd, { tier: path as string }, scope);
51
+ return { data: { tier: path, action: "set", path: written } };
79
52
  }
80
53
 
81
54
  const config = readConfig(rt.cwd);
@@ -97,18 +70,25 @@ const specs: readonly ToolSpec[] = [
97
70
  description: "Reset channel to normal",
98
71
  optional: true,
99
72
  },
73
+ global: {
74
+ type: "boolean",
75
+ description: "Write to the global config instead of the project file",
76
+ optional: true,
77
+ },
100
78
  },
101
79
  readOnly: false,
102
- async execute(rt, { value, reset }) {
80
+ async execute(rt, { value, reset, global: globalScope }) {
81
+ const scope = globalScope === true ? "global" : undefined;
82
+
103
83
  if (reset) {
104
- writeConfig(rt.cwd, { channel: undefined });
105
- return { data: { channel: "normal", action: "reset" } };
84
+ const written = writeConfig(rt.cwd, { channel: undefined }, scope);
85
+ return { data: { channel: "normal", action: "reset", path: written } };
106
86
  }
107
87
 
108
88
  if (value) {
109
89
  const channel = validateChannel(value as string);
110
- writeConfig(rt.cwd, { channel });
111
- return { data: { channel, action: "set" } };
90
+ const written = writeConfig(rt.cwd, { channel }, scope);
91
+ return { data: { channel, action: "set", path: written } };
112
92
  }
113
93
 
114
94
  const config = readConfig(rt.cwd);
@@ -1,6 +1,21 @@
1
1
  import { describe, expect, it } from "vitest";
2
+ import type { ConfigOrigins } from "#config";
2
3
  import resolveConfigShow from "./show.js";
3
4
 
5
+ const PROJECT_ORIGINS: ConfigOrigins = {
6
+ tier: "project",
7
+ channel: "project",
8
+ packages: "default",
9
+ trace: "default",
10
+ framework: "default",
11
+ };
12
+
13
+ const PROVENANCE = {
14
+ globalConfigPath: "/home/u/.config/pragma/config.json",
15
+ globalConfigExists: false,
16
+ origins: PROJECT_ORIGINS,
17
+ };
18
+
4
19
  describe("resolveConfigShow", () => {
5
20
  it("resolves tier chain and included releases", () => {
6
21
  const data = resolveConfigShow(
@@ -10,6 +25,7 @@ describe("resolveConfigShow", () => {
10
25
  installSource: "bun (global)",
11
26
  configFilePath: "/tmp/pragma.config.json",
12
27
  configFileExists: true,
28
+ ...PROVENANCE,
13
29
  },
14
30
  );
15
31
 
@@ -30,6 +46,7 @@ describe("resolveConfigShow", () => {
30
46
  installSource: "bun (local)",
31
47
  configFilePath: "/tmp/pragma.config.json",
32
48
  configFileExists: false,
49
+ ...PROVENANCE,
33
50
  },
34
51
  );
35
52
 
@@ -47,6 +64,7 @@ describe("resolveConfigShow", () => {
47
64
  installSource: "pnpm (global)",
48
65
  configFilePath: "/tmp/pragma.config.json",
49
66
  configFileExists: true,
67
+ ...PROVENANCE,
50
68
  },
51
69
  );
52
70
 
@@ -1,4 +1,4 @@
1
- import type { PragmaConfig } from "#config";
1
+ import type { ConfigOrigins, PragmaConfig } from "#config";
2
2
  import { CHANNEL_RELEASES } from "../../shared/filters/buildChannelFilter.js";
3
3
  import { resolveTierChain } from "../../shared/filters/buildTierFilter.js";
4
4
  import type { ConfigShowData } from "./types.js";
@@ -20,6 +20,9 @@ export default function resolveConfigShow(
20
20
  installSource: string;
21
21
  configFilePath: string;
22
22
  configFileExists: boolean;
23
+ globalConfigPath: string;
24
+ globalConfigExists: boolean;
25
+ origins: ConfigOrigins;
23
26
  },
24
27
  ): ConfigShowData {
25
28
  // Don't throw for stale config — show always displays what's configured.
@@ -38,5 +41,8 @@ export default function resolveConfigShow(
38
41
  installSource: opts.installSource,
39
42
  configFilePath: opts.configFilePath,
40
43
  configFileExists: opts.configFileExists,
44
+ globalConfigPath: opts.globalConfigPath,
45
+ globalConfigExists: opts.globalConfigExists,
46
+ origins: opts.origins,
41
47
  };
42
48
  }
@@ -1,3 +1,4 @@
1
+ import type { ConfigOrigins } from "#config";
1
2
  import type { Channel } from "#constants";
2
3
 
3
4
  /** Data returned by `pragma config show`, including resolved tier chain and channel releases. */
@@ -8,6 +9,12 @@ export interface ConfigShowData {
8
9
  readonly includedReleases: readonly string[];
9
10
  readonly packageManager: string;
10
11
  readonly installSource: string;
12
+ /** Nearest project config file (or the would-be path at cwd). */
11
13
  readonly configFilePath: string;
12
14
  readonly configFileExists: boolean;
15
+ /** The global XDG config file. */
16
+ readonly globalConfigPath: string;
17
+ readonly globalConfigExists: boolean;
18
+ /** Which layer supplied each effective field. */
19
+ readonly origins: ConfigOrigins;
13
20
  }
@@ -30,6 +30,15 @@ describe("validateTier", () => {
30
30
  expect(entry.path).toBe("global");
31
31
  });
32
32
 
33
+ it("matches case- and whitespace-insensitively", async () => {
34
+ // The documented lowercase slash-path must match regardless of the label's
35
+ // casing in the ontology (e.g. real graph uses `Apps/LXD`).
36
+ const upper = await validateTier(store, "APPS/LXD");
37
+ expect(upper.path).toBe("apps/lxd");
38
+ const padded = await validateTier(store, " Apps/Lxd ");
39
+ expect(padded.path).toBe("apps/lxd");
40
+ });
41
+
33
42
  it("throws INVALID_INPUT for a nonexistent tier", async () => {
34
43
  await expect(validateTier(store, "apps/nonexistent")).rejects.toThrow(
35
44
  'Invalid tier "apps/nonexistent"',
@@ -1,7 +1,12 @@
1
1
  import type { Store } from "@canonical/ke";
2
2
  import { PragmaError } from "#error";
3
+ import { listTiers } from "../../shared/listTiers.js";
3
4
  import type { TierEntry } from "../../shared/types/index.js";
4
- import { listTiers } from "../../tier/operations/index.js";
5
+
6
+ /** Normalise a tier path for comparison: trim and lowercase. */
7
+ function normalizeTierPath(path: string): string {
8
+ return path.trim().toLowerCase();
9
+ }
5
10
 
6
11
  /**
7
12
  * Validate a tier path against the ke store ontology.
@@ -17,7 +22,10 @@ export default async function validateTier(
17
22
  tierPath: string,
18
23
  ): Promise<TierEntry> {
19
24
  const tiers = await listTiers(store);
20
- const match = tiers.find((t) => t.path === tierPath);
25
+ // Compare case/format-insensitively so the documented lowercase slash-path
26
+ // (`apps/lxd`) matches the ontology's title-case label (`Apps/LXD`).
27
+ const target = normalizeTierPath(tierPath);
28
+ const match = tiers.find((t) => normalizeTierPath(t.path) === target);
21
29
 
22
30
  if (!match) {
23
31
  const validPaths = tiers.map((t) => t.path);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Config read story — the single declaration of `config show` for both
3
+ * surfaces.
4
+ *
5
+ * Both surfaces resolve from the filesystem, so the MCP tool now reports
6
+ * the real config path and existence (it previously hardcoded
7
+ * `pragma.config.json` / `true` regardless of the working directory).
8
+ */
9
+
10
+ import { readConfigLayers } from "#config";
11
+ import { detectInstallSource } from "#package-manager";
12
+ import type { ReadStory } from "../shared/stories/index.js";
13
+ import { showFormatters } from "./formatters/index.js";
14
+ import { resolveConfigShow } from "./operations/index.js";
15
+ import type { ConfigShowData } from "./operations/types.js";
16
+
17
+ /** The `config show` / `config_show` read story. */
18
+ export const configShowStory: ReadStory<ConfigShowData, ConfigShowData> = {
19
+ noun: "config",
20
+ verb: "show",
21
+ description: "Display resolved configuration",
22
+ toolDescription:
23
+ "Show current pragma configuration (tier and channel settings).",
24
+ params: [],
25
+ examples: ["pragma config show"],
26
+ resolve: async (rt) => {
27
+ const layers = readConfigLayers(rt.cwd);
28
+ const install = detectInstallSource();
29
+ return resolveConfigShow(layers.config, {
30
+ packageManager: install.packageManager,
31
+ installSource: install.label,
32
+ configFilePath: layers.project.path,
33
+ configFileExists: layers.project.exists,
34
+ globalConfigPath: layers.global.path,
35
+ globalConfigExists: layers.global.exists,
36
+ origins: layers.origins,
37
+ });
38
+ },
39
+ toOutput: (data) => data,
40
+ formatters: showFormatters,
41
+ toEnvelope: (data) => ({ data }),
42
+ };
@@ -3,3 +3,4 @@ export { default as setChannelTask } from "./setChannel.js";
3
3
  export { default as setFrameworkTask } from "./setFramework.js";
4
4
  export { default as setTierTask } from "./setTier.js";
5
5
  export { default as setTraceTask } from "./setTrace.js";
6
+ export { default as writeConfigFieldTask } from "./writeConfigField.js";
@@ -1,36 +1,21 @@
1
- import { $, gen, type Task, writeFile } from "@canonical/task";
2
- import { readConfig } from "../../../config/index.js";
3
- import resolveConfigPath from "../../../config/resolveConfigPath.js";
1
+ import type { Task } from "@canonical/task";
2
+ import type { ConfigScope } from "../../../config/index.js";
3
+ import type { Channel } from "../../../constants.js";
4
+ import writeConfigField from "./writeConfigField.js";
4
5
 
5
6
  /**
6
7
  * Build a config-write task for setting or resetting the release channel.
7
8
  *
8
- * @param cwd - Working directory containing pragma.config.json.
9
- * @param channel - Channel value to persist, or `undefined` to reset.
10
- * @returns Task yielding the persisted field/value payload.
11
- * @note - Impure — reads config and writes to the file system.
9
+ * @param cwd - Working directory the target config layer is resolved from.
10
+ * @param channel - Channel to persist, or `undefined` to reset.
11
+ * @param scope - Optional explicit target layer (`"global"` | `"local"`).
12
+ * @returns Task yielding the persisted field/value payload and target path.
13
+ * @note - Impure — reads and writes the target config file.
12
14
  */
13
15
  export default function setChannel(
14
16
  cwd: string,
15
- channel: string | undefined,
16
- ): Task<{ field: "channel"; value: string | undefined }> {
17
- return gen(function* () {
18
- // Merge onto the full existing config so other fields (e.g. `packages`,
19
- // `trace`) survive. `readConfig` normalises defaults (tier: undefined,
20
- // channel: "normal"); strip those to keep the file minimal, then apply
21
- // the channel change (`undefined` resets it).
22
- const current = readConfig(cwd);
23
- const { tier, channel: _current, ...rest } = current;
24
- const next = {
25
- ...rest,
26
- ...(tier !== undefined ? { tier } : {}),
27
- ...(channel !== undefined ? { channel } : {}),
28
- };
29
-
30
- yield* $(
31
- writeFile(resolveConfigPath(cwd), `${JSON.stringify(next, null, 2)}\n`),
32
- );
33
-
34
- return { field: "channel", value: channel };
35
- });
17
+ channel: Channel | undefined,
18
+ scope?: ConfigScope,
19
+ ): Task<{ field: "channel"; value: Channel | undefined; path: string }> {
20
+ return writeConfigField(cwd, "channel", channel, scope);
36
21
  }
@@ -1,40 +1,21 @@
1
- import { $, gen, type Task, writeFile } from "@canonical/task";
2
- import type { Framework } from "#constants";
3
- import { readConfig } from "../../../config/index.js";
4
- import resolveConfigPath from "../../../config/resolveConfigPath.js";
1
+ import type { Task } from "@canonical/task";
2
+ import type { ConfigScope } from "../../../config/index.js";
3
+ import type { Framework } from "../../../constants.js";
4
+ import writeConfigField from "./writeConfigField.js";
5
5
 
6
6
  /**
7
7
  * Build a config-write task for setting or resetting the preferred framework.
8
8
  *
9
- * @param cwd - Working directory containing pragma.config.json.
9
+ * @param cwd - Working directory the target config layer is resolved from.
10
10
  * @param framework - Framework to persist, or `undefined` to reset.
11
- * @returns Task yielding the persisted field/value payload.
12
- * @note - Impure — reads config and writes to the file system.
11
+ * @param scope - Optional explicit target layer (`"global"` | `"local"`).
12
+ * @returns Task yielding the persisted field/value payload and target path.
13
+ * @note - Impure — reads and writes the target config file.
13
14
  */
14
15
  export default function setFramework(
15
16
  cwd: string,
16
17
  framework: Framework | undefined,
17
- ): Task<{ field: "framework"; value: Framework | undefined }> {
18
- return gen(function* () {
19
- // Merge onto the full existing config so other fields (e.g. `packages`,
20
- // `trace`) survive. `readConfig` normalises defaults (tier: undefined,
21
- // channel: "normal"); strip those to keep the file minimal, then apply
22
- // the framework change (`undefined` resets it).
23
- const current = readConfig(cwd);
24
- // Destructure out `framework` too, so a reset (framework === undefined)
25
- // actually removes it rather than letting the old value survive in `rest`.
26
- const { tier, channel, framework: _current, ...rest } = current;
27
- const next = {
28
- ...rest,
29
- ...(tier !== undefined ? { tier } : {}),
30
- ...(channel !== "normal" ? { channel } : {}),
31
- ...(framework !== undefined ? { framework } : {}),
32
- };
33
-
34
- yield* $(
35
- writeFile(resolveConfigPath(cwd), `${JSON.stringify(next, null, 2)}\n`),
36
- );
37
-
38
- return { field: "framework", value: framework };
39
- });
18
+ scope?: ConfigScope,
19
+ ): Task<{ field: "framework"; value: Framework | undefined; path: string }> {
20
+ return writeConfigField(cwd, "framework", framework, scope);
40
21
  }