@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,80 @@
1
+ import type { Store } from "@canonical/ke";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import { createTestStore, DS_ALL_TTL } from "#testing";
4
+ import type { FilterConfig } from "../../shared/types.js";
5
+ import listBlocks from "./list.js";
6
+
7
+ let store: Store;
8
+ let cleanup: () => void;
9
+
10
+ beforeAll(async () => {
11
+ const result = await createTestStore({ ttl: DS_ALL_TTL });
12
+ store = result.store;
13
+ cleanup = result.cleanup;
14
+ });
15
+
16
+ afterAll(() => cleanup());
17
+
18
+ const noFilter: FilterConfig = { tier: undefined, channel: "prerelease" };
19
+ const normalChannel: FilterConfig = { tier: undefined, channel: "normal" };
20
+ const globalTier: FilterConfig = { tier: "global", channel: "prerelease" };
21
+ const lxdTier: FilterConfig = { tier: "apps/lxd", channel: "normal" };
22
+
23
+ describe("listBlocks", () => {
24
+ it("returns all blocks with no tier filter and prerelease channel", async () => {
25
+ const result = await listBlocks(store, noFilter);
26
+ const names = result.map((c) => c.name);
27
+ expect(names).toContain("Button");
28
+ expect(names).toContain("Card");
29
+ expect(names).toContain("LXD Panel");
30
+ expect(names).toContain("Beta Widget");
31
+ });
32
+
33
+ it("excludes beta blocks with normal channel", async () => {
34
+ const result = await listBlocks(store, normalChannel);
35
+ const names = result.map((c) => c.name);
36
+ expect(names).toContain("Button");
37
+ expect(names).toContain("Card");
38
+ expect(names).toContain("LXD Panel");
39
+ expect(names).not.toContain("Beta Widget");
40
+ });
41
+
42
+ it("filters by tier (global only)", async () => {
43
+ const result = await listBlocks(store, globalTier);
44
+ const names = result.map((c) => c.name);
45
+ expect(names).toContain("Button");
46
+ expect(names).toContain("Card");
47
+ expect(names).toContain("Beta Widget");
48
+ expect(names).not.toContain("LXD Panel");
49
+ });
50
+
51
+ it("applies orthogonal tier + channel filters", async () => {
52
+ const result = await listBlocks(store, lxdTier);
53
+ const names = result.map((c) => c.name);
54
+ expect(names).toContain("Button");
55
+ expect(names).toContain("Card");
56
+ expect(names).toContain("LXD Panel");
57
+ expect(names).not.toContain("Beta Widget");
58
+ });
59
+
60
+ it("returns sorted by name", async () => {
61
+ const result = await listBlocks(store, noFilter);
62
+ const names = result.map((c) => c.name);
63
+ const sorted = [...names].sort();
64
+ expect(names).toEqual(sorted);
65
+ });
66
+
67
+ it("includes modifier names on Button", async () => {
68
+ const result = await listBlocks(store, noFilter);
69
+ const button = result.find((c) => c.name === "Button");
70
+ expect(button).toBeDefined();
71
+ expect(button?.modifiers).toContain("importance");
72
+ expect(button?.modifiers).toContain("density");
73
+ });
74
+
75
+ it("counts tokens on Button", async () => {
76
+ const result = await listBlocks(store, noFilter);
77
+ const button = result.find((c) => c.name === "Button");
78
+ expect(button?.tokenCount).toBe(1);
79
+ });
80
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Lists all blocks visible under the given tier and channel filters.
3
+ *
4
+ * Returns summary-level data (name, tier, modifiers, node/token counts)
5
+ * for each matching block, ordered alphabetically by name.
6
+ *
7
+ * @param store - ke store to query
8
+ * @param filters - tier and channel filter configuration
9
+ * @returns array of block summaries, empty when none match
10
+ * @note Queries ke store
11
+ */
12
+
13
+ import type { Store, URI } from "@canonical/ke";
14
+ import { buildQuery } from "../../shared/buildQuery.js";
15
+ import extractLocalName from "../../shared/extractLocalName.js";
16
+ import { buildFilters } from "../../shared/filters/buildFilters.js";
17
+ import { P } from "../../shared/prefixes.js";
18
+ import type { BlockSummary, FilterConfig } from "../../shared/types.js";
19
+
20
+ function normalizeBlockType(value: string | undefined): BlockSummary["type"] {
21
+ const localName = extractLocalName(value ?? "").toLowerCase();
22
+
23
+ switch (localName) {
24
+ case "pattern":
25
+ case "layout":
26
+ case "subcomponent":
27
+ return localName;
28
+ default:
29
+ return "component";
30
+ }
31
+ }
32
+
33
+ export default async function listBlocks(
34
+ store: Store,
35
+ filters: FilterConfig,
36
+ ): Promise<BlockSummary[]> {
37
+ const filterClauses = buildFilters(filters);
38
+
39
+ const result = await store.query(
40
+ buildQuery(`
41
+ SELECT ?component ?type ?name ?tier
42
+ (GROUP_CONCAT(DISTINCT ?modName; separator="|") AS ?modifiers)
43
+ (COUNT(DISTINCT ?node) AS ?nodeCount)
44
+ (COUNT(DISTINCT ?token) AS ?tokenCount)
45
+ WHERE {
46
+ VALUES ?type { ${P.ds}Component ${P.ds}Pattern ${P.ds}Layout ${P.ds}Subcomponent }
47
+ ?component a ?type ;
48
+ ${P.ds}name ?name ;
49
+ ${P.ds}tier ?tier .
50
+ ${filterClauses}
51
+ OPTIONAL { ?component ${P.ds}hasModifierFamily ?mod . ?mod ${P.ds}name ?modName }
52
+ OPTIONAL { ?component ${P.ds}anatomyNode ?node }
53
+ OPTIONAL { ?component ${P.ds}usesToken ?token }
54
+ }
55
+ GROUP BY ?component ?type ?name ?tier
56
+ ORDER BY ?name
57
+ `),
58
+ );
59
+
60
+ if (result.type !== "select") return [];
61
+
62
+ return result.bindings.map((b) => ({
63
+ uri: (b.component ?? "") as URI,
64
+ name: b.name ?? "",
65
+ type: normalizeBlockType(b.type),
66
+ tier: extractLocalName(b.tier ?? ""),
67
+ modifiers: b.modifiers ? b.modifiers.split("|").filter(Boolean) : [],
68
+ implementations: [],
69
+ nodeCount: Number.parseInt(b.nodeCount ?? "0", 10) || 0,
70
+ tokenCount: Number.parseInt(b.tokenCount ?? "0", 10) || 0,
71
+ }));
72
+ }
@@ -0,0 +1,108 @@
1
+ import type { Store } from "@canonical/ke";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import { PragmaError } from "#error";
4
+ import { createTestStore, DS_ALL_TTL } from "#testing";
5
+ import type { FilterConfig } from "../../shared/types.js";
6
+ import lookupBlock from "./lookup.js";
7
+
8
+ let store: Store;
9
+ let cleanup: () => void;
10
+
11
+ beforeAll(async () => {
12
+ const result = await createTestStore({ ttl: DS_ALL_TTL });
13
+ store = result.store;
14
+ cleanup = result.cleanup;
15
+ });
16
+
17
+ afterAll(() => cleanup());
18
+
19
+ const noFilter: FilterConfig = { tier: undefined, channel: "prerelease" };
20
+ const normalChannel: FilterConfig = { tier: undefined, channel: "normal" };
21
+
22
+ describe("lookupBlock", () => {
23
+ it("returns detailed data for Button", async () => {
24
+ const result = await lookupBlock(store, "Button", noFilter);
25
+ expect(result.name).toBe("Button");
26
+ expect(result.tier).toBe("global");
27
+ expect(result.modifierValues.length).toBeGreaterThan(0);
28
+
29
+ const importance = result.modifierValues.find(
30
+ (m) => m.family === "importance",
31
+ );
32
+ expect(importance?.values).toContain("primary");
33
+ expect(importance?.values).toContain("secondary");
34
+ });
35
+
36
+ it("includes implementation paths", async () => {
37
+ const result = await lookupBlock(store, "Button", noFilter);
38
+ const react = result.implementationPaths.find(
39
+ (i) => i.framework === "react",
40
+ );
41
+ expect(react).toBeDefined();
42
+ expect(react?.path).toContain("Button.tsx");
43
+ });
44
+
45
+ it("includes token references", async () => {
46
+ const result = await lookupBlock(store, "Button", noFilter);
47
+ expect(result.tokens.length).toBe(1);
48
+ expect(result.tokens[0].name).toBe("color.primary");
49
+ });
50
+
51
+ it("populates nodeCount from anatomy nodes", async () => {
52
+ const result = await lookupBlock(store, "Button", noFilter);
53
+ expect(result.nodeCount).toBe(3);
54
+ expect(result.anatomy?.root.name).toBe("button");
55
+ });
56
+
57
+ it("returns enriched summary, properties, and subcomponents", async () => {
58
+ const result = await lookupBlock(store, "Button", noFilter);
59
+ expect(result.summary).toContain("Primary action trigger");
60
+ expect(result.whenToUse).toContain("high-priority actions");
61
+ expect(result.properties.map((property) => property.name)).toEqual(
62
+ expect.arrayContaining(["disabled", "loading"]),
63
+ );
64
+ expect(result.subcomponents[0]?.name).toBe("Button Icon");
65
+ expect(result.modifierFamilies.map((family) => family.name)).toEqual(
66
+ expect.arrayContaining(["importance", "density"]),
67
+ );
68
+ });
69
+
70
+ it("throws PragmaError.notFound for unknown block", async () => {
71
+ await expect(lookupBlock(store, "NonExistent", noFilter)).rejects.toThrow(
72
+ PragmaError,
73
+ );
74
+
75
+ try {
76
+ await lookupBlock(store, "NonExistent", noFilter);
77
+ } catch (e) {
78
+ expect(e).toBeInstanceOf(PragmaError);
79
+ expect((e as PragmaError).code).toBe("ENTITY_NOT_FOUND");
80
+ }
81
+ });
82
+
83
+ it("respects channel filter", async () => {
84
+ await expect(
85
+ lookupBlock(store, "Beta Widget", normalChannel),
86
+ ).rejects.toThrow(PragmaError);
87
+ });
88
+
89
+ it("resolves prefixed IRIs", async () => {
90
+ const result = await lookupBlock(
91
+ store,
92
+ "ds:global.component.button",
93
+ noFilter,
94
+ );
95
+ expect(result.name).toBe("Button");
96
+ expect(result.uri).toBe("https://ds.canonical.com/global.component.button");
97
+ });
98
+
99
+ it("resolves full IRIs", async () => {
100
+ const result = await lookupBlock(
101
+ store,
102
+ "https://ds.canonical.com/global.component.button",
103
+ noFilter,
104
+ );
105
+ expect(result.name).toBe("Button");
106
+ expect(result.uri).toBe("https://ds.canonical.com/global.component.button");
107
+ });
108
+ });
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Look up detailed information for a single block.
3
+ *
4
+ * Queries the base block, then resolves related modifiers, implementations,
5
+ * tokens, anatomy nodes, blank-node properties, and direct subcomponents.
6
+ * The result is assembled into a {@link BlockDetailed} object.
7
+ *
8
+ * @param store - ke store to query
9
+ * @param name - block name (e.g. "Button")
10
+ * @param filters - tier and channel filter configuration
11
+ * @returns fully populated block detail
12
+ * @throws PragmaError.notFound if the block does not exist
13
+ * @note Queries ke store
14
+ */
15
+
16
+ import type { Store, URI } from "@canonical/ke";
17
+ import { escapeSparqlValue } from "@canonical/ke";
18
+ import { PragmaError } from "#error";
19
+ import resolveUri from "../../graph/helpers/resolveUri.js";
20
+ import { buildQuery } from "../../shared/buildQuery.js";
21
+ import extractLocalName from "../../shared/extractLocalName.js";
22
+ import { buildFilters } from "../../shared/filters/buildFilters.js";
23
+ import { P } from "../../shared/prefixes.js";
24
+ import type {
25
+ BlockDetailed,
26
+ FilterConfig,
27
+ StandardRef,
28
+ TokenRef,
29
+ } from "../../shared/types.js";
30
+
31
+ function normalizeBlockType(value: string | undefined): BlockDetailed["type"] {
32
+ const localName = extractLocalName(value ?? "").toLowerCase();
33
+
34
+ switch (localName) {
35
+ case "pattern":
36
+ case "layout":
37
+ case "subcomponent":
38
+ return localName;
39
+ default:
40
+ return "component";
41
+ }
42
+ }
43
+
44
+ export default async function lookupBlock(
45
+ store: Store,
46
+ nameOrUri: string,
47
+ filters: FilterConfig,
48
+ ): Promise<BlockDetailed> {
49
+ const filterClauses = buildFilters(filters);
50
+ const subjectClause = buildLookupSubjectClause(nameOrUri, store.prefixes);
51
+ const bindClause =
52
+ subjectClause === null ? "" : `BIND(${subjectClause} AS ?component)`;
53
+ const matchClause =
54
+ subjectClause === null
55
+ ? `?component ${P.ds}name ${escapeSparqlValue(nameOrUri)} .`
56
+ : "";
57
+
58
+ // Base query: find the block
59
+ const baseResult = await store.query(
60
+ buildQuery(`
61
+ SELECT ?component ?name ?type ?tier ?summary ?whenToUse ?whenNotToUse ?guidelines ?anatomyDsl ?anatomyClassic ?figmaLink
62
+ WHERE {
63
+ VALUES ?type { ${P.ds}Component ${P.ds}Pattern ${P.ds}Layout ${P.ds}Subcomponent }
64
+ ${bindClause}
65
+ ?component a ?type ;
66
+ ${P.ds}name ?name ;
67
+ ${P.ds}tier ?tier .
68
+ ${matchClause}
69
+ OPTIONAL { ?component ${P.ds}summary ?summary }
70
+ OPTIONAL { ?component ${P.ds}whenToUse ?whenToUse }
71
+ OPTIONAL { ?component ${P.ds}whenNotToUse ?whenNotToUse }
72
+ OPTIONAL { ?component ${P.ds}guidelines ?guidelines }
73
+ OPTIONAL { ?component ${P.ds}anatomyDsl ?anatomyDsl }
74
+ OPTIONAL { ?component ${P.ds}anatomyClassic ?anatomyClassic }
75
+ OPTIONAL { ?component ${P.ds}figmaLink ?figmaLink }
76
+ ${filterClauses}
77
+ }
78
+ LIMIT 1
79
+ `),
80
+ );
81
+
82
+ if (baseResult.type !== "select" || baseResult.bindings.length === 0) {
83
+ throw PragmaError.notFound("block", nameOrUri, {
84
+ recovery: {
85
+ message: "List available blocks.",
86
+ cli: "pragma block list",
87
+ mcp: { tool: "block_list" },
88
+ },
89
+ });
90
+ }
91
+
92
+ // Safe: length check above guarantees bindings[0] exists
93
+ const base = baseResult.bindings[0] as (typeof baseResult.bindings)[number];
94
+ const componentUri = base.component;
95
+
96
+ // Modifier families with values
97
+ const modResult = await store.query(
98
+ buildQuery(`
99
+ SELECT ?family ?modName ?value
100
+ WHERE {
101
+ <${componentUri}> ${P.ds}hasModifierFamily ?family .
102
+ ?family ${P.ds}name ?modName .
103
+ OPTIONAL {
104
+ ?mod a ${P.ds}Modifier ;
105
+ ${P.ds}modifierFamily ?family ;
106
+ ${P.ds}name ?value .
107
+ }
108
+ }
109
+ ORDER BY ?modName ?value
110
+ `),
111
+ );
112
+
113
+ const modifierMap = new Map<string, string[]>();
114
+ const modifierFamilyUriMap = new Map<string, URI>();
115
+ if (modResult.type === "select") {
116
+ for (const b of modResult.bindings) {
117
+ const existing = modifierMap.get(b.modName ?? "") ?? [];
118
+ if (b.value) {
119
+ existing.push(b.value);
120
+ }
121
+ modifierMap.set(b.modName ?? "", existing);
122
+
123
+ if (b.modName && b.family) {
124
+ modifierFamilyUriMap.set(b.modName, b.family as URI);
125
+ }
126
+ }
127
+ }
128
+
129
+ const modifierFamilies = [...modifierMap.entries()].map(([name, values]) => ({
130
+ uri: modifierFamilyUriMap.get(name) ?? ("" as URI),
131
+ name,
132
+ values,
133
+ }));
134
+
135
+ // Implementations
136
+ const implResult = await store.query(
137
+ buildQuery(`
138
+ SELECT ?framework ?path
139
+ WHERE {
140
+ ?lib ${P.ds}hasImplementation ?impl .
141
+ ?impl ${P.ds}implementsBlock <${componentUri}> ;
142
+ ${P.ds}headLink ?path .
143
+ ?lib ${P.ds}platform ?framework .
144
+ }
145
+ ORDER BY ?framework
146
+ `),
147
+ );
148
+
149
+ const implementationPaths =
150
+ implResult.type === "select"
151
+ ? implResult.bindings.map((b) => ({
152
+ framework: b.framework ?? "",
153
+ path: b.path ?? "",
154
+ }))
155
+ : [];
156
+
157
+ // Build implementations summary (which frameworks are available)
158
+ const frameworkSet = new Set(implementationPaths.map((i) => i.framework));
159
+ const allFrameworks = await listAllFrameworks(store);
160
+ const implementations = allFrameworks.map((fw) => ({
161
+ framework: fw,
162
+ available: frameworkSet.has(fw),
163
+ }));
164
+
165
+ // Tokens
166
+ const tokenResult = await store.query(
167
+ buildQuery(`
168
+ SELECT ?token ?tokenId
169
+ WHERE {
170
+ <${componentUri}> ${P.ds}usesToken ?token .
171
+ ?token ${P.ds}tokenId ?tokenId .
172
+ }
173
+ ORDER BY ?tokenId
174
+ `),
175
+ );
176
+
177
+ const tokens: TokenRef[] =
178
+ tokenResult.type === "select"
179
+ ? tokenResult.bindings.map((b) => ({
180
+ uri: (b.token ?? "") as URI,
181
+ name: b.tokenId ?? "",
182
+ }))
183
+ : [];
184
+
185
+ // Anatomy nodes
186
+ const anatomyResult = await store.query(
187
+ buildQuery(`
188
+ SELECT DISTINCT ?node ?name
189
+ WHERE {
190
+ <${componentUri}> ${P.ds}anatomyNode ?node .
191
+ OPTIONAL { ?node ${P.ds}name ?name }
192
+ }
193
+ ORDER BY ?node
194
+ `),
195
+ );
196
+
197
+ const anatomyNodes =
198
+ anatomyResult.type === "select"
199
+ ? anatomyResult.bindings.map((binding) => ({
200
+ uri: (binding.node ?? "") as URI,
201
+ name: binding.name,
202
+ }))
203
+ : [];
204
+ const nodeCount = anatomyNodes.length;
205
+ const anatomy = buildAnatomyTree(anatomyNodes);
206
+
207
+ // Component properties from blank nodes
208
+ const propertiesResult = await store.query(
209
+ buildQuery(`
210
+ SELECT ?property ?name ?propertyType ?optional ?defaultValue ?constraints
211
+ WHERE {
212
+ <${componentUri}> ${P.ds}hasProperty ?property .
213
+ ?property ${P.ds}name ?name .
214
+ OPTIONAL { ?property ${P.ds}propertyType ?propertyType }
215
+ OPTIONAL { ?property ${P.ds}optional ?optional }
216
+ OPTIONAL { ?property ${P.ds}defaultValue ?defaultValue }
217
+ OPTIONAL { ?property ${P.ds}constraints ?constraints }
218
+ }
219
+ ORDER BY ?name
220
+ `),
221
+ );
222
+
223
+ const properties =
224
+ propertiesResult.type === "select"
225
+ ? propertiesResult.bindings.map((binding) => ({
226
+ name: binding.name ?? "",
227
+ propertyType: binding.propertyType ?? "",
228
+ optional: binding.optional === "true",
229
+ defaultValue: binding.defaultValue ?? null,
230
+ constraints: binding.constraints ?? null,
231
+ }))
232
+ : [];
233
+
234
+ // Direct subcomponents
235
+ const subcomponentsResult = await store.query(
236
+ buildQuery(`
237
+ SELECT ?subcomponent ?name
238
+ WHERE {
239
+ <${componentUri}> ${P.ds}hasSubcomponent ?subcomponent .
240
+ ?subcomponent ${P.ds}name ?name .
241
+ }
242
+ ORDER BY ?name
243
+ `),
244
+ );
245
+
246
+ const subcomponents =
247
+ subcomponentsResult.type === "select"
248
+ ? subcomponentsResult.bindings.map((binding) => ({
249
+ uri: (binding.subcomponent ?? "") as URI,
250
+ name: binding.name ?? "",
251
+ }))
252
+ : [];
253
+
254
+ // Standards (not linked directly to blocks in current ontology;
255
+ // will be populated via @follows in v0.3)
256
+ const standards: StandardRef[] = [];
257
+
258
+ return {
259
+ uri: (componentUri ?? "") as URI,
260
+ name: base.name ?? nameOrUri,
261
+ type: normalizeBlockType(base.type),
262
+ tier: extractLocalName(base.tier ?? ""),
263
+ modifiers: [...modifierMap.keys()],
264
+ implementations,
265
+ summary: base.summary ?? null,
266
+ nodeCount,
267
+ tokenCount: tokens.length,
268
+ whenToUse: base.whenToUse ?? null,
269
+ whenNotToUse: base.whenNotToUse ?? null,
270
+ guidelines: base.guidelines ?? null,
271
+ anatomyDsl: base.anatomyDsl ?? null,
272
+ anatomyClassic: base.anatomyClassic ?? null,
273
+ figmaLink: base.figmaLink ?? null,
274
+ anatomy,
275
+ modifierValues: [...modifierMap.entries()].map(([family, values]) => ({
276
+ family,
277
+ values,
278
+ })),
279
+ modifierFamilies,
280
+ properties,
281
+ subcomponents,
282
+ implementationPaths,
283
+ tokens,
284
+ standards,
285
+ };
286
+ }
287
+
288
+ function buildLookupSubjectClause(
289
+ query: string,
290
+ prefixes: Readonly<Record<string, string>>,
291
+ ): string | null {
292
+ if (!looksLikeUri(query)) {
293
+ return null;
294
+ }
295
+
296
+ return `<${resolveUri(query, prefixes)}>`;
297
+ }
298
+
299
+ function looksLikeUri(query: string): boolean {
300
+ return (
301
+ query.startsWith("http://") ||
302
+ query.startsWith("https://") ||
303
+ query.includes(":")
304
+ );
305
+ }
306
+
307
+ function buildAnatomyTree(
308
+ nodes: readonly { uri: URI; name?: string }[],
309
+ ): BlockDetailed["anatomy"] {
310
+ if (nodes.length === 0) {
311
+ return null;
312
+ }
313
+
314
+ const sorted = [...nodes].sort(
315
+ (left, right) => scoreAnatomyNode(right) - scoreAnatomyNode(left),
316
+ );
317
+ const [root, ...children] = sorted;
318
+ return {
319
+ root: {
320
+ name: root?.name ?? extractLocalName(root?.uri ?? "node"),
321
+ type: root?.name ? "named" : "anonymous",
322
+ children: children.map((node) => ({
323
+ name: node.name ?? extractLocalName(node.uri),
324
+ type: node.name ? "named" : "anonymous",
325
+ children: [],
326
+ })),
327
+ },
328
+ };
329
+ }
330
+
331
+ function scoreAnatomyNode(node: { uri: URI; name?: string }): number {
332
+ const uri = String(node.uri);
333
+ if (uri.endsWith(".root") || node.name === "button" || node.name === "card") {
334
+ return 2;
335
+ }
336
+ if (node.name) {
337
+ return 1;
338
+ }
339
+ return 0;
340
+ }
341
+
342
+ /**
343
+ * Get all known implementation frameworks from the store.
344
+ */
345
+ async function listAllFrameworks(store: Store): Promise<string[]> {
346
+ const result = await store.query(
347
+ buildQuery(`
348
+ SELECT DISTINCT ?framework
349
+ WHERE {
350
+ ?lib a ${P.ds}ImplementationLibrary ;
351
+ ${P.ds}platform ?framework .
352
+ }
353
+ ORDER BY ?framework
354
+ `),
355
+ );
356
+
357
+ if (result.type !== "select") return [];
358
+ return result.bindings.map((b) => b.framework ?? "");
359
+ }
@@ -0,0 +1,19 @@
1
+ import { PragmaError } from "#error";
2
+ import { describeFilters } from "../../shared/filters/index.js";
3
+ import type { FilterConfig } from "../../shared/types.js";
4
+
5
+ export default function blockEmptyError(
6
+ filters: FilterConfig,
7
+ allTiers?: boolean,
8
+ ): PragmaError {
9
+ return PragmaError.emptyResults("block", {
10
+ filters: describeFilters(filters),
11
+ recovery: allTiers
12
+ ? undefined
13
+ : {
14
+ message: "Widen the search to show all tiers.",
15
+ cli: "pragma block list --all-tiers",
16
+ mcp: { tool: "block_list", params: { allTiers: true } },
17
+ },
18
+ });
19
+ }
@@ -0,0 +1,12 @@
1
+ import type { PragmaRuntime } from "../../shared/runtime.js";
2
+ import type { FilterConfig } from "../../shared/types.js";
3
+
4
+ export default function buildBlockFilters(
5
+ rt: Pick<PragmaRuntime, "config">,
6
+ allTiers?: boolean,
7
+ ): FilterConfig {
8
+ return {
9
+ tier: allTiers ? undefined : rt.config.tier,
10
+ channel: rt.config.channel,
11
+ };
12
+ }
@@ -0,0 +1,37 @@
1
+ import type { PragmaRuntime } from "../../shared/runtime.js";
2
+ import type {
3
+ BlockDetailed,
4
+ BlockSummary,
5
+ Disclosure,
6
+ FilterConfig,
7
+ } from "../../shared/types.js";
8
+ import { lookupBlock } from "../operations/index.js";
9
+ import type { BlockListDigest } from "../types.js";
10
+
11
+ export default async function enrichBlocks(
12
+ summaries: readonly BlockSummary[],
13
+ store: Pick<PragmaRuntime, "store">["store"],
14
+ filters: FilterConfig,
15
+ disclosure: Disclosure,
16
+ ): Promise<readonly (BlockListDigest | BlockDetailed)[]> {
17
+ const details = await Promise.all(
18
+ summaries.map((summary) => lookupBlock(store, summary.name, filters)),
19
+ );
20
+
21
+ if (disclosure.level === "detailed") {
22
+ return details;
23
+ }
24
+
25
+ return details.map((detail) => ({
26
+ uri: detail.uri,
27
+ name: detail.name,
28
+ type: detail.type,
29
+ tier: detail.tier,
30
+ summary: detail.summary,
31
+ modifiers: detail.modifiers,
32
+ implementations: detail.implementations,
33
+ nodeCount: detail.nodeCount,
34
+ tokenCount: detail.tokenCount,
35
+ implementationPaths: detail.implementationPaths,
36
+ }));
37
+ }
@@ -0,0 +1,5 @@
1
+ export { default as blockEmptyError } from "./blockEmptyError.js";
2
+ export { default as buildBlockFilters } from "./buildBlockFilters.js";
3
+ export { default as enrichBlocks } from "./enrichBlocks.js";
4
+ export { default as resolveBlockList } from "./resolveBlockList.js";
5
+ export { default as resolveBlockLookup } from "./resolveBlockLookup.js";