@canonical/pragma-cli 0.35.0 → 0.37.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 +2 -2
  2. package/bundled-skills/add-standard/SKILL.md +338 -0
  3. package/bundled-skills/adoption-a1-styles/SKILL.md +269 -0
  4. package/bundled-skills/adoption-a2-components/SKILL.md +291 -0
  5. package/bundled-skills/adoption-a3-forms/SKILL.md +260 -0
  6. package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +406 -0
  7. package/bundled-skills/anatomy-author/SKILL.md +1226 -0
  8. package/bundled-skills/design-auditor/SKILL.md +455 -0
  9. package/bundled-skills/implement-component/SKILL.md +333 -0
  10. package/bundled-skills/specify-component/SKILL.md +331 -0
  11. package/bundled-skills/specify-pattern/SKILL.md +292 -0
  12. package/dist/package.json +24 -20
  13. package/dist/pragma.conf.js +266 -34
  14. package/dist/pragma.conf.js.map +1 -1
  15. package/dist/src/bin.js +1 -1
  16. package/dist/src/bin.js.map +1 -1
  17. package/dist/src/capabilities/capabilities/hints.js +1 -1
  18. package/dist/src/capabilities/capabilities/hints.js.map +1 -1
  19. package/dist/src/capabilities/colophon/colophon.render.js +1 -1
  20. package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
  21. package/dist/src/capabilities/create/create.verb.js +2 -1
  22. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  23. package/dist/src/capabilities/create/createSurface.generated.js +23 -1
  24. package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
  25. package/dist/src/capabilities/create/mount.js +4 -3
  26. package/dist/src/capabilities/create/mount.js.map +1 -1
  27. package/dist/src/capabilities/doctor/checks/checkNodeVersion.js +11 -6
  28. package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
  29. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +60 -4
  30. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
  31. package/dist/src/capabilities/doctor/checks/checkShellCompletions.js +8 -8
  32. package/dist/src/capabilities/doctor/checks/checkShellCompletions.js.map +1 -1
  33. package/dist/src/capabilities/doctor/checks/harnessInventory.js +195 -0
  34. package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -0
  35. package/dist/src/capabilities/doctor/checks/index.js +6 -6
  36. package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js +38 -0
  37. package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js.map +1 -0
  38. package/dist/src/capabilities/doctor/checks/targetHealth.js +194 -50
  39. package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
  40. package/dist/src/capabilities/doctor/doctor.render.js +43 -64
  41. package/dist/src/capabilities/doctor/doctor.render.js.map +1 -1
  42. package/dist/src/capabilities/doctor/doctor.verb.js +6 -2
  43. package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
  44. package/dist/src/capabilities/doctor/runChecks.js +14 -11
  45. package/dist/src/capabilities/doctor/runChecks.js.map +1 -1
  46. package/dist/src/capabilities/graph/query.render.js +2 -2
  47. package/dist/src/capabilities/graph/query.render.js.map +1 -1
  48. package/dist/src/capabilities/info/collectInfo.js +8 -2
  49. package/dist/src/capabilities/info/collectInfo.js.map +1 -1
  50. package/dist/src/capabilities/info/info.render.js +6 -2
  51. package/dist/src/capabilities/info/info.render.js.map +1 -1
  52. package/dist/src/capabilities/prompt/prompt.render.js +2 -2
  53. package/dist/src/capabilities/prompt/prompt.render.js.map +1 -1
  54. package/dist/src/capabilities/setup/buildPlan.js +43 -39
  55. package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
  56. package/dist/src/capabilities/setup/mode.js +2 -2
  57. package/dist/src/capabilities/setup/operations/index.js +4 -4
  58. package/dist/src/capabilities/setup/operations/index.js.map +1 -1
  59. package/dist/src/capabilities/setup/operations/setupCompletions.js +6 -2
  60. package/dist/src/capabilities/setup/operations/setupCompletions.js.map +1 -1
  61. package/dist/src/capabilities/setup/operations/setupConfig.js +4 -2
  62. package/dist/src/capabilities/setup/operations/setupConfig.js.map +1 -1
  63. package/dist/src/capabilities/setup/operations/setupGenerator.js +156 -58
  64. package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
  65. package/dist/src/capabilities/setup/operations/setupLsp.js +136 -38
  66. package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
  67. package/dist/src/capabilities/setup/operations/setupMcp.js +101 -52
  68. package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
  69. package/dist/src/capabilities/setup/operations/setupSkills.js +329 -49
  70. package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
  71. package/dist/src/capabilities/setup/plan.js.map +1 -1
  72. package/dist/src/capabilities/setup/plan.render.js +144 -66
  73. package/dist/src/capabilities/setup/plan.render.js.map +1 -1
  74. package/dist/src/capabilities/setup/setup.render.js +1 -1
  75. package/dist/src/capabilities/setup/setup.verb.js +165 -63
  76. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  77. package/dist/src/capabilities/setup/targets.js +122 -51
  78. package/dist/src/capabilities/setup/targets.js.map +1 -1
  79. package/dist/src/capabilities/shared/index.js +7 -6
  80. package/dist/src/capabilities/shared/index.js.map +1 -1
  81. package/dist/src/capabilities/shared/packageManager.js +300 -54
  82. package/dist/src/capabilities/shared/packageManager.js.map +1 -1
  83. package/dist/src/capabilities/skill/discover.js +97 -11
  84. package/dist/src/capabilities/skill/discover.js.map +1 -1
  85. package/dist/src/capabilities/skill/render.js +2 -2
  86. package/dist/src/capabilities/skill/render.js.map +1 -1
  87. package/dist/src/capabilities/sources/installSkills.js +145 -3
  88. package/dist/src/capabilities/sources/installSkills.js.map +1 -1
  89. package/dist/src/capabilities/sources/runUpdate.js +68 -2
  90. package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
  91. package/dist/src/capabilities/sources/update.verb.js +3 -3
  92. package/dist/src/capabilities/sources/update.verb.js.map +1 -1
  93. package/dist/src/capabilities/upgrade/runUpgrade.js +43 -7
  94. package/dist/src/capabilities/upgrade/runUpgrade.js.map +1 -1
  95. package/dist/src/capabilities/upgrade/upgrade.render.js +10 -2
  96. package/dist/src/capabilities/upgrade/upgrade.render.js.map +1 -1
  97. package/dist/src/constants.js +13 -2
  98. package/dist/src/constants.js.map +1 -1
  99. package/dist/src/kernel/index.js +11 -2
  100. package/dist/src/kernel/index.js.map +1 -1
  101. package/dist/src/kernel/interactivity.js +68 -0
  102. package/dist/src/kernel/interactivity.js.map +1 -0
  103. package/dist/src/kernel/packs/renderPack.js +3 -3
  104. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  105. package/dist/src/kernel/packs/resolveEntity.js +63 -12
  106. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  107. package/dist/src/kernel/packs/runBodies.js +57 -4
  108. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  109. package/dist/src/kernel/packs/schema.js +54 -2
  110. package/dist/src/kernel/packs/schema.js.map +1 -1
  111. package/dist/src/kernel/packs/sparql/applyFilters.js +104 -5
  112. package/dist/src/kernel/packs/sparql/applyFilters.js.map +1 -1
  113. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +310 -43
  114. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  115. package/dist/src/kernel/packs/types.js.map +1 -1
  116. package/dist/src/kernel/project/cli/buildProgram.js +1 -1
  117. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  118. package/dist/src/kernel/project/cli/dispatch.js +46 -24
  119. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  120. package/dist/src/kernel/project/cli/globalFlags.js +4 -1
  121. package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
  122. package/dist/src/kernel/project/cli/helpFormat.js +1 -2
  123. package/dist/src/kernel/project/cli/helpFormat.js.map +1 -1
  124. package/dist/src/kernel/project/cli/rootHelp.js +42 -9
  125. package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
  126. package/dist/src/kernel/project/mcp/registerVerb.js +16 -1
  127. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  128. package/dist/src/kernel/render/renderers.js +1 -1
  129. package/dist/src/kernel/render/style.js +29 -9
  130. package/dist/src/kernel/render/style.js.map +1 -1
  131. package/dist/src/kernel/render/vocabulary.js +174 -0
  132. package/dist/src/kernel/render/vocabulary.js.map +1 -0
  133. package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js +2 -3
  134. package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js.map +1 -1
  135. package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js +1 -1
  136. package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js.map +1 -1
  137. package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js +4 -0
  138. package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js.map +1 -0
  139. package/dist/src/kernel/runtime/graphpack/embedded.js +13 -6
  140. package/dist/src/kernel/runtime/graphpack/embedded.js.map +1 -1
  141. package/dist/src/kernel/runtime/graphpack/read.js +14 -9
  142. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  143. package/dist/src/kernel/runtime/loadSession.js +3 -1
  144. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  145. package/dist/src/kernel/runtime/probe.js +1 -1
  146. package/dist/src/kernel/runtime/probe.js.map +1 -1
  147. package/dist/src/kernel/runtime/refs/parseRef.js +17 -1
  148. package/dist/src/kernel/runtime/refs/parseRef.js.map +1 -1
  149. package/dist/src/kernel/runtime/refs/resolve.js +9 -3
  150. package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
  151. package/dist/src/kernel/spec/emitReference.js +1 -1
  152. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  153. package/package.json +24 -20
  154. package/pragma.conf.ts +272 -34
  155. package/src/bin.ts +8 -1
  156. package/src/capabilities/block.parity.test.ts +25 -11
  157. package/src/capabilities/block.shipped.exec.test.ts +282 -0
  158. package/src/capabilities/block.tierRank.exec.test.ts +300 -0
  159. package/src/capabilities/capabilities/hints.ts +1 -1
  160. package/src/capabilities/colophon/colophon.render.ts +1 -2
  161. package/src/capabilities/colophon/colophon.test.ts +2 -2
  162. package/src/capabilities/config/collectConfigShow.test.ts +1 -0
  163. package/src/capabilities/create/byteEquality.test.ts +28 -1
  164. package/src/capabilities/create/create.test.ts +1 -0
  165. package/src/capabilities/create/create.verb.ts +2 -1
  166. package/src/capabilities/create/createGrammar.test.ts +5 -2
  167. package/src/capabilities/create/createSurface.generated.ts +23 -1
  168. package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
  169. package/src/capabilities/create/mount.ts +3 -3
  170. package/src/capabilities/distribution.parity.test.ts +4 -0
  171. package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
  172. package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
  173. package/src/capabilities/doctor/checks/checkPackageRefs.ts +69 -4
  174. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +6 -4
  175. package/src/capabilities/doctor/checks/checkShellCompletions.ts +8 -8
  176. package/src/capabilities/doctor/checks/harnessInventory.test.ts +219 -0
  177. package/src/capabilities/doctor/checks/harnessInventory.ts +298 -0
  178. package/src/capabilities/doctor/checks/index.ts +6 -6
  179. package/src/capabilities/doctor/checks/isSupportedNodeVersion.test.ts +111 -0
  180. package/src/capabilities/doctor/checks/isSupportedNodeVersion.ts +34 -0
  181. package/src/capabilities/doctor/checks/mcpCommand.test.ts +7 -1
  182. package/src/capabilities/doctor/checks/targetHealth.ts +229 -59
  183. package/src/capabilities/doctor/doctor.render.test.ts +108 -32
  184. package/src/capabilities/doctor/doctor.render.ts +56 -80
  185. package/src/capabilities/doctor/doctor.test.ts +247 -35
  186. package/src/capabilities/doctor/doctor.verb.ts +7 -2
  187. package/src/capabilities/doctor/runChecks.ts +14 -11
  188. package/src/capabilities/doctor/types.ts +15 -24
  189. package/src/capabilities/emptyRecovery.test.ts +10 -2
  190. package/src/capabilities/graph/query.render.ts +2 -2
  191. package/src/capabilities/info/collectInfo.ts +8 -1
  192. package/src/capabilities/info/info.render.ts +6 -2
  193. package/src/capabilities/info/info.test.ts +37 -1
  194. package/src/capabilities/info/types.ts +10 -2
  195. package/src/capabilities/lazy.test.ts +26 -2
  196. package/src/capabilities/listLookup.shipped.exec.test.ts +340 -0
  197. package/src/capabilities/prompt/prompt.render.ts +2 -2
  198. package/src/capabilities/renderVocabularySync.test.ts +82 -0
  199. package/src/capabilities/resources/resources.test.ts +38 -2
  200. package/src/capabilities/scopeTypeSync.test.ts +29 -15
  201. package/src/capabilities/setup/buildPlan.ts +45 -41
  202. package/src/capabilities/setup/index.ts +1 -1
  203. package/src/capabilities/setup/mode.ts +2 -2
  204. package/src/capabilities/setup/operations/index.ts +6 -1
  205. package/src/capabilities/setup/operations/setupCompletions.ts +9 -2
  206. package/src/capabilities/setup/operations/setupConfig.ts +7 -2
  207. package/src/capabilities/setup/operations/setupGenerator.ts +227 -84
  208. package/src/capabilities/setup/operations/setupLsp.ts +203 -38
  209. package/src/capabilities/setup/operations/setupMcp.ts +139 -62
  210. package/src/capabilities/setup/operations/setupSkills.ts +399 -60
  211. package/src/capabilities/setup/plan.render.ts +171 -76
  212. package/src/capabilities/setup/plan.ts +17 -12
  213. package/src/capabilities/setup/setup.mcp.correctness.test.ts +9 -9
  214. package/src/capabilities/setup/setup.render.test.ts +50 -36
  215. package/src/capabilities/setup/setup.render.ts +1 -1
  216. package/src/capabilities/setup/setup.test.ts +1126 -50
  217. package/src/capabilities/setup/setup.verb.ts +174 -68
  218. package/src/capabilities/setup/targets.ts +138 -60
  219. package/src/capabilities/setup/types.ts +10 -18
  220. package/src/capabilities/shared/index.ts +18 -6
  221. package/src/capabilities/shared/packageManager.test.ts +421 -6
  222. package/src/capabilities/shared/packageManager.ts +394 -59
  223. package/src/capabilities/skill/bundledSkills.test.ts +347 -0
  224. package/src/capabilities/skill/discover.ts +100 -11
  225. package/src/capabilities/skill/render.ts +2 -2
  226. package/src/capabilities/skill/skill.test.ts +17 -4
  227. package/src/capabilities/sources/installSkills.ts +181 -3
  228. package/src/capabilities/sources/runUpdate.ts +78 -2
  229. package/src/capabilities/sources/sources.test.ts +228 -0
  230. package/src/capabilities/sources/update.verb.ts +3 -3
  231. package/src/capabilities/surface.test.ts +14 -6
  232. package/src/capabilities/upgrade/runUpgrade.ts +45 -6
  233. package/src/capabilities/upgrade/types.ts +10 -2
  234. package/src/capabilities/upgrade/upgrade.render.ts +9 -2
  235. package/src/capabilities/upgrade/upgrade.test.ts +72 -0
  236. package/src/constants.ts +14 -1
  237. package/src/identity.test.ts +18 -5
  238. package/src/kernel/completion/entitySource.test.ts +15 -2
  239. package/src/kernel/completion/safety.test.ts +16 -9
  240. package/src/kernel/config/defaults.test.ts +19 -3
  241. package/src/kernel/config/readConfig.test.ts +1 -1
  242. package/src/kernel/index.ts +11 -2
  243. package/src/kernel/interactivity.test.ts +76 -0
  244. package/src/kernel/interactivity.ts +69 -0
  245. package/src/kernel/packs/compile.test.ts +92 -0
  246. package/src/kernel/packs/renderPack.ts +3 -3
  247. package/src/kernel/packs/resolveEntity.test.ts +75 -14
  248. package/src/kernel/packs/resolveEntity.ts +88 -24
  249. package/src/kernel/packs/runBodies.ts +84 -19
  250. package/src/kernel/packs/schema.ts +60 -4
  251. package/src/kernel/packs/sparql/applyFilters.ts +125 -4
  252. package/src/kernel/packs/sparql/buildLookupQuery.test.ts +290 -0
  253. package/src/kernel/packs/sparql/buildLookupQuery.ts +327 -45
  254. package/src/kernel/packs/toolDescription.test.ts +35 -1
  255. package/src/kernel/packs/types.ts +125 -2
  256. package/src/kernel/project/cli/buildProgram.test.ts +73 -2
  257. package/src/kernel/project/cli/buildProgram.ts +8 -1
  258. package/src/kernel/project/cli/dispatch.test.ts +5 -4
  259. package/src/kernel/project/cli/dispatch.ts +48 -25
  260. package/src/kernel/project/cli/globalFlags.ts +4 -1
  261. package/src/kernel/project/cli/helpFormat.ts +1 -2
  262. package/src/kernel/project/cli/rootHelp.ts +44 -8
  263. package/src/kernel/project/mcp/registerVerb.ts +20 -1
  264. package/src/kernel/render/prefixes.test.ts +2 -0
  265. package/src/kernel/render/renderers.ts +1 -1
  266. package/src/kernel/render/style.ts +32 -9
  267. package/src/kernel/render/vocabulary.ts +276 -0
  268. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +2 -3
  269. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +1 -1
  270. package/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.ts +3 -0
  271. package/src/kernel/runtime/graphpack/embedded.ts +14 -6
  272. package/src/kernel/runtime/graphpack/graphpack.test.ts +31 -5
  273. package/src/kernel/runtime/graphpack/read.ts +14 -12
  274. package/src/kernel/runtime/loadSession.ts +3 -1
  275. package/src/kernel/runtime/probe.ts +1 -1
  276. package/src/kernel/runtime/refs/parseRef.test.ts +49 -1
  277. package/src/kernel/runtime/refs/parseRef.ts +25 -1
  278. package/src/kernel/runtime/refs/resolve.ts +9 -3
  279. package/src/kernel/runtime/types.ts +17 -0
  280. package/src/kernel/spec/emitReference.ts +1 -1
  281. package/src/kernel/spec/types.ts +35 -6
  282. package/src/testing/PARITY_GAPS.ts +9 -3
  283. package/src/testing/behavioral/docExamples.test.ts +4 -2
  284. package/src/testing/behavioral/errorMatrix.mcp.test.ts +110 -7
  285. package/src/testing/behavioral/journeys.firstInstall.test.ts +7 -2
  286. package/src/testing/behavioral/journeys.packageStories.test.ts +6 -3
  287. package/src/testing/eval/__snapshots__/eval.test.ts.snap +26 -1
  288. package/src/testing/eval/cases/readNouns.ts +245 -9
  289. package/src/testing/fixtures/blockGraph.ts +28 -13
  290. package/src/testing/fixtures/graph/canonical.ts +95 -12
  291. package/src/testing/helpers/runCli.test.ts +44 -0
  292. package/src/testing/helpers/runCli.ts +27 -3
  293. package/dist/src/capabilities/shared/bands.js +0 -16
  294. package/dist/src/capabilities/shared/bands.js.map +0 -1
  295. package/src/capabilities/shared/bands.ts +0 -16
@@ -9,18 +9,18 @@
9
9
  * only the self-verb is an MCP tool. `destructive: false` keeps MCP from
10
10
  * advertising the tool as destructive.
11
11
  *
12
- * THE BAND DEFAULT IS GLOBAL. `--scope` used to default to `both`, and `both`
13
- * runs each harness's own default band — which is `project` for most of the
12
+ * THE SCOPE DEFAULT IS GLOBAL. `--scope` used to default to `both`, and `both`
13
+ * runs each harness's own default scope — which is `project` for most of the
14
14
  * registry — so a bare `setup` scattered `.mcp.json`, `.gemini/settings.json`
15
15
  * and `opencode.json` into whatever directory you happened to be standing in.
16
- * The user band is what a machine-level installer configures; the project band
16
+ * The user scope is what a machine-level installer configures; the project scope
17
17
  * is a deliberate, checked-in, team-shared choice, and is now chosen with
18
18
  * `--local` rather than assumed.
19
19
  *
20
- * Every sub-verb registers the scope flags UNIFORMLY. That is what lets the band
20
+ * Every sub-verb registers the scope flags UNIFORMLY. That is what lets the scope
21
21
  * answer come from the table instead of per-verb flag wiring — previously
22
22
  * `setup completions --local` died as an unknown option, while `setup mcp
23
- * --local` worked, for no reason a user could see. Asking a target for a band it
23
+ * --local` worked, for no reason a user could see. Asking a target for a scope it
24
24
  * cannot honour is a usage error (exit 2) with the corrected command; asking the
25
25
  * RUN-ALL for such a band merely filters, and the out-of-band target still shows
26
26
  * as a named row.
@@ -43,20 +43,25 @@ import type {
43
43
  } from "../../kernel/spec/index.js";
44
44
  import { resolveSetupMode, setupModeInput } from "./mode.js";
45
45
  import {
46
+ isActionable,
46
47
  planExitFailed,
47
48
  planTally,
48
49
  type SetupPlan,
49
50
  TARGET_IDS,
50
51
  } from "./plan.js";
51
- import { renderProgressLine, renderRecap } from "./plan.render.js";
52
+ import {
53
+ renderDetectionSummary,
54
+ renderProgressLine,
55
+ renderRecap,
56
+ } from "./plan.render.js";
52
57
  import { renderDryRun, setupFormatters } from "./setup.render.js";
53
- import type { ScopeBand, ScopeSelection, SetupMode } from "./types.js";
58
+ import type { Scope, ScopeSelection, SetupMode } from "./types.js";
54
59
 
55
60
  /**
56
61
  * The `--scope` flag + its `--global`/`--local` boolean sugars. Registered by
57
- * EVERY setup verb, including the single-band ones: a flag that exists on some
58
- * sub-verbs and not others is a grammar the user has to memorise, and the band
59
- * a single-band target can honour is a question the target table answers.
62
+ * EVERY setup verb, including the single-scope ones: a flag that exists on some
63
+ * sub-verbs and not others is a grammar the user has to memorise, and the scope
64
+ * a single-scope target can honour is a question the target table answers.
60
65
  *
61
66
  * `--global` is a synonym of the default; it stays for scripts and for symmetry
62
67
  * with `--local`.
@@ -65,19 +70,19 @@ const SCOPE_PARAMS: readonly ParamSpec[] = [
65
70
  {
66
71
  kind: "enum",
67
72
  name: "scope",
68
- doc: "Which config band(s) to configure: global (the default), project, or both.",
73
+ doc: "Where to configure: global (your home directory, the default), project (this repository), or both.",
69
74
  values: ["project", "global", "both"],
70
75
  default: "global",
71
76
  },
72
77
  {
73
78
  kind: "boolean",
74
79
  name: "global",
75
- doc: "Shorthand for --scope global (configure the user/home band).",
80
+ doc: "Shorthand for --scope global — configure your home directory.",
76
81
  },
77
82
  {
78
83
  kind: "boolean",
79
84
  name: "local",
80
- doc: "Shorthand for --scope project (configure the per-project band).",
85
+ doc: "Shorthand for --scope project — configure this project only.",
81
86
  },
82
87
  ];
83
88
 
@@ -96,34 +101,35 @@ function resolveScope(params: Record<string, unknown>): ScopeSelection {
96
101
  }
97
102
 
98
103
  /**
99
- * Reject a sub-verb asked for a band its target cannot hold.
104
+ * Reject a sub-verb asked for a scope its target cannot hold.
100
105
  *
101
106
  * This is a usage error, not a skip: the user typed a contradiction, and
102
- * correcting it is exactly what exit 2 means. The message names the band the
107
+ * correcting it is exactly what exit 2 means. The message names the scope the
103
108
  * target DOES have and prints the command that works, so the correction is one
104
109
  * paste rather than one manual page.
105
110
  *
106
111
  * @param mode - The sub-verb's target, or `all`.
107
112
  * @param scope - The resolved selection.
108
- * @throws PragmaError INVALID_INPUT when the target has no such band.
113
+ * @throws PragmaError INVALID_INPUT when the target has no such scope.
109
114
  */
110
- async function assertBandIsPossible(
115
+ async function assertScopeIsPossible(
111
116
  mode: SetupMode,
112
117
  scope: ScopeSelection,
113
118
  ): Promise<void> {
114
119
  if (mode === "all" || scope === "both") return;
115
120
  const { findTarget } = await import("./targets.js");
116
121
  const target = findTarget(mode);
117
- if (target === undefined || target.bands.includes(scope as ScopeBand)) return;
118
- const only = target.bands[0] as ScopeBand;
119
- const level = only === "global" ? "user-level" : "project-level";
122
+ if (target === undefined || target.scopes.includes(scope as Scope)) return;
123
+ const only = target.scopes[0] as Scope;
124
+ const level = only === "global" ? "global only" : "per-project only";
125
+ const asked = scope === "global" ? "globally" : "per project";
120
126
  const corrected =
121
127
  only === "global"
122
128
  ? `${BIN_NAME} setup ${mode}`
123
129
  : `${BIN_NAME} setup ${mode} --local`;
124
130
  throw new PragmaError({
125
131
  code: "INVALID_INPUT",
126
- message: `${mode} is ${level} — it has no ${scope} band.`,
132
+ message: `${mode} is ${level} — it cannot be configured ${asked}.`,
127
133
  recovery: { message: `Run: ${corrected}` },
128
134
  });
129
135
  }
@@ -169,7 +175,7 @@ const SUB_CAPABILITY = {
169
175
  *
170
176
  * @param mode - Which entry point (`all` or one target).
171
177
  * @param rt - The per-invocation runtime (mutated: `rt.exec` and `rt.planData`).
172
- * @param scope - The resolved band selection.
178
+ * @param scope - The resolved scope selection.
173
179
  * @returns The `Task<SetupPlan>` the dispatcher/MCP handler interprets.
174
180
  * @note Impure — runs every target's real detection before returning a Task.
175
181
  */
@@ -178,7 +184,7 @@ async function runSetup(
178
184
  rt: PragmaRuntime,
179
185
  scope: ScopeSelection,
180
186
  ): Promise<Task<SetupPlan>> {
181
- await assertBandIsPossible(mode, scope);
187
+ await assertScopeIsPossible(mode, scope);
182
188
 
183
189
  // Lazy dynamic imports (lazy-React discipline): summon-core's barrel is
184
190
  // React-free, and the non-TTY branch picks autoPrompt/mcpPrompt (never mounts
@@ -203,6 +209,53 @@ async function runSetup(
203
209
  // to know which of the two it is.
204
210
  const previewing = interactionMode === "batch-dry-run";
205
211
 
212
+ // The undo truth channel. A real `--undo` interprets this verb's task ONLY
213
+ // as the undo interpreter's mocked collection walk — every exec exits 0,
214
+ // every prompt answers its default, and the OutcomeSink is never written —
215
+ // so nothing the task body could report describes what the collected
216
+ // reversals later did. The body therefore says NOTHING on the undo path
217
+ // (see the guard in the gen body below); the kernel calls this seam after
218
+ // the reversals actually ran, handing over the interpreter's per-undo
219
+ // outcomes, and `appliedUndo` projects them onto the rows through the SAME
220
+ // sink + `applied` path the forward run reports through. One outcome
221
+ // model, both directions.
222
+ if (!previewing && input.undo) {
223
+ rt.undoReport = (outcomes) => {
224
+ const applied = run.appliedUndo(outcomes);
225
+ const width = Math.max(...applied.rows.map((row) => row.target.length));
226
+ for (const row of applied.rows) {
227
+ if (row.outcome === undefined) continue;
228
+ rt.report?.(renderProgressLine(row, width));
229
+ }
230
+ if (planExitFailed(applied)) {
231
+ rt.report?.(renderRecap(applied, "Removed"));
232
+ raiseFailedRows(applied, true);
233
+ }
234
+ };
235
+ }
236
+
237
+ // A CONVERGED machine has no question to ask. Every row is `none` or `skip`,
238
+ // so nothing composes an effect — and the wizard's confirm gate then mounted
239
+ // Ink to render summon-core's "No operations planned." above a "Proceed?"
240
+ // over an empty tally. For a sub-verb, whose prompt list is empty, that
241
+ // contentless gate was the FIRST AND ONLY thing the user ever saw. The
242
+ // explanation existed the whole time (each row's `none` detail, one child row
243
+ // per file/editor marked `unchanged`) and was dropped at the render seam.
244
+ //
245
+ // So: report the plan instead of asking a question with no content. There is
246
+ // no `preview: true` here — the run really did complete. `applied({})` marks
247
+ // each `none` row `{status:"noop", note:"unchanged"}`, `wasApplied` is then
248
+ // true, and `setupFormatters.plain` renders the recap. That also retires a
249
+ // second defect: a non-interactive converged run used to print the misleading
250
+ // "Nothing was applied. Run again with --yes to apply." over an all-`none`
251
+ // plan. A run whose every DETECTION threw is not converged in this sense —
252
+ // its rows are skips that report `failed` — so it falls through to the normal
253
+ // path, which is the one that raises.
254
+ if (!previewing && run.plan.rows.every((row) => !isActionable(row.action))) {
255
+ const converged = run.applied({});
256
+ if (!planExitFailed(converged)) return pure<SetupPlan>(converged);
257
+ }
258
+
206
259
  // Adaptation (b): a non-interactive run without `--yes` previews rather than
207
260
  // mutating. It is the same plan the dry-run prints, plus one hint line, at
208
261
  // exit 0 — the invocation completed a real read-only unit of work.
@@ -210,6 +263,23 @@ async function runSetup(
210
263
  return pure<SetupPlan>({ ...run.plan, preview: true });
211
264
  }
212
265
 
266
+ // Say what was DETECTED before asking anything about it. The data has been
267
+ // sitting in `run.plan` since before the prompt list was built — `inkPrompt`
268
+ // mounts no React until the first Prompt effect — so this is a rendering gap,
269
+ // not an ordering one, and closing it is nearly free.
270
+ //
271
+ // It MUST be emitted here, before `execute` is driven: the summary and the
272
+ // Ink frame both write to stderr, and only a line written first lands in
273
+ // scrollback ABOVE the frame. `rt.report` is the non-Ink seam — `--quiet`
274
+ // silences it and it is a no-op over MCP — so no React is loaded to print it.
275
+ // A preview says nothing here: the plan table IS its output.
276
+ if (!previewing) {
277
+ const summary = renderDetectionSummary(run.plan, {
278
+ verbose: rt.globalFlags.verbose,
279
+ });
280
+ if (summary !== undefined) rt.report?.(summary);
281
+ }
282
+
213
283
  const { transport, yes, signal, abort } = rt.interaction ?? {
214
284
  isTTY: false,
215
285
  transport: "cli" as const,
@@ -226,6 +296,19 @@ async function runSetup(
226
296
  signal,
227
297
  onCancel: abort,
228
298
  });
299
+ // Live progress in the PLAN's units, not the interpreter's: the wizard's
300
+ // default execution view is the per-effect transcript — right for `create`,
301
+ // whose unit of work IS the file, and wrong here, where one unit is one
302
+ // configured target and the transcript renders eighteen symlink rows for
303
+ // the row the plan called `skills link 9 skills → 2 folders`. Each row
304
+ // event carries the SAME sentence `renderProgressLine` prints, so the
305
+ // watcher and the recap reader see one dialect. `--verbose` withholds the
306
+ // listener, which falls the wizard back to the full effect transcript —
307
+ // the same detected-only-unless-verbose rule the detection summary and
308
+ // doctor already follow.
309
+ if (rt.globalFlags.verbose !== true) {
310
+ run.setRowListener((event) => session.reportStep(event));
311
+ }
229
312
  rt.exec = {
230
313
  promptHandler: session.promptHandler,
231
314
  onEffectStart: session.onEffectStart,
@@ -255,6 +338,16 @@ async function runSetup(
255
338
  const result = yield* $(task);
256
339
  const applied = run.applied(result.answers);
257
340
 
341
+ // A real `--undo` only ever drives this body under the undo
342
+ // interpreter's COLLECTION walk, with every effect mocked. `applied` is
343
+ // then a projection of the mocked walk (`removed` rows for reversals
344
+ // that have not run yet), so reporting it here printed green checks
345
+ // before anything happened. Say nothing; the kernel hands the executed
346
+ // reversals' outcomes to `rt.undoReport` afterwards, and `appliedUndo`
347
+ // grades the rows from those. (`--undo --dry-run` stays on the shared
348
+ // preview path below, where the same suppressions already apply.)
349
+ if (input.undo && !previewing) return applied;
350
+
258
351
  // Progress: one line per row on stderr for an unattended run (the Ink
259
352
  // session already draws them for the wizard). stdout stays the data stream.
260
353
  //
@@ -275,45 +368,58 @@ async function runSetup(
275
368
  // stdout on a failing run.
276
369
  if (planExitFailed(applied)) {
277
370
  rt.report?.(renderRecap(applied));
278
- const failed = applied.rows.filter(
279
- (row) => row.outcome?.status === "failed",
280
- );
281
-
282
- // ONE failure reports itself. The row already carries the cause it was
283
- // given ("`bun` is not found on your PATH") and a remedy that runs on
284
- // this machine; replacing them with a count would throw away the only
285
- // two sentences that say what is wrong and what to do about it.
286
- const only = failed.length === 1 ? failed[0] : undefined;
287
- if (only?.outcome !== undefined) {
288
- throw new PragmaError({
289
- code: "UNSUPPORTED",
290
- message: `${only.target}: ${only.outcome.note ?? "the step did not complete"}`,
291
- ...(only.outcome.remedy === undefined
292
- ? {}
293
- : { recovery: { message: only.outcome.remedy } }),
294
- });
295
- }
371
+ raiseFailedRows(applied);
372
+ }
373
+ return applied;
374
+ });
375
+ }
376
+
377
+ /**
378
+ * Raise the run's failure from its rows — shared by the forward run's recap
379
+ * and the post-undo truth report, so the two paths name a shortfall in the
380
+ * same words. `undo` only re-words the multi-failure recovery line: the
381
+ * command that retries a removal is `setup <target> --undo`, not the
382
+ * installer.
383
+ *
384
+ * @param applied - The plan with outcomes filled in (at least one `failed`).
385
+ * @param undo - Whether the failed run was a removal.
386
+ * @throws PragmaError naming the failed row(s), always.
387
+ */
388
+ function raiseFailedRows(applied: SetupPlan, undo = false): never {
389
+ const failed = applied.rows.filter((row) => row.outcome?.status === "failed");
390
+
391
+ // ONE failure reports itself. The row already carries the cause it was
392
+ // given ("`bun` is not found on your PATH") and a remedy that runs on
393
+ // this machine; replacing them with a count would throw away the only
394
+ // two sentences that say what is wrong and what to do about it.
395
+ const only = failed.length === 1 ? failed[0] : undefined;
396
+ if (only?.outcome !== undefined) {
397
+ throw new PragmaError({
398
+ code: "UNSUPPORTED",
399
+ message: `${only.target}: ${only.outcome.note ?? "the step did not complete"}`,
400
+ ...(only.outcome.remedy === undefined
401
+ ? {}
402
+ : { recovery: { message: only.outcome.remedy } }),
403
+ });
404
+ }
296
405
 
297
- // Several failures: name every one with its own cause, then point at the
298
- // recap above. A bare list of target names would make the reader re-run
299
- // each one to find out why it failed.
300
- const named = failed
406
+ // Several failures: name every one with its own cause, then point at the
407
+ // recap above. A bare list of target names would make the reader re-run
408
+ // each one to find out why it failed.
409
+ const named = failed
410
+ .map((row) => `${row.target} (${row.outcome?.note ?? "no cause recorded"})`)
411
+ .join("; ");
412
+ throw new PragmaError({
413
+ code: "UNSUPPORTED",
414
+ message: `${failed.length} of ${planTally(applied).accountable} targets did not complete: ${named}.`,
415
+ recovery: {
416
+ message: `The other targets are ${undo ? "removed" : "configured"}. Re-run the ones that did not complete: ${failed
301
417
  .map(
302
418
  (row) =>
303
- `${row.target} (${row.outcome?.note ?? "no cause recorded"})`,
419
+ `\`${BIN_NAME} setup ${row.target}${undo ? " --undo" : ""}\``,
304
420
  )
305
- .join("; ");
306
- throw new PragmaError({
307
- code: "UNSUPPORTED",
308
- message: `${failed.length} of ${planTally(applied).accountable} targets did not complete: ${named}.`,
309
- recovery: {
310
- message: `The other targets are configured. Re-run the ones that did not complete: ${failed
311
- .map((row) => `\`${BIN_NAME} setup ${row.target}\``)
312
- .join(", ")}.`,
313
- },
314
- });
315
- }
316
- return applied;
421
+ .join(", ")}.`,
422
+ },
317
423
  });
318
424
  }
319
425
 
@@ -345,24 +451,24 @@ function setupVerb(
345
451
 
346
452
  const setupAllVerb = setupVerb(
347
453
  ["setup"],
348
- "Configure the global config, completions, the LSP, MCP, and skills.",
454
+ "Set up your config file, TAB completion, the editor extension, MCP, and skills.",
349
455
  "all",
350
456
  SELF_CAPABILITY,
351
457
  {
352
- doc: "Plans every target in the selected band, then applies the ones you keep. The user/home band is the default; the scope option chooses the per-project band, or both. A run with no attended terminal prints the plan and applies nothing unless the run is confirmed.",
458
+ doc: "Shows what each target needs, then applies the ones you keep. Everything is configured in your home directory by default; the scope option moves the run to this project alone, or covers both. Without an attended terminal the plan is printed and nothing is written unless the run is explicitly confirmed.",
353
459
  examples: [
354
460
  { cmd: `${BIN_NAME} setup` },
355
461
  {
356
462
  cmd: `${BIN_NAME} setup --dry-run`,
357
- note: "print the plan, write nothing",
463
+ note: "show the plan, write nothing",
358
464
  },
359
465
  {
360
466
  cmd: `${BIN_NAME} setup --local`,
361
- note: "configure the project band instead",
467
+ note: "configure this project instead of your home directory",
362
468
  },
363
469
  {
364
470
  cmd: `${BIN_NAME} setup mcp`,
365
- note: "just the MCP server registration",
471
+ note: "only register the MCP server",
366
472
  },
367
473
  ],
368
474
  },
@@ -370,35 +476,35 @@ const setupAllVerb = setupVerb(
370
476
 
371
477
  const configVerb = setupVerb(
372
478
  ["setup", "config"],
373
- "Create the global config file with its defaults.",
479
+ "Create your global config file, filled in with the defaults.",
374
480
  "config",
375
481
  SUB_CAPABILITY,
376
482
  );
377
483
 
378
484
  const mcpVerb = setupVerb(
379
485
  ["setup", "mcp"],
380
- `Register the ${BIN_NAME} MCP server in detected AI harnesses.`,
486
+ `Register the ${BIN_NAME} MCP server with the AI harnesses on this machine.`,
381
487
  "mcp",
382
488
  SUB_CAPABILITY,
383
489
  );
384
490
 
385
491
  const completionsVerb = setupVerb(
386
492
  ["setup", "completions"],
387
- "Install the shell-completion script for your shell.",
493
+ "Install TAB completion for the shell you are running.",
388
494
  "completions",
389
495
  SUB_CAPABILITY,
390
496
  );
391
497
 
392
498
  const skillsVerb = setupVerb(
393
499
  ["setup", "skills"],
394
- "Symlink discovered skills into each AI harness.",
500
+ "Link the skills you have installed into every AI harness that reads them.",
395
501
  "skills",
396
502
  SUB_CAPABILITY,
397
503
  );
398
504
 
399
505
  const lspVerb = setupVerb(
400
506
  ["setup", "lsp"],
401
- "Ensure the Terrazzo LSP VS Code extension is installed.",
507
+ "Install the Terrazzo design-token extension into your VS Code-family editors.",
402
508
  "lsp",
403
509
  SUB_CAPABILITY,
404
510
  );