@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,51 +1,11 @@
1
- import {
2
- type CommandDefinition,
3
- type CommandResult,
4
- createOutputResult,
5
- } from "@canonical/cli-core";
6
- import { configExists, readConfig, resolveConfigPath } from "#config";
7
- import { detectInstallSource } from "#package-manager";
1
+ import type { CommandDefinition } from "@canonical/cli-core";
8
2
  import type { PragmaContext } from "../../shared/context.js";
9
- import { selectFormatter } from "../../shared/formatters.js";
10
- import { showFormatters } from "../formatters/index.js";
11
- import { resolveConfigShow } from "../operations/index.js";
3
+ import { compileReadCommand } from "../../shared/stories/index.js";
4
+ import { configShowStory } from "../stories.js";
12
5
 
13
- /**
14
- * Build the `pragma config show` command definition.
15
- *
16
- * Displays the resolved configuration including tier, channel,
17
- * package manager, and config file location.
18
- *
19
- * @param ctx - Pragma context providing cwd and formatter selection.
20
- * @returns The command definition for `pragma config show`.
21
- * @note Impure
22
- */
6
+ /** Wire the `pragma config show` CLI command from the config show story. */
23
7
  export default function buildShowCommand(
24
8
  ctx: PragmaContext,
25
9
  ): CommandDefinition {
26
- return {
27
- path: ["config", "show"],
28
- description: "Display resolved configuration",
29
- parameters: [],
30
- meta: {
31
- examples: ["pragma config show"],
32
- },
33
- execute: async (): Promise<CommandResult> => {
34
- const config = readConfig(ctx.cwd);
35
- const install = detectInstallSource();
36
- const cfgPath = resolveConfigPath(ctx.cwd);
37
- const cfgExists = configExists(ctx.cwd);
38
-
39
- const data = resolveConfigShow(config, {
40
- packageManager: install.packageManager,
41
- installSource: install.label,
42
- configFilePath: cfgPath,
43
- configFileExists: cfgExists,
44
- });
45
-
46
- return createOutputResult(data, {
47
- plain: selectFormatter(ctx, showFormatters),
48
- });
49
- },
50
- };
10
+ return compileReadCommand(ctx, configShowStory);
51
11
  }
@@ -1,4 +1,4 @@
1
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { Store } from "@canonical/ke";
@@ -18,13 +18,24 @@ function makeCtx(cwd: string): PragmaContext {
18
18
 
19
19
  describe("config tier command", () => {
20
20
  let dir: string;
21
+ let xdgDir: string;
22
+ let originalXdg: string | undefined;
21
23
 
22
24
  beforeEach(() => {
23
25
  dir = mkdtempSync(join(tmpdir(), "pragma-cmd-tier-"));
26
+ // Bound the config walk at the fixture root, seed an empty project
27
+ // file so writes stay local, and isolate the global XDG layer.
28
+ mkdirSync(join(dir, ".git"));
29
+ writeFileSync(join(dir, "pragma.config.json"), "{}");
30
+ originalXdg = process.env.XDG_CONFIG_HOME;
31
+ xdgDir = mkdtempSync(join(tmpdir(), "pragma-cmd-tier-xdg-"));
32
+ process.env.XDG_CONFIG_HOME = xdgDir;
24
33
  });
25
34
 
26
35
  afterEach(() => {
36
+ process.env.XDG_CONFIG_HOME = originalXdg;
27
37
  rmSync(dir, { recursive: true, force: true });
38
+ rmSync(xdgDir, { recursive: true, force: true });
28
39
  });
29
40
 
30
41
  it("resets tier via --reset flag", async () => {
@@ -51,7 +62,7 @@ describe("config tier command", () => {
51
62
  expect(result.tag).toBe("output");
52
63
  if (result.tag === "output") {
53
64
  const text = result.render.plain(result.value);
54
- expect(text).toBe("Reset tier to default.");
65
+ expect(text).toContain("Reset tier to default.");
55
66
  }
56
67
  });
57
68
 
@@ -3,13 +3,14 @@ import {
3
3
  type CommandResult,
4
4
  createOutputResult,
5
5
  } from "@canonical/cli-core";
6
- import { runTask } from "@canonical/task";
6
+ import { runTask } from "@canonical/task/node";
7
7
  import { readConfig } from "#config";
8
8
  import type { PragmaContext } from "../../shared/context.js";
9
9
  import { selectFormatter } from "../../shared/formatters.js";
10
10
  import { tierFormatters } from "../formatters/index.js";
11
11
  import { validateTier } from "../operations/index.js";
12
12
  import { setTierTask } from "../tasks/index.js";
13
+ import { resolveConfigScope, SCOPE_PARAMETERS } from "./configScope.js";
13
14
 
14
15
  /**
15
16
  * Build the `pragma config tier` command definition.
@@ -40,6 +41,7 @@ export default function buildTierCommand(
40
41
  type: "boolean",
41
42
  default: false,
42
43
  },
44
+ ...SCOPE_PARAMETERS,
43
45
  ],
44
46
  meta: {
45
47
  examples: ["pragma config tier apps/lxd", "pragma config tier --reset"],
@@ -47,15 +49,17 @@ export default function buildTierCommand(
47
49
  execute: async (
48
50
  params: Record<string, unknown>,
49
51
  ): Promise<CommandResult> => {
52
+ const scope = resolveConfigScope(params);
50
53
  const reset = params.reset === true;
51
54
  const tierPath = params.path as string | undefined;
52
55
 
53
56
  if (reset) {
54
- await runTask(setTierTask(ctx.cwd, undefined));
57
+ const result = await runTask(setTierTask(ctx.cwd, undefined, scope));
55
58
  const format = selectFormatter(ctx, tierFormatters.reset);
56
- return createOutputResult("Reset tier to default.", {
57
- plain: format,
58
- });
59
+ return createOutputResult(
60
+ { field: "tier", path: result.path },
61
+ { plain: format },
62
+ );
59
63
  }
60
64
 
61
65
  if (!tierPath) {
@@ -69,10 +73,10 @@ export default function buildTierCommand(
69
73
  // Validate tier against ontology
70
74
  await validateTier(ctx.store, tierPath);
71
75
 
72
- await runTask(setTierTask(ctx.cwd, tierPath));
76
+ const result = await runTask(setTierTask(ctx.cwd, tierPath, scope));
73
77
  const format = selectFormatter(ctx, tierFormatters.set);
74
78
  return createOutputResult(
75
- { field: "tier", value: tierPath },
79
+ { field: "tier", value: tierPath, path: result.path },
76
80
  { plain: format },
77
81
  );
78
82
  },
@@ -0,0 +1,165 @@
1
+ import {
2
+ mkdirSync,
3
+ mkdtempSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import type { Store } from "@canonical/ke";
11
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
12
+ import { readConfig } from "#config";
13
+ import type { PragmaContext } from "../../shared/context.js";
14
+ import buildTraceCommand from "./trace.js";
15
+
16
+ function makeCtx(cwd: string): PragmaContext {
17
+ return {
18
+ cwd,
19
+ globalFlags: { llm: false, format: "text" as const, verbose: false },
20
+ store: {} as Store,
21
+ config: { tier: undefined, channel: "normal" },
22
+ };
23
+ }
24
+
25
+ describe("config trace command", () => {
26
+ let dir: string;
27
+ let xdgDir: string;
28
+ let originalXdg: string | undefined;
29
+
30
+ beforeEach(() => {
31
+ dir = mkdtempSync(join(tmpdir(), "pragma-cmd-trace-"));
32
+ // Bound the config walk at the fixture root, seed an empty project
33
+ // file so writes stay local, and isolate the global XDG layer.
34
+ mkdirSync(join(dir, ".git"));
35
+ writeFileSync(join(dir, "pragma.config.json"), "{}");
36
+ originalXdg = process.env.XDG_CONFIG_HOME;
37
+ xdgDir = mkdtempSync(join(tmpdir(), "pragma-cmd-trace-xdg-"));
38
+ process.env.XDG_CONFIG_HOME = xdgDir;
39
+ });
40
+
41
+ afterEach(() => {
42
+ process.env.XDG_CONFIG_HOME = originalXdg;
43
+ rmSync(dir, { recursive: true, force: true });
44
+ rmSync(xdgDir, { recursive: true, force: true });
45
+ });
46
+
47
+ it("enables tracing", async () => {
48
+ const ctx = makeCtx(dir);
49
+ const cmd = buildTraceCommand(ctx);
50
+ const result = await cmd.execute({ value: "on" }, ctx);
51
+
52
+ expect(result.tag).toBe("output");
53
+ expect(readConfig(dir).trace).toBe(true);
54
+ });
55
+
56
+ it("disables tracing", async () => {
57
+ writeFileSync(join(dir, "pragma.config.json"), '{"trace":true}');
58
+
59
+ const ctx = makeCtx(dir);
60
+ const cmd = buildTraceCommand(ctx);
61
+ const result = await cmd.execute({ value: "off" }, ctx);
62
+
63
+ expect(result.tag).toBe("output");
64
+ expect(readConfig(dir).trace).toBe(false);
65
+ });
66
+
67
+ it("renders enable output", async () => {
68
+ const ctx = makeCtx(dir);
69
+ const cmd = buildTraceCommand(ctx);
70
+ const result = await cmd.execute({ value: "on" }, ctx);
71
+
72
+ expect(result.tag).toBe("output");
73
+ if (result.tag === "output") {
74
+ expect(result.render.plain(result.value)).toContain("Tracing enabled.");
75
+ }
76
+ });
77
+
78
+ it("shows current status when no argument", async () => {
79
+ writeFileSync(join(dir, "pragma.config.json"), '{"trace":true}');
80
+
81
+ const ctx = makeCtx(dir);
82
+ const cmd = buildTraceCommand(ctx);
83
+ const result = await cmd.execute({}, ctx);
84
+
85
+ expect(result.tag).toBe("output");
86
+ if (result.tag === "output") {
87
+ expect(result.value).toBe("on");
88
+ }
89
+ });
90
+
91
+ it("reports off when unset", async () => {
92
+ const ctx = makeCtx(dir);
93
+ const cmd = buildTraceCommand(ctx);
94
+ const result = await cmd.execute({}, ctx);
95
+
96
+ expect(result.tag).toBe("output");
97
+ if (result.tag === "output") {
98
+ expect(result.value).toBe("off");
99
+ }
100
+ });
101
+
102
+ it("throws INVALID_INPUT for an invalid value", async () => {
103
+ const ctx = makeCtx(dir);
104
+ const cmd = buildTraceCommand(ctx);
105
+ await expect(cmd.execute({ value: "yes" }, ctx)).rejects.toThrow(/trace/i);
106
+ });
107
+
108
+ // Regression guard for the data-loss bug: enabling/disabling trace must NOT
109
+ // drop other persisted config fields. `packages` is runtime-consumed, so
110
+ // silently discarding it on a trace toggle would break package resolution.
111
+ it("preserves the packages field when toggling trace", async () => {
112
+ writeFileSync(
113
+ join(dir, "pragma.config.json"),
114
+ JSON.stringify({
115
+ tier: "Apps/WPE",
116
+ channel: "experimental",
117
+ packages: [
118
+ "@canonical/design-system",
119
+ {
120
+ name: "@canonical/anatomy-dsl",
121
+ source: "git+https://github.com/canonical/anatomy-dsl.git#main",
122
+ },
123
+ ],
124
+ }),
125
+ );
126
+
127
+ const ctx = makeCtx(dir);
128
+ const cmd = buildTraceCommand(ctx);
129
+ await cmd.execute({ value: "on" }, ctx);
130
+
131
+ const config = readConfig(dir);
132
+ expect(config.trace).toBe(true);
133
+ // Every pre-existing field survives the write.
134
+ expect(config.tier).toBe("Apps/WPE");
135
+ expect(config.channel).toBe("experimental");
136
+ expect(config.packages).toEqual([
137
+ "@canonical/design-system",
138
+ {
139
+ name: "@canonical/anatomy-dsl",
140
+ source: "git+https://github.com/canonical/anatomy-dsl.git#main",
141
+ },
142
+ ]);
143
+
144
+ // And toggling off again still preserves them.
145
+ await cmd.execute({ value: "off" }, ctx);
146
+ const after = readConfig(dir);
147
+ expect(after.trace).toBe(false);
148
+ expect(after.packages).toHaveLength(2);
149
+ });
150
+
151
+ it("does not persist the default channel when toggling trace", async () => {
152
+ // A bare `trace on` on an empty config should not write channel:"normal"
153
+ // (the default), keeping the file minimal per the existing convention.
154
+ const ctx = makeCtx(dir);
155
+ const cmd = buildTraceCommand(ctx);
156
+ await cmd.execute({ value: "on" }, ctx);
157
+
158
+ const raw = JSON.parse(
159
+ readFileSync(join(dir, "pragma.config.json"), "utf-8"),
160
+ ) as Record<string, unknown>;
161
+ expect(raw.trace).toBe(true);
162
+ expect(raw).not.toHaveProperty("channel");
163
+ expect(raw).not.toHaveProperty("tier");
164
+ });
165
+ });
@@ -0,0 +1,83 @@
1
+ import {
2
+ type CommandDefinition,
3
+ type CommandResult,
4
+ createOutputResult,
5
+ } from "@canonical/cli-core";
6
+ import { runTask } from "@canonical/task/node";
7
+ import { readConfig } from "#config";
8
+ import { PragmaError } from "#error";
9
+ import type { PragmaContext } from "../../shared/context.js";
10
+ import { selectFormatter } from "../../shared/formatters.js";
11
+ import { traceFormatters } from "../formatters/index.js";
12
+ import { setTraceTask } from "../tasks/index.js";
13
+ import { resolveConfigScope, SCOPE_PARAMETERS } from "./configScope.js";
14
+
15
+ /**
16
+ * Build the `pragma config trace` command definition.
17
+ *
18
+ * Supports three modes: enable (`on`), disable (`off`), or query
19
+ * when no argument is provided.
20
+ */
21
+ export default function buildTraceCommand(
22
+ ctx: PragmaContext,
23
+ ): CommandDefinition {
24
+ return {
25
+ path: ["config", "trace"],
26
+ description: "Enable or disable query access tracing",
27
+ parameters: [
28
+ {
29
+ name: "value",
30
+ description: "on or off",
31
+ type: "string",
32
+ positional: true,
33
+ },
34
+ ...SCOPE_PARAMETERS,
35
+ ],
36
+ meta: {
37
+ examples: [
38
+ "pragma config trace on",
39
+ "pragma config trace off",
40
+ "pragma config trace",
41
+ ],
42
+ extendedHelp:
43
+ "Persists to pragma.config.json. The PRAGMA_TRACE=1 env var overrides this setting.",
44
+ },
45
+ execute: async (
46
+ params: Record<string, unknown>,
47
+ ): Promise<CommandResult> => {
48
+ const scope = resolveConfigScope(params);
49
+ const value = params.value as string | undefined;
50
+
51
+ // Query mode — no argument
52
+ if (!value) {
53
+ const config = readConfig(ctx.cwd);
54
+ const enabled = config.trace === true;
55
+ const envOverride = process.env.PRAGMA_TRACE === "1";
56
+ const status = envOverride
57
+ ? "on (PRAGMA_TRACE=1 override)"
58
+ : enabled
59
+ ? "on"
60
+ : "off";
61
+ const format = selectFormatter(ctx, traceFormatters.query);
62
+ return createOutputResult(status, { plain: format });
63
+ }
64
+
65
+ // Validate
66
+ if (value !== "on" && value !== "off") {
67
+ throw PragmaError.invalidInput("trace", value, {
68
+ validOptions: ["on", "off"],
69
+ });
70
+ }
71
+
72
+ // Set mode
73
+ const enabled = value === "on";
74
+ const result = await runTask(setTraceTask(ctx.cwd, enabled, scope));
75
+
76
+ const format = selectFormatter(ctx, traceFormatters.set);
77
+ return createOutputResult(
78
+ { field: "trace", value: enabled, path: result.path },
79
+ { plain: format },
80
+ );
81
+ },
82
+ };
83
+ }
@@ -2,14 +2,22 @@ import { describe, expect, it } from "vitest";
2
2
  import formatters from "./channel.js";
3
3
 
4
4
  describe("channelFormatters.set", () => {
5
- const data = { field: "channel", value: "experimental" };
5
+ const data = {
6
+ field: "channel",
7
+ value: "experimental",
8
+ path: "/repo/pragma.config.json",
9
+ };
6
10
 
7
11
  it("plain renders set message", () => {
8
- expect(formatters.set.plain(data)).toBe('Set channel to "experimental".');
12
+ expect(formatters.set.plain(data)).toBe(
13
+ 'Set channel to "experimental".\nWrote /repo/pragma.config.json',
14
+ );
9
15
  });
10
16
 
11
17
  it("llm renders set message", () => {
12
- expect(formatters.set.llm(data)).toBe('Set channel to "experimental".');
18
+ expect(formatters.set.llm(data)).toBe(
19
+ 'Set channel to "experimental".\nWrote /repo/pragma.config.json',
20
+ );
13
21
  });
14
22
 
15
23
  it("json returns valid JSON", () => {
@@ -21,15 +29,21 @@ describe("channelFormatters.set", () => {
21
29
 
22
30
  describe("channelFormatters.reset", () => {
23
31
  it("plain renders reset message", () => {
24
- expect(formatters.reset.plain("")).toBe("Reset channel to default.");
32
+ expect(
33
+ formatters.reset.plain({ field: "channel", path: "/repo/p.json" }),
34
+ ).toBe("Reset channel to default.\nWrote /repo/p.json");
25
35
  });
26
36
 
27
37
  it("llm renders reset message", () => {
28
- expect(formatters.reset.llm("")).toBe("Reset channel to default.");
38
+ expect(
39
+ formatters.reset.llm({ field: "channel", path: "/repo/p.json" }),
40
+ ).toBe("Reset channel to default.\nWrote /repo/p.json");
29
41
  });
30
42
 
31
43
  it("json returns valid JSON", () => {
32
- const parsed = JSON.parse(formatters.reset.json(""));
44
+ const parsed = JSON.parse(
45
+ formatters.reset.json({ field: "channel", path: "/repo/p.json" }),
46
+ );
33
47
  expect(parsed.field).toBe("channel");
34
48
  expect(parsed.reset).toBe(true);
35
49
  });
@@ -9,19 +9,22 @@ import type { Formatters } from "../../shared/formatters.js";
9
9
  */
10
10
  const channelFormatters = {
11
11
  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 }>,
12
+ plain: (d: { field: string; value: string; path: string }) =>
13
+ `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
14
+ llm: (d: { field: string; value: string; path: string }) =>
15
+ `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
16
+ json: (d: { field: string; value: string; path: string }) =>
17
+ JSON.stringify({ field: d.field, value: d.value, path: d.path }),
18
+ } satisfies Formatters<{ field: string; value: string; path: string }>,
19
19
 
20
20
  reset: {
21
- plain: () => "Reset channel to default.",
22
- llm: () => "Reset channel to default.",
23
- json: () => JSON.stringify({ field: "channel", reset: true }),
24
- } satisfies Formatters<string>,
21
+ plain: (d: { field: string; path: string }) =>
22
+ `Reset channel to default.\nWrote ${d.path}`,
23
+ llm: (d: { field: string; path: string }) =>
24
+ `Reset channel to default.\nWrote ${d.path}`,
25
+ json: (d: { field: string; path: string }) =>
26
+ JSON.stringify({ field: "channel", reset: true, path: d.path }),
27
+ } satisfies Formatters<{ field: string; path: string }>,
25
28
 
26
29
  query: {
27
30
  plain: (channel: string) => `Current channel: ${channel}`,
@@ -0,0 +1,37 @@
1
+ import type { Formatters } from "../../shared/formatters.js";
2
+
3
+ /**
4
+ * Formatter sets for `pragma config framework` output, grouped by branch:
5
+ *
6
+ * - **set** formats the confirmation after setting a framework.
7
+ * - **reset** formats the confirmation after resetting to default.
8
+ * - **query** formats the current framework value (or "none").
9
+ */
10
+ const frameworkFormatters = {
11
+ set: {
12
+ plain: (d: { field: string; value: string; path: string }) =>
13
+ `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
14
+ llm: (d: { field: string; value: string; path: string }) =>
15
+ `Set ${d.field} to "${d.value}".\nWrote ${d.path}`,
16
+ json: (d: { field: string; value: string; path: string }) =>
17
+ JSON.stringify({ field: d.field, value: d.value, path: d.path }),
18
+ } satisfies Formatters<{ field: string; value: string; path: string }>,
19
+
20
+ reset: {
21
+ plain: (d: { field: string; path: string }) =>
22
+ `Reset framework to default.\nWrote ${d.path}`,
23
+ llm: (d: { field: string; path: string }) =>
24
+ `Reset framework to default.\nWrote ${d.path}`,
25
+ json: (d: { field: string; path: string }) =>
26
+ JSON.stringify({ field: "framework", reset: true, path: d.path }),
27
+ } satisfies Formatters<{ field: string; path: string }>,
28
+
29
+ query: {
30
+ plain: (framework: string) => `Current framework: ${framework}`,
31
+ llm: (framework: string) => `Current framework: ${framework}`,
32
+ json: (framework: string) =>
33
+ JSON.stringify({ field: "framework", value: framework }),
34
+ } satisfies Formatters<string>,
35
+ };
36
+
37
+ export default frameworkFormatters;
@@ -1,4 +1,6 @@
1
1
  /** @module Formatter sets for config domain output. */
2
2
  export { default as channelFormatters } from "./channel.js";
3
+ export { default as frameworkFormatters } from "./framework.js";
3
4
  export { default as showFormatters } from "./show.js";
4
5
  export { default as tierFormatters } from "./tier.js";
6
+ export { default as traceFormatters } from "./trace.js";
@@ -11,6 +11,15 @@ const WITH_TIER: ConfigShowData = {
11
11
  installSource: "bun (global)",
12
12
  configFilePath: "/project/pragma.config.json",
13
13
  configFileExists: true,
14
+ globalConfigPath: "/home/u/.config/pragma/config.json",
15
+ globalConfigExists: false,
16
+ origins: {
17
+ tier: "project",
18
+ channel: "project",
19
+ packages: "default",
20
+ trace: "default",
21
+ framework: "default",
22
+ },
14
23
  };
15
24
 
16
25
  const WITHOUT_TIER: ConfigShowData = {
@@ -22,6 +31,15 @@ const WITHOUT_TIER: ConfigShowData = {
22
31
  installSource: "bun (local)",
23
32
  configFilePath: "/project/pragma.config.json",
24
33
  configFileExists: false,
34
+ globalConfigPath: "/home/u/.config/pragma/config.json",
35
+ globalConfigExists: false,
36
+ origins: {
37
+ tier: "project",
38
+ channel: "project",
39
+ packages: "default",
40
+ trace: "default",
41
+ framework: "default",
42
+ },
25
43
  };
26
44
 
27
45
  const PRERELEASE: ConfigShowData = {
@@ -33,6 +51,15 @@ const PRERELEASE: ConfigShowData = {
33
51
  installSource: "pnpm (global)",
34
52
  configFilePath: "/project/pragma.config.json",
35
53
  configFileExists: true,
54
+ globalConfigPath: "/home/u/.config/pragma/config.json",
55
+ globalConfigExists: false,
56
+ origins: {
57
+ tier: "project",
58
+ channel: "project",
59
+ packages: "default",
60
+ trace: "default",
61
+ framework: "default",
62
+ },
36
63
  };
37
64
 
38
65
  describe("formatters.plain", () => {
@@ -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
  },