@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
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The published configuration reference agrees with the validator a config
3
+ * actually hits.
4
+ *
5
+ * Two independently produced artifacts: `docs/reference/config.md` is rendered
6
+ * from an exhaustive `Record<keyof RawConfig, …>` (so `tsc` pins its row set to
7
+ * the TYPE), while `rawConfigSchema` is hand-written zod. `parseRawConfig`
8
+ * casts its result `as RawConfig`, so nothing else compares the two — a field
9
+ * the type declares and zod strips, or one zod accepts and the page never
10
+ * mentions, would be invisible.
11
+ *
12
+ * The page is parsed exactly as a reader would read it: the `Field` column of
13
+ * its `## Fields` table. Zod lives here already, so no new import crosses a
14
+ * boundary.
15
+ */
16
+
17
+ import { describe, expect, it } from "vitest";
18
+ import { emitReference } from "../spec/emitReference.js";
19
+ import { rawConfigSchema } from "./schema.js";
20
+
21
+ /** The `field` cells of the generated page's Fields table, in page order. */
22
+ function readDocumentedFields(): string[] {
23
+ const page = emitReference([]).get("config.md") ?? "";
24
+ return [...page.matchAll(/^\| `([a-zA-Z]+)` \| /gm)].map((row) => row.at(1));
25
+ }
26
+
27
+ describe("the configuration reference agrees with the validator", () => {
28
+ it("documents exactly the fields the validator accepts", () => {
29
+ expect(readDocumentedFields().sort()).toEqual(
30
+ Object.keys(rawConfigSchema.shape).sort(),
31
+ );
32
+ });
33
+
34
+ it("marks a field optional iff the validator lets a layer omit it", () => {
35
+ // Every layer field is optional — a layer declares only what it overrides,
36
+ // which is what makes per-field provenance possible. Stated as a derived
37
+ // correspondence rather than a constant so a required field added later
38
+ // fails here instead of quietly contradicting the page.
39
+ const page = emitReference([]).get("config.md") ?? "";
40
+ for (const [field, schema] of Object.entries(rawConfigSchema.shape)) {
41
+ const row = page
42
+ .split("\n")
43
+ .find((line) => line.startsWith(`| \`${field}\` |`));
44
+ expect(row, `no row for \`${field}\``).toBeDefined();
45
+ expect(row?.includes("(optional)"), `\`${field}\` optionality`).toBe(
46
+ schema.safeParse(undefined).success,
47
+ );
48
+ }
49
+ });
50
+ });
@@ -6,32 +6,79 @@
6
6
  * path, which are storeless and config-free. Validates the raw shape a global
7
7
  * JSON file or an evaluated `pragma.config.ts` declares; unknown keys are
8
8
  * stripped for forward compatibility, and only present keys survive so layer
9
- * merging keeps honest per-field provenance.
9
+ * merging keeps honest per-field provenance. Legacy shapes are NOT left to the
10
+ * unknown-key stripping: each is detected before validation and rejected with
11
+ * a loud CONFIG_ERROR naming the file and the fix, so an old config fails
12
+ * telling the user exactly what to change instead of silently ignoring the
13
+ * field. Three today: the `packages` key (renamed to `packs`), and the removed
14
+ * `completion.caseSensitive` and `generators` fields (each validated and read
15
+ * by nothing).
10
16
  */
11
17
 
12
18
  import { z } from "zod";
13
19
  import { PragmaError } from "../error/PragmaError.js";
14
- import { CHANNELS, type RawConfig } from "./types.js";
20
+ import { CHANNELS, DETAIL_LEVELS, type RawConfig } from "./types.js";
15
21
 
16
- const packageEntrySchema = z.union([
22
+ const packDeclarationSchema = z.union([
17
23
  z.string().min(1),
18
- z.object({ name: z.string().min(1), source: z.string().optional() }),
24
+ z.object({
25
+ name: z.string().min(1),
26
+ source: z.string().optional(),
27
+ // Opaque here: the pack grammar is validated by `parsePackDefinition` at
28
+ // dispatch, not by the config layer. Without this key the object arm would
29
+ // STRIP declared stories silently (unknown keys are dropped for forward
30
+ // compatibility), so a pack's stories would vanish with no error.
31
+ stories: z.array(z.unknown()).optional(),
32
+ }),
19
33
  ]);
20
34
 
21
35
  const completionSchema = z.object({
22
36
  minChars: z.number().int().min(0).optional(),
23
- caseSensitive: z.boolean().optional(),
24
37
  families: z.record(z.string(), z.boolean()).optional(),
25
38
  });
26
39
 
27
- const rawConfigSchema = z.object({
40
+ /**
41
+ * The `completion` object a raw layer declares, when it declares one — read
42
+ * WITHOUT validation, for the removed-field detection that must run before
43
+ * the schema's unknown-key stripping could hide the key.
44
+ */
45
+ function declaredCompletion(value: unknown): Record<string, unknown> | null {
46
+ if (typeof value !== "object" || value === null) return null;
47
+ const { completion } = value as { completion?: unknown };
48
+ return typeof completion === "object" && completion !== null
49
+ ? (completion as Record<string, unknown>)
50
+ : null;
51
+ }
52
+
53
+ /**
54
+ * The validator every config layer passes through. EXPORTED for
55
+ * `schema.test.ts`, which compares its field set and each field's optionality
56
+ * against the generated `docs/reference/config.md` — the two are produced
57
+ * independently (the page from `keyof RawConfig`, this from hand-written zod)
58
+ * and `parseRawConfig`'s `as RawConfig` cast means `tsc` cannot catch them
59
+ * drifting apart.
60
+ */
61
+ export const rawConfigSchema = z.object({
62
+ name: z.string().min(1).optional(),
63
+ help: z.string().min(1).optional(),
64
+ // Declared toolchain content: the `colophon` verb renders whatever the
65
+ // distribution declares here (markdown body + optional condensed summary).
66
+ colophon: z
67
+ .object({
68
+ markdown: z.string().min(1),
69
+ summary: z.string().min(1).optional(),
70
+ })
71
+ .optional(),
72
+ issuesUrl: z.string().url().optional(),
28
73
  tier: z.string().optional(),
29
74
  channel: z.enum(CHANNELS).optional(),
30
- detail: z.string().optional(),
31
- packages: z.array(packageEntrySchema).optional(),
75
+ // Closed over the documented ladder, like `channel`: a level the renderer
76
+ // would silently degrade to `standard` is a config error naming the file and
77
+ // the three valid values, not a value `config show` reports as honoured.
78
+ detail: z.enum(DETAIL_LEVELS).optional(),
79
+ packs: z.array(packDeclarationSchema).optional(),
32
80
  stories: z.array(z.unknown()).optional(),
33
81
  prefixes: z.record(z.string(), z.string()).optional(),
34
- prompts: z.record(z.string(), z.unknown()).optional(),
35
82
  completion: completionSchema.optional(),
36
83
  });
37
84
 
@@ -41,9 +88,49 @@ const rawConfigSchema = z.object({
41
88
  * @param value - The parsed JSON or evaluated module default.
42
89
  * @param source - The file path, used in error messages.
43
90
  * @returns The validated layer values (only the keys actually present).
44
- * @throws PragmaError with code `CONFIG_ERROR` on an invalid shape.
91
+ * @throws PragmaError with code `CONFIG_ERROR` on an invalid shape, when the
92
+ * value declares the legacy `packages` key (renamed to `packs`), or when it
93
+ * still sets the removed `generators` or `completion.caseSensitive` fields.
45
94
  */
46
95
  export function parseRawConfig(value: unknown, source: string): RawConfig {
96
+ // Rename detection must precede validation: unknown keys are stripped, so a
97
+ // legacy `packages` field would otherwise vanish silently.
98
+ if (typeof value === "object" && value !== null && "packages" in value) {
99
+ throw PragmaError.configError(
100
+ `Invalid config in ${source}: the "packages" field was renamed to "packs". The entry shape is unchanged.`,
101
+ {
102
+ recovery: {
103
+ message: `In ${source}, rename "packages:" to "packs".`,
104
+ },
105
+ },
106
+ );
107
+ }
108
+ // Removed-field detection, before validation for the same reason: each field
109
+ // was accepted and read by NOTHING, so a config still setting one gets a
110
+ // loud error naming the removed field, not silence. `generators` was ruled
111
+ // dead (L-OPEN-1): `resolveSources` never read it and the `create` verbs
112
+ // resolve their generators statically — declaring it only changed what
113
+ // `config show` printed.
114
+ if (typeof value === "object" && value !== null && "generators" in value) {
115
+ throw PragmaError.configError(
116
+ `Invalid config in ${source}: the "generators" field was removed — it was read by nothing. The create verbs resolve their generators statically.`,
117
+ {
118
+ recovery: {
119
+ message: `In ${source}, delete the "generators" field.`,
120
+ },
121
+ },
122
+ );
123
+ }
124
+ if ("caseSensitive" in (declaredCompletion(value) ?? {})) {
125
+ throw PragmaError.configError(
126
+ `Invalid config in ${source}: the "completion.caseSensitive" field was removed — it was read by nothing. Completion matching is declared by the grammar, not configured.`,
127
+ {
128
+ recovery: {
129
+ message: `In ${source}, delete "caseSensitive" from "completion".`,
130
+ },
131
+ },
132
+ );
133
+ }
47
134
  const result = rawConfigSchema.safeParse(value ?? {});
48
135
  if (!result.success) {
49
136
  const issue = result.error.issues[0];
@@ -13,14 +13,47 @@ export const CHANNELS = ["normal", "experimental", "prerelease"] as const;
13
13
  /** A release channel name. */
14
14
  export type Channel = (typeof CHANNELS)[number];
15
15
 
16
- /** The object form of a package source declaration. */
17
- export interface PackageDeclaration {
16
+ /**
17
+ * Progressive-disclosure levels, least to most detail. Declared HERE, beside
18
+ * {@link CHANNELS}, because the config validator closes `detail` over this set
19
+ * and this module is the one config module the storeless fast path may reach
20
+ * (`completion/safety.test.ts` positive-lists it; `capabilities/lazy.test.ts`
21
+ * pins it inert). `src/constants.ts` re-exports it for every other reader.
22
+ */
23
+ export const DETAIL_LEVELS = ["summary", "standard", "detailed"] as const;
24
+
25
+ /** A progressive-disclosure level. */
26
+ export type DetailLevel = (typeof DETAIL_LEVELS)[number];
27
+
28
+ /** The object form of a pack source declaration. */
29
+ export interface PackSource {
18
30
  readonly name: string;
19
31
  readonly source?: string;
32
+ /**
33
+ * Declarative read stories this pack supplies, in the pack grammar
34
+ * (`kernel/packs/types.PackDefinition`). OPAQUE here on purpose — the config
35
+ * layer does not know that grammar; `parsePackDefinition` validates it at
36
+ * dispatch. Declared beside the pack so a story can move out to the package's
37
+ * own `stories/*.json` by deleting this field and nothing else.
38
+ */
39
+ readonly stories?: readonly unknown[];
20
40
  }
21
41
 
22
- /** A `packages` entry: a bare npm name or a `{ name, source }` declaration. */
23
- export type PackageEntry = string | PackageDeclaration;
42
+ /** A `packs` entry: a bare npm name or a `{ name, source }` declaration. */
43
+ export type PackDeclaration = string | PackSource;
44
+
45
+ /**
46
+ * The toolchain colophon the distribution declares — CONTENT, not machinery.
47
+ * The `colophon` verb renders whatever is declared here as its first section,
48
+ * titled with the distribution's name; a fork tells its own story by editing
49
+ * its config. Both strings are Markdown BODIES with no leading H1 (the
50
+ * renderer supplies the heading). `summary` is the condensed `--format llm`
51
+ * form; omitted, the full `markdown` serves both.
52
+ */
53
+ export interface ColophonDeclaration {
54
+ readonly markdown: string;
55
+ readonly summary?: string;
56
+ }
24
57
 
25
58
  /**
26
59
  * Completion policy, read at `setup completions` emit time (never on the
@@ -31,28 +64,41 @@ export type PackageEntry = string | PackageDeclaration;
31
64
  export interface CompletionConfig {
32
65
  /** Minimum typed chars before the shell execs `__complete` (default 2). */
33
66
  readonly minChars?: number;
34
- /** Match case-sensitively (default false — loose match, canonical emit). */
35
- readonly caseSensitive?: boolean;
36
67
  /** Per-family opt-out: a noun mapped to `false` drops its name completion. */
37
68
  readonly families?: Readonly<Record<string, boolean>>;
38
69
  }
39
70
 
40
- /** The effective, resolved configuration. `channel` always has a value. */
71
+ /**
72
+ * The effective, resolved configuration. `channel` always has a value.
73
+ *
74
+ * IDENTITY IS NOT HERE. `name`, `help` and `issuesUrl` are read from
75
+ * `pragma.conf.ts` by `src/constants.ts` at module load, because the surfaces
76
+ * that need them — `--help`, `__complete`, the MCP handshake, first-run
77
+ * onboarding — all run before or without the config layer; `colophon` is read
78
+ * from the same file at render time by the `colophon` verb. They stay in
79
+ * {@link RawConfig} (the distribution config is `satisfies RawConfig`), but
80
+ * merging them into the effective config only bought `config show` a
81
+ * `[project]` marker the kernel does not honour.
82
+ */
41
83
  export interface PragmaConfig {
42
84
  /** Active tier path, or absent when no tier is configured. */
43
85
  readonly tier?: string;
44
86
  /** Release channel controlling component visibility. */
45
87
  readonly channel: Channel;
46
88
  /** Default progressive-disclosure level. */
47
- readonly detail?: string;
48
- /** Semantic package sources; replaces (does not merge) across layers. */
49
- readonly packages?: readonly PackageEntry[];
50
- /** Declarative read stories compiled at boot (experimental; opaque here). */
89
+ readonly detail?: DetailLevel;
90
+ /** Semantic pack sources; replaces (does not merge) across layers. */
91
+ readonly packs?: readonly PackDeclaration[];
92
+ /** Declarative read stories, compiled at DISPATCH (opaque here). */
51
93
  readonly stories?: readonly unknown[];
52
- /** Additional namespace prefixes merged over the built-in map. */
94
+ /**
95
+ * Namespace prefixes the pack is built with — they win every harvest, so a
96
+ * layer here decides what the store binds and what the index is keyed under.
97
+ * The DISTRIBUTION layer's entries additionally seed the compiled-in
98
+ * `DEFAULT_PREFIX_MAP`; a project layer's do not, because that map is read on
99
+ * the storeless fast path where no config layer exists.
100
+ */
53
101
  readonly prefixes?: Readonly<Record<string, string>>;
54
- /** Named prompt overrides (global machine state). */
55
- readonly prompts?: Readonly<Record<string, unknown>>;
56
102
  /** Completion policy (read at `setup completions` emit time). */
57
103
  readonly completion?: CompletionConfig;
58
104
  }
@@ -61,15 +107,24 @@ export interface PragmaConfig {
61
107
  * The fields a single config layer (global JSON or project TS) may declare. A
62
108
  * key is present only when that layer sets it — presence drives which layer
63
109
  * wins during the merge.
110
+ *
111
+ * WIDER than {@link PragmaConfig}: the DISTRIBUTION layer (`pragma.conf.ts`)
112
+ * is `satisfies RawConfig` and declares the four identity fields, which are
113
+ * read statically and never merged. A global or project layer declaring one is
114
+ * accepted by the validator and has no effect — `docs/reference/config.md`
115
+ * says so, and `readConfig.test.ts` pins it.
64
116
  */
65
117
  export interface RawConfig {
118
+ readonly name?: string;
119
+ readonly help?: string;
120
+ readonly colophon?: ColophonDeclaration;
121
+ readonly issuesUrl?: string;
66
122
  readonly tier?: string;
67
123
  readonly channel?: Channel;
68
- readonly detail?: string;
69
- readonly packages?: readonly PackageEntry[];
124
+ readonly detail?: DetailLevel;
125
+ readonly packs?: readonly PackDeclaration[];
70
126
  readonly stories?: readonly unknown[];
71
127
  readonly prefixes?: Readonly<Record<string, string>>;
72
- readonly prompts?: Readonly<Record<string, unknown>>;
73
128
  readonly completion?: CompletionConfig;
74
129
  }
75
130
 
@@ -81,10 +136,9 @@ export interface ConfigOrigins {
81
136
  readonly tier: ConfigOrigin;
82
137
  readonly channel: ConfigOrigin;
83
138
  readonly detail: ConfigOrigin;
84
- readonly packages: ConfigOrigin;
139
+ readonly packs: ConfigOrigin;
85
140
  readonly stories: ConfigOrigin;
86
141
  readonly prefixes: ConfigOrigin;
87
- readonly prompts: ConfigOrigin;
88
142
  }
89
143
 
90
144
  /** A resolved config layer's file location and existence. */