@canonical/pragma-cli 0.18.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 (439) hide show
  1. package/README.md +377 -0
  2. package/package.json +78 -0
  3. package/src/bin.test.ts +52 -0
  4. package/src/bin.ts +19 -0
  5. package/src/compile-validation.runner.ts +137 -0
  6. package/src/compile-validation.ts +192 -0
  7. package/src/completions/computeSocketPath.test.ts +26 -0
  8. package/src/completions/computeSocketPath.ts +16 -0
  9. package/src/completions/constants.ts +18 -0
  10. package/src/completions/handleQuery.test.ts +93 -0
  11. package/src/completions/handleQuery.ts +30 -0
  12. package/src/completions/index.ts +14 -0
  13. package/src/completions/queryCompletions.test.ts +100 -0
  14. package/src/completions/queryCompletions.ts +61 -0
  15. package/src/completions/querySocket.test.ts +70 -0
  16. package/src/completions/querySocket.ts +44 -0
  17. package/src/completions/startCompletionsServer.test.ts +74 -0
  18. package/src/completions/startCompletionsServer.ts +75 -0
  19. package/src/completions/startCompletionsServer.unit.test.ts +131 -0
  20. package/src/completions/waitForSocket.test.ts +27 -0
  21. package/src/completions/waitForSocket.ts +27 -0
  22. package/src/config/configExists.ts +14 -0
  23. package/src/config/index.ts +6 -0
  24. package/src/config/readConfig.test.ts +152 -0
  25. package/src/config/readConfig.ts +64 -0
  26. package/src/config/resolveConfigPath.test.ts +20 -0
  27. package/src/config/resolveConfigPath.ts +11 -0
  28. package/src/config/types.ts +26 -0
  29. package/src/config/writeConfig.ts +49 -0
  30. package/src/constants.ts +78 -0
  31. package/src/domains/block/blockConfig.ts +53 -0
  32. package/src/domains/block/commands/index.ts +4 -0
  33. package/src/domains/block/commands/list.test.ts +111 -0
  34. package/src/domains/block/commands/list.ts +69 -0
  35. package/src/domains/block/commands/lookup.test.ts +187 -0
  36. package/src/domains/block/commands/lookup.ts +199 -0
  37. package/src/domains/block/formatters/index.ts +4 -0
  38. package/src/domains/block/formatters/list.test.ts +107 -0
  39. package/src/domains/block/formatters/list.ts +64 -0
  40. package/src/domains/block/formatters/lookup.test.ts +317 -0
  41. package/src/domains/block/formatters/lookup.ts +354 -0
  42. package/src/domains/block/formatters/types.ts +16 -0
  43. package/src/domains/block/helpers/index.ts +4 -0
  44. package/src/domains/block/helpers/resolveAspects.test.ts +64 -0
  45. package/src/domains/block/helpers/resolveAspects.ts +35 -0
  46. package/src/domains/block/index.ts +25 -0
  47. package/src/domains/block/mcp/index.ts +2 -0
  48. package/src/domains/block/mcp/specs.ts +199 -0
  49. package/src/domains/block/operations/index.ts +4 -0
  50. package/src/domains/block/operations/list.test.ts +80 -0
  51. package/src/domains/block/operations/list.ts +72 -0
  52. package/src/domains/block/operations/lookup.test.ts +108 -0
  53. package/src/domains/block/operations/lookup.ts +359 -0
  54. package/src/domains/block/orchestration/blockEmptyError.ts +19 -0
  55. package/src/domains/block/orchestration/buildBlockFilters.ts +12 -0
  56. package/src/domains/block/orchestration/enrichBlocks.ts +37 -0
  57. package/src/domains/block/orchestration/index.ts +5 -0
  58. package/src/domains/block/orchestration/resolveBlockList.ts +39 -0
  59. package/src/domains/block/orchestration/resolveBlockLookup.ts +21 -0
  60. package/src/domains/block/types.ts +30 -0
  61. package/src/domains/config/commands/channel.test.ts +101 -0
  62. package/src/domains/config/commands/channel.ts +80 -0
  63. package/src/domains/config/commands/index.ts +4 -0
  64. package/src/domains/config/commands/show.test.ts +61 -0
  65. package/src/domains/config/commands/show.ts +51 -0
  66. package/src/domains/config/commands/tier.test.ts +84 -0
  67. package/src/domains/config/commands/tier.ts +78 -0
  68. package/src/domains/config/formatters/channel.test.ts +56 -0
  69. package/src/domains/config/formatters/channel.ts +34 -0
  70. package/src/domains/config/formatters/index.ts +4 -0
  71. package/src/domains/config/formatters/show.test.ts +126 -0
  72. package/src/domains/config/formatters/show.ts +66 -0
  73. package/src/domains/config/formatters/tier.test.ts +70 -0
  74. package/src/domains/config/formatters/tier.ts +40 -0
  75. package/src/domains/config/index.ts +23 -0
  76. package/src/domains/config/mcp/index.ts +2 -0
  77. package/src/domains/config/mcp/specs.ts +108 -0
  78. package/src/domains/config/operations/index.ts +5 -0
  79. package/src/domains/config/operations/show.test.ts +60 -0
  80. package/src/domains/config/operations/show.ts +42 -0
  81. package/src/domains/config/operations/types.ts +13 -0
  82. package/src/domains/config/operations/validateChannel.test.ts +36 -0
  83. package/src/domains/config/operations/validateChannel.ts +24 -0
  84. package/src/domains/config/operations/validateTier.test.ts +56 -0
  85. package/src/domains/config/operations/validateTier.ts +35 -0
  86. package/src/domains/create/commands/component.test.ts +166 -0
  87. package/src/domains/create/commands/component.ts +129 -0
  88. package/src/domains/create/commands/index.ts +3 -0
  89. package/src/domains/create/commands/package.test.ts +35 -0
  90. package/src/domains/create/commands/package.ts +21 -0
  91. package/src/domains/create/index.ts +15 -0
  92. package/src/domains/create/mcp/index.ts +2 -0
  93. package/src/domains/create/mcp/specs.ts +174 -0
  94. package/src/domains/doctor/commands/doctor.test.ts +75 -0
  95. package/src/domains/doctor/commands/doctor.ts +43 -0
  96. package/src/domains/doctor/commands/index.ts +2 -0
  97. package/src/domains/doctor/formatters/doctor.test.ts +117 -0
  98. package/src/domains/doctor/formatters/doctor.ts +84 -0
  99. package/src/domains/doctor/formatters/index.ts +2 -0
  100. package/src/domains/doctor/index.ts +9 -0
  101. package/src/domains/doctor/mcp/index.ts +2 -0
  102. package/src/domains/doctor/mcp/specs.ts +88 -0
  103. package/src/domains/doctor/operations/checks/checkConfigFile.ts +28 -0
  104. package/src/domains/doctor/operations/checks/checkKeStore.ts +38 -0
  105. package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +65 -0
  106. package/src/domains/doctor/operations/checks/checkNodeVersion.ts +25 -0
  107. package/src/domains/doctor/operations/checks/checkPragmaVersion.ts +20 -0
  108. package/src/domains/doctor/operations/checks/checkShellCompletions.ts +42 -0
  109. package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +64 -0
  110. package/src/domains/doctor/operations/checks/index.ts +8 -0
  111. package/src/domains/doctor/operations/checks.test.ts +151 -0
  112. package/src/domains/doctor/operations/index.ts +3 -0
  113. package/src/domains/doctor/operations/runChecks.test.ts +89 -0
  114. package/src/domains/doctor/operations/runChecks.ts +38 -0
  115. package/src/domains/doctor/operations/types.ts +20 -0
  116. package/src/domains/graph/commands/index.ts +4 -0
  117. package/src/domains/graph/commands/inspect.test.ts +94 -0
  118. package/src/domains/graph/commands/inspect.ts +63 -0
  119. package/src/domains/graph/commands/query.test.ts +92 -0
  120. package/src/domains/graph/commands/query.ts +60 -0
  121. package/src/domains/graph/formatters/index.ts +4 -0
  122. package/src/domains/graph/formatters/inspect.ts +45 -0
  123. package/src/domains/graph/formatters/query.ts +37 -0
  124. package/src/domains/graph/helpers/index.ts +3 -0
  125. package/src/domains/graph/helpers/resolveUri.ts +70 -0
  126. package/src/domains/graph/index.ts +22 -0
  127. package/src/domains/graph/mcp/index.ts +2 -0
  128. package/src/domains/graph/mcp/specs.ts +82 -0
  129. package/src/domains/graph/operations/executeQuery.test.ts +68 -0
  130. package/src/domains/graph/operations/executeQuery.ts +32 -0
  131. package/src/domains/graph/operations/index.ts +4 -0
  132. package/src/domains/graph/operations/inspectUri.test.ts +101 -0
  133. package/src/domains/graph/operations/inspectUri.ts +61 -0
  134. package/src/domains/info/commands/index.ts +4 -0
  135. package/src/domains/info/commands/info.test.ts +88 -0
  136. package/src/domains/info/commands/info.ts +47 -0
  137. package/src/domains/info/commands/upgrade.test.ts +183 -0
  138. package/src/domains/info/commands/upgrade.ts +150 -0
  139. package/src/domains/info/constants.test.ts +22 -0
  140. package/src/domains/info/constants.ts +15 -0
  141. package/src/domains/info/formatters/index.ts +8 -0
  142. package/src/domains/info/formatters/info.test.ts +173 -0
  143. package/src/domains/info/formatters/info.ts +116 -0
  144. package/src/domains/info/formatters/upgrade.test.ts +133 -0
  145. package/src/domains/info/formatters/upgrade.ts +68 -0
  146. package/src/domains/info/index.ts +7 -0
  147. package/src/domains/info/operations/checkRegistryVersion.test.ts +124 -0
  148. package/src/domains/info/operations/checkRegistryVersion.ts +45 -0
  149. package/src/domains/info/operations/collectInfo.test.ts +111 -0
  150. package/src/domains/info/operations/collectInfo.ts +68 -0
  151. package/src/domains/info/operations/collectStoreSummary.test.ts +61 -0
  152. package/src/domains/info/operations/collectStoreSummary.ts +42 -0
  153. package/src/domains/info/operations/index.ts +5 -0
  154. package/src/domains/info/types.test-d.ts +35 -0
  155. package/src/domains/info/types.ts +46 -0
  156. package/src/domains/llm/commands/index.ts +3 -0
  157. package/src/domains/llm/commands/llm.ts +37 -0
  158. package/src/domains/llm/data/commandReference.ts +25 -0
  159. package/src/domains/llm/data/decisionTrees.ts +46 -0
  160. package/src/domains/llm/data/index.ts +4 -0
  161. package/src/domains/llm/formatters/index.ts +3 -0
  162. package/src/domains/llm/formatters/orientation.ts +53 -0
  163. package/src/domains/llm/index.ts +8 -0
  164. package/src/domains/llm/llmCommand.test.ts +93 -0
  165. package/src/domains/llm/mcp/index.ts +2 -0
  166. package/src/domains/llm/mcp/specs.ts +87 -0
  167. package/src/domains/llm/operations/collectContext.ts +47 -0
  168. package/src/domains/llm/operations/index.ts +3 -0
  169. package/src/domains/llm/types.ts +32 -0
  170. package/src/domains/modifier/commands/index.ts +4 -0
  171. package/src/domains/modifier/commands/list.test.ts +57 -0
  172. package/src/domains/modifier/commands/list.ts +33 -0
  173. package/src/domains/modifier/commands/lookup.test.ts +91 -0
  174. package/src/domains/modifier/commands/lookup.ts +105 -0
  175. package/src/domains/modifier/formatters/index.ts +4 -0
  176. package/src/domains/modifier/formatters/list.test.ts +38 -0
  177. package/src/domains/modifier/formatters/list.ts +35 -0
  178. package/src/domains/modifier/formatters/lookup.test.ts +33 -0
  179. package/src/domains/modifier/formatters/lookup.ts +38 -0
  180. package/src/domains/modifier/index.ts +22 -0
  181. package/src/domains/modifier/mcp/index.ts +2 -0
  182. package/src/domains/modifier/mcp/specs.ts +97 -0
  183. package/src/domains/modifier/operations/index.ts +4 -0
  184. package/src/domains/modifier/operations/list.test.ts +39 -0
  185. package/src/domains/modifier/operations/list.ts +41 -0
  186. package/src/domains/modifier/operations/lookup.test.ts +37 -0
  187. package/src/domains/modifier/operations/lookup.ts +57 -0
  188. package/src/domains/modifier/orchestration/index.ts +3 -0
  189. package/src/domains/modifier/orchestration/modifierEmptyError.ts +10 -0
  190. package/src/domains/modifier/orchestration/resolveModifierList.ts +16 -0
  191. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +20 -0
  192. package/src/domains/modifier/types.ts +5 -0
  193. package/src/domains/ontology/commands/index.ts +4 -0
  194. package/src/domains/ontology/commands/list.test.ts +82 -0
  195. package/src/domains/ontology/commands/list.ts +46 -0
  196. package/src/domains/ontology/commands/show.test.ts +103 -0
  197. package/src/domains/ontology/commands/show.ts +66 -0
  198. package/src/domains/ontology/formatters/index.ts +4 -0
  199. package/src/domains/ontology/formatters/list.ts +37 -0
  200. package/src/domains/ontology/formatters/show.ts +74 -0
  201. package/src/domains/ontology/helpers/findNamespace.ts +23 -0
  202. package/src/domains/ontology/helpers/index.ts +7 -0
  203. package/src/domains/ontology/helpers/queryClasses.ts +50 -0
  204. package/src/domains/ontology/helpers/queryProperties.ts +57 -0
  205. package/src/domains/ontology/helpers/resolvePrefix.ts +45 -0
  206. package/src/domains/ontology/index.ts +26 -0
  207. package/src/domains/ontology/mcp/index.ts +2 -0
  208. package/src/domains/ontology/mcp/specs.ts +93 -0
  209. package/src/domains/ontology/operations/index.ts +5 -0
  210. package/src/domains/ontology/operations/listOntologies.test.ts +45 -0
  211. package/src/domains/ontology/operations/listOntologies.ts +101 -0
  212. package/src/domains/ontology/operations/showOntology.test.ts +65 -0
  213. package/src/domains/ontology/operations/showOntology.ts +41 -0
  214. package/src/domains/ontology/operations/showOntologyRaw.test.ts +39 -0
  215. package/src/domains/ontology/operations/showOntologyRaw.ts +50 -0
  216. package/src/domains/setup/commands/all.ts +58 -0
  217. package/src/domains/setup/commands/completions.ts +85 -0
  218. package/src/domains/setup/commands/entrypoints.test.ts +170 -0
  219. package/src/domains/setup/commands/index.ts +6 -0
  220. package/src/domains/setup/commands/lsp.ts +49 -0
  221. package/src/domains/setup/commands/mcp.ts +119 -0
  222. package/src/domains/setup/commands/skills.test.ts +64 -0
  223. package/src/domains/setup/commands/skills.ts +106 -0
  224. package/src/domains/setup/formatters/index.ts +2 -0
  225. package/src/domains/setup/formatters/skills.test.ts +103 -0
  226. package/src/domains/setup/formatters/skills.ts +112 -0
  227. package/src/domains/setup/formatters/types.ts +7 -0
  228. package/src/domains/setup/helpers/completionScripts.test.ts +58 -0
  229. package/src/domains/setup/helpers/completionScripts.ts +92 -0
  230. package/src/domains/setup/helpers/constants.ts +2 -0
  231. package/src/domains/setup/helpers/detectShell.test.ts +48 -0
  232. package/src/domains/setup/helpers/detectShell.ts +20 -0
  233. package/src/domains/setup/helpers/index.ts +12 -0
  234. package/src/domains/setup/helpers/runSetupTask.test.ts +279 -0
  235. package/src/domains/setup/helpers/runSetupTask.ts +229 -0
  236. package/src/domains/setup/index.ts +33 -0
  237. package/src/domains/setup/operations/index.ts +6 -0
  238. package/src/domains/setup/operations/setupAll.test.ts +48 -0
  239. package/src/domains/setup/operations/setupAll.ts +41 -0
  240. package/src/domains/setup/operations/setupCompletions.test.ts +94 -0
  241. package/src/domains/setup/operations/setupCompletions.ts +54 -0
  242. package/src/domains/setup/operations/setupLsp.test.ts +29 -0
  243. package/src/domains/setup/operations/setupLsp.ts +17 -0
  244. package/src/domains/setup/operations/setupMcp.test.ts +115 -0
  245. package/src/domains/setup/operations/setupMcp.ts +108 -0
  246. package/src/domains/setup/operations/setupSkills.test.ts +191 -0
  247. package/src/domains/setup/operations/setupSkills.ts +160 -0
  248. package/src/domains/setup/types.ts +18 -0
  249. package/src/domains/shared/ToolSpec.ts +49 -0
  250. package/src/domains/shared/bootStore.ts +77 -0
  251. package/src/domains/shared/buildQuery.ts +18 -0
  252. package/src/domains/shared/compactUri.test.ts +21 -0
  253. package/src/domains/shared/compactUri.ts +16 -0
  254. package/src/domains/shared/context.ts +15 -0
  255. package/src/domains/shared/contracts.ts +113 -0
  256. package/src/domains/shared/extractLocalName.test.ts +21 -0
  257. package/src/domains/shared/extractLocalName.ts +14 -0
  258. package/src/domains/shared/filters/buildChannelFilter.test.ts +52 -0
  259. package/src/domains/shared/filters/buildChannelFilter.ts +41 -0
  260. package/src/domains/shared/filters/buildFilterConfig.ts +16 -0
  261. package/src/domains/shared/filters/buildFilters.test.ts +33 -0
  262. package/src/domains/shared/filters/buildFilters.ts +30 -0
  263. package/src/domains/shared/filters/buildTierFilter.test.ts +78 -0
  264. package/src/domains/shared/filters/buildTierFilter.ts +69 -0
  265. package/src/domains/shared/filters/describeFilters.ts +14 -0
  266. package/src/domains/shared/filters/index.ts +10 -0
  267. package/src/domains/shared/formatters.ts +35 -0
  268. package/src/domains/shared/lookupMany.ts +45 -0
  269. package/src/domains/shared/namespaces.ts +18 -0
  270. package/src/domains/shared/packages.ts +76 -0
  271. package/src/domains/shared/prefixes.ts +37 -0
  272. package/src/domains/shared/runtime.ts +62 -0
  273. package/src/domains/shared/types.test-d.ts +127 -0
  274. package/src/domains/shared/types.ts +348 -0
  275. package/src/domains/skill/commands/index.ts +2 -0
  276. package/src/domains/skill/commands/list.test.ts +111 -0
  277. package/src/domains/skill/commands/list.ts +69 -0
  278. package/src/domains/skill/formatters/index.ts +2 -0
  279. package/src/domains/skill/formatters/list.test.ts +140 -0
  280. package/src/domains/skill/formatters/list.ts +119 -0
  281. package/src/domains/skill/formatters/types.ts +8 -0
  282. package/src/domains/skill/helpers/index.ts +6 -0
  283. package/src/domains/skill/helpers/parseFrontmatter.test.ts +122 -0
  284. package/src/domains/skill/helpers/parseFrontmatter.ts +112 -0
  285. package/src/domains/skill/helpers/resolveSkillSources.ts +34 -0
  286. package/src/domains/skill/index.ts +19 -0
  287. package/src/domains/skill/mcp/index.ts +2 -0
  288. package/src/domains/skill/mcp/specs.ts +38 -0
  289. package/src/domains/skill/operations/discover.test.ts +145 -0
  290. package/src/domains/skill/operations/discover.ts +68 -0
  291. package/src/domains/skill/operations/index.ts +4 -0
  292. package/src/domains/skill/operations/list.test.ts +68 -0
  293. package/src/domains/skill/operations/list.ts +47 -0
  294. package/src/domains/skill/types.ts +25 -0
  295. package/src/domains/standard/commands/categories.test.ts +70 -0
  296. package/src/domains/standard/commands/categories.ts +48 -0
  297. package/src/domains/standard/commands/index.ts +5 -0
  298. package/src/domains/standard/commands/list.test.ts +105 -0
  299. package/src/domains/standard/commands/list.ts +81 -0
  300. package/src/domains/standard/commands/lookup.test.ts +135 -0
  301. package/src/domains/standard/commands/lookup.ts +127 -0
  302. package/src/domains/standard/formatters/categories.test.ts +44 -0
  303. package/src/domains/standard/formatters/categories.ts +38 -0
  304. package/src/domains/standard/formatters/index.ts +6 -0
  305. package/src/domains/standard/formatters/list.test.ts +166 -0
  306. package/src/domains/standard/formatters/list.ts +156 -0
  307. package/src/domains/standard/formatters/lookup.test.ts +95 -0
  308. package/src/domains/standard/formatters/lookup.ts +86 -0
  309. package/src/domains/standard/formatters/types.ts +31 -0
  310. package/src/domains/standard/index.ts +32 -0
  311. package/src/domains/standard/mcp/index.ts +2 -0
  312. package/src/domains/standard/mcp/specs.ts +197 -0
  313. package/src/domains/standard/operations/categories.test.ts +61 -0
  314. package/src/domains/standard/operations/categories.ts +38 -0
  315. package/src/domains/standard/operations/index.ts +5 -0
  316. package/src/domains/standard/operations/list.test.ts +75 -0
  317. package/src/domains/standard/operations/list.ts +64 -0
  318. package/src/domains/standard/operations/lookup.test.ts +58 -0
  319. package/src/domains/standard/operations/lookup.ts +142 -0
  320. package/src/domains/standard/orchestration/buildStandardFilters.ts +11 -0
  321. package/src/domains/standard/orchestration/index.ts +4 -0
  322. package/src/domains/standard/orchestration/resolveStandardList.ts +42 -0
  323. package/src/domains/standard/orchestration/resolveStandardLookup.ts +20 -0
  324. package/src/domains/standard/orchestration/standardEmptyError.ts +19 -0
  325. package/src/domains/standard/types.ts +15 -0
  326. package/src/domains/tier/commands/index.ts +3 -0
  327. package/src/domains/tier/commands/list.test.ts +67 -0
  328. package/src/domains/tier/commands/list.ts +43 -0
  329. package/src/domains/tier/formatters/index.ts +3 -0
  330. package/src/domains/tier/formatters/list.test.ts +33 -0
  331. package/src/domains/tier/formatters/list.ts +40 -0
  332. package/src/domains/tier/index.ts +17 -0
  333. package/src/domains/tier/mcp/index.ts +2 -0
  334. package/src/domains/tier/mcp/specs.ts +38 -0
  335. package/src/domains/tier/operations/index.ts +3 -0
  336. package/src/domains/tier/operations/list.test.ts +32 -0
  337. package/src/domains/tier/operations/list.ts +33 -0
  338. package/src/domains/token/commands/add-config.test.ts +68 -0
  339. package/src/domains/token/commands/add-config.ts +62 -0
  340. package/src/domains/token/commands/index.ts +5 -0
  341. package/src/domains/token/commands/list.test.ts +82 -0
  342. package/src/domains/token/commands/list.ts +45 -0
  343. package/src/domains/token/commands/lookup.test.ts +125 -0
  344. package/src/domains/token/commands/lookup.ts +121 -0
  345. package/src/domains/token/formatters/index.ts +4 -0
  346. package/src/domains/token/formatters/list.test.ts +37 -0
  347. package/src/domains/token/formatters/list.ts +37 -0
  348. package/src/domains/token/formatters/lookup.test.ts +61 -0
  349. package/src/domains/token/formatters/lookup.ts +72 -0
  350. package/src/domains/token/index.ts +32 -0
  351. package/src/domains/token/mcp/index.ts +2 -0
  352. package/src/domains/token/mcp/specs.ts +145 -0
  353. package/src/domains/token/operations/add-config.test.ts +68 -0
  354. package/src/domains/token/operations/add-config.ts +61 -0
  355. package/src/domains/token/operations/index.ts +7 -0
  356. package/src/domains/token/operations/list.test.ts +50 -0
  357. package/src/domains/token/operations/list.ts +52 -0
  358. package/src/domains/token/operations/lookup.test.ts +42 -0
  359. package/src/domains/token/operations/lookup.ts +66 -0
  360. package/src/domains/token/orchestration/index.ts +3 -0
  361. package/src/domains/token/orchestration/resolveTokenList.ts +17 -0
  362. package/src/domains/token/orchestration/resolveTokenLookup.ts +20 -0
  363. package/src/domains/token/orchestration/tokenEmptyError.ts +17 -0
  364. package/src/domains/token/types.ts +6 -0
  365. package/src/error/PragmaError.test.ts +121 -0
  366. package/src/error/PragmaError.ts +164 -0
  367. package/src/error/constants.ts +12 -0
  368. package/src/error/index.ts +4 -0
  369. package/src/error/types.ts +41 -0
  370. package/src/index.ts +141 -0
  371. package/src/mcp/createMcpServer.test.ts +41 -0
  372. package/src/mcp/createMcpServer.ts +53 -0
  373. package/src/mcp/error/buildRecovery.test.ts +37 -0
  374. package/src/mcp/error/buildRecovery.ts +17 -0
  375. package/src/mcp/error/index.ts +12 -0
  376. package/src/mcp/error/serializeError.test.ts +121 -0
  377. package/src/mcp/error/serializeError.ts +31 -0
  378. package/src/mcp/error/serializeErrorPayload.test.ts +38 -0
  379. package/src/mcp/error/serializeErrorPayload.ts +27 -0
  380. package/src/mcp/error/types.ts +51 -0
  381. package/src/mcp/index.ts +15 -0
  382. package/src/mcp/registerResources.test.ts +236 -0
  383. package/src/mcp/registerResources.ts +303 -0
  384. package/src/mcp/runMcpServer.test.ts +66 -0
  385. package/src/mcp/runMcpServer.ts +24 -0
  386. package/src/mcp/tools/index.ts +55 -0
  387. package/src/mcp/tools/registerFromSpec.ts +97 -0
  388. package/src/mcp/tools/registerTools.test.ts +1124 -0
  389. package/src/mcp/types.ts +62 -0
  390. package/src/mcp/utils/estimateTokens.test.ts +14 -0
  391. package/src/mcp/utils/estimateTokens.ts +9 -0
  392. package/src/mcp/utils/index.ts +11 -0
  393. package/src/mcp/utils/wrapTool.test.ts +108 -0
  394. package/src/mcp/utils/wrapTool.ts +81 -0
  395. package/src/package-manager/constants.ts +29 -0
  396. package/src/package-manager/detectInstallSource.test.ts +23 -0
  397. package/src/package-manager/detectInstallSource.ts +111 -0
  398. package/src/package-manager/detectLocalInstall.test.ts +16 -0
  399. package/src/package-manager/detectLocalInstall.ts +43 -0
  400. package/src/package-manager/detectPackageManager.test.ts +81 -0
  401. package/src/package-manager/detectPackageManager.ts +30 -0
  402. package/src/package-manager/index.ts +6 -0
  403. package/src/package-manager/types.ts +12 -0
  404. package/src/pipeline/collectCommands.test.ts +37 -0
  405. package/src/pipeline/collectCommands.ts +47 -0
  406. package/src/pipeline/constants.ts +14 -0
  407. package/src/pipeline/createProgram.test.ts +112 -0
  408. package/src/pipeline/createProgram.ts +70 -0
  409. package/src/pipeline/formatTerminal.test.ts +43 -0
  410. package/src/pipeline/formatTerminal.ts +53 -0
  411. package/src/pipeline/index.ts +19 -0
  412. package/src/pipeline/mapExitCode.test.ts +41 -0
  413. package/src/pipeline/mapExitCode.ts +12 -0
  414. package/src/pipeline/parseGlobalFlags.ts +21 -0
  415. package/src/pipeline/renderError.test.ts +151 -0
  416. package/src/pipeline/renderError.ts +114 -0
  417. package/src/pipeline/resolveCommandKind.test.ts +68 -0
  418. package/src/pipeline/resolveCommandKind.ts +71 -0
  419. package/src/pipeline/runCli.orchestration.test.ts +418 -0
  420. package/src/pipeline/runCli.test.ts +70 -0
  421. package/src/pipeline/runCli.ts +215 -0
  422. package/src/pipeline/types.ts +17 -0
  423. package/src/testing/cli.ts +43 -0
  424. package/src/testing/createTestMcpClient.ts +64 -0
  425. package/src/testing/dsFixtures.ts +506 -0
  426. package/src/testing/fixtures/canonical-config.json +4 -0
  427. package/src/testing/fixtures/canonical.ttl +437 -0
  428. package/src/testing/fixtures/filtered-config.json +4 -0
  429. package/src/testing/fixtures.ts +34 -0
  430. package/src/testing/helpers/assertParity.ts +28 -0
  431. package/src/testing/helpers/createTestMcpClient.ts +40 -0
  432. package/src/testing/helpers/createTestRuntime.ts +67 -0
  433. package/src/testing/index.ts +8 -0
  434. package/src/testing/integration/agent-session.test.ts +148 -0
  435. package/src/testing/integration/boot.test.ts +67 -0
  436. package/src/testing/integration/mcp-surface.test.ts +574 -0
  437. package/src/testing/integration/parity.test.ts +192 -0
  438. package/src/testing/store.ts +39 -0
  439. package/src/testing/types.ts +20 -0
@@ -0,0 +1,37 @@
1
+ import type { QueryResult } from "@canonical/ke";
2
+ import type { Formatters } from "../../shared/formatters.js";
3
+
4
+ /**
5
+ * Formatters for `pragma graph query` output.
6
+ *
7
+ * - **plain**: tab-separated table for SELECT results, `ASK: true/false` for ASK
8
+ * queries, JSON fallback for CONSTRUCT.
9
+ * - **llm**: indented JSON for all query types.
10
+ * - **json**: serialized {@link QueryResult} as indented JSON.
11
+ */
12
+ const formatters: Formatters<QueryResult> = {
13
+ plain(result) {
14
+ if (result.type === "select") {
15
+ if (result.bindings.length === 0) return "No results.";
16
+ const cols = Object.keys(result.bindings[0] ?? {});
17
+ const rows = result.bindings.map((b) =>
18
+ cols.map((c) => b[c] ?? "").join("\t"),
19
+ );
20
+ return [cols.join("\t"), ...rows].join("\n");
21
+ }
22
+ if (result.type === "ask") {
23
+ return `ASK: ${String(result.result)}`;
24
+ }
25
+ return JSON.stringify(result, null, 2);
26
+ },
27
+
28
+ llm(result) {
29
+ return JSON.stringify(result, null, 2);
30
+ },
31
+
32
+ json(result) {
33
+ return JSON.stringify(result, null, 2);
34
+ },
35
+ };
36
+
37
+ export default formatters;
@@ -0,0 +1,3 @@
1
+ /** @module Barrel for graph helpers. */
2
+
3
+ export { default as resolveUri } from "./resolveUri.js";
@@ -0,0 +1,70 @@
1
+ import { PragmaError } from "#error";
2
+
3
+ /**
4
+ * Characters not allowed inside `<IRI>` in SPARQL.
5
+ * Mirrors `validateIri` from `@canonical/ke`.
6
+ */
7
+ const UNSAFE_IRI_PATTERN = /[<>"{}|\\^`\s]/;
8
+
9
+ /**
10
+ * Resolves a potentially prefixed URI (e.g. `ds:UIBlock`) to a full URI.
11
+ *
12
+ * If the URI is already a full URI (starts with `http`), validates IRI safety
13
+ * and returns it as-is. Otherwise, expands the prefix using the provided
14
+ * prefix map.
15
+ *
16
+ * @param uri - A full or prefixed URI string.
17
+ * @param prefixes - Registered prefix-to-namespace mapping from the store.
18
+ * @returns The fully expanded URI string.
19
+ * @throws PragmaError.invalidInput if the URI format is invalid or the prefix is unknown.
20
+ */
21
+ export default function resolveUri(
22
+ uri: string,
23
+ prefixes: Readonly<Record<string, string>>,
24
+ ): string {
25
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
26
+ assertSafeIri(uri);
27
+ return uri;
28
+ }
29
+
30
+ const colonIdx = uri.indexOf(":");
31
+ if (colonIdx === -1) {
32
+ throw PragmaError.invalidInput("uri", uri, {
33
+ recovery: {
34
+ message:
35
+ 'Use a prefixed URI (e.g., "ds:global.component.button") or a full URI (e.g., "https://ds.canonical.com/global.component.button").',
36
+ },
37
+ });
38
+ }
39
+
40
+ const prefix = uri.slice(0, colonIdx);
41
+ const localName = uri.slice(colonIdx + 1);
42
+ const namespace = prefixes[prefix];
43
+
44
+ if (namespace === undefined) {
45
+ throw PragmaError.invalidInput("prefix", prefix, {
46
+ validOptions: Object.keys(prefixes),
47
+ recovery: {
48
+ message: "List known ontology prefixes.",
49
+ cli: "pragma ontology list",
50
+ mcp: { tool: "ontology_list" },
51
+ },
52
+ });
53
+ }
54
+
55
+ const resolved = `${namespace}${localName}`;
56
+ assertSafeIri(resolved);
57
+ return resolved;
58
+ }
59
+
60
+ /**
61
+ * Reject URIs containing characters that would break SPARQL `<IRI>` syntax
62
+ * or enable injection.
63
+ */
64
+ function assertSafeIri(uri: string): void {
65
+ if (UNSAFE_IRI_PATTERN.test(uri)) {
66
+ throw PragmaError.invalidInput("uri", uri, {
67
+ recovery: { message: "URI contains characters not allowed in IRIs." },
68
+ });
69
+ }
70
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @module Graph domain barrel.
3
+ *
4
+ * Provides raw SPARQL querying and URI inspection against the ke store.
5
+ */
6
+
7
+ import type { CommandDefinition } from "@canonical/cli-core";
8
+ import type { PragmaContext } from "../shared/context.js";
9
+ import { inspectCommand, queryCommand } from "./commands/index.js";
10
+
11
+ /**
12
+ * Returns all command definitions for the graph domain.
13
+ *
14
+ * @param ctx - Pragma runtime context.
15
+ * @returns An array of command definitions (`graph query`, `graph inspect`).
16
+ */
17
+ export function commands(ctx: PragmaContext): CommandDefinition[] {
18
+ return [queryCommand(ctx), inspectCommand(ctx)];
19
+ }
20
+
21
+ export { specs as mcpSpecs } from "./mcp/index.js";
22
+ export { executeQuery, inspectUri } from "./operations/index.js";
@@ -0,0 +1,2 @@
1
+ /** @module Graph domain MCP barrel — graph_query, graph_inspect. */
2
+ export { default as specs } from "./specs.js";
@@ -0,0 +1,82 @@
1
+ /**
2
+ * MCP tool specs for graph domain — graph_query, graph_inspect.
3
+ */
4
+
5
+ import type { ToolSpec } from "../../shared/ToolSpec.js";
6
+ import { executeQuery, inspectUri } from "../operations/index.js";
7
+
8
+ const specs: readonly ToolSpec[] = [
9
+ {
10
+ name: "graph_query",
11
+ description:
12
+ "Execute a SPARQL query against the knowledge graph. Returns raw query results. Use ontology_list to discover available namespaces and prefixes.",
13
+ params: {
14
+ sparql: {
15
+ type: "string",
16
+ description: "SPARQL query string",
17
+ optional: false,
18
+ },
19
+ condensed: {
20
+ type: "boolean",
21
+ description: "Token-optimized output",
22
+ optional: true,
23
+ },
24
+ },
25
+ readOnly: true,
26
+ async execute(rt, params) {
27
+ const result = await executeQuery(rt.store, params.sparql as string);
28
+
29
+ if (params.condensed) {
30
+ const text = JSON.stringify(result, null, 2);
31
+ return {
32
+ condensed: true,
33
+ text,
34
+ tokens: `~${Math.ceil(text.length / 4)}`,
35
+ };
36
+ }
37
+
38
+ return { data: result };
39
+ },
40
+ },
41
+ {
42
+ name: "graph_inspect",
43
+ description:
44
+ "Inspect a URI in the knowledge graph. Returns all predicates and objects for the given subject.",
45
+ params: {
46
+ uri: {
47
+ type: "string",
48
+ description: "URI to inspect (full or prefixed, e.g. 'ds:Button')",
49
+ optional: false,
50
+ },
51
+ condensed: {
52
+ type: "boolean",
53
+ description: "Token-optimized output",
54
+ optional: true,
55
+ },
56
+ },
57
+ readOnly: true,
58
+ async execute(rt, params) {
59
+ const result = await inspectUri(rt.store, params.uri as string);
60
+
61
+ if (params.condensed) {
62
+ const lines = [`## ${result.uri}`, ""];
63
+ for (const g of result.groups) {
64
+ lines.push(`**${g.predicate}:**`);
65
+ for (const o of g.objects) {
66
+ lines.push(` - ${o}`);
67
+ }
68
+ }
69
+ const text = lines.join("\n");
70
+ return {
71
+ condensed: true,
72
+ text,
73
+ tokens: `~${Math.ceil(text.length / 4)}`,
74
+ };
75
+ }
76
+
77
+ return { data: result };
78
+ },
79
+ },
80
+ ];
81
+
82
+ export default specs;
@@ -0,0 +1,68 @@
1
+ import type { Store } from "@canonical/ke";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import { PragmaError } from "#error";
4
+ import { createTestStore, DS_ALL_TTL } from "#testing";
5
+ import { P } from "../../shared/prefixes.js";
6
+ import executeQuery from "./executeQuery.js";
7
+
8
+ let store: Store;
9
+ let cleanup: () => void;
10
+
11
+ beforeAll(async () => {
12
+ const result = await createTestStore({ ttl: DS_ALL_TTL });
13
+ store = result.store;
14
+ cleanup = result.cleanup;
15
+ });
16
+
17
+ afterAll(() => cleanup());
18
+
19
+ describe("executeQuery", () => {
20
+ it("executes a SELECT query and returns bindings", async () => {
21
+ const result = await executeQuery(
22
+ store,
23
+ `SELECT ?name WHERE { ?c a ${P.ds}Component ; ${P.ds}name ?name } ORDER BY ?name`,
24
+ );
25
+ expect(result.type).toBe("select");
26
+ if (result.type === "select") {
27
+ const names = result.bindings.map((b) => b.name);
28
+ expect(names).toContain("Button");
29
+ expect(names).toContain("Card");
30
+ }
31
+ });
32
+
33
+ it("executes a CONSTRUCT query and returns triples", async () => {
34
+ const result = await executeQuery(
35
+ store,
36
+ `CONSTRUCT { ?c ${P.ds}name ?name } WHERE { ?c a ${P.ds}Component ; ${P.ds}name ?name }`,
37
+ );
38
+ expect(result.type).toBe("construct");
39
+ if (result.type === "construct") {
40
+ expect(result.triples.length).toBeGreaterThan(0);
41
+ const names = result.triples.map((t) => t.object);
42
+ expect(names).toContain("Button");
43
+ }
44
+ });
45
+
46
+ it("executes an ASK query and returns boolean", async () => {
47
+ const result = await executeQuery(
48
+ store,
49
+ `ASK { ?c a ${P.ds}Component ; ${P.ds}name ?name }`,
50
+ );
51
+ expect(result.type).toBe("ask");
52
+ if (result.type === "ask") {
53
+ expect(result.result).toBe(true);
54
+ }
55
+ });
56
+
57
+ it("throws PragmaError on invalid SPARQL", async () => {
58
+ await expect(executeQuery(store, "SELECT INVALID SPARQL")).rejects.toThrow(
59
+ PragmaError,
60
+ );
61
+
62
+ try {
63
+ await executeQuery(store, "SELECT INVALID SPARQL");
64
+ } catch (e) {
65
+ expect((e as PragmaError).code).toBe("STORE_ERROR");
66
+ }
67
+ });
68
+ });
@@ -0,0 +1,32 @@
1
+ import type { QueryResult, Store } from "@canonical/ke";
2
+ import { PragmaError } from "#error";
3
+ import { buildQuery } from "../../shared/buildQuery.js";
4
+
5
+ /**
6
+ * Executes a raw SPARQL query against the ke store.
7
+ *
8
+ * @note Queries ke store
9
+ *
10
+ * @param store - The ke store to query.
11
+ * @param sparql - A raw SPARQL query string.
12
+ * @returns The query result (select bindings, ask boolean, or construct triples).
13
+ * @throws PragmaError with code `STORE_ERROR` on query failure.
14
+ */
15
+ export default async function executeQuery(
16
+ store: Store,
17
+ sparql: string,
18
+ ): Promise<QueryResult> {
19
+ try {
20
+ return await store.query(buildQuery(sparql));
21
+ } catch (error) {
22
+ throw new PragmaError({
23
+ code: "STORE_ERROR",
24
+ message: `Failed to execute query. ${error instanceof Error ? error.message : String(error)}`,
25
+ recovery: {
26
+ message: "Check your SPARQL syntax and see loaded namespaces.",
27
+ cli: "pragma ontology list",
28
+ mcp: { tool: "ontology_list" },
29
+ },
30
+ });
31
+ }
32
+ }
@@ -0,0 +1,4 @@
1
+ /** @module Barrel for graph operations. */
2
+
3
+ export { default as executeQuery } from "./executeQuery.js";
4
+ export { default as inspectUri } from "./inspectUri.js";
@@ -0,0 +1,101 @@
1
+ import type { Store } from "@canonical/ke";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import { PragmaError } from "#error";
4
+ import { createTestStore, DS_ALL_TTL } from "#testing";
5
+ import { P, PREFIX_MAP } from "../../shared/prefixes.js";
6
+ import inspectUri from "./inspectUri.js";
7
+
8
+ let store: Store;
9
+ let cleanup: () => void;
10
+
11
+ beforeAll(async () => {
12
+ const result = await createTestStore({ ttl: DS_ALL_TTL });
13
+ store = result.store;
14
+ cleanup = result.cleanup;
15
+ });
16
+
17
+ afterAll(() => cleanup());
18
+
19
+ describe("inspectUri", () => {
20
+ it("returns grouped triples for a known URI", async () => {
21
+ const result = await inspectUri(store, `${P.ds}global.component.button`);
22
+ expect(result.uri).toBe(`${PREFIX_MAP.ds}global.component.button`);
23
+ expect(result.groups.length).toBeGreaterThan(0);
24
+
25
+ const predicates = result.groups.map((g) => g.predicate);
26
+ expect(predicates).toContain(
27
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
28
+ );
29
+ });
30
+
31
+ it("resolves prefixed URIs", async () => {
32
+ const result = await inspectUri(store, `${P.ds}global.component.button`);
33
+ expect(result.uri).toBe(`${PREFIX_MAP.ds}global.component.button`);
34
+ });
35
+
36
+ it("accepts full URIs", async () => {
37
+ const result = await inspectUri(
38
+ store,
39
+ `${PREFIX_MAP.ds}global.component.button`,
40
+ );
41
+ expect(result.uri).toBe(`${PREFIX_MAP.ds}global.component.button`);
42
+ expect(result.groups.length).toBeGreaterThan(0);
43
+ });
44
+
45
+ it("groups objects by predicate", async () => {
46
+ const result = await inspectUri(store, `${P.ds}global.component.button`);
47
+ const modifierGroup = result.groups.find((g) =>
48
+ g.predicate.includes("modifier"),
49
+ );
50
+ // Button has two modifiers (importance, density)
51
+ if (modifierGroup) {
52
+ expect(modifierGroup.objects.length).toBe(2);
53
+ }
54
+ });
55
+
56
+ it("throws PragmaError.notFound for unknown URI", async () => {
57
+ await expect(
58
+ inspectUri(store, `${P.ds}nonexistent_entity`),
59
+ ).rejects.toThrow(PragmaError);
60
+
61
+ try {
62
+ await inspectUri(store, `${P.ds}nonexistent_entity`);
63
+ } catch (e) {
64
+ expect((e as PragmaError).code).toBe("ENTITY_NOT_FOUND");
65
+ }
66
+ });
67
+
68
+ it("throws PragmaError.invalidInput for unknown prefix", async () => {
69
+ await expect(inspectUri(store, "unknown:something")).rejects.toThrow(
70
+ PragmaError,
71
+ );
72
+
73
+ try {
74
+ await inspectUri(store, "unknown:something");
75
+ } catch (e) {
76
+ expect((e as PragmaError).code).toBe("INVALID_INPUT");
77
+ }
78
+ });
79
+
80
+ it("throws PragmaError.invalidInput for bare string without prefix", async () => {
81
+ await expect(inspectUri(store, "button")).rejects.toThrow(PragmaError);
82
+
83
+ try {
84
+ await inspectUri(store, "button");
85
+ } catch (e) {
86
+ expect((e as PragmaError).code).toBe("INVALID_INPUT");
87
+ }
88
+ });
89
+
90
+ it("throws PragmaError.invalidInput for URI with unsafe IRI characters", async () => {
91
+ await expect(
92
+ inspectUri(store, "https://example.com/<injected>"),
93
+ ).rejects.toThrow(PragmaError);
94
+
95
+ try {
96
+ await inspectUri(store, "https://example.com/<injected>");
97
+ } catch (e) {
98
+ expect((e as PragmaError).code).toBe("INVALID_INPUT");
99
+ }
100
+ });
101
+ });
@@ -0,0 +1,61 @@
1
+ import type { Store } from "@canonical/ke";
2
+ import { PragmaError } from "#error";
3
+ import { buildQuery } from "../../shared/buildQuery.js";
4
+ import type { InspectResult, PredicateGroup } from "../../shared/types.js";
5
+ import resolveUri from "../helpers/resolveUri.js";
6
+
7
+ /**
8
+ * Inspects a URI by retrieving all triples where it is the subject,
9
+ * grouped by predicate.
10
+ *
11
+ * Resolves prefixed URIs (e.g. `ds:Button`) to full URIs before querying.
12
+ *
13
+ * @note Queries ke store
14
+ *
15
+ * @param store - The ke store to query.
16
+ * @param uri - A full or prefixed URI to inspect.
17
+ * @returns An {@link InspectResult} with the resolved URI and predicate groups.
18
+ * @throws PragmaError.notFound if the URI has no triples in the store.
19
+ */
20
+ export default async function inspectUri(
21
+ store: Store,
22
+ uri: string,
23
+ ): Promise<InspectResult> {
24
+ const resolvedUri = resolveUri(uri, store.prefixes);
25
+
26
+ const result = await store.query(
27
+ buildQuery(`
28
+ SELECT ?predicate ?object
29
+ WHERE {
30
+ <${resolvedUri}> ?predicate ?object .
31
+ }
32
+ ORDER BY ?predicate ?object
33
+ `),
34
+ );
35
+
36
+ if (result.type !== "select" || result.bindings.length === 0) {
37
+ throw PragmaError.notFound("entity", uri, {
38
+ recovery: {
39
+ message:
40
+ "Check the URI is correct and run a SPARQL query to find valid URIs.",
41
+ cli: "pragma graph query 'SELECT ?s WHERE { ?s ?p ?o } LIMIT 10'",
42
+ mcp: { tool: "graph_query" },
43
+ },
44
+ });
45
+ }
46
+
47
+ const groupMap = new Map<string, string[]>();
48
+ for (const b of result.bindings) {
49
+ const predicate = b.predicate ?? "";
50
+ const object = b.object ?? "";
51
+ const existing = groupMap.get(predicate) ?? [];
52
+ existing.push(object);
53
+ groupMap.set(predicate, existing);
54
+ }
55
+
56
+ const groups: PredicateGroup[] = [...groupMap.entries()].map(
57
+ ([predicate, objects]) => ({ predicate, objects }),
58
+ );
59
+
60
+ return { uri: resolvedUri, groups };
61
+ }
@@ -0,0 +1,4 @@
1
+ /** @module Barrel for info/upgrade command definitions. */
2
+
3
+ export { default as infoCommand } from "./info.js";
4
+ export { default as upgradeCommand } from "./upgrade.js";
@@ -0,0 +1,88 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const {
4
+ collectInfoMock,
5
+ renderInfoJsonMock,
6
+ renderInfoLlmMock,
7
+ renderInfoPlainMock,
8
+ } = vi.hoisted(() => ({
9
+ collectInfoMock: vi.fn(),
10
+ renderInfoJsonMock: vi.fn(() => "json"),
11
+ renderInfoLlmMock: vi.fn(() => "llm"),
12
+ renderInfoPlainMock: vi.fn(() => "plain"),
13
+ }));
14
+
15
+ vi.mock("../operations/collectInfo.js", () => ({
16
+ default: collectInfoMock,
17
+ }));
18
+
19
+ vi.mock("../formatters/index.js", () => ({
20
+ renderInfoJson: renderInfoJsonMock,
21
+ renderInfoLlm: renderInfoLlmMock,
22
+ renderInfoPlain: renderInfoPlainMock,
23
+ }));
24
+
25
+ import infoCommand from "./info.js";
26
+
27
+ describe("infoCommand", () => {
28
+ beforeEach(() => {
29
+ vi.clearAllMocks();
30
+ collectInfoMock.mockResolvedValue({ version: "1.0.0" });
31
+ });
32
+
33
+ it("has path ['info']", () => {
34
+ expect(infoCommand.path).toEqual(["info"]);
35
+ });
36
+
37
+ it("has a description", () => {
38
+ expect(infoCommand.description).toBeTruthy();
39
+ });
40
+
41
+ it("has no parameters", () => {
42
+ expect(infoCommand.parameters).toEqual([]);
43
+ });
44
+
45
+ it("has an execute function", () => {
46
+ expect(typeof infoCommand.execute).toBe("function");
47
+ });
48
+
49
+ it("uses the plain renderer by default", async () => {
50
+ const result = await infoCommand.execute(
51
+ {},
52
+ { cwd: "/workspace", globalFlags: { llm: false, format: "text" } },
53
+ );
54
+
55
+ expect(collectInfoMock).toHaveBeenCalledWith("/workspace");
56
+ expect(result.tag).toBe("output");
57
+ if (result.tag === "output") {
58
+ expect(result.render.plain(result.value)).toBe("plain");
59
+ expect(renderInfoPlainMock).toHaveBeenCalledWith({ version: "1.0.0" });
60
+ }
61
+ });
62
+
63
+ it("uses the llm renderer when llm output is requested", async () => {
64
+ const result = await infoCommand.execute(
65
+ {},
66
+ { cwd: "/workspace", globalFlags: { llm: true, format: "text" } },
67
+ );
68
+
69
+ expect(result.tag).toBe("output");
70
+ if (result.tag === "output") {
71
+ expect(result.render.plain(result.value)).toBe("llm");
72
+ expect(renderInfoLlmMock).toHaveBeenCalledWith({ version: "1.0.0" });
73
+ }
74
+ });
75
+
76
+ it("prefers the json renderer over llm mode", async () => {
77
+ const result = await infoCommand.execute(
78
+ {},
79
+ { cwd: "/workspace", globalFlags: { llm: true, format: "json" } },
80
+ );
81
+
82
+ expect(result.tag).toBe("output");
83
+ if (result.tag === "output") {
84
+ expect(result.render.plain(result.value)).toBe("json");
85
+ expect(renderInfoJsonMock).toHaveBeenCalledWith({ version: "1.0.0" });
86
+ }
87
+ });
88
+ });
@@ -0,0 +1,47 @@
1
+ import type { CommandDefinition, CommandResult } from "@canonical/cli-core";
2
+ import {
3
+ renderInfoJson,
4
+ renderInfoLlm,
5
+ renderInfoPlain,
6
+ } from "../formatters/index.js";
7
+ import collectInfo from "../operations/collectInfo.js";
8
+ import type { InfoData } from "../types.js";
9
+
10
+ /**
11
+ * Selects the appropriate info renderer based on global CLI flags.
12
+ *
13
+ * @param flags - Global flags indicating output format.
14
+ * @returns A render function mapping {@link InfoData} to a string.
15
+ */
16
+ function selectInfoRenderer(flags: {
17
+ llm: boolean;
18
+ format: "text" | "json";
19
+ }): (data: InfoData) => string {
20
+ if (flags.format === "json") return renderInfoJson;
21
+ if (flags.llm) return renderInfoLlm;
22
+ return renderInfoPlain;
23
+ }
24
+
25
+ /**
26
+ * The `pragma info` command definition.
27
+ *
28
+ * Displays version, config, update status, and store summary.
29
+ */
30
+ const infoCommand: CommandDefinition = {
31
+ path: ["info"],
32
+ description: "Show version, config, update status, and store summary",
33
+ parameters: [],
34
+ execute: async (
35
+ _params: Record<string, unknown>,
36
+ ctx: {
37
+ cwd: string;
38
+ globalFlags: { llm: boolean; format: "text" | "json" };
39
+ },
40
+ ): Promise<CommandResult> => {
41
+ const data = await collectInfo(ctx.cwd);
42
+ const render = selectInfoRenderer(ctx.globalFlags);
43
+ return { tag: "output", value: data, render: { plain: render } };
44
+ },
45
+ };
46
+
47
+ export default infoCommand;