@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,62 @@
1
+ /**
2
+ * MCP tool envelope types.
3
+ *
4
+ * Defines shapes for MCP response envelopes returned by `wrapTool`.
5
+ * Error-related types live in `./error/types.js`.
6
+ */
7
+
8
+ export type {
9
+ McpErrorPayload,
10
+ McpRecovery,
11
+ ToolErrorResponse,
12
+ } from "./error/types.js";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Tool handler payload (returned by tool implementation functions)
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /**
19
+ * Union type returned by tool handler functions to `wrapTool`.
20
+ * Either structured data with optional metadata, or condensed text output.
21
+ */
22
+ export type ToolPayload =
23
+ | {
24
+ readonly data: unknown;
25
+ readonly meta?: {
26
+ readonly count?: number;
27
+ readonly filters?: Record<string, string>;
28
+ };
29
+ }
30
+ | {
31
+ readonly condensed: true;
32
+ readonly text: string;
33
+ readonly tokens: string;
34
+ };
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Response envelopes (serialized into MCP content)
38
+ // ---------------------------------------------------------------------------
39
+
40
+ /**
41
+ * Success envelope — full structured data response.
42
+ * Agents branch on `ok: true` to access `data` and `meta`.
43
+ */
44
+ export interface ToolResponse<T = unknown> {
45
+ readonly ok: true;
46
+ readonly data: T;
47
+ readonly meta: {
48
+ readonly count?: number;
49
+ readonly filters?: Record<string, string>;
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Success envelope — condensed text response for token-budgeted agents.
55
+ * Agents branch on `condensed: true` to access markdown text.
56
+ */
57
+ export interface ToolResponseCondensed {
58
+ readonly ok: true;
59
+ readonly condensed: true;
60
+ readonly text: string;
61
+ readonly tokens: string;
62
+ }
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import estimateTokens from "./estimateTokens.js";
3
+
4
+ describe("estimateTokens", () => {
5
+ it("estimates ~1 token per 4 chars", () => {
6
+ expect(estimateTokens("abcd")).toBe("~1");
7
+ expect(estimateTokens("abcdefgh")).toBe("~2");
8
+ expect(estimateTokens("a")).toBe("~1");
9
+ });
10
+
11
+ it("rounds up", () => {
12
+ expect(estimateTokens("abcde")).toBe("~2");
13
+ });
14
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Estimate token count from text length using the ~4 chars/token heuristic.
3
+ *
4
+ * @param text - The text to estimate tokens for.
5
+ * @returns A human-readable string like `"~250"`.
6
+ */
7
+ export default function estimateTokens(text: string): string {
8
+ return `~${Math.ceil(text.length / 4)}`;
9
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP utility functions.
3
+ *
4
+ * Re-exports the tool wrapper and token estimation utilities shared
5
+ * across all MCP tool registration modules.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ export { default as estimateTokens } from "./estimateTokens.js";
11
+ export { default as wrapTool } from "./wrapTool.js";
@@ -0,0 +1,108 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { PragmaError } from "#error";
3
+ import type { PragmaRuntime } from "../../domains/shared/runtime.js";
4
+ import wrapTool from "./wrapTool.js";
5
+
6
+ const stubRuntime = {
7
+ store: {} as PragmaRuntime["store"],
8
+ config: { tier: undefined, channel: "normal" },
9
+ cwd: "/test",
10
+ dispose: () => {},
11
+ } as PragmaRuntime;
12
+
13
+ function parseEnvelope(result: { content: unknown[] }): unknown {
14
+ const first = result.content[0] as { type: string; text: string };
15
+ return JSON.parse(first.text);
16
+ }
17
+
18
+ describe("wrapTool", () => {
19
+ it("wraps data payload in success envelope", async () => {
20
+ const handler = wrapTool(stubRuntime, async () => ({
21
+ data: [{ name: "Button" }],
22
+ meta: { count: 1, filters: { tier: "global" } },
23
+ }));
24
+
25
+ const result = await handler({});
26
+ const envelope = parseEnvelope(result) as Record<string, unknown>;
27
+
28
+ expect(envelope.ok).toBe(true);
29
+ expect(envelope.data).toEqual([{ name: "Button" }]);
30
+ expect(envelope.meta).toEqual({ count: 1, filters: { tier: "global" } });
31
+ });
32
+
33
+ it("defaults meta to empty object when omitted", async () => {
34
+ const handler = wrapTool(stubRuntime, async () => ({
35
+ data: { name: "Button" },
36
+ }));
37
+
38
+ const result = await handler({});
39
+ const envelope = parseEnvelope(result) as Record<string, unknown>;
40
+
41
+ expect(envelope.ok).toBe(true);
42
+ expect(envelope.meta).toEqual({});
43
+ });
44
+
45
+ it("wraps condensed payload in condensed envelope", async () => {
46
+ const handler = wrapTool(stubRuntime, async () => ({
47
+ condensed: true as const,
48
+ text: "## Components\n- Button",
49
+ tokens: "~50",
50
+ }));
51
+
52
+ const result = await handler({});
53
+ const envelope = parseEnvelope(result) as Record<string, unknown>;
54
+
55
+ expect(envelope.ok).toBe(true);
56
+ expect(envelope.condensed).toBe(true);
57
+ expect(envelope.text).toBe("## Components\n- Button");
58
+ expect(envelope.tokens).toBe("~50");
59
+ expect(envelope).not.toHaveProperty("data");
60
+ });
61
+
62
+ it("serializes PragmaError into error envelope", async () => {
63
+ const handler = wrapTool(stubRuntime, async () => {
64
+ throw PragmaError.notFound("component", "Buton", {
65
+ suggestions: ["Button"],
66
+ recovery: {
67
+ message: "List available components.",
68
+ cli: "pragma block list",
69
+ mcp: { tool: "block_list" },
70
+ },
71
+ });
72
+ });
73
+
74
+ const result = await handler({});
75
+ expect(result.isError).toBe(true);
76
+
77
+ const envelope = parseEnvelope(result) as {
78
+ ok: boolean;
79
+ error: Record<string, unknown>;
80
+ };
81
+ expect(envelope.ok).toBe(false);
82
+ expect(envelope.error.code).toBe("ENTITY_NOT_FOUND");
83
+ expect(envelope.error.message).toContain("Buton");
84
+ expect(envelope.error.suggestions).toEqual(["Button"]);
85
+ });
86
+
87
+ it("rethrows unknown errors", async () => {
88
+ const handler = wrapTool(stubRuntime, async () => {
89
+ throw new TypeError("unexpected");
90
+ });
91
+
92
+ await expect(handler({})).rejects.toThrow("unexpected");
93
+ });
94
+
95
+ it("passes runtime and params to handler", async () => {
96
+ const handler = wrapTool(stubRuntime, async (rt, params) => ({
97
+ data: { cwd: rt.cwd, name: params.name },
98
+ }));
99
+
100
+ const result = await handler({ name: "Button" });
101
+ const envelope = parseEnvelope(result) as {
102
+ data: Record<string, unknown>;
103
+ };
104
+
105
+ expect(envelope.data.cwd).toBe("/test");
106
+ expect(envelope.data.name).toBe("Button");
107
+ });
108
+ });
@@ -0,0 +1,81 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { PragmaError } from "#error";
3
+ import type { PragmaRuntime } from "../../domains/shared/runtime.js";
4
+ import serializeErrorPayload from "../error/serializeErrorPayload.js";
5
+ import type { ToolPayload } from "../types.js";
6
+
7
+ /**
8
+ * Wrap a tool handler function with envelope construction and error handling.
9
+ *
10
+ * Every MCP tool is wrapped by this function — no tool constructs its own
11
+ * envelope. This ensures consistent `{ ok, data, meta }` success responses,
12
+ * `{ ok, condensed, text, tokens }` condensed responses, and
13
+ * `{ ok: false, error }` error responses across all tools.
14
+ *
15
+ * The returned handler catches `PragmaError` instances and serializes them
16
+ * as `{ ok: false, error }` responses. Unknown errors propagate to the MCP
17
+ * SDK's transport-level error handling (JSON-RPC -32603).
18
+ *
19
+ * @param runtime - The pragma runtime providing store, config, and cwd.
20
+ * @param fn - The tool implementation. Receives runtime and parsed params,
21
+ * returns a `ToolPayload` (either `{ data, meta }` or `{ condensed, text, tokens }`).
22
+ * @returns An MCP tool handler function.
23
+ */
24
+ export default function wrapTool(
25
+ runtime: PragmaRuntime,
26
+ fn: (
27
+ rt: PragmaRuntime,
28
+ params: Record<string, unknown>,
29
+ ) => Promise<ToolPayload>,
30
+ ): (params: Record<string, unknown>) => Promise<CallToolResult> {
31
+ return async (params) => {
32
+ try {
33
+ const result = await fn(runtime, params);
34
+
35
+ if ("condensed" in result) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: "text",
40
+ text: JSON.stringify({
41
+ ok: true,
42
+ condensed: true,
43
+ text: result.text,
44
+ tokens: result.tokens,
45
+ }),
46
+ },
47
+ ],
48
+ };
49
+ }
50
+
51
+ return {
52
+ content: [
53
+ {
54
+ type: "text",
55
+ text: JSON.stringify({
56
+ ok: true,
57
+ data: result.data,
58
+ meta: result.meta ?? {},
59
+ }),
60
+ },
61
+ ],
62
+ };
63
+ } catch (error) {
64
+ if (error instanceof PragmaError) {
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: JSON.stringify({
70
+ ok: false,
71
+ error: serializeErrorPayload(error),
72
+ }),
73
+ },
74
+ ],
75
+ isError: true,
76
+ };
77
+ }
78
+ throw error;
79
+ }
80
+ };
81
+ }
@@ -0,0 +1,29 @@
1
+ import type { PackageManager } from "./types.js";
2
+
3
+ /** Per-package-manager CLI command templates for global install and update. */
4
+ const PM_COMMANDS: Record<
5
+ PackageManager,
6
+ {
7
+ install: (pkg: string) => string;
8
+ update: (pkg: string) => string;
9
+ }
10
+ > = {
11
+ bun: {
12
+ install: (pkg) => `bun add -g ${pkg}`,
13
+ update: (pkg) => `bun update -g ${pkg}`,
14
+ },
15
+ npm: {
16
+ install: (pkg) => `npm install -g ${pkg}`,
17
+ update: (pkg) => `npm update -g ${pkg}`,
18
+ },
19
+ pnpm: {
20
+ install: (pkg) => `pnpm add -g ${pkg}`,
21
+ update: (pkg) => `pnpm update -g ${pkg}`,
22
+ },
23
+ yarn: {
24
+ install: (pkg) => `yarn global add ${pkg}`,
25
+ update: (pkg) => `yarn global upgrade ${pkg}`,
26
+ },
27
+ };
28
+
29
+ export { PM_COMMANDS };
@@ -0,0 +1,23 @@
1
+ import { resolve } from "node:path";
2
+ import { describe, expect, it } from "vitest";
3
+ import detectInstallSource from "./detectInstallSource.js";
4
+
5
+ describe("detectInstallSource", () => {
6
+ it("returns local install for a checked-out source entrypoint", () => {
7
+ const result = detectInstallSource(
8
+ resolve(import.meta.dirname, "../bin.ts"),
9
+ );
10
+
11
+ expect(result.scope).toBe("local");
12
+ expect(result.label).toBe("bun (local)");
13
+ });
14
+
15
+ it("returns global install for a global bun path", () => {
16
+ const result = detectInstallSource(
17
+ "/home/user/.bun/install/global/node_modules/.bin/pragma",
18
+ );
19
+
20
+ expect(result.scope).toBe("global");
21
+ expect(result.label).toBe("bun (global)");
22
+ });
23
+ });
@@ -0,0 +1,111 @@
1
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import detectPackageManager from "./detectPackageManager.js";
4
+ import type { InstallSource } from "./types.js";
5
+
6
+ function isGlobalPrefix(path: string): boolean {
7
+ return (
8
+ path.includes("/.bun/") ||
9
+ path.includes("/pnpm/") ||
10
+ path.includes("/yarn/") ||
11
+ path.includes("/.yarn/")
12
+ );
13
+ }
14
+
15
+ function resolvePath(binPath: string): string {
16
+ try {
17
+ return realpathSync(binPath);
18
+ } catch {
19
+ return binPath;
20
+ }
21
+ }
22
+
23
+ function findPackageName(startPath: string): string | undefined {
24
+ let current = dirname(startPath);
25
+
26
+ while (true) {
27
+ const packageJsonPath = join(current, "package.json");
28
+ if (existsSync(packageJsonPath)) {
29
+ try {
30
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
31
+ name?: string;
32
+ };
33
+ return parsed.name;
34
+ } catch {
35
+ return undefined;
36
+ }
37
+ }
38
+
39
+ const parent = dirname(current);
40
+ if (parent === current) return undefined;
41
+ current = parent;
42
+ }
43
+ }
44
+
45
+ function isLocalCheckout(path: string): boolean {
46
+ if (path.includes("/node_modules/")) return false;
47
+ if (!path.endsWith("/src/bin.ts") && !path.endsWith("/dist/pragma")) {
48
+ return false;
49
+ }
50
+
51
+ return findPackageName(path) === "@canonical/pragma-cli";
52
+ }
53
+
54
+ function detectRuntimePackageManager(): InstallSource["packageManager"] {
55
+ const execPath = process.execPath;
56
+ const npmExecPath = process.env.npm_execpath;
57
+ const userAgent = process.env.npm_config_user_agent ?? "";
58
+
59
+ if (execPath) {
60
+ const fromExec = detectPackageManager(execPath);
61
+ if (fromExec !== "npm") return fromExec;
62
+ }
63
+
64
+ if (npmExecPath) {
65
+ const fromNpmExec = detectPackageManager(npmExecPath);
66
+ if (fromNpmExec !== "npm") return fromNpmExec;
67
+ }
68
+
69
+ if (userAgent.startsWith("bun/")) return "bun";
70
+ if (userAgent.startsWith("pnpm/")) return "pnpm";
71
+ if (userAgent.startsWith("yarn/")) return "yarn";
72
+
73
+ return "npm";
74
+ }
75
+
76
+ /**
77
+ * Detect whether pragma is running from a global install, local
78
+ * `node_modules`, or a local source checkout, and which package
79
+ * manager owns the installation.
80
+ *
81
+ * @param binPath - The `process.argv[1]` binary path (defaults to current).
82
+ * @returns Install source with package manager, scope, and display label.
83
+ *
84
+ * @note Impure
85
+ */
86
+ export default function detectInstallSource(
87
+ binPath: string = process.argv[1] ?? "",
88
+ ): InstallSource {
89
+ const resolved = resolvePath(binPath);
90
+ const localCheckout = isLocalCheckout(resolved);
91
+ const packageManager = localCheckout
92
+ ? detectRuntimePackageManager()
93
+ : detectPackageManager(binPath);
94
+
95
+ if (
96
+ (resolved.includes("node_modules/.bin") && !isGlobalPrefix(resolved)) ||
97
+ localCheckout
98
+ ) {
99
+ return {
100
+ packageManager,
101
+ scope: "local",
102
+ label: `${packageManager} (local)`,
103
+ };
104
+ }
105
+
106
+ return {
107
+ packageManager,
108
+ scope: "global",
109
+ label: `${packageManager} (global)`,
110
+ };
111
+ }
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import detectLocalInstall from "./detectLocalInstall.js";
3
+
4
+ describe("detectLocalInstall", () => {
5
+ it("returns undefined for global installs", () => {
6
+ expect(
7
+ detectLocalInstall(
8
+ "/home/user/.bun/install/global/node_modules/.bin/pragma",
9
+ ),
10
+ ).toBeUndefined();
11
+ });
12
+
13
+ it("returns undefined when path cannot be resolved", () => {
14
+ expect(detectLocalInstall("/nonexistent/path/pragma")).toBeUndefined();
15
+ });
16
+ });
@@ -0,0 +1,43 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { PM_COMMANDS } from "./constants.js";
3
+ import detectPackageManager from "./detectPackageManager.js";
4
+
5
+ /**
6
+ * Check whether a resolved binary path falls within a known global prefix directory.
7
+ *
8
+ * @param path - Resolved filesystem path.
9
+ * @returns `true` if the path is under a global package manager directory.
10
+ */
11
+ function isGlobalPrefix(path: string): boolean {
12
+ return (
13
+ path.includes("/.bun/") ||
14
+ path.includes("/pnpm/") ||
15
+ path.includes("/yarn/") ||
16
+ path.includes("/.yarn/")
17
+ );
18
+ }
19
+
20
+ /**
21
+ * Warn if pragma is installed locally (in node_modules/.bin) instead of globally.
22
+ *
23
+ * @param binPath - Path to the pragma binary (defaults to `process.argv[1]`).
24
+ * @returns Warning string if locally installed, or `undefined` if global.
25
+ *
26
+ * @note Impure — resolves real filesystem path.
27
+ */
28
+ export default function detectLocalInstall(
29
+ binPath: string = process.argv[1] ?? "",
30
+ ): string | undefined {
31
+ let resolved: string;
32
+ try {
33
+ resolved = realpathSync(binPath);
34
+ } catch {
35
+ resolved = binPath;
36
+ }
37
+
38
+ if (!resolved.includes("node_modules/.bin")) return undefined;
39
+ if (isGlobalPrefix(resolved)) return undefined;
40
+
41
+ const pm = detectPackageManager(binPath);
42
+ return `Warning: pragma is installed locally in this project.\nGlobal installation is recommended: ${PM_COMMANDS[pm].install("@canonical/pragma-cli")}`;
43
+ }
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { PM_COMMANDS } from "./constants.js";
3
+ import detectPackageManager from "./detectPackageManager.js";
4
+
5
+ describe("detectPackageManager", () => {
6
+ it("detects bun from bin path", () => {
7
+ expect(
8
+ detectPackageManager(
9
+ "/home/user/.bun/install/global/node_modules/.bin/pragma",
10
+ ),
11
+ ).toBe("bun");
12
+ });
13
+
14
+ it("detects pnpm from bin path", () => {
15
+ expect(
16
+ detectPackageManager(
17
+ "/home/user/.local/share/pnpm/global/5/node_modules/.bin/pragma",
18
+ ),
19
+ ).toBe("pnpm");
20
+ });
21
+
22
+ it("detects yarn from bin path", () => {
23
+ expect(detectPackageManager("/home/user/.yarn/bin/pragma")).toBe("yarn");
24
+ });
25
+
26
+ it("detects yarn from global node_modules path", () => {
27
+ expect(
28
+ detectPackageManager(
29
+ "/home/user/.config/yarn/global/node_modules/.bin/pragma",
30
+ ),
31
+ ).toBe("yarn");
32
+ });
33
+
34
+ it("defaults to npm for nvm paths", () => {
35
+ expect(
36
+ detectPackageManager(
37
+ "/home/user/.config/nvm/versions/node/v24/bin/pragma",
38
+ ),
39
+ ).toBe("npm");
40
+ });
41
+
42
+ it("defaults to npm for unknown paths", () => {
43
+ expect(detectPackageManager("/usr/local/bin/pragma")).toBe("npm");
44
+ });
45
+
46
+ it("defaults to npm when path cannot be resolved", () => {
47
+ expect(detectPackageManager("/nonexistent/path/to/pragma")).toBe("npm");
48
+ });
49
+ });
50
+
51
+ describe("PM_COMMANDS", () => {
52
+ it("generates correct bun install command", () => {
53
+ expect(PM_COMMANDS.bun.install("@canonical/pragma-cli")).toBe(
54
+ "bun add -g @canonical/pragma-cli",
55
+ );
56
+ });
57
+
58
+ it("generates correct npm update command", () => {
59
+ expect(PM_COMMANDS.npm.update("@canonical/pragma-cli")).toBe(
60
+ "npm update -g @canonical/pragma-cli",
61
+ );
62
+ });
63
+
64
+ it("generates correct pnpm install command", () => {
65
+ expect(PM_COMMANDS.pnpm.install("@canonical/pragma-cli")).toBe(
66
+ "pnpm add -g @canonical/pragma-cli",
67
+ );
68
+ });
69
+
70
+ it("generates correct yarn install command", () => {
71
+ expect(PM_COMMANDS.yarn.install("@canonical/pragma-cli")).toBe(
72
+ "yarn global add @canonical/pragma-cli",
73
+ );
74
+ });
75
+
76
+ it("generates correct yarn update command", () => {
77
+ expect(PM_COMMANDS.yarn.update("@canonical/pragma-cli")).toBe(
78
+ "yarn global upgrade @canonical/pragma-cli",
79
+ );
80
+ });
81
+ });
@@ -0,0 +1,30 @@
1
+ import { realpathSync } from "node:fs";
2
+ import type { PackageManager } from "./types.js";
3
+
4
+ /**
5
+ * Detect which package manager installed pragma from the binary path.
6
+ *
7
+ * Inspects the resolved symlink target for known directory patterns
8
+ * (`.bun/`, `/pnpm/`, `/yarn/`). Falls back to `"npm"`.
9
+ *
10
+ * @param binPath - Path to the pragma binary (defaults to `process.argv[1]`).
11
+ * @returns Detected package manager identifier.
12
+ *
13
+ * @note Impure — resolves real filesystem path.
14
+ */
15
+ export default function detectPackageManager(
16
+ binPath: string = process.argv[1] ?? "",
17
+ ): PackageManager {
18
+ let resolved: string;
19
+ try {
20
+ resolved = realpathSync(binPath);
21
+ } catch {
22
+ resolved = binPath;
23
+ }
24
+
25
+ if (resolved.includes("/.bun/")) return "bun";
26
+ if (resolved.includes("/pnpm/")) return "pnpm";
27
+ if (resolved.includes("/yarn/") || resolved.includes("/.yarn/"))
28
+ return "yarn";
29
+ return "npm";
30
+ }
@@ -0,0 +1,6 @@
1
+ /** @module Package manager detection and command templates. */
2
+ export { PM_COMMANDS } from "./constants.js";
3
+ export { default as detectInstallSource } from "./detectInstallSource.js";
4
+ export { default as detectLocalInstall } from "./detectLocalInstall.js";
5
+ export { default as detectPackageManager } from "./detectPackageManager.js";
6
+ export type { InstallSource, PackageManager } from "./types.js";
@@ -0,0 +1,12 @@
1
+ /** Supported Node.js package managers. */
2
+ type PackageManager = "bun" | "npm" | "pnpm" | "yarn";
3
+
4
+ interface InstallSource {
5
+ readonly packageManager: PackageManager;
6
+ readonly scope: "global" | "local";
7
+ readonly label: string;
8
+ }
9
+
10
+ export type { PackageManager };
11
+
12
+ export type { InstallSource };
@@ -0,0 +1,37 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
+ import type { PragmaContext } from "../domains/shared/context.js";
3
+ import type { PragmaRuntime } from "../domains/shared/runtime.js";
4
+ import createTestRuntime from "../testing/helpers/createTestRuntime.js";
5
+ import collectCommands from "./collectCommands.js";
6
+
7
+ let runtime: PragmaRuntime;
8
+
9
+ beforeAll(async () => {
10
+ runtime = await createTestRuntime();
11
+ });
12
+
13
+ afterAll(() => {
14
+ runtime.dispose();
15
+ });
16
+
17
+ function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
18
+ return {
19
+ ...runtime,
20
+ globalFlags: { llm: false, format: "text" as const, verbose: false },
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ describe("collectCommands", () => {
26
+ it("includes block, ontology, and graph command paths", () => {
27
+ const commands = collectCommands(makeCtx());
28
+ const paths = commands.map((command) => command.path.join(" "));
29
+
30
+ expect(paths).toContain("block list");
31
+ expect(paths).toContain("block lookup");
32
+ expect(paths).toContain("ontology list");
33
+ expect(paths).toContain("ontology show");
34
+ expect(paths).toContain("graph query");
35
+ expect(paths).toContain("graph inspect");
36
+ });
37
+ });