@canonical/pragma-cli 0.38.0 → 0.40.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 (330) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +14 -14
  3. package/dist/pragma.conf.js +273 -176
  4. package/dist/pragma.conf.js.map +1 -1
  5. package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
  6. package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
  7. package/dist/src/capabilities/capabilities/catalog.js +47 -51
  8. package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
  9. package/dist/src/capabilities/capabilities/index.js +2 -0
  10. package/dist/src/capabilities/capabilities/index.js.map +1 -1
  11. package/dist/src/capabilities/capabilities/types.js +5 -5
  12. package/dist/src/capabilities/colophon/colophon.render.js +7 -4
  13. package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
  14. package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
  15. package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
  16. package/dist/src/capabilities/config/get.verb.js +2 -0
  17. package/dist/src/capabilities/config/get.verb.js.map +1 -1
  18. package/dist/src/capabilities/config/runField.js +2 -2
  19. package/dist/src/capabilities/config/runField.js.map +1 -1
  20. package/dist/src/capabilities/config/set.verb.js +2 -0
  21. package/dist/src/capabilities/config/set.verb.js.map +1 -1
  22. package/dist/src/capabilities/config/show.verb.js +1 -0
  23. package/dist/src/capabilities/config/show.verb.js.map +1 -1
  24. package/dist/src/capabilities/config/unset.verb.js +2 -0
  25. package/dist/src/capabilities/config/unset.verb.js.map +1 -1
  26. package/dist/src/capabilities/create/constants.js +2 -2
  27. package/dist/src/capabilities/create/create.verb.js +14 -4
  28. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  29. package/dist/src/capabilities/distribution.js +3 -1
  30. package/dist/src/capabilities/distribution.js.map +1 -1
  31. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
  32. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
  33. package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
  34. package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
  35. package/dist/src/capabilities/graph/connect.verb.js +2 -0
  36. package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
  37. package/dist/src/capabilities/graph/inspect.render.js +7 -1
  38. package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
  39. package/dist/src/capabilities/graph/inspect.verb.js +2 -0
  40. package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
  41. package/dist/src/capabilities/graph/query.render.js +37 -5
  42. package/dist/src/capabilities/graph/query.render.js.map +1 -1
  43. package/dist/src/capabilities/graph/query.verb.js +2 -0
  44. package/dist/src/capabilities/graph/query.verb.js.map +1 -1
  45. package/dist/src/capabilities/graph/queryText.js +28 -0
  46. package/dist/src/capabilities/graph/queryText.js.map +1 -1
  47. package/dist/src/capabilities/graph/runConnect.js +3 -3
  48. package/dist/src/capabilities/graph/runConnect.js.map +1 -1
  49. package/dist/src/capabilities/graph/runQuery.js +8 -6
  50. package/dist/src/capabilities/graph/runQuery.js.map +1 -1
  51. package/dist/src/capabilities/info/info.verb.js +2 -0
  52. package/dist/src/capabilities/info/info.verb.js.map +1 -1
  53. package/dist/src/capabilities/meta/mcp.verb.js +1 -0
  54. package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
  55. package/dist/src/capabilities/ontology/verbs.js +7 -10
  56. package/dist/src/capabilities/ontology/verbs.js.map +1 -1
  57. package/dist/src/capabilities/prompt/verbs.js +5 -4
  58. package/dist/src/capabilities/prompt/verbs.js.map +1 -1
  59. package/dist/src/capabilities/resources/provider.js +6 -3
  60. package/dist/src/capabilities/resources/provider.js.map +1 -1
  61. package/dist/src/capabilities/setup/setup.verb.js +13 -5
  62. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  63. package/dist/src/capabilities/shared/calls.js +16 -0
  64. package/dist/src/capabilities/shared/calls.js.map +1 -0
  65. package/dist/src/capabilities/skill/render.js +7 -5
  66. package/dist/src/capabilities/skill/render.js.map +1 -1
  67. package/dist/src/capabilities/skill/verbs.js +5 -4
  68. package/dist/src/capabilities/skill/verbs.js.map +1 -1
  69. package/dist/src/capabilities/sources/collectStatus.js +6 -1
  70. package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
  71. package/dist/src/capabilities/sources/index.js +4 -3
  72. package/dist/src/capabilities/sources/index.js.map +1 -1
  73. package/dist/src/capabilities/sources/reset.render.js +37 -0
  74. package/dist/src/capabilities/sources/reset.render.js.map +1 -0
  75. package/dist/src/capabilities/sources/reset.verb.js +51 -0
  76. package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
  77. package/dist/src/capabilities/sources/runReset.js +59 -0
  78. package/dist/src/capabilities/sources/runReset.js.map +1 -0
  79. package/dist/src/capabilities/sources/runUpdate.js +7 -6
  80. package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
  81. package/dist/src/capabilities/sources/status.render.js +24 -8
  82. package/dist/src/capabilities/sources/status.render.js.map +1 -1
  83. package/dist/src/capabilities/sources/status.verb.js +1 -0
  84. package/dist/src/capabilities/sources/status.verb.js.map +1 -1
  85. package/dist/src/capabilities/sources/update.verb.js +2 -0
  86. package/dist/src/capabilities/sources/update.verb.js.map +1 -1
  87. package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
  88. package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
  89. package/dist/src/capabilities/version/version.verb.js +1 -0
  90. package/dist/src/capabilities/version/version.verb.js.map +1 -1
  91. package/dist/src/constants.js +4 -3
  92. package/dist/src/constants.js.map +1 -1
  93. package/dist/src/kernel/error/index.js +1 -1
  94. package/dist/src/kernel/error/index.js.map +1 -1
  95. package/dist/src/kernel/error/recovery.js +24 -42
  96. package/dist/src/kernel/error/recovery.js.map +1 -1
  97. package/dist/src/kernel/packs/collect.js +123 -9
  98. package/dist/src/kernel/packs/collect.js.map +1 -1
  99. package/dist/src/kernel/packs/compile.js +74 -15
  100. package/dist/src/kernel/packs/compile.js.map +1 -1
  101. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
  102. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
  103. package/dist/src/kernel/packs/iri.js +2 -4
  104. package/dist/src/kernel/packs/iri.js.map +1 -1
  105. package/dist/src/kernel/packs/renderPack.js +90 -50
  106. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  107. package/dist/src/kernel/packs/resolveEntity.js +202 -90
  108. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  109. package/dist/src/kernel/packs/runBodies.js +89 -30
  110. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  111. package/dist/src/kernel/packs/schema.js +66 -12
  112. package/dist/src/kernel/packs/schema.js.map +1 -1
  113. package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
  114. package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
  115. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
  116. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  117. package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
  118. package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
  119. package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
  120. package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
  121. package/dist/src/kernel/packs/storyRules.js +31 -4
  122. package/dist/src/kernel/packs/storyRules.js.map +1 -1
  123. package/dist/src/kernel/packs/tierScope.js +3 -3
  124. package/dist/src/kernel/packs/tierScope.js.map +1 -1
  125. package/dist/src/kernel/packs/types.js +4 -0
  126. package/dist/src/kernel/packs/types.js.map +1 -1
  127. package/dist/src/kernel/project/cli/buildProgram.js +4 -0
  128. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  129. package/dist/src/kernel/project/cli/dispatch.js +32 -8
  130. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  131. package/dist/src/kernel/project/cli/suggestNames.js +44 -1
  132. package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
  133. package/dist/src/kernel/project/cli/verbHelp.js +17 -18
  134. package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
  135. package/dist/src/kernel/project/mcp/buildServer.js +11 -4
  136. package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
  137. package/dist/src/kernel/project/mcp/envelope.js +3 -2
  138. package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
  139. package/dist/src/kernel/project/mcp/instructions.js +101 -31
  140. package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
  141. package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
  142. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  143. package/dist/src/kernel/render/renderers.js +3 -3
  144. package/dist/src/kernel/render/turtle.js +21 -4
  145. package/dist/src/kernel/render/turtle.js.map +1 -1
  146. package/dist/src/kernel/runtime/graphpack/build.js +54 -1
  147. package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
  148. package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
  149. package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
  150. package/dist/src/kernel/runtime/graphpack/read.js +3 -5
  151. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  152. package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
  153. package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
  154. package/dist/src/kernel/runtime/loadSession.js +8 -1
  155. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  156. package/dist/src/kernel/runtime/packVersion.js +75 -0
  157. package/dist/src/kernel/runtime/packVersion.js.map +1 -0
  158. package/dist/src/kernel/runtime/readEntity.js +43 -4
  159. package/dist/src/kernel/runtime/readEntity.js.map +1 -1
  160. package/dist/src/kernel/runtime/resolveSources.js +61 -9
  161. package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
  162. package/dist/src/kernel/runtime/storeReadiness.js +3 -2
  163. package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
  164. package/dist/src/kernel/spec/call.js +167 -0
  165. package/dist/src/kernel/spec/call.js.map +1 -0
  166. package/dist/src/kernel/spec/emitReference.js +9 -13
  167. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  168. package/dist/src/kernel/spec/emitSurface.js +8 -1
  169. package/dist/src/kernel/spec/emitSurface.js.map +1 -1
  170. package/dist/src/kernel/spec/guidance.js +63 -0
  171. package/dist/src/kernel/spec/guidance.js.map +1 -0
  172. package/dist/src/kernel/spec/wireType.js +36 -0
  173. package/dist/src/kernel/spec/wireType.js.map +1 -0
  174. package/package.json +14 -14
  175. package/pragma.conf.ts +294 -177
  176. package/src/capabilities/block.parity.test.ts +1 -1
  177. package/src/capabilities/callRule.test.ts +147 -0
  178. package/src/capabilities/capabilities/capabilities.test.ts +47 -19
  179. package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
  180. package/src/capabilities/capabilities/catalog.ts +60 -55
  181. package/src/capabilities/capabilities/index.ts +2 -0
  182. package/src/capabilities/capabilities/types.ts +16 -19
  183. package/src/capabilities/colophon/colophon.render.ts +9 -5
  184. package/src/capabilities/colophon/colophon.verb.ts +1 -0
  185. package/src/capabilities/config/get.verb.ts +2 -0
  186. package/src/capabilities/config/runField.ts +3 -3
  187. package/src/capabilities/config/set.verb.ts +3 -0
  188. package/src/capabilities/config/show.verb.ts +2 -0
  189. package/src/capabilities/config/unset.verb.ts +2 -0
  190. package/src/capabilities/create/constants.ts +2 -2
  191. package/src/capabilities/create/create.verb.ts +16 -0
  192. package/src/capabilities/create/createGrammar.test.ts +1 -1
  193. package/src/capabilities/distribution.test.ts +6 -3
  194. package/src/capabilities/distribution.ts +6 -0
  195. package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
  196. package/src/capabilities/doctor/doctor.test.ts +38 -1
  197. package/src/capabilities/doctor/doctor.verb.ts +2 -0
  198. package/src/capabilities/emptyRecovery.test.ts +5 -0
  199. package/src/capabilities/graph/connect.verb.ts +2 -0
  200. package/src/capabilities/graph/inspect.render.ts +9 -1
  201. package/src/capabilities/graph/inspect.test.ts +73 -0
  202. package/src/capabilities/graph/inspect.verb.ts +2 -0
  203. package/src/capabilities/graph/query.render.test.ts +35 -0
  204. package/src/capabilities/graph/query.render.ts +45 -7
  205. package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
  206. package/src/capabilities/graph/query.verb.ts +2 -0
  207. package/src/capabilities/graph/queryText.test.ts +55 -1
  208. package/src/capabilities/graph/queryText.ts +47 -0
  209. package/src/capabilities/graph/runConnect.ts +4 -5
  210. package/src/capabilities/graph/runQuery.ts +23 -9
  211. package/src/capabilities/info/info.verb.ts +2 -0
  212. package/src/capabilities/links.shipped.exec.test.ts +398 -0
  213. package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
  214. package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
  215. package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
  216. package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
  217. package/src/capabilities/meta/mcp.verb.ts +2 -0
  218. package/src/capabilities/ontology/ontology.test.ts +24 -0
  219. package/src/capabilities/ontology/verbs.ts +16 -12
  220. package/src/capabilities/prompt/verbs.ts +10 -4
  221. package/src/capabilities/resources/provider.ts +7 -3
  222. package/src/capabilities/resources/resources.test.ts +2 -1
  223. package/src/capabilities/setup/setup.verb.ts +16 -0
  224. package/src/capabilities/shared/calls.ts +20 -0
  225. package/src/capabilities/skill/render.ts +9 -6
  226. package/src/capabilities/skill/verbs.ts +8 -4
  227. package/src/capabilities/sources/collectStatus.ts +6 -1
  228. package/src/capabilities/sources/index.ts +4 -3
  229. package/src/capabilities/sources/reset.render.test.ts +33 -0
  230. package/src/capabilities/sources/reset.render.ts +43 -0
  231. package/src/capabilities/sources/reset.verb.ts +59 -0
  232. package/src/capabilities/sources/runReset.ts +65 -0
  233. package/src/capabilities/sources/runUpdate.ts +12 -11
  234. package/src/capabilities/sources/sources.test.ts +158 -3
  235. package/src/capabilities/sources/status.render.test.ts +63 -1
  236. package/src/capabilities/sources/status.render.ts +25 -8
  237. package/src/capabilities/sources/status.verb.ts +2 -0
  238. package/src/capabilities/sources/types.ts +26 -0
  239. package/src/capabilities/sources/update.verb.ts +3 -0
  240. package/src/capabilities/surface.test.ts +2 -2
  241. package/src/capabilities/token.parity.test.ts +3 -2
  242. package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
  243. package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
  244. package/src/capabilities/variable.parity.test.ts +2 -1
  245. package/src/capabilities/version/version.verb.ts +1 -0
  246. package/src/capabilities/wireContract.test.ts +110 -0
  247. package/src/constants.ts +4 -3
  248. package/src/kernel/copy.test.ts +1 -1
  249. package/src/kernel/error/PragmaError.test.ts +7 -3
  250. package/src/kernel/error/index.ts +1 -1
  251. package/src/kernel/error/recovery.test.ts +52 -18
  252. package/src/kernel/error/recovery.ts +25 -46
  253. package/src/kernel/error/types.ts +1 -1
  254. package/src/kernel/packs/collect.test.ts +79 -0
  255. package/src/kernel/packs/collect.ts +151 -11
  256. package/src/kernel/packs/compile.test.ts +107 -10
  257. package/src/kernel/packs/compile.ts +100 -11
  258. package/src/kernel/packs/filteredEmpty.test.ts +22 -5
  259. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
  260. package/src/kernel/packs/graphqlSource.test.ts +42 -0
  261. package/src/kernel/packs/iri.ts +5 -4
  262. package/src/kernel/packs/listNotice.test.ts +1 -1
  263. package/src/kernel/packs/nounLink.test.ts +385 -0
  264. package/src/kernel/packs/renderPack.ts +137 -57
  265. package/src/kernel/packs/resolveEntity.test.ts +170 -1
  266. package/src/kernel/packs/resolveEntity.ts +281 -101
  267. package/src/kernel/packs/runBodies.ts +133 -31
  268. package/src/kernel/packs/schema.ts +77 -12
  269. package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
  270. package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
  271. package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
  272. package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
  273. package/src/kernel/packs/sparql/filterValues.ts +50 -31
  274. package/src/kernel/packs/sparql/runSelect.ts +4 -6
  275. package/src/kernel/packs/storyRules.ts +37 -4
  276. package/src/kernel/packs/tierScope.test.ts +120 -6
  277. package/src/kernel/packs/tierScope.ts +5 -7
  278. package/src/kernel/packs/toolDescription.test.ts +59 -74
  279. package/src/kernel/packs/types.ts +93 -11
  280. package/src/kernel/project/cli/buildProgram.ts +4 -0
  281. package/src/kernel/project/cli/dispatch.ts +34 -8
  282. package/src/kernel/project/cli/suggest.test.ts +77 -0
  283. package/src/kernel/project/cli/suggestNames.ts +47 -1
  284. package/src/kernel/project/cli/verbHelp.ts +16 -19
  285. package/src/kernel/project/mcp/buildServer.ts +15 -4
  286. package/src/kernel/project/mcp/envelope.ts +3 -2
  287. package/src/kernel/project/mcp/instructions.ts +123 -35
  288. package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
  289. package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
  290. package/src/kernel/project/mcp/registerVerb.ts +67 -29
  291. package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
  292. package/src/kernel/render/renderers.test.ts +2 -2
  293. package/src/kernel/render/renderers.ts +3 -3
  294. package/src/kernel/render/turtle.ts +24 -4
  295. package/src/kernel/runtime/graphpack/build.ts +58 -1
  296. package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
  297. package/src/kernel/runtime/graphpack/manifest.ts +15 -0
  298. package/src/kernel/runtime/graphpack/read.ts +4 -6
  299. package/src/kernel/runtime/graphpack/schemas.ts +1 -0
  300. package/src/kernel/runtime/graphpack/types.ts +13 -0
  301. package/src/kernel/runtime/loadSession.ts +8 -1
  302. package/src/kernel/runtime/packVersion.test.ts +61 -0
  303. package/src/kernel/runtime/packVersion.ts +84 -0
  304. package/src/kernel/runtime/readEntity.ts +61 -6
  305. package/src/kernel/runtime/resolveSources.test.ts +90 -7
  306. package/src/kernel/runtime/resolveSources.ts +91 -10
  307. package/src/kernel/runtime/storeReadiness.ts +4 -4
  308. package/src/kernel/runtime/types.ts +12 -0
  309. package/src/kernel/spec/call.test.ts +95 -0
  310. package/src/kernel/spec/call.ts +186 -0
  311. package/src/kernel/spec/emitReference.ts +9 -13
  312. package/src/kernel/spec/emitSurface.ts +9 -1
  313. package/src/kernel/spec/guidance.test.ts +49 -0
  314. package/src/kernel/spec/guidance.ts +68 -0
  315. package/src/kernel/spec/index.ts +4 -0
  316. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  317. package/src/kernel/spec/types.ts +64 -7
  318. package/src/kernel/spec/wireType.ts +53 -0
  319. package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
  320. package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
  321. package/src/testing/behavioral/parity.test.ts +40 -0
  322. package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
  323. package/src/testing/eval/cases/assembly.ts +4 -3
  324. package/src/testing/eval/cases/stable.ts +2 -3
  325. package/src/testing/helpers/callSchema.ts +40 -0
  326. package/src/testing/helpers/parity.ts +35 -2
  327. package/src/testing/setupCallChecking.ts +30 -0
  328. package/dist/src/capabilities/capabilities/hints.js +0 -223
  329. package/dist/src/capabilities/capabilities/hints.js.map +0 -1
  330. package/src/capabilities/capabilities/hints.ts +0 -266
@@ -32,7 +32,7 @@
32
32
  */
33
33
 
34
34
  import type { QueryResult } from "@canonical/ke";
35
- import { cliRecovery, PragmaError } from "../../kernel/error/index.js";
35
+ import { callRecovery, PragmaError } from "../../kernel/error/index.js";
36
36
  import { resolveUri } from "../../kernel/packs/iri.js";
37
37
  import { suggestNames } from "../../kernel/project/cli/suggestNames.js";
38
38
  import { ROSTER_THRESHOLD } from "../../kernel/runtime/readEntity.js";
@@ -258,12 +258,11 @@ async function assertPresent(
258
258
  message: `Argument ${label} "${input}" is not in the loaded graph, so nothing can be said about what it connects to.`,
259
259
  entity: { type: "entity", name: input },
260
260
  suggestions: suggestNames(input, candidates),
261
- // `cliRecovery` prepends the distribution's own binary name, so the
261
+ // `callRecovery` prepends the distribution's own binary name, so the
262
262
  // command passed here is the SUFFIX only.
263
- recovery: cliRecovery(
264
- `graph inspect ${input}`,
263
+ recovery: callRecovery(
264
+ { verb: "graph inspect", params: { uri: input } },
265
265
  "Check the name against the graph, or list the loaded namespaces.",
266
- { tool: "graph_inspect", params: { uri: input } },
267
266
  ),
268
267
  });
269
268
  }
@@ -17,9 +17,23 @@
17
17
  */
18
18
 
19
19
  import type { QueryResult } from "@canonical/ke";
20
- import { cliRecovery, PragmaError } from "../../kernel/error/index.js";
20
+ import { callRecovery, PragmaError } from "../../kernel/error/index.js";
21
21
  import type { PragmaRuntime } from "../../kernel/runtime/index.js";
22
- import { expandPrefixedNames, trimQueryError } from "./queryText.js";
22
+ import {
23
+ expandPrefixedNames,
24
+ findUnknownPrefixes,
25
+ trimQueryError,
26
+ type UnknownPrefix,
27
+ } from "./queryText.js";
28
+
29
+ /**
30
+ * A query's result, plus — on an EMPTY select only — the prefixes the caller
31
+ * declared that are not namespaces of this graph. It rides beside the result
32
+ * for the notice to explain; the JSON payload stays the engine's own shape.
33
+ */
34
+ export type QueryAnswer = QueryResult & {
35
+ readonly unknownPrefixes?: readonly UnknownPrefix[];
36
+ };
23
37
 
24
38
  /**
25
39
  * Execute a raw SPARQL query against the booted store.
@@ -32,14 +46,17 @@ import { expandPrefixedNames, trimQueryError } from "./queryText.js";
32
46
  export async function runQuery(
33
47
  rt: PragmaRuntime,
34
48
  sparql: string,
35
- ): Promise<QueryResult> {
49
+ ): Promise<QueryAnswer> {
36
50
  // The store's own prefix map: what the facade prepends to the query (one
37
51
  // PREFIX line each, which is the offset a reported line number carries), and
38
52
  // what a prefixed name in the caller's text will be resolved against.
39
53
  const { prefixes } = await rt.store.get();
40
54
  const query = expandPrefixedNames(sparql, prefixes);
41
55
  try {
42
- return await rt.query.sparql(query.text);
56
+ const result = await rt.query.sparql(query.text);
57
+ if (result.type !== "select" || result.bindings.length > 0) return result;
58
+ const unknownPrefixes = findUnknownPrefixes(sparql, prefixes);
59
+ return unknownPrefixes.length > 0 ? { ...result, unknownPrefixes } : result;
43
60
  } catch (error) {
44
61
  if (error instanceof PragmaError) throw error;
45
62
  // Keep the parser's own message — the WHY (bad token, an unknown prefix) —
@@ -54,12 +71,9 @@ export async function runQuery(
54
71
  message: detail
55
72
  ? `Invalid SPARQL query: ${detail}`
56
73
  : "Invalid SPARQL query.",
57
- recovery: cliRecovery(
58
- "ontology list",
74
+ recovery: callRecovery(
75
+ { verb: "ontology list" },
59
76
  "Check your SPARQL syntax and the loaded namespaces.",
60
- {
61
- tool: "ontology_list",
62
- },
63
77
  ),
64
78
  });
65
79
  }
@@ -16,6 +16,8 @@ const infoVerb: VerbSpec<Record<string, unknown>, InfoData> = {
16
16
  path: ["info"],
17
17
  summary: "Show version, resolved config, provenance, and update status.",
18
18
  doc: "Storeless — reports the CLI version, how it was installed, the layered config with per-field origins, an entity total from the pack index, and (network, silent-fail) whether a newer release is available.",
19
+ useWhen: "when asked which version is installed or whether an update exists",
20
+ category: "diagnostic",
19
21
  params: [],
20
22
  output: { formatters: infoFormatters },
21
23
  examples: [
@@ -0,0 +1,398 @@
1
+ /**
2
+ * A cell may declare the noun its values name (`noun: "token"`). Two promises
3
+ * follow, held over the distribution's own stories (PROTECTED):
4
+ *
5
+ * - DECLARED (storeless): when a cell of noun A names noun B, a cell of B names
6
+ * A, or a table of A's can be filtered by B; and a list column naming a noun
7
+ * carries a filter of that noun over itself.
8
+ * - SHIPPED (embedded pack, whole corpus): every value such a cell prints
9
+ * resolves through the resolver a noun filter uses.
10
+ *
11
+ * A cell that declares no `noun` is unseen. Each half has a negative control.
12
+ */
13
+
14
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
15
+ import { compileStoryModule } from "../kernel/packs/compile.js";
16
+ import { MAX_LIST_WINDOW } from "../kernel/packs/paging.js";
17
+ import {
18
+ type LookupOutput,
19
+ listEntityNames,
20
+ resolveEntityIris,
21
+ } from "../kernel/packs/resolveEntity.js";
22
+ import { nounsNamed } from "../kernel/packs/storyRules.js";
23
+ import {
24
+ distributionSource,
25
+ isNestedExpand,
26
+ type NounLookups,
27
+ type PackCellLink,
28
+ type PackDefinition,
29
+ type PackEntity,
30
+ type PackList,
31
+ type PackPage,
32
+ } from "../kernel/packs/types.js";
33
+ import { verbKey } from "../kernel/packs/uniqueness.js";
34
+ import { bootRuntime } from "../kernel/runtime/boot.js";
35
+ import type { PragmaRuntime } from "../kernel/runtime/types.js";
36
+ import type { CapabilityModule } from "../kernel/spec/types.js";
37
+ import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
38
+ import { TEST_FLAGS } from "../testing/helpers/projectCli.js";
39
+ import { declaredStories } from "./distribution.js";
40
+
41
+ const SOURCE = distributionSource("pragma.conf.ts");
42
+
43
+ type Stories = ReadonlyMap<string, PackDefinition>;
44
+
45
+ /** One list-shaped body of a story, labelled as a caller would type it. */
46
+ interface Body {
47
+ readonly noun: string;
48
+ readonly verb: string;
49
+ readonly shape: PackList;
50
+ }
51
+
52
+ const bodiesOf = (stories: Stories): Body[] =>
53
+ [...stories].flatMap(([noun, story]) => [
54
+ ...(story.list ? [{ noun, verb: "list", shape: story.list }] : []),
55
+ ...(story.verbs ?? []).map((verb) => ({
56
+ noun,
57
+ verb: verb.verb,
58
+ shape: verb,
59
+ })),
60
+ ]);
61
+
62
+ /** A lookup cell that names a noun: where its value sits on an entity. */
63
+ interface LookupCell {
64
+ readonly noun: string;
65
+ readonly names: string;
66
+ /** The expand holding it, absent for a field or section. */
67
+ readonly expand?: string;
68
+ readonly field: string;
69
+ }
70
+
71
+ const lookupCellsOf = (stories: Stories): LookupCell[] =>
72
+ [...stories].flatMap(([noun, story]) => {
73
+ const lookup = story.lookup;
74
+ if (!lookup) return [];
75
+ const named = (cell: PackCellLink): cell is { noun: string } =>
76
+ cell.noun !== undefined;
77
+ return [
78
+ ...[...(lookup.fields ?? []), ...(lookup.sections ?? [])]
79
+ .filter(named)
80
+ .map((cell) => ({ noun, names: cell.noun, field: cell.name })),
81
+ ...(lookup.expand ?? []).flatMap((expand) =>
82
+ expand.select.flatMap((entry) =>
83
+ isNestedExpand(entry) || !named(entry)
84
+ ? []
85
+ : [
86
+ {
87
+ noun,
88
+ names: entry.noun,
89
+ expand: expand.name,
90
+ field: entry.name,
91
+ },
92
+ ],
93
+ ),
94
+ ),
95
+ ];
96
+ });
97
+
98
+ /** Every declared link that has no way back, and every unfilterable column. */
99
+ function linkViolations(stories: Stories): string[] {
100
+ const bodies = bodiesOf(stories);
101
+ const links = [
102
+ ...lookupCellsOf(stories).map((cell) => ({
103
+ from: cell.noun,
104
+ to: cell.names,
105
+ at: `${cell.noun} lookup ${cell.expand ?? ""}.${cell.field}`,
106
+ })),
107
+ ...bodies.flatMap((body) =>
108
+ body.shape.columns.flatMap((column) =>
109
+ column.noun
110
+ ? [
111
+ {
112
+ from: body.noun,
113
+ to: column.noun,
114
+ at: `${body.noun} ${body.verb} ${column.field}`,
115
+ },
116
+ ]
117
+ : [],
118
+ ),
119
+ ),
120
+ ];
121
+ /** Whether some table of `of`'s entities takes a filter naming `by`. */
122
+ const filterable = (of: string, by: string): boolean =>
123
+ bodies
124
+ .filter(
125
+ (body) =>
126
+ (body.noun === of && body.verb === "list") ||
127
+ body.shape.columns.some((column) => column.noun === of),
128
+ )
129
+ .some((body) => body.shape.filters?.some((f) => f.noun === by));
130
+
131
+ const oneWay = links
132
+ .filter((link) => link.from !== link.to)
133
+ .filter(
134
+ (link) =>
135
+ !links.some((back) => back.from === link.to && back.to === link.from) &&
136
+ !filterable(link.from, link.to),
137
+ )
138
+ .map((link) => `${link.at} names a ${link.to}, with no way back`);
139
+ const unfilterable = bodies.flatMap((body) =>
140
+ body.shape.columns
141
+ .filter(
142
+ (column) =>
143
+ column.noun !== undefined &&
144
+ !body.shape.filters?.some(
145
+ (f) => f.noun === column.noun && f.variable === column.field,
146
+ ),
147
+ )
148
+ .map(
149
+ (column) =>
150
+ `${body.noun} ${body.verb} lists a ${column.noun} in "${column.field}" it cannot be filtered by`,
151
+ ),
152
+ );
153
+ const unknown = [...stories].flatMap(([noun, story]) =>
154
+ nounsNamed(story)
155
+ .filter((named) => !stories.get(named)?.lookup)
156
+ .map((named) => `${noun} names a ${named}, which has no lookup`),
157
+ );
158
+ return [...oneWay, ...unfilterable, ...unknown];
159
+ }
160
+
161
+ /** A value a `noun` cell printed, and where. */
162
+ interface Printed {
163
+ readonly at: string;
164
+ readonly names: string;
165
+ readonly value: string;
166
+ }
167
+
168
+ /** Every value every `noun` cell prints, over each story's whole corpus. */
169
+ async function printedNames(
170
+ rt: PragmaRuntime,
171
+ stories: Stories,
172
+ ): Promise<Printed[]> {
173
+ const nouns: NounLookups = (noun) => stories.get(noun)?.lookup;
174
+ const modules = new Map<string, CapabilityModule>(
175
+ [...stories].map(([noun, story]) => [
176
+ noun,
177
+ compileStoryModule(story, SOURCE, {}, nouns),
178
+ ]),
179
+ );
180
+ const run = (noun: string, verb: string, params: Record<string, unknown>) => {
181
+ const found = modules
182
+ .get(noun)
183
+ ?.verbs.find((v) => verbKey(v.path) === `${noun} ${verb}`);
184
+ if (!found) throw new Error(`no compiled "${noun} ${verb}" verb`);
185
+ return found.run(params, rt);
186
+ };
187
+ const everyTier = (noun: string) =>
188
+ stories.get(noun)?.tierScope ? { tier: "all" } : {};
189
+ const printed = new Map<string, Printed>();
190
+ const record = (at: string, names: string, value: unknown) => {
191
+ if (typeof value !== "string" || value === "") return;
192
+ printed.set(`${at}\n${value}`, { at, names, value });
193
+ };
194
+
195
+ for (const body of bodiesOf(stories)) {
196
+ const columns = body.shape.columns.filter((column) => column.noun);
197
+ if (columns.length === 0) continue;
198
+ let after: string | undefined;
199
+ do {
200
+ const page = (await run(body.noun, body.verb, {
201
+ limit: MAX_LIST_WINDOW,
202
+ ...everyTier(body.noun),
203
+ ...(after ? { after } : {}),
204
+ })) as PackPage;
205
+ for (const row of page.rows) {
206
+ for (const column of columns) {
207
+ record(
208
+ `${body.noun} ${body.verb} ${column.field}`,
209
+ column.noun as string,
210
+ row[column.field],
211
+ );
212
+ }
213
+ }
214
+ after = page.nextAfter;
215
+ } while (after !== undefined);
216
+ }
217
+
218
+ const cells = lookupCellsOf(stories);
219
+ for (const noun of new Set(cells.map((cell) => cell.noun))) {
220
+ const lookup = stories.get(noun)?.lookup;
221
+ if (!lookup) continue;
222
+ const names = await listEntityNames(rt, lookup, SOURCE);
223
+ if (names.length === 0) continue;
224
+ const output = (await run(noun, "lookup", {
225
+ name: names,
226
+ ...everyTier(noun),
227
+ })) as LookupOutput;
228
+ for (const entity of output.results as PackEntity[]) {
229
+ for (const cell of cells.filter((c) => c.noun === noun)) {
230
+ const at = `${noun} lookup ${cell.expand ?? ""}.${cell.field}`;
231
+ const held = cell.expand ? entity[cell.expand] : [entity];
232
+ for (const row of Array.isArray(held) ? held : []) {
233
+ record(at, cell.names, (row as Record<string, unknown>)[cell.field]);
234
+ }
235
+ }
236
+ }
237
+ }
238
+ return [...printed.values()];
239
+ }
240
+
241
+ /**
242
+ * The printed values the named noun cannot resolve, through the resolver a
243
+ * noun filter uses. A cell is one name, or a space-separated set of names (a
244
+ * `many` cell).
245
+ */
246
+ async function unresolved(
247
+ rt: PragmaRuntime,
248
+ stories: Stories,
249
+ printed: readonly Printed[],
250
+ ): Promise<string[]> {
251
+ const answers = new Map<string, Promise<boolean>>();
252
+ const resolves = (noun: string, value: string): Promise<boolean> => {
253
+ const key = `${noun}\n${value}`;
254
+ const lookup = stories.get(noun)?.lookup;
255
+ const answer =
256
+ answers.get(key) ??
257
+ (lookup
258
+ ? resolveEntityIris(rt, lookup, noun, [value], SOURCE, {}).then(
259
+ (iris) => iris.length > 0,
260
+ () => false,
261
+ )
262
+ : Promise.resolve(false));
263
+ answers.set(key, answer);
264
+ return answer;
265
+ };
266
+ const missing: string[] = [];
267
+ for (const cell of printed) {
268
+ const members = cell.value.split(/\s+/);
269
+ const found =
270
+ (await resolves(cell.names, cell.value)) ||
271
+ (members.length > 1 &&
272
+ (await Promise.all(members.map((m) => resolves(cell.names, m)))).every(
273
+ Boolean,
274
+ ));
275
+ if (!found) missing.push(`${cell.at}: "${cell.value}" is no ${cell.names}`);
276
+ }
277
+ return missing;
278
+ }
279
+
280
+ describe("a declared link has a way back (PROTECTED)", () => {
281
+ it("derives links from the config", () => {
282
+ // A guard, not a no-op: no annotated cell would make the rule vacuous.
283
+ const nouns = new Set(lookupCellsOf(declaredStories).map((c) => c.names));
284
+ expect([...nouns].sort()).toEqual([
285
+ "block",
286
+ "modifier",
287
+ "token",
288
+ "variable",
289
+ ]);
290
+ });
291
+
292
+ it("every cell naming another noun can be followed back, and every such column filtered", () => {
293
+ expect(linkViolations(declaredStories)).toEqual([]);
294
+ });
295
+
296
+ it("negative control: a one-way link and an unfilterable column are both named", () => {
297
+ const stories: Stories = new Map(
298
+ [
299
+ WIDGET,
300
+ { noun: "maker", lookup: { by: "ex:name", type: "ex:Maker" } },
301
+ ].map((story) => [story.noun, story]),
302
+ );
303
+ expect(linkViolations(stories)).toEqual([
304
+ "widget list maker names a maker, with no way back",
305
+ 'widget list lists a maker in "maker" it cannot be filtered by',
306
+ ]);
307
+ });
308
+ });
309
+
310
+ describe("every printed link resolves, whole corpus (PROTECTED)", () => {
311
+ let rt: PragmaRuntime;
312
+ beforeAll(async () => {
313
+ rt = bootRuntime(TEST_FLAGS);
314
+ await rt.store.get();
315
+ }, 60_000);
316
+ afterAll(async () => {
317
+ (await rt.store.get()).store.dispose();
318
+ });
319
+
320
+ it("every value a noun cell prints is a name that noun's lookup resolves", async () => {
321
+ const printed = await printedNames(rt, declaredStories);
322
+ // Each annotated cell printed something: a cell that printed nothing
323
+ // would pass by saying nothing.
324
+ const silent = [
325
+ ...lookupCellsOf(declaredStories).map(
326
+ (cell) => `${cell.noun} lookup ${cell.expand ?? ""}.${cell.field}`,
327
+ ),
328
+ ...bodiesOf(declaredStories).flatMap((body) =>
329
+ body.shape.columns
330
+ .filter((column) => column.noun)
331
+ .map((column) => `${body.noun} ${body.verb} ${column.field}`),
332
+ ),
333
+ ].filter((at) => !printed.some((cell) => cell.at === at));
334
+ expect(silent).toEqual([]);
335
+ expect(await unresolved(rt, declaredStories, printed)).toEqual(
336
+ UNRESOLVED_TODAY,
337
+ );
338
+ }, 300_000);
339
+
340
+ it("negative control: a cell printing a name its noun cannot resolve is named", async () => {
341
+ const { rt: fixture } = await buildFixtureRuntime({
342
+ ttl: TTL,
343
+ prefixes: PREFIXES,
344
+ });
345
+ const stories: Stories = new Map(
346
+ [
347
+ WIDGET,
348
+ { noun: "maker", lookup: { by: "ex:name", type: "ex:Maker" } },
349
+ ].map((story) => [story.noun, story]),
350
+ );
351
+ const printed = await printedNames(fixture, stories);
352
+ expect(await unresolved(fixture, stories, printed)).toEqual([
353
+ 'widget list maker: "anon" is no maker',
354
+ ]);
355
+ }, 60_000);
356
+ });
357
+
358
+ /**
359
+ * The printed names their noun cannot resolve TODAY — asserted exactly, so an
360
+ * entry goes red the day it is fixed and cannot outlive its reason.
361
+ *
362
+ * `KeyboardKeys` is a `ds:Group`: a kind of block the design system defines,
363
+ * one of which records token bindings, and which the block story does not
364
+ * address — so `token consumers` prints a block that `block lookup` and
365
+ * `--block` both miss.
366
+ */
367
+ const UNRESOLVED_TODAY: readonly string[] = [
368
+ 'token consumers block: "KeyboardKeys" is no block',
369
+ ];
370
+
371
+ const PREFIXES = { ex: "https://example.org/shop#" };
372
+
373
+ /** Two widgets; one maker carries no name, so its cell falls back to the IRI. */
374
+ const TTL = `
375
+ @prefix ex: <https://example.org/shop#> .
376
+ ex:acme a ex:Maker ; ex:name "Acme" .
377
+ ex:anon a ex:Maker .
378
+ ex:button a ex:Widget ; ex:name "Button" ; ex:madeBy ex:acme .
379
+ ex:label a ex:Widget ; ex:name "Label" ; ex:madeBy ex:anon .
380
+ `;
381
+
382
+ /** Names a maker in a column with no filter, and nothing of a maker's names it. */
383
+ const WIDGET: PackDefinition = {
384
+ noun: "widget",
385
+ list: {
386
+ query: [
387
+ "SELECT ?uri ?name ?maker WHERE {",
388
+ " ?uri a ex:Widget ; ex:name ?name ; ex:madeBy ?makerUri .",
389
+ " OPTIONAL { ?makerUri ex:name ?makerName }",
390
+ ' BIND(COALESCE(?makerName, REPLACE(STR(?makerUri), "^.*#", "")) AS ?maker)',
391
+ "} ORDER BY ?name",
392
+ ].join("\n"),
393
+ columns: [
394
+ { field: "name", label: "Name" },
395
+ { field: "maker", label: "Maker", noun: "maker" },
396
+ ],
397
+ },
398
+ };
@@ -43,7 +43,7 @@ import { bootRuntime } from "../kernel/runtime/boot.js";
43
43
  import type { PragmaRuntime } from "../kernel/runtime/types.js";
44
44
  import type { VerbSpec } from "../kernel/spec/types.js";
45
45
  import { TEST_FLAGS } from "../testing/helpers/projectCli.js";
46
- import { declaredStories } from "./distribution.js";
46
+ import { declaredLookups, declaredStories } from "./distribution.js";
47
47
 
48
48
  /**
49
49
  * The ceiling on ONE list-shaped answer, in bytes of the serialised payload.
@@ -82,7 +82,7 @@ interface Body {
82
82
  */
83
83
  const BODIES: readonly Body[] = [...declaredStories].flatMap(
84
84
  ([noun, story]) => {
85
- const module = compileStoryModule(story, SOURCE, {});
85
+ const module = compileStoryModule(story, SOURCE, {}, declaredLookups);
86
86
  const find = (verb: string): Body => {
87
87
  const found = module.verbs.find(
88
88
  (v) => verbKey(v.path) === `${noun} ${verb}`,