@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,47 @@
1
+ import type { CommandDefinition } from "@canonical/cli-core";
2
+ import { commands as blockCommands } from "../domains/block/index.js";
3
+ import { commands as configCommands } from "../domains/config/index.js";
4
+ import { commands as createCommands } from "../domains/create/index.js";
5
+ import { doctorCommand } from "../domains/doctor/commands/index.js";
6
+ import { commands as graphCommands } from "../domains/graph/index.js";
7
+ import { infoCommand, upgradeCommand } from "../domains/info/index.js";
8
+ import { buildLlmCommand } from "../domains/llm/index.js";
9
+ import { commands as modifierCommands } from "../domains/modifier/index.js";
10
+ import { commands as ontologyCommands } from "../domains/ontology/index.js";
11
+ import { commands as setupCommands } from "../domains/setup/index.js";
12
+ import type { PragmaContext } from "../domains/shared/context.js";
13
+ import { commands as skillCommands } from "../domains/skill/index.js";
14
+ import { commands as standardCommands } from "../domains/standard/index.js";
15
+ import { commands as tierCommands } from "../domains/tier/index.js";
16
+ import { commands as tokenCommands } from "../domains/token/index.js";
17
+
18
+ /**
19
+ * Collect all CLI command definitions from domain modules.
20
+ *
21
+ * Extracted from runCli so the completions server can import
22
+ * the command list independently without running the full CLI pipeline.
23
+ *
24
+ * @param ctx - The pragma context providing store, config, and global flags.
25
+ * @returns The full array of command definitions for all domains.
26
+ */
27
+ export default function collectCommands(
28
+ ctx: PragmaContext,
29
+ ): CommandDefinition[] {
30
+ return [
31
+ ...configCommands(ctx),
32
+ ...createCommands(),
33
+ ...setupCommands(),
34
+ ...standardCommands(ctx),
35
+ ...modifierCommands(ctx),
36
+ ...tierCommands(ctx),
37
+ ...tokenCommands(ctx),
38
+ ...blockCommands(ctx),
39
+ ...ontologyCommands(ctx),
40
+ ...graphCommands(ctx),
41
+ ...skillCommands(ctx),
42
+ doctorCommand,
43
+ infoCommand,
44
+ upgradeCommand,
45
+ buildLlmCommand(ctx),
46
+ ];
47
+ }
@@ -0,0 +1,14 @@
1
+ import type { ErrorCode } from "../error/types.js";
2
+
3
+ /** Map from error codes to numeric process exit codes. */
4
+ const EXIT_CODES: Record<ErrorCode, number> = {
5
+ ENTITY_NOT_FOUND: 1,
6
+ EMPTY_RESULTS: 2,
7
+ INVALID_INPUT: 3,
8
+ AMBIGUOUS_INPUT: 3,
9
+ CONFIG_ERROR: 4,
10
+ STORE_ERROR: 5,
11
+ INTERNAL_ERROR: 127,
12
+ };
13
+
14
+ export { EXIT_CODES };
@@ -0,0 +1,112 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const runMcpServerMock = vi.fn(async () => undefined);
4
+
5
+ vi.mock("../mcp/runMcpServer.js", () => ({
6
+ default: runMcpServerMock,
7
+ }));
8
+
9
+ describe("createProgram", () => {
10
+ beforeEach(() => {
11
+ vi.clearAllMocks();
12
+ });
13
+
14
+ afterEach(() => {
15
+ process.exitCode = undefined;
16
+ });
17
+
18
+ it("uses plain root help when llm is disabled", async () => {
19
+ const { default: createProgram } = await import("./createProgram.js");
20
+
21
+ const program = createProgram([], {
22
+ cwd: "/tmp/work",
23
+ store: {},
24
+ config: { tier: undefined, channel: "normal" },
25
+ globalFlags: { llm: false, format: "text", verbose: false },
26
+ });
27
+ const write = vi.fn();
28
+ (
29
+ program as {
30
+ _events: {
31
+ beforeAllHelp: (ctx: {
32
+ command: unknown;
33
+ write: (value: string) => void;
34
+ }) => void;
35
+ };
36
+ }
37
+ )._events.beforeAllHelp({ command: program, write });
38
+
39
+ expect(write).toHaveBeenCalledWith(
40
+ expect.stringContaining("Usage: pragma <command> [options]"),
41
+ );
42
+ });
43
+
44
+ it("uses llm root help when llm is enabled", async () => {
45
+ const { default: createProgram } = await import("./createProgram.js");
46
+
47
+ const program = createProgram([], {
48
+ cwd: "/tmp/work",
49
+ store: {},
50
+ config: { tier: undefined, channel: "normal" },
51
+ globalFlags: { llm: true, format: "text", verbose: false },
52
+ });
53
+ const write = vi.fn();
54
+ (
55
+ program as {
56
+ _events: {
57
+ beforeAllHelp: (ctx: {
58
+ command: unknown;
59
+ write: (value: string) => void;
60
+ }) => void;
61
+ };
62
+ }
63
+ )._events.beforeAllHelp({ command: program, write });
64
+
65
+ expect(write).toHaveBeenCalledWith(
66
+ expect.stringContaining("# pragma commands"),
67
+ );
68
+ });
69
+
70
+ it("does not inject root help text into subcommand help", async () => {
71
+ const { default: createProgram } = await import("./createProgram.js");
72
+
73
+ const program = createProgram([], {
74
+ cwd: "/tmp/work",
75
+ store: {},
76
+ config: { tier: undefined, channel: "normal" },
77
+ globalFlags: { llm: false, format: "text", verbose: false },
78
+ });
79
+
80
+ const mcpCommand = program.commands.find(
81
+ (command) => command.name() === "mcp",
82
+ );
83
+ expect(mcpCommand).toBeDefined();
84
+ const write = vi.fn();
85
+ (
86
+ program as {
87
+ _events: {
88
+ beforeAllHelp: (ctx: {
89
+ command: unknown;
90
+ write: (value: string) => void;
91
+ }) => void;
92
+ };
93
+ }
94
+ )._events.beforeAllHelp({ command: mcpCommand, write });
95
+ expect(write).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it("runs the mcp subcommand action", async () => {
99
+ const { default: createProgram } = await import("./createProgram.js");
100
+
101
+ const program = createProgram([], {
102
+ cwd: "/tmp/work",
103
+ store: {},
104
+ config: { tier: undefined, channel: "normal" },
105
+ globalFlags: { llm: false, format: "text", verbose: false },
106
+ });
107
+
108
+ await program.parseAsync(["node", "pragma", "mcp"]);
109
+
110
+ expect(runMcpServerMock).toHaveBeenCalledTimes(1);
111
+ });
112
+ });
@@ -0,0 +1,70 @@
1
+ import {
2
+ type CommandDefinition,
3
+ formatHelp,
4
+ formatLlmHelp,
5
+ registerAll,
6
+ } from "@canonical/cli-core";
7
+ import { Command } from "commander";
8
+ import { PROGRAM_DESCRIPTION, PROGRAM_NAME, VERSION } from "../constants.js";
9
+ import type { PragmaContext } from "../domains/shared/context.js";
10
+
11
+ /**
12
+ * Build and configure the top-level Commander program.
13
+ *
14
+ * Registers all domain commands, global flags, help formatting,
15
+ * and the `mcp` subcommand. Returns the configured Commander instance
16
+ * ready for `parseAsync`.
17
+ *
18
+ * @param commands - Domain command definitions to register.
19
+ * @param ctx - Shared pragma context (global flags, store, config).
20
+ * @returns Configured Commander program.
21
+ *
22
+ * @note Impure — attaches process.stdout/stderr handlers and lazy-imports the MCP server.
23
+ */
24
+ export default function createProgram(
25
+ commands: readonly CommandDefinition[],
26
+ ctx: PragmaContext,
27
+ ): Command {
28
+ const program = new Command();
29
+ program.name(PROGRAM_NAME);
30
+ program.version(VERSION, "-V, --version");
31
+ program.description(PROGRAM_DESCRIPTION);
32
+ program.exitOverride();
33
+ program.configureOutput({
34
+ writeOut: (str) => process.stdout.write(str),
35
+ writeErr: (str) => process.stderr.write(str),
36
+ });
37
+
38
+ program.option(
39
+ "--llm",
40
+ "Condensed Markdown output for LLM consumption",
41
+ false,
42
+ );
43
+ program.option("--format <type>", "Output format (text or json)", "text");
44
+ program.option("--verbose", "Diagnostic output to stderr", false);
45
+
46
+ program.addHelpText("beforeAll", (_ctx) => {
47
+ // Only show root-level help when the help is for the root program itself.
48
+ // Subcommand help is handled by registerAll via formatNounHelp/formatVerbHelp.
49
+ if (_ctx.command !== program) return "";
50
+ if (ctx.globalFlags.llm) {
51
+ return formatLlmHelp(PROGRAM_NAME, commands);
52
+ }
53
+ return formatHelp(PROGRAM_NAME, PROGRAM_DESCRIPTION, commands);
54
+ });
55
+
56
+ program.helpOption("-h, --help", "Show help");
57
+ program.configureHelp({ formatHelp: () => "" });
58
+
59
+ program
60
+ .command("mcp")
61
+ .description("Start the MCP server over stdio")
62
+ .action(async () => {
63
+ const { default: runMcpServer } = await import("../mcp/runMcpServer.js");
64
+ await runMcpServer();
65
+ });
66
+
67
+ registerAll(program, commands, ctx);
68
+
69
+ return program;
70
+ }
@@ -0,0 +1,43 @@
1
+ import chalk from "chalk";
2
+ import { describe, expect, it } from "vitest";
3
+ import {
4
+ formatField,
5
+ formatHeading,
6
+ formatList,
7
+ formatSection,
8
+ } from "./formatTerminal.js";
9
+
10
+ describe("formatHeading", () => {
11
+ it("applies bold and underline", () => {
12
+ expect(formatHeading("Title")).toBe(chalk.bold.underline("Title"));
13
+ });
14
+ });
15
+
16
+ describe("formatField", () => {
17
+ it("renders dim label with value", () => {
18
+ expect(formatField("Name:", "Button")).toBe(`${chalk.dim("Name:")} Button`);
19
+ });
20
+ });
21
+
22
+ describe("formatList", () => {
23
+ it("renders items with default bullet", () => {
24
+ const result = formatList(["one", "two", "three"]);
25
+ expect(result).toBe(" - one\n - two\n - three");
26
+ });
27
+
28
+ it("uses custom bullet", () => {
29
+ const result = formatList(["a", "b"], "*");
30
+ expect(result).toBe(" * a\n * b");
31
+ });
32
+
33
+ it("returns empty string for empty array", () => {
34
+ expect(formatList([])).toBe("");
35
+ });
36
+ });
37
+
38
+ describe("formatSection", () => {
39
+ it("combines heading and body", () => {
40
+ const result = formatSection("Details", "some content");
41
+ expect(result).toBe(`${chalk.bold.underline("Details")}\nsome content`);
42
+ });
43
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Terminal formatting helpers for CLI output.
3
+ *
4
+ * Pure string transformers that apply chalk styling
5
+ * for headings, fields, lists, and sections.
6
+ */
7
+
8
+ import chalk from "chalk";
9
+
10
+ /**
11
+ * Format text as a bold underlined heading.
12
+ *
13
+ * @param text - Heading content.
14
+ * @returns Styled string.
15
+ */
16
+ function formatHeading(text: string): string {
17
+ return chalk.bold.underline(text);
18
+ }
19
+
20
+ /**
21
+ * Format a label–value pair with a dimmed label.
22
+ *
23
+ * @param label - Field label (rendered dim).
24
+ * @param value - Field value.
25
+ * @returns Styled `"label value"` string.
26
+ */
27
+ function formatField(label: string, value: string): string {
28
+ return `${chalk.dim(label)} ${value}`;
29
+ }
30
+
31
+ /**
32
+ * Format an array of items as a bulleted list.
33
+ *
34
+ * @param items - Lines to render.
35
+ * @param bullet - Bullet character (default `"-"`).
36
+ * @returns Multi-line bulleted string.
37
+ */
38
+ function formatList(items: string[], bullet = "-"): string {
39
+ return items.map((item) => ` ${bullet} ${item}`).join("\n");
40
+ }
41
+
42
+ /**
43
+ * Combine a heading and a body block into a section.
44
+ *
45
+ * @param heading - Section heading text.
46
+ * @param body - Pre-formatted body content.
47
+ * @returns Heading followed by body, separated by a newline.
48
+ */
49
+ function formatSection(heading: string, body: string): string {
50
+ return `${formatHeading(heading)}\n${body}`;
51
+ }
52
+
53
+ export { formatField, formatHeading, formatList, formatSection };
@@ -0,0 +1,19 @@
1
+ /** @module CLI pipeline — program construction, flag parsing, error rendering, and command routing. */
2
+ export { default as collectCommands } from "./collectCommands.js";
3
+ export { default as createProgram } from "./createProgram.js";
4
+ export {
5
+ formatField,
6
+ formatHeading,
7
+ formatList,
8
+ formatSection,
9
+ } from "./formatTerminal.js";
10
+ export { default as mapExitCode } from "./mapExitCode.js";
11
+ export { default as parseGlobalFlags } from "./parseGlobalFlags.js";
12
+ export {
13
+ renderErrorJson,
14
+ renderErrorLlm,
15
+ renderErrorPlain,
16
+ } from "./renderError.js";
17
+ export { default as resolveCommandKind } from "./resolveCommandKind.js";
18
+ export { default as runCli } from "./runCli.js";
19
+ export type { CommandKind } from "./types.js";
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ERROR_CODES } from "../error/index.js";
3
+ import { EXIT_CODES } from "./constants.js";
4
+ import mapExitCode from "./mapExitCode.js";
5
+
6
+ describe("mapExitCode", () => {
7
+ it("maps ENTITY_NOT_FOUND to 1", () => {
8
+ expect(mapExitCode("ENTITY_NOT_FOUND")).toBe(1);
9
+ });
10
+
11
+ it("maps EMPTY_RESULTS to 2", () => {
12
+ expect(mapExitCode("EMPTY_RESULTS")).toBe(2);
13
+ });
14
+
15
+ it("maps INVALID_INPUT to 3", () => {
16
+ expect(mapExitCode("INVALID_INPUT")).toBe(3);
17
+ });
18
+
19
+ it("maps AMBIGUOUS_INPUT to 3", () => {
20
+ expect(mapExitCode("AMBIGUOUS_INPUT")).toBe(3);
21
+ });
22
+
23
+ it("maps CONFIG_ERROR to 4", () => {
24
+ expect(mapExitCode("CONFIG_ERROR")).toBe(4);
25
+ });
26
+
27
+ it("maps STORE_ERROR to 5", () => {
28
+ expect(mapExitCode("STORE_ERROR")).toBe(5);
29
+ });
30
+
31
+ it("maps INTERNAL_ERROR to 127", () => {
32
+ expect(mapExitCode("INTERNAL_ERROR")).toBe(127);
33
+ });
34
+
35
+ it("covers every ErrorCode", () => {
36
+ for (const code of ERROR_CODES) {
37
+ expect(typeof mapExitCode(code)).toBe("number");
38
+ }
39
+ expect(Object.keys(EXIT_CODES)).toHaveLength(ERROR_CODES.length);
40
+ });
41
+ });
@@ -0,0 +1,12 @@
1
+ import type { ErrorCode } from "../error/types.js";
2
+ import { EXIT_CODES } from "./constants.js";
3
+
4
+ /**
5
+ * Map a PragmaError error code to a numeric process exit code.
6
+ *
7
+ * @param code - Machine-readable error code.
8
+ * @returns Corresponding exit code from the EXIT_CODES table.
9
+ */
10
+ export default function mapExitCode(code: ErrorCode): number {
11
+ return EXIT_CODES[code];
12
+ }
@@ -0,0 +1,21 @@
1
+ import type { GlobalFlags } from "@canonical/cli-core";
2
+
3
+ /**
4
+ * Extract global flags (--llm, --format, --verbose) from raw argv.
5
+ *
6
+ * Performs simple indexOf scanning rather than full argument parsing
7
+ * so it can run before Commander processes the command tree.
8
+ *
9
+ * @param argv - Raw process.argv array.
10
+ * @returns Parsed global flags.
11
+ */
12
+ export default function parseGlobalFlags(argv: readonly string[]): GlobalFlags {
13
+ return {
14
+ llm: argv.includes("--llm"),
15
+ format:
16
+ argv.includes("--format") && argv[argv.indexOf("--format") + 1] === "json"
17
+ ? "json"
18
+ : "text",
19
+ verbose: argv.includes("--verbose"),
20
+ };
21
+ }
@@ -0,0 +1,151 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { PragmaError } from "../error/index.js";
3
+ import {
4
+ renderErrorJson,
5
+ renderErrorLlm,
6
+ renderErrorPlain,
7
+ } from "./renderError.js";
8
+
9
+ describe("renderErrorPlain", () => {
10
+ it("renders a basic error", () => {
11
+ const err = PragmaError.internalError("something broke");
12
+ const out = renderErrorPlain(err);
13
+ expect(out).toContain("Error: Internal error: something broke");
14
+ expect(out).toContain("Please report this issue.");
15
+ });
16
+
17
+ it("renders suggestions", () => {
18
+ const err = PragmaError.notFound("component", "Buton", {
19
+ suggestions: ["Button", "ButtonGroup"],
20
+ });
21
+ const out = renderErrorPlain(err);
22
+ expect(out).toContain("Did you mean?");
23
+ expect(out).toContain(" - Button");
24
+ expect(out).toContain(" - ButtonGroup");
25
+ });
26
+
27
+ it("renders filters", () => {
28
+ const err = PragmaError.emptyResults("component", {
29
+ filters: { tier: "apps/lxd", channel: "normal" },
30
+ });
31
+ const out = renderErrorPlain(err);
32
+ expect(out).toContain("Active filters:");
33
+ expect(out).toContain(" tier: apps/lxd");
34
+ expect(out).toContain(" channel: normal");
35
+ });
36
+
37
+ it("renders validOptions", () => {
38
+ const err = PragmaError.invalidInput("channel", "aggressive", {
39
+ validOptions: ["normal", "experimental", "prerelease"],
40
+ });
41
+ const out = renderErrorPlain(err);
42
+ expect(out).toContain("Valid options: normal, experimental, prerelease");
43
+ });
44
+
45
+ it("renders recovery with cli command", () => {
46
+ const err = PragmaError.notFound("component", "Foo", {
47
+ recovery: {
48
+ message: "List available components.",
49
+ cli: "pragma block list",
50
+ mcp: { tool: "block_list" },
51
+ },
52
+ });
53
+ const out = renderErrorPlain(err);
54
+ expect(out).toContain("Run `pragma block list`");
55
+ });
56
+
57
+ it("renders recovery with message only (no cli)", () => {
58
+ const err = PragmaError.emptyResults("component", {
59
+ recovery: {
60
+ message: "Ensure design system packages are installed.",
61
+ },
62
+ });
63
+ const out = renderErrorPlain(err);
64
+ expect(out).toContain("Ensure design system packages are installed.");
65
+ });
66
+ });
67
+
68
+ describe("renderErrorLlm", () => {
69
+ it("renders structured markdown", () => {
70
+ const err = PragmaError.notFound("component", "Buton", {
71
+ suggestions: ["Button", "ButtonGroup"],
72
+ recovery: {
73
+ message: "List available components.",
74
+ cli: "pragma block list",
75
+ mcp: { tool: "block_list" },
76
+ },
77
+ });
78
+ const out = renderErrorLlm(err);
79
+ expect(out).toContain("## Error: ENTITY_NOT_FOUND");
80
+ expect(out).toContain('component "Buton" not found.');
81
+ expect(out).toContain("Suggestions: Button, ButtonGroup");
82
+ expect(out).toContain("Recovery: `pragma block list`");
83
+ });
84
+
85
+ it("renders filters", () => {
86
+ const err = PragmaError.emptyResults("component", {
87
+ filters: { tier: "apps/lxd" },
88
+ });
89
+ const out = renderErrorLlm(err);
90
+ expect(out).toContain("Filters: tier=apps/lxd");
91
+ });
92
+
93
+ it("renders validOptions", () => {
94
+ const err = PragmaError.invalidInput("channel", "bad", {
95
+ validOptions: ["normal", "experimental"],
96
+ });
97
+ const out = renderErrorLlm(err);
98
+ expect(out).toContain("Valid options: normal, experimental");
99
+ });
100
+
101
+ it("renders recovery message when no cli", () => {
102
+ const err = PragmaError.emptyResults("component", {
103
+ recovery: {
104
+ message: "Install packages first.",
105
+ },
106
+ });
107
+ const out = renderErrorLlm(err);
108
+ expect(out).toContain("Recovery: Install packages first.");
109
+ });
110
+ });
111
+
112
+ describe("renderErrorJson", () => {
113
+ it("produces valid JSON with all fields", () => {
114
+ const err = PragmaError.notFound("component", "Buton", {
115
+ suggestions: ["Button"],
116
+ recovery: {
117
+ message: "List available components.",
118
+ cli: "pragma block list",
119
+ mcp: { tool: "block_list" },
120
+ },
121
+ });
122
+ const parsed = JSON.parse(renderErrorJson(err));
123
+ expect(parsed.code).toBe("ENTITY_NOT_FOUND");
124
+ expect(parsed.message).toBe('component "Buton" not found.');
125
+ expect(parsed.entity).toEqual({ type: "component", name: "Buton" });
126
+ expect(parsed.suggestions).toEqual(["Button"]);
127
+ expect(parsed.recovery).toEqual({
128
+ message: "List available components.",
129
+ cli: "pragma block list",
130
+ mcp: { tool: "block_list" },
131
+ });
132
+ });
133
+
134
+ it("includes filters and validOptions", () => {
135
+ const err = PragmaError.emptyResults("component", {
136
+ filters: { tier: "global" },
137
+ });
138
+ const parsed = JSON.parse(renderErrorJson(err));
139
+ expect(parsed.code).toBe("EMPTY_RESULTS");
140
+ expect(parsed.filters).toEqual({ tier: "global" });
141
+ });
142
+
143
+ it("includes undefined fields as undefined/null", () => {
144
+ const err = PragmaError.storeError("disk full");
145
+ const parsed = JSON.parse(renderErrorJson(err));
146
+ expect(parsed.code).toBe("STORE_ERROR");
147
+ expect(parsed.entity).toBeUndefined();
148
+ expect(parsed.filters).toBeUndefined();
149
+ expect(parsed.validOptions).toBeUndefined();
150
+ });
151
+ });