@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,151 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ vi.mock("#config", async (importOriginal) => ({
4
+ ...(await importOriginal<typeof import("#config")>()),
5
+ configExists: vi.fn(),
6
+ }));
7
+ vi.mock("../../shared/bootStore.js", () => ({
8
+ bootStore: vi.fn(),
9
+ }));
10
+ vi.mock("../../info/operations/collectStoreSummary.js", () => ({
11
+ collectStoreSummary: vi.fn(),
12
+ }));
13
+ vi.mock("@canonical/harnesses", () => ({
14
+ detectHarnesses: vi.fn(),
15
+ readMcpConfig: vi.fn(),
16
+ }));
17
+ vi.mock("@canonical/task", () => ({
18
+ runTask: vi.fn((task: unknown) => task),
19
+ }));
20
+
21
+ import { detectHarnesses, readMcpConfig } from "@canonical/harnesses";
22
+ import { configExists } from "#config";
23
+ import { collectStoreSummary } from "../../info/operations/index.js";
24
+ import { bootStore } from "../../shared/bootStore.js";
25
+ import {
26
+ checkConfigFile,
27
+ checkKeStore,
28
+ checkMcpConfigured,
29
+ checkNodeVersion,
30
+ checkPragmaVersion,
31
+ checkShellCompletions,
32
+ checkSkillsSymlinked,
33
+ } from "./checks/index.js";
34
+
35
+ const ctx = { cwd: "/test/project" };
36
+
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ });
40
+
41
+ describe("checkNodeVersion", () => {
42
+ it("passes for current Node version", async () => {
43
+ const result = await checkNodeVersion();
44
+ expect(result.status).toBe("pass");
45
+ expect(result.detail).toContain(process.versions.node);
46
+ });
47
+ });
48
+
49
+ describe("checkPragmaVersion", () => {
50
+ it("always passes and includes version", async () => {
51
+ const result = await checkPragmaVersion();
52
+ expect(result.status).toBe("pass");
53
+ expect(result.detail).toContain("v");
54
+ });
55
+ });
56
+
57
+ describe("checkConfigFile", () => {
58
+ it("passes when config exists", async () => {
59
+ vi.mocked(configExists).mockReturnValue(true);
60
+ const result = await checkConfigFile(ctx);
61
+ expect(result.status).toBe("pass");
62
+ });
63
+
64
+ it("fails with remedy when config missing", async () => {
65
+ vi.mocked(configExists).mockReturnValue(false);
66
+ const result = await checkConfigFile(ctx);
67
+ expect(result.status).toBe("fail");
68
+ expect(result.remedy).toBeDefined();
69
+ });
70
+ });
71
+
72
+ describe("checkKeStore", () => {
73
+ it("passes when store boots and reports triple count", async () => {
74
+ const mockStore = {
75
+ query: vi.fn(),
76
+ dispose: vi.fn(),
77
+ };
78
+ vi.mocked(bootStore).mockResolvedValue(mockStore as never);
79
+ vi.mocked(collectStoreSummary).mockResolvedValue({
80
+ tripleCount: 12847,
81
+ graphNames: [],
82
+ });
83
+
84
+ const result = await checkKeStore(ctx);
85
+ expect(result.status).toBe("pass");
86
+ expect(result.detail).toContain("12,847");
87
+ expect(mockStore.dispose).toHaveBeenCalled();
88
+ });
89
+
90
+ it("fails when store boot throws", async () => {
91
+ vi.mocked(bootStore).mockRejectedValue(new Error("no data"));
92
+ const result = await checkKeStore(ctx);
93
+ expect(result.status).toBe("fail");
94
+ expect(result.remedy).toBeDefined();
95
+ });
96
+ });
97
+
98
+ describe("checkShellCompletions", () => {
99
+ it("returns pass or fail without throwing", async () => {
100
+ const result = await checkShellCompletions();
101
+ expect(["pass", "fail"]).toContain(result.status);
102
+ });
103
+ });
104
+
105
+ describe("checkMcpConfigured", () => {
106
+ it("fails when no harnesses detected", async () => {
107
+ vi.mocked(detectHarnesses).mockReturnValue(Promise.resolve([]) as never);
108
+ const result = await checkMcpConfigured(ctx);
109
+ expect(result.status).toBe("fail");
110
+ });
111
+
112
+ it("passes when pragma is configured in a harness", async () => {
113
+ const harness = {
114
+ id: "claude-code",
115
+ name: "Claude Code",
116
+ configPath: () => "/test/.mcp.json",
117
+ skillsPath: () => "/test/.claude/skills",
118
+ detect: [],
119
+ version: "*",
120
+ configFormat: "json" as const,
121
+ mcpKey: "mcpServers",
122
+ };
123
+ vi.mocked(detectHarnesses).mockReturnValue(
124
+ Promise.resolve([
125
+ {
126
+ harness,
127
+ confidence: "high" as const,
128
+ configExists: true,
129
+ configPath: "/test/.mcp.json",
130
+ },
131
+ ]) as never,
132
+ );
133
+ vi.mocked(readMcpConfig).mockReturnValue(
134
+ Promise.resolve({
135
+ pragma: { command: "pragma", args: ["mcp"] },
136
+ }) as never,
137
+ );
138
+
139
+ const result = await checkMcpConfigured(ctx);
140
+ expect(result.status).toBe("pass");
141
+ expect(result.detail).toContain("Claude Code");
142
+ });
143
+ });
144
+
145
+ describe("checkSkillsSymlinked", () => {
146
+ it("skips when no harnesses detected", async () => {
147
+ vi.mocked(detectHarnesses).mockReturnValue(Promise.resolve([]) as never);
148
+ const result = await checkSkillsSymlinked(ctx);
149
+ expect(result.status).toBe("skip");
150
+ });
151
+ });
@@ -0,0 +1,3 @@
1
+ /** @module Doctor operations: check orchestration and types. */
2
+ export { default as runChecks } from "./runChecks.js";
3
+ export type { CheckContext, CheckResult, DoctorData } from "./types.js";
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ vi.mock("./checks/index.js", () => ({
4
+ checkNodeVersion: vi.fn(),
5
+ checkPragmaVersion: vi.fn(),
6
+ checkConfigFile: vi.fn(),
7
+ checkKeStore: vi.fn(),
8
+ checkShellCompletions: vi.fn(),
9
+ checkMcpConfigured: vi.fn(),
10
+ checkSkillsSymlinked: vi.fn(),
11
+ }));
12
+
13
+ import {
14
+ checkConfigFile,
15
+ checkKeStore,
16
+ checkMcpConfigured,
17
+ checkNodeVersion,
18
+ checkPragmaVersion,
19
+ checkShellCompletions,
20
+ checkSkillsSymlinked,
21
+ } from "./checks/index.js";
22
+ import runChecks from "./runChecks.js";
23
+
24
+ const pass = (name: string) => ({
25
+ name,
26
+ status: "pass" as const,
27
+ detail: "ok",
28
+ });
29
+ const fail = (name: string) => ({
30
+ name,
31
+ status: "fail" as const,
32
+ detail: "bad",
33
+ remedy: "fix it",
34
+ });
35
+ const skip = (name: string) => ({
36
+ name,
37
+ status: "skip" as const,
38
+ detail: "skipped",
39
+ });
40
+
41
+ describe("runChecks", () => {
42
+ it("returns correct counts when all pass", async () => {
43
+ vi.mocked(checkNodeVersion).mockResolvedValue(pass("Node"));
44
+ vi.mocked(checkPragmaVersion).mockResolvedValue(pass("pragma"));
45
+ vi.mocked(checkConfigFile).mockResolvedValue(pass("config"));
46
+ vi.mocked(checkKeStore).mockResolvedValue(pass("store"));
47
+ vi.mocked(checkShellCompletions).mockResolvedValue(pass("completions"));
48
+ vi.mocked(checkMcpConfigured).mockResolvedValue(pass("mcp"));
49
+ vi.mocked(checkSkillsSymlinked).mockResolvedValue(pass("skills"));
50
+
51
+ const data = await runChecks({ cwd: "/test" });
52
+ expect(data.passed).toBe(7);
53
+ expect(data.failed).toBe(0);
54
+ expect(data.skipped).toBe(0);
55
+ expect(data.checks).toHaveLength(7);
56
+ });
57
+
58
+ it("counts failures and skips correctly", async () => {
59
+ vi.mocked(checkNodeVersion).mockResolvedValue(pass("Node"));
60
+ vi.mocked(checkPragmaVersion).mockResolvedValue(pass("pragma"));
61
+ vi.mocked(checkConfigFile).mockResolvedValue(fail("config"));
62
+ vi.mocked(checkKeStore).mockResolvedValue(fail("store"));
63
+ vi.mocked(checkShellCompletions).mockResolvedValue(fail("completions"));
64
+ vi.mocked(checkMcpConfigured).mockResolvedValue(pass("mcp"));
65
+ vi.mocked(checkSkillsSymlinked).mockResolvedValue(skip("skills"));
66
+
67
+ const data = await runChecks({ cwd: "/test" });
68
+ expect(data.passed).toBe(3);
69
+ expect(data.failed).toBe(3);
70
+ expect(data.skipped).toBe(1);
71
+ });
72
+
73
+ it("preserves check order", async () => {
74
+ vi.mocked(checkNodeVersion).mockResolvedValue(pass("Node"));
75
+ vi.mocked(checkPragmaVersion).mockResolvedValue(pass("pragma"));
76
+ vi.mocked(checkConfigFile).mockResolvedValue(pass("config"));
77
+ vi.mocked(checkKeStore).mockResolvedValue(pass("store"));
78
+ vi.mocked(checkShellCompletions).mockResolvedValue(pass("completions"));
79
+ vi.mocked(checkMcpConfigured).mockResolvedValue(pass("mcp"));
80
+ vi.mocked(checkSkillsSymlinked).mockResolvedValue(pass("skills"));
81
+
82
+ const data = await runChecks({ cwd: "/test" });
83
+ expect(data.checks[0].name).toBe("Node");
84
+ expect(data.checks[1].name).toBe("pragma");
85
+ expect(data.checks[2].name).toBe("config");
86
+ expect(data.checks[3].name).toBe("store");
87
+ expect(data.checks[6].name).toBe("skills");
88
+ });
89
+ });
@@ -0,0 +1,38 @@
1
+ import {
2
+ checkConfigFile,
3
+ checkKeStore,
4
+ checkMcpConfigured,
5
+ checkNodeVersion,
6
+ checkPragmaVersion,
7
+ checkShellCompletions,
8
+ checkSkillsSymlinked,
9
+ } from "./checks/index.js";
10
+ import type { CheckContext, CheckResult, DoctorData } from "./types.js";
11
+
12
+ /**
13
+ * Orchestrate all environment health checks sequentially and return
14
+ * a DoctorData summary with pass/fail/skip counts.
15
+ *
16
+ * @param ctx - Check context containing the working directory.
17
+ * @returns Aggregated check results.
18
+ * @note Impure
19
+ */
20
+ export default async function runChecks(
21
+ ctx: CheckContext,
22
+ ): Promise<DoctorData> {
23
+ const checks: CheckResult[] = [];
24
+
25
+ checks.push(await checkNodeVersion());
26
+ checks.push(await checkPragmaVersion());
27
+ checks.push(await checkConfigFile(ctx));
28
+ checks.push(await checkKeStore(ctx));
29
+ checks.push(await checkShellCompletions());
30
+ checks.push(await checkMcpConfigured(ctx));
31
+ checks.push(await checkSkillsSymlinked(ctx));
32
+
33
+ const passed = checks.filter((c) => c.status === "pass").length;
34
+ const failed = checks.filter((c) => c.status === "fail").length;
35
+ const skipped = checks.filter((c) => c.status === "skip").length;
36
+
37
+ return { checks, passed, failed, skipped };
38
+ }
@@ -0,0 +1,20 @@
1
+ /** Result of a single doctor check. */
2
+ export interface CheckResult {
3
+ readonly name: string;
4
+ readonly status: "pass" | "fail" | "skip";
5
+ readonly detail: string;
6
+ readonly remedy?: string;
7
+ }
8
+
9
+ /** Context passed to each doctor check, providing the working directory. */
10
+ export interface CheckContext {
11
+ readonly cwd: string;
12
+ }
13
+
14
+ /** Aggregated results from all doctor checks. */
15
+ export interface DoctorData {
16
+ readonly checks: readonly CheckResult[];
17
+ readonly passed: number;
18
+ readonly failed: number;
19
+ readonly skipped: number;
20
+ }
@@ -0,0 +1,4 @@
1
+ /** @module Barrel for graph command builders. */
2
+
3
+ export { default as inspectCommand } from "./inspect.js";
4
+ export { default as queryCommand } from "./query.js";
@@ -0,0 +1,94 @@
1
+ import type { CommandDefinition } from "@canonical/cli-core";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import createTestRuntime from "../../../testing/helpers/createTestRuntime.js";
4
+ import type { PragmaContext } from "../../shared/context.js";
5
+ import { PREFIX_MAP } from "../../shared/prefixes.js";
6
+ import type { PragmaRuntime } from "../../shared/runtime.js";
7
+ import type { InspectResult } from "../../shared/types.js";
8
+ import buildInspectCommand from "./inspect.js";
9
+
10
+ let runtime: PragmaRuntime;
11
+
12
+ beforeAll(async () => {
13
+ runtime = await createTestRuntime();
14
+ });
15
+
16
+ afterAll(() => {
17
+ runtime.dispose();
18
+ });
19
+
20
+ function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
21
+ return {
22
+ ...runtime,
23
+ globalFlags: { llm: false, format: "text" as const, verbose: false },
24
+ ...overrides,
25
+ };
26
+ }
27
+
28
+ async function executeOutput(
29
+ cmd: CommandDefinition,
30
+ params: Record<string, unknown>,
31
+ ctx: PragmaContext,
32
+ ): Promise<{ value: unknown; text: string }> {
33
+ const result = await cmd.execute(params, ctx);
34
+ expect(result.tag).toBe("output");
35
+ if (result.tag !== "output") throw new Error("Expected output result");
36
+ return { value: result.value, text: result.render.plain(result.value) };
37
+ }
38
+
39
+ describe("graph inspect command", () => {
40
+ const uri = `${PREFIX_MAP.ds}global.component.button`;
41
+
42
+ it("returns inspect results", async () => {
43
+ const ctx = makeCtx();
44
+ const cmd = buildInspectCommand(ctx);
45
+ const { value } = await executeOutput(cmd, { uri }, ctx);
46
+
47
+ const result = value as InspectResult;
48
+ expect(result.uri).toBe(uri);
49
+ expect(result.groups.length).toBeGreaterThan(0);
50
+ });
51
+
52
+ it("renders plain output", async () => {
53
+ const ctx = makeCtx();
54
+ const cmd = buildInspectCommand(ctx);
55
+ const { text } = await executeOutput(cmd, { uri }, ctx);
56
+
57
+ expect(text).toContain("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
58
+ });
59
+
60
+ it("renders llm output", async () => {
61
+ const ctx = makeCtx({
62
+ globalFlags: { llm: true, format: "text" as const, verbose: false },
63
+ });
64
+ const cmd = buildInspectCommand(ctx);
65
+ const { text } = await executeOutput(cmd, { uri }, ctx);
66
+
67
+ expect(text).toContain("##");
68
+ expect(text).toContain(uri);
69
+ });
70
+
71
+ it("renders json output", async () => {
72
+ const ctx = makeCtx({
73
+ globalFlags: { llm: false, format: "json" as const, verbose: false },
74
+ });
75
+ const cmd = buildInspectCommand(ctx);
76
+ const { text } = await executeOutput(cmd, { uri }, ctx);
77
+
78
+ const parsed = JSON.parse(text) as InspectResult;
79
+ expect(parsed.uri).toBe(uri);
80
+ });
81
+
82
+ it("throws structured error when uri is missing", async () => {
83
+ const ctx = makeCtx();
84
+ const cmd = buildInspectCommand(ctx);
85
+
86
+ await expect(cmd.execute({}, ctx)).rejects.toMatchObject({
87
+ code: "INVALID_INPUT",
88
+ recovery: {
89
+ cli: "pragma graph query 'SELECT ?s WHERE { ?s ?p ?o } LIMIT 10'",
90
+ mcp: { tool: "graph_query" },
91
+ },
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,63 @@
1
+ import {
2
+ type CommandDefinition,
3
+ type CommandResult,
4
+ createOutputResult,
5
+ } from "@canonical/cli-core";
6
+ import { PragmaError } from "#error";
7
+ import type { PragmaContext } from "../../shared/context.js";
8
+ import { selectFormatter } from "../../shared/formatters.js";
9
+ import { inspectFormatters } from "../formatters/index.js";
10
+ import { inspectUri } from "../operations/index.js";
11
+
12
+ /**
13
+ * Builds the `pragma graph inspect` command definition.
14
+ *
15
+ * Accepts a URI (full or prefixed) and returns all triples where
16
+ * the URI is the subject, grouped by predicate.
17
+ *
18
+ * @param ctx - Pragma runtime context providing the ke store and config.
19
+ * @returns A command definition for `graph inspect`.
20
+ */
21
+ export default function buildInspectCommand(
22
+ ctx: PragmaContext,
23
+ ): CommandDefinition {
24
+ return {
25
+ path: ["graph", "inspect"],
26
+ description: "Inspect a URI in the knowledge graph",
27
+ parameters: [
28
+ {
29
+ name: "uri",
30
+ description: "URI to inspect (full or prefixed, e.g. 'ds:Button')",
31
+ type: "string",
32
+ positional: true,
33
+ required: true,
34
+ },
35
+ ],
36
+ meta: {
37
+ examples: [
38
+ "pragma graph inspect ds:Button",
39
+ "pragma graph inspect https://ds.canonical.com/Button --llm",
40
+ ],
41
+ },
42
+ execute: async (
43
+ params: Record<string, unknown>,
44
+ ): Promise<CommandResult> => {
45
+ const uri = params.uri as string;
46
+ if (!uri) {
47
+ throw PragmaError.invalidInput("uri", "(empty)", {
48
+ recovery: {
49
+ message: "Provide a URI to inspect.",
50
+ cli: "pragma graph query 'SELECT ?s WHERE { ?s ?p ?o } LIMIT 10'",
51
+ mcp: { tool: "graph_query" },
52
+ },
53
+ });
54
+ }
55
+
56
+ const result = await inspectUri(ctx.store, uri);
57
+
58
+ return createOutputResult(result, {
59
+ plain: selectFormatter(ctx, inspectFormatters),
60
+ });
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,92 @@
1
+ import type { CommandDefinition } from "@canonical/cli-core";
2
+ import type { QueryResult } from "@canonical/ke";
3
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
4
+ import createTestRuntime from "../../../testing/helpers/createTestRuntime.js";
5
+ import type { PragmaContext } from "../../shared/context.js";
6
+ import type { PragmaRuntime } from "../../shared/runtime.js";
7
+ import buildQueryCommand from "./query.js";
8
+
9
+ let runtime: PragmaRuntime;
10
+
11
+ beforeAll(async () => {
12
+ runtime = await createTestRuntime();
13
+ });
14
+
15
+ afterAll(() => {
16
+ runtime.dispose();
17
+ });
18
+
19
+ function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
20
+ return {
21
+ ...runtime,
22
+ globalFlags: { llm: false, format: "text" as const, verbose: false },
23
+ ...overrides,
24
+ };
25
+ }
26
+
27
+ async function executeOutput(
28
+ cmd: CommandDefinition,
29
+ params: Record<string, unknown>,
30
+ ctx: PragmaContext,
31
+ ): Promise<{ value: unknown; text: string }> {
32
+ const result = await cmd.execute(params, ctx);
33
+ expect(result.tag).toBe("output");
34
+ if (result.tag !== "output") throw new Error("Expected output result");
35
+ return { value: result.value, text: result.render.plain(result.value) };
36
+ }
37
+
38
+ describe("graph query command", () => {
39
+ const sparql = "SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }";
40
+
41
+ it("returns query results", async () => {
42
+ const ctx = makeCtx();
43
+ const cmd = buildQueryCommand(ctx);
44
+ const { value } = await executeOutput(cmd, { sparql }, ctx);
45
+
46
+ const result = value as QueryResult;
47
+ expect(result.type).toBe("select");
48
+ if (result.type === "select") {
49
+ expect(Number(result.bindings[0]?.n)).toBeGreaterThan(0);
50
+ }
51
+ });
52
+
53
+ it("renders plain output", async () => {
54
+ const ctx = makeCtx();
55
+ const cmd = buildQueryCommand(ctx);
56
+ const { text } = await executeOutput(cmd, { sparql }, ctx);
57
+
58
+ expect(text).toContain("n");
59
+ });
60
+
61
+ it("renders llm output", async () => {
62
+ const ctx = makeCtx({
63
+ globalFlags: { llm: true, format: "text" as const, verbose: false },
64
+ });
65
+ const cmd = buildQueryCommand(ctx);
66
+ const { text } = await executeOutput(cmd, { sparql }, ctx);
67
+
68
+ const parsed = JSON.parse(text) as QueryResult;
69
+ expect(parsed.type).toBe("select");
70
+ });
71
+
72
+ it("renders json output", async () => {
73
+ const ctx = makeCtx({
74
+ globalFlags: { llm: false, format: "json" as const, verbose: false },
75
+ });
76
+ const cmd = buildQueryCommand(ctx);
77
+ const { text } = await executeOutput(cmd, { sparql }, ctx);
78
+
79
+ const parsed = JSON.parse(text) as QueryResult;
80
+ expect(parsed.type).toBe("select");
81
+ });
82
+
83
+ it("throws structured error when sparql is missing", async () => {
84
+ const ctx = makeCtx();
85
+ const cmd = buildQueryCommand(ctx);
86
+
87
+ await expect(cmd.execute({}, ctx)).rejects.toMatchObject({
88
+ code: "INVALID_INPUT",
89
+ recovery: { message: "Provide a SPARQL query string." },
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,60 @@
1
+ import {
2
+ type CommandDefinition,
3
+ type CommandResult,
4
+ createOutputResult,
5
+ } from "@canonical/cli-core";
6
+ import { PragmaError } from "#error";
7
+ import type { PragmaContext } from "../../shared/context.js";
8
+ import { selectFormatter } from "../../shared/formatters.js";
9
+ import { queryFormatters } from "../formatters/index.js";
10
+ import { executeQuery } from "../operations/index.js";
11
+
12
+ /**
13
+ * Builds the `pragma graph query` command definition.
14
+ *
15
+ * Accepts a raw SPARQL query string and executes it against the ke store.
16
+ *
17
+ * @param ctx - Pragma runtime context providing the ke store and config.
18
+ * @returns A command definition for `graph query`.
19
+ */
20
+ export default function buildQueryCommand(
21
+ ctx: PragmaContext,
22
+ ): CommandDefinition {
23
+ return {
24
+ path: ["graph", "query"],
25
+ description: "Execute a SPARQL query",
26
+ parameters: [
27
+ {
28
+ name: "sparql",
29
+ description: "SPARQL query string",
30
+ type: "string",
31
+ positional: true,
32
+ required: true,
33
+ },
34
+ ],
35
+ meta: {
36
+ examples: [
37
+ 'pragma graph query "SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }"',
38
+ 'pragma graph query "SELECT ?s WHERE { ?s a ds:Component }" --llm',
39
+ ],
40
+ },
41
+ execute: async (
42
+ params: Record<string, unknown>,
43
+ ): Promise<CommandResult> => {
44
+ const sparql = params.sparql as string;
45
+ if (!sparql) {
46
+ throw PragmaError.invalidInput("sparql", "(empty)", {
47
+ recovery: {
48
+ message: "Provide a SPARQL query string.",
49
+ },
50
+ });
51
+ }
52
+
53
+ const result = await executeQuery(ctx.store, sparql);
54
+
55
+ return createOutputResult(result, {
56
+ plain: selectFormatter(ctx, queryFormatters),
57
+ });
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,4 @@
1
+ /** @module Barrel for graph formatters. */
2
+
3
+ export { default as inspectFormatters } from "./inspect.js";
4
+ export { default as queryFormatters } from "./query.js";
@@ -0,0 +1,45 @@
1
+ import chalk from "chalk";
2
+ import { formatHeading } from "#pipeline";
3
+ import type { Formatters } from "../../shared/formatters.js";
4
+ import type { InspectResult } from "../../shared/types.js";
5
+
6
+ /**
7
+ * Formatters for `pragma graph inspect` output.
8
+ *
9
+ * - **plain**: terminal output with chalk-styled URI heading and bold predicates.
10
+ * - **llm**: condensed Markdown with heading and bulleted object lists.
11
+ * - **json**: serialized {@link InspectResult} as indented JSON.
12
+ */
13
+ const formatters: Formatters<InspectResult> = {
14
+ plain(result) {
15
+ const lines: string[] = [];
16
+ lines.push(formatHeading(result.uri));
17
+
18
+ for (const g of result.groups) {
19
+ lines.push("");
20
+ lines.push(chalk.bold(g.predicate));
21
+ for (const o of g.objects) {
22
+ lines.push(` ${o}`);
23
+ }
24
+ }
25
+
26
+ return lines.join("\n");
27
+ },
28
+
29
+ llm(result) {
30
+ const lines = [`## ${result.uri}`, ""];
31
+ for (const g of result.groups) {
32
+ lines.push(`**${g.predicate}:**`);
33
+ for (const o of g.objects) {
34
+ lines.push(` - ${o}`);
35
+ }
36
+ }
37
+ return lines.join("\n");
38
+ },
39
+
40
+ json(result) {
41
+ return JSON.stringify(result, null, 2);
42
+ },
43
+ };
44
+
45
+ export default formatters;