@canonical/pragma-cli 0.32.0 → 0.34.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 (295) hide show
  1. package/README.md +72 -0
  2. package/dist/pragma +0 -0
  3. package/package.json +19 -15
  4. package/pragma.conf.ts +664 -0
  5. package/src/bin.ts +15 -6
  6. package/src/capabilities/block.parity.test.ts +250 -0
  7. package/src/capabilities/capabilities/capabilities.render.ts +3 -2
  8. package/src/capabilities/capabilities/capabilities.test.ts +10 -1
  9. package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
  10. package/src/capabilities/capabilities/catalog.ts +20 -8
  11. package/src/capabilities/capabilities/hints.ts +21 -36
  12. package/src/capabilities/capabilities/types.ts +2 -0
  13. package/src/capabilities/colophon/collectColophon.ts +34 -19
  14. package/src/capabilities/colophon/colophon.render.test.ts +31 -4
  15. package/src/capabilities/colophon/colophon.render.ts +13 -4
  16. package/src/capabilities/colophon/colophon.test.ts +3 -3
  17. package/src/capabilities/colophon/colophon.verb.ts +11 -4
  18. package/src/capabilities/colophon/markdownTerminal.ts +33 -14
  19. package/src/capabilities/colophon/types.ts +16 -7
  20. package/src/capabilities/config/collectConfigShow.test.ts +57 -0
  21. package/src/capabilities/config/collectConfigShow.ts +41 -4
  22. package/src/capabilities/config/fields.ts +10 -108
  23. package/src/capabilities/config/set.test.ts +6 -6
  24. package/src/capabilities/config/set.verb.ts +12 -8
  25. package/src/capabilities/config/show.render.test.ts +137 -0
  26. package/src/capabilities/config/show.render.ts +57 -16
  27. package/src/capabilities/config/show.verb.ts +9 -8
  28. package/src/capabilities/config/types.ts +8 -0
  29. package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
  30. package/src/capabilities/create/byteEquality.test.ts +82 -101
  31. package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
  32. package/src/capabilities/create/constants.ts +75 -0
  33. package/src/capabilities/create/create.mcp.test.ts +2 -2
  34. package/src/capabilities/create/create.test.ts +192 -16
  35. package/src/capabilities/create/create.verb.ts +208 -79
  36. package/src/capabilities/create/index.ts +2 -10
  37. package/src/capabilities/create/pickGenerator.ts +34 -28
  38. package/src/capabilities/create/templates.embedded.generated.ts +4 -29
  39. package/src/capabilities/create/types.ts +10 -6
  40. package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
  41. package/src/capabilities/distribution.test.ts +120 -0
  42. package/src/capabilities/distribution.ts +59 -0
  43. package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
  44. package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
  45. package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
  46. package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
  47. package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
  48. package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
  49. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
  50. package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
  51. package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
  52. package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
  53. package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
  54. package/src/capabilities/doctor/doctor.render.test.ts +170 -0
  55. package/src/capabilities/doctor/doctor.render.ts +117 -34
  56. package/src/capabilities/doctor/doctor.test.ts +68 -9
  57. package/src/capabilities/doctor/doctor.verb.ts +6 -2
  58. package/src/capabilities/doctor/runChecks.ts +33 -9
  59. package/src/capabilities/doctor/types.ts +16 -1
  60. package/src/capabilities/emptyRecovery.test.ts +32 -15
  61. package/src/capabilities/graph/inspect.verb.ts +3 -2
  62. package/src/capabilities/graph/query.verb.ts +3 -2
  63. package/src/capabilities/graph/runQuery.ts +8 -5
  64. package/src/capabilities/index.ts +22 -18
  65. package/src/capabilities/info/collectInfo.ts +10 -6
  66. package/src/capabilities/info/info.render.ts +3 -2
  67. package/src/capabilities/info/info.verb.ts +3 -2
  68. package/src/capabilities/lazy.test.ts +93 -2
  69. package/src/capabilities/ontology/index.ts +10 -3
  70. package/src/capabilities/ontology/ontology.test.ts +157 -5
  71. package/src/capabilities/ontology/verbs.ts +203 -104
  72. package/src/capabilities/prompt/prompt.render.test.ts +94 -0
  73. package/src/capabilities/prompt/prompt.render.ts +23 -6
  74. package/src/capabilities/prompt/prompt.test.ts +68 -1
  75. package/src/capabilities/prompt/verbs.ts +24 -11
  76. package/src/capabilities/reference.test.ts +32 -0
  77. package/src/capabilities/resources/provider.ts +58 -25
  78. package/src/capabilities/resources/resources.test.ts +176 -21
  79. package/src/capabilities/scopeTypeSync.test.ts +37 -0
  80. package/src/capabilities/setup/index.ts +7 -1
  81. package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
  82. package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
  83. package/src/capabilities/setup/operations/setupLsp.ts +56 -12
  84. package/src/capabilities/setup/operations/setupMcp.ts +216 -39
  85. package/src/capabilities/setup/operations/setupSkills.ts +10 -4
  86. package/src/capabilities/setup/setup.render.test.ts +109 -0
  87. package/src/capabilities/setup/setup.render.ts +61 -14
  88. package/src/capabilities/setup/setup.test.ts +352 -1
  89. package/src/capabilities/setup/setup.verb.ts +68 -9
  90. package/src/capabilities/setup/shell.ts +29 -3
  91. package/src/capabilities/setup/types.ts +59 -2
  92. package/src/capabilities/shared/bands.ts +16 -0
  93. package/src/capabilities/skill/discover.ts +11 -5
  94. package/src/capabilities/skill/render.ts +4 -5
  95. package/src/capabilities/skill/verbs.ts +7 -7
  96. package/src/capabilities/sources/collectStatus.ts +69 -49
  97. package/src/capabilities/sources/index.ts +1 -1
  98. package/src/capabilities/sources/installSkills.ts +2 -2
  99. package/src/capabilities/sources/runUpdate.ts +146 -98
  100. package/src/capabilities/sources/sources.test.ts +264 -84
  101. package/src/capabilities/sources/status.render.test.ts +94 -0
  102. package/src/capabilities/sources/status.render.ts +55 -33
  103. package/src/capabilities/sources/status.verb.ts +15 -8
  104. package/src/capabilities/sources/types.ts +18 -25
  105. package/src/capabilities/sources/update.render.ts +9 -2
  106. package/src/capabilities/sources/update.verb.ts +12 -23
  107. package/src/capabilities/surface.test.ts +65 -17
  108. package/src/capabilities/tier.parity.test.ts +144 -0
  109. package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
  110. package/src/constants.test.ts +9 -7
  111. package/src/constants.ts +49 -23
  112. package/src/identity.test.ts +333 -0
  113. package/src/index.ts +2 -2
  114. package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
  115. package/src/kernel/completion/complete.test.ts +3 -5
  116. package/src/kernel/completion/emitScripts.test.ts +1 -1
  117. package/src/kernel/completion/emitScripts.ts +4 -2
  118. package/src/kernel/completion/entitySource.test.ts +185 -41
  119. package/src/kernel/completion/entitySource.ts +131 -113
  120. package/src/kernel/completion/model.test.ts +3 -3
  121. package/src/kernel/completion/model.ts +11 -1
  122. package/src/kernel/completion/parse.test.ts +2 -4
  123. package/src/kernel/completion/safety.test.ts +82 -36
  124. package/src/kernel/completion/shellDrive.test.ts +592 -0
  125. package/src/kernel/completion/templates/fish.ts +28 -6
  126. package/src/kernel/completion/types.ts +3 -3
  127. package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
  128. package/src/kernel/config/defaults.test.ts +85 -0
  129. package/src/kernel/config/defaults.ts +12 -23
  130. package/src/kernel/config/evaluateProjectConfig.ts +2 -3
  131. package/src/kernel/config/findProjectConfig.ts +4 -3
  132. package/src/kernel/config/firstRun.ts +12 -5
  133. package/src/kernel/config/globalConfig.ts +2 -2
  134. package/src/kernel/config/index.ts +4 -5
  135. package/src/kernel/config/paths.ts +17 -17
  136. package/src/kernel/config/readConfig.test.ts +242 -6
  137. package/src/kernel/config/readConfig.ts +8 -8
  138. package/src/kernel/config/schema.test.ts +50 -0
  139. package/src/kernel/config/schema.ts +97 -10
  140. package/src/kernel/config/types.ts +73 -19
  141. package/src/kernel/copy.test.ts +453 -0
  142. package/src/kernel/error/PragmaError.test.ts +5 -5
  143. package/src/kernel/error/constants.ts +3 -2
  144. package/src/kernel/error/index.ts +1 -1
  145. package/src/kernel/error/recovery.test.ts +22 -45
  146. package/src/kernel/error/recovery.ts +22 -27
  147. package/src/kernel/error/renderError.ts +1 -1
  148. package/src/kernel/error/types.ts +5 -4
  149. package/src/kernel/packs/collect.test.ts +195 -11
  150. package/src/kernel/packs/collect.ts +213 -58
  151. package/src/kernel/packs/compile.test.ts +163 -7
  152. package/src/kernel/packs/compile.ts +15 -12
  153. package/src/kernel/packs/disclosure.test.ts +5 -2
  154. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
  155. package/src/kernel/packs/graphqlSource.test.ts +6 -3
  156. package/src/kernel/packs/iri.ts +5 -7
  157. package/src/kernel/packs/renderPack.ts +5 -3
  158. package/src/kernel/packs/resolveEntity.ts +21 -17
  159. package/src/kernel/packs/runBodies.ts +15 -14
  160. package/src/kernel/packs/schema.ts +96 -10
  161. package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
  162. package/src/kernel/packs/sparql/runSelect.ts +73 -34
  163. package/src/kernel/packs/toolDescription.test.ts +10 -2
  164. package/src/kernel/packs/types.ts +39 -4
  165. package/src/kernel/packs/uniqueness.ts +4 -4
  166. package/src/kernel/project/cli/buildProgram.test.ts +60 -11
  167. package/src/kernel/project/cli/buildProgram.ts +11 -0
  168. package/src/kernel/project/cli/dispatch.test.ts +67 -0
  169. package/src/kernel/project/cli/dispatch.ts +34 -12
  170. package/src/kernel/project/cli/globalFlags.test.ts +12 -4
  171. package/src/kernel/project/cli/globalFlags.ts +14 -14
  172. package/src/kernel/project/cli/helpFormat.ts +1 -1
  173. package/src/kernel/project/cli/rootHelp.ts +109 -107
  174. package/src/kernel/project/mcp/buildServer.ts +11 -6
  175. package/src/kernel/project/mcp/instructions.ts +12 -7
  176. package/src/kernel/project/mcp/mcpError.ts +52 -0
  177. package/src/kernel/project/mcp/parity.test.ts +39 -1
  178. package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
  179. package/src/kernel/project/mcp/prompts/provider.ts +51 -7
  180. package/src/kernel/project/mcp/prompts/source.ts +68 -67
  181. package/src/kernel/project/mcp/registerVerb.ts +25 -6
  182. package/src/kernel/project/mcp/serve.ts +11 -3
  183. package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
  184. package/src/kernel/render/disclosure.ts +4 -2
  185. package/src/kernel/render/formatters.ts +5 -4
  186. package/src/kernel/render/prefixes.test.ts +36 -0
  187. package/src/kernel/render/prefixes.ts +40 -13
  188. package/src/kernel/render/renderers.test.ts +35 -1
  189. package/src/kernel/render/renderers.ts +25 -7
  190. package/src/kernel/render/style.test.ts +31 -0
  191. package/src/kernel/render/style.ts +74 -0
  192. package/src/kernel/runtime/graphpack/build.ts +57 -3
  193. package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
  194. package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
  195. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
  196. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
  197. package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
  198. package/src/kernel/runtime/graphpack/embedded.ts +37 -11
  199. package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
  200. package/src/kernel/runtime/graphpack/hash.ts +3 -2
  201. package/src/kernel/runtime/graphpack/index.ts +1 -1
  202. package/src/kernel/runtime/graphpack/manifest.ts +114 -12
  203. package/src/kernel/runtime/graphpack/read.ts +32 -22
  204. package/src/kernel/runtime/graphpack/schemas.ts +74 -0
  205. package/src/kernel/runtime/graphpack/stories.ts +84 -0
  206. package/src/kernel/runtime/graphpack/types.ts +72 -45
  207. package/src/kernel/runtime/loadSession.ts +4 -12
  208. package/src/kernel/runtime/paths.ts +64 -15
  209. package/src/kernel/runtime/probe.test.ts +8 -4
  210. package/src/kernel/runtime/readEntity.ts +6 -5
  211. package/src/kernel/runtime/refs/gitOps.ts +1 -19
  212. package/src/kernel/runtime/refs/parseRef.ts +7 -7
  213. package/src/kernel/runtime/refs/resolve.ts +141 -65
  214. package/src/kernel/runtime/resolveSources.test.ts +63 -24
  215. package/src/kernel/runtime/resolveSources.ts +36 -25
  216. package/src/kernel/runtime/storeReadiness.ts +62 -0
  217. package/src/kernel/runtime/storeless.test.ts +1 -1
  218. package/src/kernel/runtime/types.ts +1 -1
  219. package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
  220. package/src/kernel/spec/emitReference.test.ts +196 -0
  221. package/src/kernel/spec/emitReference.ts +624 -0
  222. package/src/kernel/spec/emitSurface.ts +13 -7
  223. package/src/kernel/spec/index.ts +2 -0
  224. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  225. package/src/kernel/spec/surfaceConformance.ts +8 -0
  226. package/src/kernel/spec/types.ts +29 -9
  227. package/src/kernel/spec/validate.ts +2 -1
  228. package/src/kernel/vocabulary.test.ts +164 -0
  229. package/src/kernel/vocabulary.ts +146 -0
  230. package/src/testing/PARITY_GAPS.ts +9 -3
  231. package/src/testing/behavioral/README.md +3 -3
  232. package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
  233. package/src/testing/behavioral/completion.test.ts +21 -8
  234. package/src/testing/behavioral/disclosure.test.ts +12 -0
  235. package/src/testing/behavioral/docExamples.test.ts +283 -0
  236. package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
  237. package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
  238. package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
  239. package/src/testing/behavioral/harness.foundation.test.ts +14 -5
  240. package/src/testing/behavioral/journeys.cli.test.ts +116 -73
  241. package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
  242. package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
  243. package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
  244. package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
  245. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
  246. package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
  247. package/src/testing/behavioral/liveReadSurface.ts +4 -3
  248. package/src/testing/behavioral/mcpSurface.test.ts +10 -5
  249. package/src/testing/behavioral/staleCommands.test.ts +86 -0
  250. package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
  251. package/src/testing/eval/cases/assembly.ts +16 -9
  252. package/src/testing/eval/cases/effectful.ts +31 -16
  253. package/src/testing/eval/cases/readNouns.ts +23 -18
  254. package/src/testing/eval/cases/stable.ts +5 -1
  255. package/src/testing/eval/eval.test.ts +7 -1
  256. package/src/testing/fixtures/fixtureCapability.ts +201 -1
  257. package/src/testing/fixtures/graph/canonical.ts +6 -3
  258. package/src/testing/fixtures/graph/defaultPack.ts +179 -0
  259. package/src/testing/fixtures/graph/owlExport.ts +97 -0
  260. package/src/testing/helpers/extractSnippets.ts +29 -0
  261. package/src/testing/helpers/fixtureGraph.ts +33 -16
  262. package/src/testing/helpers/golden.ts +1 -1
  263. package/src/testing/helpers/packRuntime.ts +2 -3
  264. package/src/testing/helpers/projectCli.ts +2 -1
  265. package/src/testing/helpers/projectMcp.ts +6 -0
  266. package/src/testing/perf/budgets.test.ts +4 -0
  267. package/src/testing/perf/budgets.ts +22 -7
  268. package/src/testing/perf/globalSetup.ts +121 -6
  269. package/src/types.ts +2 -2
  270. package/src/capabilities/block/blockList.verb.ts +0 -150
  271. package/src/capabilities/block/index.ts +0 -25
  272. package/src/capabilities/block/pack.ts +0 -155
  273. package/src/capabilities/block/parity.test.ts +0 -180
  274. package/src/capabilities/block/tierChain.ts +0 -78
  275. package/src/capabilities/colophon/pragmaColophon.ts +0 -66
  276. package/src/capabilities/config/field.test.ts +0 -188
  277. package/src/capabilities/modifier/index.ts +0 -15
  278. package/src/capabilities/modifier/pack.ts +0 -64
  279. package/src/capabilities/standard/index.ts +0 -18
  280. package/src/capabilities/standard/pack.ts +0 -128
  281. package/src/capabilities/tier/index.ts +0 -19
  282. package/src/capabilities/tier/lookup.render.ts +0 -34
  283. package/src/capabilities/tier/lookup.test.ts +0 -68
  284. package/src/capabilities/tier/lookup.verb.ts +0 -44
  285. package/src/capabilities/tier/pack.ts +0 -28
  286. package/src/capabilities/tier/runLookup.ts +0 -59
  287. package/src/capabilities/token/addConfig.render.ts +0 -27
  288. package/src/capabilities/token/addConfig.test.ts +0 -62
  289. package/src/capabilities/token/addConfig.verb.ts +0 -40
  290. package/src/capabilities/token/index.ts +0 -19
  291. package/src/capabilities/token/pack.ts +0 -58
  292. package/src/capabilities/token/runAddConfig.ts +0 -95
  293. package/src/kernel/completion/bashDrive.test.ts +0 -73
  294. package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
  295. package/src/kernel/runtime/lock.ts +0 -90
@@ -1,45 +1,68 @@
1
1
  import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
- import { join } from "node:path";
3
+ import { dirname, join } from "node:path";
4
4
  import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { readConfig } from "../config/readConfig.js";
6
+ import { embeddedManifest } from "../runtime/graphpack/embedded.js";
7
+ import { activePackPath, packDir } from "../runtime/paths.js";
8
+ import { resolveSources } from "../runtime/resolveSources.js";
5
9
  import type { CapabilityModule, VerbSpec } from "../spec/types.js";
6
10
  import { runComplete } from "./complete.js";
7
- import { createIndexEntityReader, indexCompletionEnv } from "./entitySource.js";
11
+ import {
12
+ createIndexEntityReader,
13
+ indexCompletionEnv,
14
+ readPackIndex,
15
+ } from "./entitySource.js";
8
16
 
9
- /** A fresh cwd with no lock → the reader falls back to the embedded pack. */
17
+ /** A fresh cwd with no pointer → the reader falls back to the embedded pack. */
10
18
  const freshCwd = (): string => mkdtempSync(join(tmpdir(), "pragma-entity-"));
11
19
 
12
- /** A project whose lock points at a crafted index.json in a temp pack cache. */
20
+ /** The 64-hex content hash the crafted pack is filed under. */
21
+ const CRAFTED_HASH = "1".repeat(64);
22
+
23
+ /** A project pointed at a crafted index.json in a temp pack cache. */
13
24
  function projectWithIndex(index: unknown): string {
14
25
  const cwd = mkdtempSync(join(tmpdir(), "pragma-idx-cwd-"));
15
26
  const cache = mkdtempSync(join(tmpdir(), "pragma-idx-cache-"));
16
27
  vi.stubEnv("XDG_CACHE_HOME", cache);
17
- writeFileSync(
18
- join(cwd, "pragma.lock.json"),
19
- JSON.stringify({ contentHash: "testhash" }),
20
- );
21
- const packDir = join(cache, "pragma", "packs", "testhash");
28
+ const pointer = activePackPath(cwd);
29
+ mkdirSync(dirname(pointer), { recursive: true });
30
+ writeFileSync(pointer, CRAFTED_HASH);
31
+ const packDir = join(cache, "pragma", "packs", CRAFTED_HASH);
22
32
  mkdirSync(packDir, { recursive: true });
23
33
  writeFileSync(join(packDir, "index.json"), JSON.stringify(index));
24
34
  return cwd;
25
35
  }
26
36
 
27
- /** A crafted index exercising the index / prompts / tiers / prefixes sources. */
37
+ /**
38
+ * A crafted index exercising the index / prefixes sources. Deliberately in a
39
+ * NEUTRAL namespace: the reader knows no entity families any more, so a test
40
+ * proving it reads `label` or `altNames` must not need this distribution's
41
+ * vocabulary to say so. Since L-OPEN-9 no live verb declares the `altNames`
42
+ * field (the tier lookup completes entity names like every declared lookup),
43
+ * so this crafted index is where the field's read contract is held.
44
+ *
45
+ * Real-vocabulary coverage lives in two places, and neither is here: the
46
+ * PROTECTED contract describe below reads the shipped index for plain entity
47
+ * NAMES, and `safety.test.ts`'s "every declared name source resolves without
48
+ * constructing the store" drives the live grammar over the shipped index for
49
+ * entity names (`tier lookup ap` → `ds:apps`) and for `prefixes`.
50
+ */
28
51
  const CRAFTED_INDEX = {
29
52
  version: 2,
30
- contentHash: "testhash",
31
- prefixes: { ex: "https://example.com/", ds: "https://ds.canonical.com/" },
53
+ contentHash: CRAFTED_HASH,
54
+ prefixes: { ex: "https://example.com/" },
32
55
  instanceCountByType: {},
33
56
  entities: [
34
57
  { name: "ex:Button", type: "ex:Component" },
35
- { name: "ds:prompt.build", type: "ds:Prompt", label: "build-a-block" },
58
+ { name: "ex:prompt.build", type: "ex:Prompt", label: "build-a-block" },
36
59
  {
37
- name: "ds:tier.lxd",
38
- type: "ds:Tier",
60
+ name: "ex:tier.lxd",
61
+ type: "ex:Tier",
39
62
  label: "LXD",
40
63
  altNames: ["apps/lxd"],
41
64
  },
42
- { name: "ds:tier.core", type: "ds:Tier", label: "core" },
65
+ { name: "ex:tier.core", type: "ex:Tier", label: "core" },
43
66
  ],
44
67
  };
45
68
 
@@ -55,15 +78,27 @@ describe("indexCompletionEnv — multi-source names(ref)", () => {
55
78
  ]);
56
79
  });
57
80
 
58
- it("prompts: ds:Prompt entities emit label || name", async () => {
81
+ it("index + field label: emits the label, and nothing for an entity without one", async () => {
59
82
  const env = indexCompletionEnv(projectWithIndex(CRAFTED_INDEX));
60
- expect(await env.names({ from: "prompts" })).toEqual(["build-a-block"]);
83
+ expect(
84
+ await env.names({ from: "index", type: "ex:Prompt", field: "label" }),
85
+ ).toEqual(["build-a-block"]);
86
+ // `ex:Button` carries no label. The lookup that declared this field matches
87
+ // on the label, so standing the name in would offer a token that lookup
88
+ // cannot resolve.
89
+ expect(
90
+ await env.names({ from: "index", type: "ex:Component", field: "label" }),
91
+ ).toEqual([]);
61
92
  });
62
93
 
63
- it("tiers: emit ds:name (altNames) when present, else label ?? name", async () => {
94
+ it("index + field altNames: emits only the alt names, never a label in their place", async () => {
64
95
  const env = indexCompletionEnv(projectWithIndex(CRAFTED_INDEX));
65
- // apps/lxd from altNames (the ds:name); core from the label fallback.
66
- expect(await env.names({ from: "tiers" })).toEqual(["apps/lxd", "core"]);
96
+ // `ex:tier.lxd` carries `apps/lxd`; `ex:tier.core` carries none, so its
97
+ // label `core` is NOT offered — a bespoke tier lookup filters on the
98
+ // declared alt-name property and would refuse it.
99
+ expect(
100
+ await env.names({ from: "index", type: "ex:Tier", field: "altNames" }),
101
+ ).toEqual(["apps/lxd"]);
67
102
  });
68
103
 
69
104
  it("prefixes: the index's prefixes ∪ the default display map", async () => {
@@ -92,18 +127,23 @@ describe("indexCompletionEnv — multi-source names(ref)", () => {
92
127
  expect(await env.names({ from: "skills" })).toEqual(["docx"]);
93
128
  });
94
129
 
95
- it("missing sources degrade to [] (prefixes still lists the default map)", async () => {
130
+ it("a type nothing matches yields [] (prefixes still list the default map)", async () => {
96
131
  const env = indexCompletionEnv(
97
132
  projectWithIndex({
98
133
  version: 2,
99
- contentHash: "testhash",
134
+ contentHash: CRAFTED_HASH,
100
135
  prefixes: {},
101
136
  instanceCountByType: {},
102
137
  entities: [{ name: "ex:Button", type: "ex:Component" }],
103
138
  }),
104
139
  );
105
- expect(await env.names({ from: "prompts" })).toEqual([]);
106
- expect(await env.names({ from: "tiers" })).toEqual([]);
140
+ expect(
141
+ await env.names({ from: "index", type: "ex:Prompt", field: "label" }),
142
+ ).toEqual([]);
143
+ expect(
144
+ await env.names({ from: "index", type: "ex:Tier", field: "altNames" }),
145
+ ).toEqual([]);
146
+ // The display map is compiled in, so it answers with no index at all.
107
147
  expect(await env.names({ from: "prefixes" })).toContain("ds");
108
148
  });
109
149
  });
@@ -112,16 +152,18 @@ describe("entity source contract (PROTECTED)", () => {
112
152
  it("reads the embedded index storelessly, filtering by type + partial", () => {
113
153
  const read = createIndexEntityReader(freshCwd());
114
154
 
115
- // Abox individuals of ex:Component, sorted.
116
- expect(read("ex:Component", "")).toEqual([
117
- "ex:Button",
118
- "ex:Card",
119
- "ex:Dialog",
120
- ]);
155
+ // An abox individual of ds:Component. Membership, not the full 100+ list:
156
+ // the roster moves whenever the design system does, but a design system
157
+ // without a button is a change a human should look at.
158
+ expect(read("ds:Component", "")).toContain("ds:global.component.button");
121
159
  // Tbox class.
122
- expect(read("owl:Class", "")).toEqual(["ex:Component"]);
123
- // Partial-prefix filter.
124
- expect(read("ex:Component", "ex:B")).toEqual(["ex:Button"]);
160
+ expect(read("owl:Class", "")).toContain("ds:Component");
161
+ // Partial-prefix filter: every candidate starts with what was typed.
162
+ const partial = read("ds:Component", "ds:global.component.but");
163
+ expect(partial).toContain("ds:global.component.button");
164
+ for (const name of partial) {
165
+ expect(name.startsWith("ds:global.component.but")).toBe(true);
166
+ }
125
167
  // Unknown type → no matches (never throws).
126
168
  expect(read("ds:Nope", "")).toEqual([]);
127
169
  });
@@ -129,16 +171,16 @@ describe("entity source contract (PROTECTED)", () => {
129
171
  it("relies only on the frozen { name, type } minimum", () => {
130
172
  const read = createIndexEntityReader(freshCwd());
131
173
  // Every result is a bare name token (string), usable with no other field.
132
- for (const name of read("ex:Component", "")) {
174
+ for (const name of read("ds:Component", "")) {
133
175
  expect(typeof name).toBe("string");
134
- expect(name.startsWith("ex:")).toBe(true);
176
+ expect(name.startsWith("ds:")).toBe(true);
135
177
  }
136
178
  });
137
179
 
138
180
  it("is fast (well under the 50ms storeless budget)", () => {
139
181
  const read = createIndexEntityReader(freshCwd());
140
182
  const start = performance.now();
141
- read("ex:Component", "");
183
+ read("ds:Component", "");
142
184
  expect(performance.now() - start).toBeLessThan(50);
143
185
  });
144
186
  });
@@ -159,7 +201,7 @@ describe("__complete entity tier wiring", () => {
159
201
  required: true,
160
202
  complete: {
161
203
  kind: "names",
162
- source: { from: "index", type: "ex:Component" },
204
+ source: { from: "index", type: "ds:Component" },
163
205
  },
164
206
  },
165
207
  ],
@@ -185,14 +227,116 @@ describe("__complete entity tier wiring", () => {
185
227
  // index-backed reader over cwd, adapted to the resolver's EntityNameReader.
186
228
  await expect(
187
229
  runComplete(
188
- ["block", "lookup", "ex:B"],
230
+ ["block", "lookup", "ds:global.component.but"],
189
231
  [lookupModule],
190
232
  indexCompletionEnv(freshCwd()),
191
233
  ),
192
- ).resolves.toEqual(["ex:Button"]);
234
+ ).resolves.toContain("ds:global.component.button");
193
235
  // Without the env, the entity tier yields nothing (grammar-only).
194
236
  await expect(
195
- runComplete(["block", "lookup", "ex:B"], [lookupModule]),
237
+ runComplete(
238
+ ["block", "lookup", "ds:global.component.but"],
239
+ [lookupModule],
240
+ ),
196
241
  ).resolves.toEqual([]);
197
242
  });
198
243
  });
244
+
245
+ describe("readPackIndex answers the boot decision, never around it", () => {
246
+ afterEach(() => {
247
+ vi.unstubAllEnvs();
248
+ });
249
+
250
+ // `info`, `doctor`, the MCP resource browser and native `prompts/list` all
251
+ // read through this. It takes the decision rather than re-deriving one, so it
252
+ // covers all three arms — including the one a re-derivation gets wrong.
253
+ it("reads the pack the decision names", () => {
254
+ projectWithIndex(CRAFTED_INDEX); // plants the pack under a stubbed cache
255
+ const index = readPackIndex({
256
+ kind: "pack",
257
+ dir: packDir(CRAFTED_HASH),
258
+ contentHash: CRAFTED_HASH,
259
+ });
260
+ expect(index?.contentHash).toBe(CRAFTED_HASH);
261
+ expect(index?.entities.map((entity) => entity.name)).toContain("ex:Button");
262
+ });
263
+
264
+ it("reads the embedded snapshot on the embedded arm", () => {
265
+ const index = readPackIndex({ kind: "embedded" });
266
+ expect(index?.contentHash).toBe(embeddedManifest().contentHash);
267
+ });
268
+
269
+ it("reads NOTHING when the store is unavailable", () => {
270
+ // The row that matters: a project that declared its own packs and never
271
+ // built them must not be listed the distribution's graph while every read
272
+ // of it fails STORE_UNAVAILABLE.
273
+ expect(readPackIndex({ kind: "unavailable", reason: "not built" })).toBe(
274
+ undefined,
275
+ );
276
+ });
277
+ });
278
+
279
+ describe("the storeless fast path implements the pointer half", () => {
280
+ afterEach(() => {
281
+ vi.unstubAllEnvs();
282
+ });
283
+
284
+ // `__complete` cannot see `origins.packs` (the config evaluator is banned on
285
+ // this graph), so it cannot distinguish a fresh install from a configured-but-
286
+ // unbuilt project. It CAN see the pointer, and must not prefer the snapshot
287
+ // over a pointer whose pack the cache lost — that is the decision table's
288
+ // second row, where the two readers used to disagree.
289
+ it("offers no candidates when the pointed-at pack is gone", async () => {
290
+ const cwd = mkdtempSync(join(tmpdir(), "pragma-idx-cwd-"));
291
+ const cache = mkdtempSync(join(tmpdir(), "pragma-idx-cache-"));
292
+ vi.stubEnv("XDG_CACHE_HOME", cache);
293
+ const pointer = activePackPath(cwd);
294
+ mkdirSync(dirname(pointer), { recursive: true });
295
+ writeFileSync(pointer, CRAFTED_HASH); // …but no pack directory for it.
296
+
297
+ expect(createIndexEntityReader(cwd)("ds:Component", "")).toEqual([]);
298
+ expect(
299
+ await indexCompletionEnv(cwd).names({ from: "index", type: "" }),
300
+ ).toEqual([]);
301
+ });
302
+
303
+ it("still offers the snapshot's names in a configured-but-unbuilt project (the documented price)", async () => {
304
+ // The bounded exception `entitySource.ts` documents, pinned as it IS and
305
+ // not as it should be. The fast path is deliberately denied the config
306
+ // layer, so a `pragma.config.ts` declaring packs is invisible to it: with
307
+ // no pointer it cannot tell this project from a fresh install, and it
308
+ // answers from the embedded snapshot.
309
+ //
310
+ // Both halves are asserted against the SAME cwd, because the ASYMMETRY is
311
+ // the whole claim — asserting only that the snapshot answers would restate
312
+ // the fresh-install case the PROTECTED contract describe already covers,
313
+ // and would pass with the config file deleted. The read half refusing is
314
+ // what makes this directory a configured-but-unbuilt project rather than a
315
+ // fresh install. (`doctor` says so too, in the same words: verified against
316
+ // the compiled binary, where `block list` raises STORE_UNAVAILABLE while
317
+ // this same completion still answers.)
318
+ //
319
+ // If a later change gives the fast path a config-free way to see
320
+ // `origins.packs`, THIS is the test that must move, deliberately.
321
+ const cwd = mkdtempSync(join(tmpdir(), "pragma-unbuilt-cwd-"));
322
+ vi.stubEnv(
323
+ "XDG_CACHE_HOME",
324
+ mkdtempSync(join(tmpdir(), "pragma-unbuilt-")),
325
+ );
326
+ writeFileSync(
327
+ join(cwd, "pragma.config.ts"),
328
+ 'export default { packs: [{ name: "unbuilt", source: "file:///pragma-never-built" }] };\n',
329
+ );
330
+
331
+ const decision = resolveSources(await readConfig(cwd), cwd);
332
+ expect(decision).toEqual({
333
+ kind: "unavailable",
334
+ reason: "packs are configured but the store has not been built",
335
+ });
336
+ expect(readPackIndex(decision)).toBeUndefined();
337
+
338
+ expect(createIndexEntityReader(cwd)("ds:Component", "")).toContain(
339
+ "ds:global.component.button",
340
+ );
341
+ });
342
+ });