@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.
- package/README.md +2 -2
- package/bundled-skills/add-standard/SKILL.md +338 -0
- package/bundled-skills/adoption-a1-styles/SKILL.md +269 -0
- package/bundled-skills/adoption-a2-components/SKILL.md +291 -0
- package/bundled-skills/adoption-a3-forms/SKILL.md +260 -0
- package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +406 -0
- package/bundled-skills/anatomy-author/SKILL.md +1226 -0
- package/bundled-skills/design-auditor/SKILL.md +455 -0
- package/bundled-skills/implement-component/SKILL.md +333 -0
- package/bundled-skills/specify-component/SKILL.md +331 -0
- package/bundled-skills/specify-pattern/SKILL.md +292 -0
- package/dist/package.json +24 -20
- package/dist/pragma.conf.js +266 -34
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/bin.js +1 -1
- package/dist/src/bin.js.map +1 -1
- package/dist/src/capabilities/capabilities/hints.js +1 -1
- package/dist/src/capabilities/capabilities/hints.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.render.js +1 -1
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +2 -1
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +23 -1
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/create/mount.js +4 -3
- package/dist/src/capabilities/create/mount.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js +11 -6
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +60 -4
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkShellCompletions.js +8 -8
- package/dist/src/capabilities/doctor/checks/checkShellCompletions.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js +195 -0
- package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -0
- package/dist/src/capabilities/doctor/checks/index.js +6 -6
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js +38 -0
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js.map +1 -0
- package/dist/src/capabilities/doctor/checks/targetHealth.js +194 -50
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.render.js +43 -64
- package/dist/src/capabilities/doctor/doctor.render.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +6 -2
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/doctor/runChecks.js +14 -11
- package/dist/src/capabilities/doctor/runChecks.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +2 -2
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/info/collectInfo.js +8 -2
- package/dist/src/capabilities/info/collectInfo.js.map +1 -1
- package/dist/src/capabilities/info/info.render.js +6 -2
- package/dist/src/capabilities/info/info.render.js.map +1 -1
- package/dist/src/capabilities/prompt/prompt.render.js +2 -2
- package/dist/src/capabilities/prompt/prompt.render.js.map +1 -1
- package/dist/src/capabilities/setup/buildPlan.js +43 -39
- package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
- package/dist/src/capabilities/setup/mode.js +2 -2
- package/dist/src/capabilities/setup/operations/index.js +4 -4
- package/dist/src/capabilities/setup/operations/index.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupCompletions.js +6 -2
- package/dist/src/capabilities/setup/operations/setupCompletions.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupConfig.js +4 -2
- package/dist/src/capabilities/setup/operations/setupConfig.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupGenerator.js +156 -58
- package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupLsp.js +136 -38
- package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupMcp.js +101 -52
- package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupSkills.js +329 -49
- package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
- package/dist/src/capabilities/setup/plan.js.map +1 -1
- package/dist/src/capabilities/setup/plan.render.js +144 -66
- package/dist/src/capabilities/setup/plan.render.js.map +1 -1
- package/dist/src/capabilities/setup/setup.render.js +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +165 -63
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/setup/targets.js +122 -51
- package/dist/src/capabilities/setup/targets.js.map +1 -1
- package/dist/src/capabilities/shared/index.js +7 -6
- package/dist/src/capabilities/shared/index.js.map +1 -1
- package/dist/src/capabilities/shared/packageManager.js +300 -54
- package/dist/src/capabilities/shared/packageManager.js.map +1 -1
- package/dist/src/capabilities/skill/discover.js +97 -11
- package/dist/src/capabilities/skill/discover.js.map +1 -1
- package/dist/src/capabilities/skill/render.js +2 -2
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/sources/installSkills.js +145 -3
- package/dist/src/capabilities/sources/installSkills.js.map +1 -1
- package/dist/src/capabilities/sources/runUpdate.js +68 -2
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +3 -3
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/runUpgrade.js +43 -7
- package/dist/src/capabilities/upgrade/runUpgrade.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.render.js +10 -2
- package/dist/src/capabilities/upgrade/upgrade.render.js.map +1 -1
- package/dist/src/constants.js +13 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/index.js +11 -2
- package/dist/src/kernel/index.js.map +1 -1
- package/dist/src/kernel/interactivity.js +68 -0
- package/dist/src/kernel/interactivity.js.map +1 -0
- package/dist/src/kernel/packs/renderPack.js +3 -3
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +63 -12
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +57 -4
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +54 -2
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/applyFilters.js +104 -5
- package/dist/src/kernel/packs/sparql/applyFilters.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +310 -43
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +46 -24
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/globalFlags.js +4 -1
- package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
- package/dist/src/kernel/project/cli/helpFormat.js +1 -2
- package/dist/src/kernel/project/cli/helpFormat.js.map +1 -1
- package/dist/src/kernel/project/cli/rootHelp.js +42 -9
- package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +16 -1
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +1 -1
- package/dist/src/kernel/render/style.js +29 -9
- package/dist/src/kernel/render/style.js.map +1 -1
- package/dist/src/kernel/render/vocabulary.js +174 -0
- package/dist/src/kernel/render/vocabulary.js.map +1 -0
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js +2 -3
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js +4 -0
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js.map +1 -0
- package/dist/src/kernel/runtime/graphpack/embedded.js +13 -6
- package/dist/src/kernel/runtime/graphpack/embedded.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +14 -9
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +3 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/probe.js +1 -1
- package/dist/src/kernel/runtime/probe.js.map +1 -1
- package/dist/src/kernel/runtime/refs/parseRef.js +17 -1
- package/dist/src/kernel/runtime/refs/parseRef.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js +9 -3
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/dist/src/kernel/spec/emitReference.js +1 -1
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/package.json +24 -20
- package/pragma.conf.ts +272 -34
- package/src/bin.ts +8 -1
- package/src/capabilities/block.parity.test.ts +25 -11
- package/src/capabilities/block.shipped.exec.test.ts +282 -0
- package/src/capabilities/block.tierRank.exec.test.ts +300 -0
- package/src/capabilities/capabilities/hints.ts +1 -1
- package/src/capabilities/colophon/colophon.render.ts +1 -2
- package/src/capabilities/colophon/colophon.test.ts +2 -2
- package/src/capabilities/config/collectConfigShow.test.ts +1 -0
- package/src/capabilities/create/byteEquality.test.ts +28 -1
- package/src/capabilities/create/create.test.ts +1 -0
- package/src/capabilities/create/create.verb.ts +2 -1
- package/src/capabilities/create/createGrammar.test.ts +5 -2
- package/src/capabilities/create/createSurface.generated.ts +23 -1
- package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
- package/src/capabilities/create/mount.ts +3 -3
- package/src/capabilities/distribution.parity.test.ts +4 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +69 -4
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +6 -4
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +8 -8
- package/src/capabilities/doctor/checks/harnessInventory.test.ts +219 -0
- package/src/capabilities/doctor/checks/harnessInventory.ts +298 -0
- package/src/capabilities/doctor/checks/index.ts +6 -6
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.test.ts +111 -0
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.ts +34 -0
- package/src/capabilities/doctor/checks/mcpCommand.test.ts +7 -1
- package/src/capabilities/doctor/checks/targetHealth.ts +229 -59
- package/src/capabilities/doctor/doctor.render.test.ts +108 -32
- package/src/capabilities/doctor/doctor.render.ts +56 -80
- package/src/capabilities/doctor/doctor.test.ts +247 -35
- package/src/capabilities/doctor/doctor.verb.ts +7 -2
- package/src/capabilities/doctor/runChecks.ts +14 -11
- package/src/capabilities/doctor/types.ts +15 -24
- package/src/capabilities/emptyRecovery.test.ts +10 -2
- package/src/capabilities/graph/query.render.ts +2 -2
- package/src/capabilities/info/collectInfo.ts +8 -1
- package/src/capabilities/info/info.render.ts +6 -2
- package/src/capabilities/info/info.test.ts +37 -1
- package/src/capabilities/info/types.ts +10 -2
- package/src/capabilities/lazy.test.ts +26 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +340 -0
- package/src/capabilities/prompt/prompt.render.ts +2 -2
- package/src/capabilities/renderVocabularySync.test.ts +82 -0
- package/src/capabilities/resources/resources.test.ts +38 -2
- package/src/capabilities/scopeTypeSync.test.ts +29 -15
- package/src/capabilities/setup/buildPlan.ts +45 -41
- package/src/capabilities/setup/index.ts +1 -1
- package/src/capabilities/setup/mode.ts +2 -2
- package/src/capabilities/setup/operations/index.ts +6 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +9 -2
- package/src/capabilities/setup/operations/setupConfig.ts +7 -2
- package/src/capabilities/setup/operations/setupGenerator.ts +227 -84
- package/src/capabilities/setup/operations/setupLsp.ts +203 -38
- package/src/capabilities/setup/operations/setupMcp.ts +139 -62
- package/src/capabilities/setup/operations/setupSkills.ts +399 -60
- package/src/capabilities/setup/plan.render.ts +171 -76
- package/src/capabilities/setup/plan.ts +17 -12
- package/src/capabilities/setup/setup.mcp.correctness.test.ts +9 -9
- package/src/capabilities/setup/setup.render.test.ts +50 -36
- package/src/capabilities/setup/setup.render.ts +1 -1
- package/src/capabilities/setup/setup.test.ts +1126 -50
- package/src/capabilities/setup/setup.verb.ts +174 -68
- package/src/capabilities/setup/targets.ts +138 -60
- package/src/capabilities/setup/types.ts +10 -18
- package/src/capabilities/shared/index.ts +18 -6
- package/src/capabilities/shared/packageManager.test.ts +421 -6
- package/src/capabilities/shared/packageManager.ts +394 -59
- package/src/capabilities/skill/bundledSkills.test.ts +347 -0
- package/src/capabilities/skill/discover.ts +100 -11
- package/src/capabilities/skill/render.ts +2 -2
- package/src/capabilities/skill/skill.test.ts +17 -4
- package/src/capabilities/sources/installSkills.ts +181 -3
- package/src/capabilities/sources/runUpdate.ts +78 -2
- package/src/capabilities/sources/sources.test.ts +228 -0
- package/src/capabilities/sources/update.verb.ts +3 -3
- package/src/capabilities/surface.test.ts +14 -6
- package/src/capabilities/upgrade/runUpgrade.ts +45 -6
- package/src/capabilities/upgrade/types.ts +10 -2
- package/src/capabilities/upgrade/upgrade.render.ts +9 -2
- package/src/capabilities/upgrade/upgrade.test.ts +72 -0
- package/src/constants.ts +14 -1
- package/src/identity.test.ts +18 -5
- package/src/kernel/completion/entitySource.test.ts +15 -2
- package/src/kernel/completion/safety.test.ts +16 -9
- package/src/kernel/config/defaults.test.ts +19 -3
- package/src/kernel/config/readConfig.test.ts +1 -1
- package/src/kernel/index.ts +11 -2
- package/src/kernel/interactivity.test.ts +76 -0
- package/src/kernel/interactivity.ts +69 -0
- package/src/kernel/packs/compile.test.ts +92 -0
- package/src/kernel/packs/renderPack.ts +3 -3
- package/src/kernel/packs/resolveEntity.test.ts +75 -14
- package/src/kernel/packs/resolveEntity.ts +88 -24
- package/src/kernel/packs/runBodies.ts +84 -19
- package/src/kernel/packs/schema.ts +60 -4
- package/src/kernel/packs/sparql/applyFilters.ts +125 -4
- package/src/kernel/packs/sparql/buildLookupQuery.test.ts +290 -0
- package/src/kernel/packs/sparql/buildLookupQuery.ts +327 -45
- package/src/kernel/packs/toolDescription.test.ts +35 -1
- package/src/kernel/packs/types.ts +125 -2
- package/src/kernel/project/cli/buildProgram.test.ts +73 -2
- package/src/kernel/project/cli/buildProgram.ts +8 -1
- package/src/kernel/project/cli/dispatch.test.ts +5 -4
- package/src/kernel/project/cli/dispatch.ts +48 -25
- package/src/kernel/project/cli/globalFlags.ts +4 -1
- package/src/kernel/project/cli/helpFormat.ts +1 -2
- package/src/kernel/project/cli/rootHelp.ts +44 -8
- package/src/kernel/project/mcp/registerVerb.ts +20 -1
- package/src/kernel/render/prefixes.test.ts +2 -0
- package/src/kernel/render/renderers.ts +1 -1
- package/src/kernel/render/style.ts +32 -9
- package/src/kernel/render/vocabulary.ts +276 -0
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +2 -3
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +1 -1
- package/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +14 -6
- package/src/kernel/runtime/graphpack/graphpack.test.ts +31 -5
- package/src/kernel/runtime/graphpack/read.ts +14 -12
- package/src/kernel/runtime/loadSession.ts +3 -1
- package/src/kernel/runtime/probe.ts +1 -1
- package/src/kernel/runtime/refs/parseRef.test.ts +49 -1
- package/src/kernel/runtime/refs/parseRef.ts +25 -1
- package/src/kernel/runtime/refs/resolve.ts +9 -3
- package/src/kernel/runtime/types.ts +17 -0
- package/src/kernel/spec/emitReference.ts +1 -1
- package/src/kernel/spec/types.ts +35 -6
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/docExamples.test.ts +4 -2
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +110 -7
- package/src/testing/behavioral/journeys.firstInstall.test.ts +7 -2
- package/src/testing/behavioral/journeys.packageStories.test.ts +6 -3
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +26 -1
- package/src/testing/eval/cases/readNouns.ts +245 -9
- package/src/testing/fixtures/blockGraph.ts +28 -13
- package/src/testing/fixtures/graph/canonical.ts +95 -12
- package/src/testing/helpers/runCli.test.ts +44 -0
- package/src/testing/helpers/runCli.ts +27 -3
- package/dist/src/capabilities/shared/bands.js +0 -16
- package/dist/src/capabilities/shared/bands.js.map +0 -1
- package/src/capabilities/shared/bands.ts +0 -16
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* gate, live progress, colours, and the shared cancel fixes by construction.
|
|
6
6
|
*
|
|
7
7
|
* The shape mirrors `create`: detection runs FOR REAL up front (once per
|
|
8
|
-
* target/
|
|
8
|
+
* target/scope, in `detectTargets`), then a PURE `generate` composes only the
|
|
9
9
|
* effects for the SELECTED rows. `generate` must stay re-interpretable —
|
|
10
10
|
* `execute` invokes it more than once (the confirm-gate preview and the build) —
|
|
11
11
|
* so it is composed from combinators and does no reads of its own.
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type TaskError,
|
|
35
35
|
warn,
|
|
36
36
|
} from "@canonical/task";
|
|
37
|
+
import type { UndoOutcome } from "@canonical/task/node";
|
|
37
38
|
import { BIN_NAME } from "../../../constants.js";
|
|
38
39
|
import { PragmaError } from "../../../kernel/error/index.js";
|
|
39
40
|
import type { PragmaRuntime } from "../../../kernel/runtime/index.js";
|
|
@@ -55,7 +56,7 @@ import {
|
|
|
55
56
|
type TargetId,
|
|
56
57
|
withRows,
|
|
57
58
|
} from "../plan.js";
|
|
58
|
-
import type {
|
|
59
|
+
import type { Scope, ScopeSelection, SetupMode } from "../types.js";
|
|
59
60
|
|
|
60
61
|
/** The answer key the run-all's row multiselect writes. */
|
|
61
62
|
const ROWS_ANSWER = "targets";
|
|
@@ -72,9 +73,26 @@ const CHILD_ANSWER: Partial<Record<TargetId, string>> = {
|
|
|
72
73
|
lsp: LSP_EDITORS_ANSWER,
|
|
73
74
|
};
|
|
74
75
|
|
|
75
|
-
/** A row's identity in an answer bag:
|
|
76
|
-
const rowKey = (
|
|
77
|
-
|
|
76
|
+
/** A row's identity in an answer bag: scope-qualified, so `both` stays unambiguous. */
|
|
77
|
+
const rowKey = (scope: Scope, target: TargetId): string => `${scope}:${target}`;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* One live row-progress event: a plan row starting, finishing, or failing,
|
|
81
|
+
* reported WHILE the composed task is being interpreted. `label` is the row's
|
|
82
|
+
* progress sentence — the same `target detail — note` shape
|
|
83
|
+
* `renderProgressLine` prints — so the listener (the Ink wizard's step view)
|
|
84
|
+
* shows the run in the plan's own vocabulary, not the effect transcript's.
|
|
85
|
+
*
|
|
86
|
+
* Structurally identical to summon-core's `StepReport` ON PURPOSE: the wizard
|
|
87
|
+
* branch forwards these events verbatim, and this module must not import
|
|
88
|
+
* summon-core values (it stays behind the verb's lazy import).
|
|
89
|
+
*/
|
|
90
|
+
export interface RowEvent {
|
|
91
|
+
/** The row's stable identity — `scope:target`. */
|
|
92
|
+
readonly key: string;
|
|
93
|
+
readonly label: string;
|
|
94
|
+
readonly status: "start" | "done" | "failed";
|
|
95
|
+
}
|
|
78
96
|
|
|
79
97
|
/**
|
|
80
98
|
* A ready-to-run setup invocation: the plan as detected, the synthesized
|
|
@@ -86,6 +104,29 @@ export interface SetupRun {
|
|
|
86
104
|
readonly generator: GeneratorDefinition;
|
|
87
105
|
/** The plan with the wizard's selection and the run's outcomes applied. */
|
|
88
106
|
applied(answers: Record<string, unknown>): SetupPlan;
|
|
107
|
+
/**
|
|
108
|
+
* The plan graded by the EXECUTED reversals — `--undo`'s own `applied`.
|
|
109
|
+
*
|
|
110
|
+
* A real `--undo` never interprets the composed task for real: the undo
|
|
111
|
+
* interpreter walks it with every effect mocked (to collect the
|
|
112
|
+
* reversals), so the {@link OutcomeSink} the forward run fills stays
|
|
113
|
+
* empty, and only the interpreter's per-undo outcomes — correlated by the
|
|
114
|
+
* `undoKey` each reversal was stamped with in `generate` — say what
|
|
115
|
+
* actually happened. This projection writes those outcomes onto the SAME
|
|
116
|
+
* sink and reads the plan back through the SAME `outcomeFor` the forward
|
|
117
|
+
* run uses: one outcome model, both directions. An actionable row none of
|
|
118
|
+
* whose reversals reported back is a failure, never a quiet `removed`.
|
|
119
|
+
*/
|
|
120
|
+
appliedUndo(outcomes: readonly UndoOutcome[]): SetupPlan;
|
|
121
|
+
/**
|
|
122
|
+
* Register THE live row listener (the Ink wizard's step adapter). The
|
|
123
|
+
* composed rows emit through it while they are interpreted — which means
|
|
124
|
+
* EVERY interpretation fires it, the confirm gate's honest preview included;
|
|
125
|
+
* the receiving session drops reports outside its executing phase, exactly
|
|
126
|
+
* as the {@link OutcomeSink} relies on last-drive-wins for its records. No
|
|
127
|
+
* listener registered (a `--yes` run, `--dry-run`, MCP) costs nothing.
|
|
128
|
+
*/
|
|
129
|
+
setRowListener(listener: (event: RowEvent) => void): void;
|
|
89
130
|
}
|
|
90
131
|
|
|
91
132
|
/**
|
|
@@ -144,25 +185,50 @@ const rowPragmaError = (error: TaskError): PragmaError =>
|
|
|
144
185
|
* throw bypasses the trampoline's recovery frames — which is why the row bodies
|
|
145
186
|
* raise via `checkExecOk`/`failPragma`, never a bare throw. Interruption bypasses
|
|
146
187
|
* recovery by interpreter invariant, so Ctrl-C still stops the whole run.
|
|
188
|
+
*
|
|
189
|
+
* Each row is also BRACKETED with {@link RowEvent}s — `start` as its body is
|
|
190
|
+
* entered, `done`/`failed` as it settles — emitted from the continuations,
|
|
191
|
+
* which run at interpretation time. Like the sink writes beside them, the
|
|
192
|
+
* emits capture nothing IN the task: the composition stays pure and
|
|
193
|
+
* re-interpretable, and each drive simply narrates itself as it runs.
|
|
147
194
|
*/
|
|
148
195
|
const runRowsIsolated = (
|
|
149
196
|
sink: OutcomeSink,
|
|
150
|
-
rows: readonly {
|
|
197
|
+
rows: readonly {
|
|
198
|
+
key: string;
|
|
199
|
+
label: string;
|
|
200
|
+
doneLabel: string;
|
|
201
|
+
task: Task<void>;
|
|
202
|
+
}[],
|
|
203
|
+
emit: (event: RowEvent) => void,
|
|
151
204
|
): Task<void> =>
|
|
152
205
|
flatMap(pure(undefined), (): Task<void> => {
|
|
153
206
|
sink.clear();
|
|
154
207
|
return sequence_(
|
|
155
|
-
rows.map(({ key, task }) =>
|
|
156
|
-
recover(
|
|
157
|
-
flatMap(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
() => {
|
|
162
|
-
sink.record(key, error);
|
|
208
|
+
rows.map(({ key, label, doneLabel, task }) =>
|
|
209
|
+
recover(
|
|
210
|
+
flatMap(pure(undefined), () => {
|
|
211
|
+
emit({ key, label, status: "start" });
|
|
212
|
+
return flatMap(task, () => {
|
|
213
|
+
emit({ key, label: doneLabel, status: "done" });
|
|
163
214
|
return pure(undefined);
|
|
164
|
-
}
|
|
165
|
-
),
|
|
215
|
+
});
|
|
216
|
+
}),
|
|
217
|
+
(error) =>
|
|
218
|
+
flatMap(
|
|
219
|
+
warn(
|
|
220
|
+
`The ${key.split(":")[1]} step did not complete — continuing with the remaining targets.`,
|
|
221
|
+
),
|
|
222
|
+
() => {
|
|
223
|
+
sink.record(key, error);
|
|
224
|
+
emit({
|
|
225
|
+
key,
|
|
226
|
+
label: `${label} — ${rowPragmaError(error).message}`,
|
|
227
|
+
status: "failed",
|
|
228
|
+
});
|
|
229
|
+
return pure(undefined);
|
|
230
|
+
},
|
|
231
|
+
),
|
|
166
232
|
),
|
|
167
233
|
),
|
|
168
234
|
);
|
|
@@ -198,7 +264,7 @@ const buildRowsPrompt = (plan: SetupPlan): PromptDefinition => {
|
|
|
198
264
|
.filter((row) => row.action !== "skip")
|
|
199
265
|
.map((row) => ({
|
|
200
266
|
label: `${row.target} — ${row.detail}${row.action === "none" ? " (already configured)" : ""}`,
|
|
201
|
-
value: rowKey(row.
|
|
267
|
+
value: rowKey(row.scope, row.target),
|
|
202
268
|
}));
|
|
203
269
|
return {
|
|
204
270
|
name: ROWS_ANSWER,
|
|
@@ -207,27 +273,27 @@ const buildRowsPrompt = (plan: SetupPlan): PromptDefinition => {
|
|
|
207
273
|
choices,
|
|
208
274
|
default: plan.rows
|
|
209
275
|
.filter((row) => row.selected)
|
|
210
|
-
.map((row) => rowKey(row.
|
|
276
|
+
.map((row) => rowKey(row.scope, row.target)),
|
|
211
277
|
};
|
|
212
278
|
};
|
|
213
279
|
|
|
214
|
-
/** The opt-in "customize which files" gate — defaults to false. */
|
|
215
|
-
const buildCustomizePrompt = (
|
|
216
|
-
when?: PromptDefinition["when"],
|
|
217
|
-
): PromptDefinition => ({
|
|
218
|
-
name: "customize",
|
|
219
|
-
type: "confirm",
|
|
220
|
-
message: `Customize which files ${BIN_NAME} configures?`,
|
|
221
|
-
default: false,
|
|
222
|
-
when,
|
|
223
|
-
});
|
|
224
|
-
|
|
225
280
|
/**
|
|
226
281
|
* The per-file MCP multiselect — one row per deduplicated config file, across
|
|
227
|
-
* every
|
|
228
|
-
* file that is absent or drifted stays selected.
|
|
229
|
-
*
|
|
230
|
-
*
|
|
282
|
+
* every scope the run covers. An already-current file is DEFAULT-DESELECTED; a
|
|
283
|
+
* file that is absent or drifted stays selected. It is row-level CHILD
|
|
284
|
+
* selection, which is exactly what the plan's child rows are.
|
|
285
|
+
*
|
|
286
|
+
* It used to sit behind a meta-question — "Customize which files pragma
|
|
287
|
+
* configures?", answered no by default. That gate was read in exactly ONE place
|
|
288
|
+
* in the repo (this prompt's `when`) and nowhere else: not by `generate`, not by
|
|
289
|
+
* `applied`, not by any plan, recap, renderer or JSON projection. Answering no
|
|
290
|
+
* meant the child prompt was never asked, its key was absent from the answer
|
|
291
|
+
* bag, `readList` returned `undefined`, and the compose bodies read that as
|
|
292
|
+
* "all" — so everything detected was configured anyway. A meta-question in front
|
|
293
|
+
* of a direct question is one more thing to answer and no more control; the
|
|
294
|
+
* choices below already carry the detected state per child (`— unchanged`,
|
|
295
|
+
* `— add`, `— update`), which is the answer the meta-question was standing in
|
|
296
|
+
* front of.
|
|
231
297
|
*/
|
|
232
298
|
const buildChildPrompt = (
|
|
233
299
|
plan: SetupPlan,
|
|
@@ -251,7 +317,7 @@ const buildChildPrompt = (
|
|
|
251
317
|
};
|
|
252
318
|
};
|
|
253
319
|
|
|
254
|
-
/** Every child row a target contributes, across the
|
|
320
|
+
/** Every child row a target contributes, across the scopes in the plan. */
|
|
255
321
|
const childrenOf = (
|
|
256
322
|
plan: SetupPlan,
|
|
257
323
|
target: TargetId,
|
|
@@ -268,7 +334,7 @@ const isChosen = (
|
|
|
268
334
|
row.action !== "skip" &&
|
|
269
335
|
(chosen === undefined
|
|
270
336
|
? row.selected
|
|
271
|
-
: chosen.includes(rowKey(row.
|
|
337
|
+
: chosen.includes(rowKey(row.scope, row.target)));
|
|
272
338
|
|
|
273
339
|
/**
|
|
274
340
|
* What a row DID, as a past-tense word. The row's detail says what and where;
|
|
@@ -320,7 +386,7 @@ function childNote(
|
|
|
320
386
|
*
|
|
321
387
|
* @param rt - The per-invocation runtime.
|
|
322
388
|
* @param mode - The entry point: the run-all, or one target.
|
|
323
|
-
* @param scope - The resolved
|
|
389
|
+
* @param scope - The resolved scope selection.
|
|
324
390
|
* @param removal - Compose the removal (`--undo`) instead of the install.
|
|
325
391
|
* @returns The plan, the generator, and the answers-to-plan projection.
|
|
326
392
|
* @note Impure — runs every selected target's real detection.
|
|
@@ -350,35 +416,51 @@ export async function buildSetupRun(
|
|
|
350
416
|
] as const
|
|
351
417
|
).filter(([target]) => !removal && childrenOf(plan, target).length > 1);
|
|
352
418
|
|
|
353
|
-
|
|
354
|
-
|
|
419
|
+
for (const [target, message] of narrowable) {
|
|
420
|
+
// Asked whenever the row it narrows is in the run — one direct question,
|
|
421
|
+
// no meta-question in front of it. Removing a prompt cannot break the
|
|
422
|
+
// non-interactive paths: `autoPrompt`/`mcpPrompt` return each question's
|
|
423
|
+
// default, so a smaller prompt list means a smaller answer bag, and the
|
|
424
|
+
// compose bodies already read an absent child answer as "all". (The
|
|
425
|
+
// asymmetry is the reason this direction is safe: ADDING a defaultless
|
|
426
|
+
// prompt would hard-fail with MISSING_REQUIRED_ANSWER.)
|
|
427
|
+
const rowChosen = (answers: Record<string, unknown>): boolean => {
|
|
355
428
|
const chosen = readList(answers, ROWS_ANSWER);
|
|
356
|
-
return
|
|
357
|
-
|
|
429
|
+
return plan.rows.some(
|
|
430
|
+
(row) => row.target === target && isChosen(row, chosen),
|
|
358
431
|
);
|
|
359
432
|
};
|
|
360
|
-
prompts.push(
|
|
361
|
-
for (const [target, message] of narrowable) {
|
|
362
|
-
const rowChosen = (answers: Record<string, unknown>): boolean => {
|
|
363
|
-
const chosen = readList(answers, ROWS_ANSWER);
|
|
364
|
-
return plan.rows.some(
|
|
365
|
-
(row) => row.target === target && isChosen(row, chosen),
|
|
366
|
-
);
|
|
367
|
-
};
|
|
368
|
-
prompts.push(
|
|
369
|
-
buildChildPrompt(
|
|
370
|
-
plan,
|
|
371
|
-
target,
|
|
372
|
-
message,
|
|
373
|
-
(answers) => answers.customize === true && rowChosen(answers),
|
|
374
|
-
),
|
|
375
|
-
);
|
|
376
|
-
}
|
|
433
|
+
prompts.push(buildChildPrompt(plan, target, message, rowChosen));
|
|
377
434
|
}
|
|
378
435
|
|
|
379
436
|
/** The detection behind a row, for composing and for re-reading its draft. */
|
|
380
437
|
const detectionFor = (row: PlanRow): DetectedRow | undefined =>
|
|
381
|
-
detected.find((d) => d.target.id === row.target && d.
|
|
438
|
+
detected.find((d) => d.target.id === row.target && d.scope === row.scope);
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* The success note a row's completion carries — ONE derivation for the
|
|
442
|
+
* recap's outcome and the live row event's `done` label, so the sentence a
|
|
443
|
+
* watcher saw land is the sentence the recap repeats. A removal never
|
|
444
|
+
* borrows the forward child summary: its children are the files the entry is
|
|
445
|
+
* being taken OUT of, and counting them as `1 updated` described the
|
|
446
|
+
* opposite of what the run had just done.
|
|
447
|
+
*/
|
|
448
|
+
const noteFor = (
|
|
449
|
+
row: PlanRow,
|
|
450
|
+
answers: Record<string, unknown>,
|
|
451
|
+
): string | undefined => {
|
|
452
|
+
if (removal) return ACTION_NOTES[row.action];
|
|
453
|
+
const childKey = CHILD_ANSWER[row.target];
|
|
454
|
+
const kept =
|
|
455
|
+
childKey === undefined ? undefined : readList(answers, childKey);
|
|
456
|
+
return childNote(row, kept) ?? ACTION_NOTES[row.action];
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
/** The live row listener, when a wizard is watching. See {@link SetupRun}. */
|
|
460
|
+
let rowListener: ((event: RowEvent) => void) | undefined;
|
|
461
|
+
|
|
462
|
+
/** The id column width — the recap's own rule, so the columns agree. */
|
|
463
|
+
const idWidth = Math.max(...plan.rows.map((row) => row.target.length));
|
|
382
464
|
|
|
383
465
|
const generator: GeneratorDefinition = {
|
|
384
466
|
meta: buildMeta(
|
|
@@ -396,12 +478,31 @@ export async function buildSetupRun(
|
|
|
396
478
|
// MCP file paths to every other target's compose.
|
|
397
479
|
const key = CHILD_ANSWER[row.target];
|
|
398
480
|
const children = key === undefined ? undefined : readList(answers, key);
|
|
481
|
+
// A removal stamps the row's identity on every reversal it composes:
|
|
482
|
+
// `--undo` executes those reversals in a LATER phase (the undo
|
|
483
|
+
// interpreter's), whose outcomes come back correlated by this key —
|
|
484
|
+
// see `appliedUndo`.
|
|
399
485
|
const task = removal
|
|
400
|
-
? hit.target.composeRemoval(
|
|
486
|
+
? hit.target.composeRemoval(
|
|
487
|
+
hit.detection,
|
|
488
|
+
rowKey(row.scope, row.target),
|
|
489
|
+
)
|
|
401
490
|
: hit.target.compose(hit.detection, children);
|
|
402
|
-
|
|
491
|
+
// The row's progress sentence — the `target detail — note` shape
|
|
492
|
+
// `renderProgressLine` prints — so the live view and the recap say the
|
|
493
|
+
// same thing about the same row.
|
|
494
|
+
const label = `${row.target.padEnd(idWidth)} ${row.detail}`;
|
|
495
|
+
const note = noteFor(row, answers);
|
|
496
|
+
return [
|
|
497
|
+
{
|
|
498
|
+
key: rowKey(row.scope, row.target),
|
|
499
|
+
label,
|
|
500
|
+
doneLabel: note === undefined ? label : `${label} — ${note}`,
|
|
501
|
+
task,
|
|
502
|
+
},
|
|
503
|
+
];
|
|
403
504
|
});
|
|
404
|
-
return runRowsIsolated(sink, tasks);
|
|
505
|
+
return runRowsIsolated(sink, tasks, (event) => rowListener?.(event));
|
|
405
506
|
},
|
|
406
507
|
};
|
|
407
508
|
|
|
@@ -430,9 +531,19 @@ export async function buildSetupRun(
|
|
|
430
531
|
// outcome at all left a converged re-run with a plan-shaped output that
|
|
431
532
|
// claimed nothing had run — while the row still rendered a bare green ✓.
|
|
432
533
|
// Convergence is a real result: one quiet line per row, zero writes.
|
|
433
|
-
|
|
534
|
+
//
|
|
535
|
+
// On a REMOVAL the same state is `kept`, not `noop`: the run owned
|
|
536
|
+
// nothing here and deliberately left the machine's state standing (the
|
|
537
|
+
// config the user edited, the editor with no extension copy). That is
|
|
538
|
+
// standing aside, which the vocabulary marks inert (○), where `noop`'s
|
|
539
|
+
// green ✓ would claim removal work that never existed.
|
|
540
|
+
if (row.action === "none") {
|
|
541
|
+
return removal
|
|
542
|
+
? { status: "kept" }
|
|
543
|
+
: { status: "noop", note: "unchanged" };
|
|
544
|
+
}
|
|
434
545
|
if (!isChosen(row, chosen)) return undefined;
|
|
435
|
-
const error = sink.get(rowKey(row.
|
|
546
|
+
const error = sink.get(rowKey(row.scope, row.target));
|
|
436
547
|
if (error !== undefined) {
|
|
437
548
|
const pragma = rowPragmaError(error);
|
|
438
549
|
return {
|
|
@@ -444,37 +555,69 @@ export async function buildSetupRun(
|
|
|
444
555
|
};
|
|
445
556
|
}
|
|
446
557
|
if (!isActionable(row.action)) return { status: "noop" };
|
|
447
|
-
|
|
448
|
-
// files the entry is being taken OUT of, and counting them as "1 updated"
|
|
449
|
-
// described the opposite of what the run had just done.
|
|
450
|
-
const childKey = CHILD_ANSWER[row.target];
|
|
451
|
-
const kept =
|
|
452
|
-
childKey === undefined ? undefined : readList(answers, childKey);
|
|
453
|
-
const note = removal
|
|
454
|
-
? ACTION_NOTES[row.action]
|
|
455
|
-
: (childNote(row, kept) ?? ACTION_NOTES[row.action]);
|
|
558
|
+
const note = noteFor(row, answers);
|
|
456
559
|
return {
|
|
457
560
|
status: removal ? "removed" : "done",
|
|
458
561
|
...(note === undefined ? {} : { note }),
|
|
459
562
|
};
|
|
460
563
|
};
|
|
461
564
|
|
|
565
|
+
/** The one answers→plan projection both `applied` faces share. */
|
|
566
|
+
const project = (answers: Record<string, unknown>): SetupPlan => {
|
|
567
|
+
const chosen = readList(answers, ROWS_ANSWER);
|
|
568
|
+
return withRows(
|
|
569
|
+
plan,
|
|
570
|
+
plan.rows.map((row): PlanRow => {
|
|
571
|
+
const outcome = outcomeFor(row, chosen, answers);
|
|
572
|
+
return {
|
|
573
|
+
...row,
|
|
574
|
+
selected: isChosen(row, chosen),
|
|
575
|
+
...(outcome === undefined ? {} : { outcome }),
|
|
576
|
+
};
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
};
|
|
580
|
+
|
|
462
581
|
return {
|
|
463
582
|
plan,
|
|
464
583
|
generator,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
584
|
+
setRowListener: (listener) => {
|
|
585
|
+
rowListener = listener;
|
|
586
|
+
},
|
|
587
|
+
applied: project,
|
|
588
|
+
appliedUndo: (outcomes) => {
|
|
589
|
+
// The undo path's sink fill. The forward run's recover frames write the
|
|
590
|
+
// sink WHILE the task is interpreted; a real `--undo` interprets the
|
|
591
|
+
// task only as the mocked collection walk, so the executed reversals'
|
|
592
|
+
// outcomes land here instead — same sink, same reader (`outcomeFor`),
|
|
593
|
+
// other direction. The first failure per row wins: it is the earliest
|
|
594
|
+
// cause, and the row's note should name it, not the knock-on.
|
|
595
|
+
sink.clear();
|
|
596
|
+
for (const outcome of outcomes) {
|
|
597
|
+
if (outcome.status !== "failed" || outcome.key === undefined) continue;
|
|
598
|
+
if (sink.get(outcome.key) !== undefined) continue;
|
|
599
|
+
sink.record(outcome.key, outcome.error);
|
|
600
|
+
}
|
|
601
|
+
// A row that owed reversals and heard back from NONE of them cannot
|
|
602
|
+
// report `removed` — nothing proved anything happened. Structurally
|
|
603
|
+
// every actionable removal row composes at least one keyed reversal,
|
|
604
|
+
// so this is a guard against a future target breaking that invariant,
|
|
605
|
+
// not a path any current target takes.
|
|
606
|
+
const reported = new Set(
|
|
607
|
+
outcomes.flatMap((o) => (o.key === undefined ? [] : [o.key])),
|
|
477
608
|
);
|
|
609
|
+
for (const row of plan.rows) {
|
|
610
|
+
const id = rowKey(row.scope, row.target);
|
|
611
|
+
if (!isActionable(row.action) || !row.selected) continue;
|
|
612
|
+
if (reported.has(id) || sink.get(id) !== undefined) continue;
|
|
613
|
+
sink.record(id, {
|
|
614
|
+
code: "UNSUPPORTED",
|
|
615
|
+
message: "no reversal was executed for this target",
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
// No answers: `--undo` asks nothing, so every row falls back to its
|
|
619
|
+
// own default selection — exactly the set `generate` composed from.
|
|
620
|
+
return project({});
|
|
478
621
|
},
|
|
479
622
|
};
|
|
480
623
|
}
|