mandrel 1.91.0 → 1.92.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 (109) hide show
  1. package/.agents/README.md +5 -5
  2. package/.agents/docs/SDLC.md +75 -107
  3. package/.agents/docs/configuration.md +1 -1
  4. package/.agents/docs/quality-gates.md +8 -7
  5. package/.agents/docs/workflows.md +4 -10
  6. package/.agents/instructions.md +2 -2
  7. package/.agents/schemas/agentrc.schema.json +1 -1
  8. package/.agents/schemas/lifecycle/README.md +2 -1
  9. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  10. package/.agents/schemas/risk-verdict.schema.json +14 -1
  11. package/.agents/scripts/README.md +0 -46
  12. package/.agents/scripts/analyze-execution.js +76 -1
  13. package/.agents/scripts/check-doc-links.js +3 -0
  14. package/.agents/scripts/check-workflow-cli-lint.js +208 -0
  15. package/.agents/scripts/epic-audit-recheck.js +1 -1
  16. package/.agents/scripts/epic-plan-clarity.js +16 -6
  17. package/.agents/scripts/epic-plan-decompose.js +37 -59
  18. package/.agents/scripts/epic-plan-healthcheck.js +39 -6
  19. package/.agents/scripts/epic-plan-spec.js +41 -168
  20. package/.agents/scripts/epic-reconcile.js +7 -1
  21. package/.agents/scripts/lib/Logger.js +4 -4
  22. package/.agents/scripts/lib/audit-suite/index.js +4 -5
  23. package/.agents/scripts/lib/audit-suite/runner.js +6 -4
  24. package/.agents/scripts/lib/audit-suite/selector.js +2 -3
  25. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  26. package/.agents/scripts/lib/command-header.js +20 -0
  27. package/.agents/scripts/lib/config/github.js +1 -3
  28. package/.agents/scripts/lib/config-settings-schema.js +4 -3
  29. package/.agents/scripts/lib/degraded-mode.js +1 -1
  30. package/.agents/scripts/lib/label-constants.js +18 -3
  31. package/.agents/scripts/lib/label-taxonomy.js +14 -3
  32. package/.agents/scripts/lib/mutation/baseline-snapshot.js +7 -6
  33. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +18 -8
  34. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -4
  35. package/.agents/scripts/lib/orchestration/docs-digest.js +2 -2
  36. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +4 -0
  37. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +22 -12
  38. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +6 -1
  39. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +18 -2
  40. package/.agents/scripts/{lifecycle-diff.js → lib/orchestration/lifecycle/ledger-diff.js} +10 -76
  41. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +1 -1
  42. package/.agents/scripts/lib/orchestration/plan-context.js +510 -0
  43. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +177 -0
  44. package/.agents/scripts/lib/orchestration/plan-metrics.js +445 -0
  45. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +359 -0
  46. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +127 -0
  47. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +978 -0
  48. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +191 -0
  49. package/.agents/scripts/lib/orchestration/plan-reachability.js +160 -0
  50. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +3 -3
  51. package/.agents/scripts/lib/orchestration/planning-risk.js +1 -1
  52. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  53. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +1 -1
  54. package/.agents/scripts/lib/orchestration/ticketing/reads.js +11 -2
  55. package/.agents/scripts/lib/plan-phase-cleanup.js +16 -0
  56. package/.agents/scripts/lib/presentation/manifest-persistence.js +2 -3
  57. package/.agents/scripts/lib/templates/spec-author-prompts.js +74 -0
  58. package/.agents/scripts/plan-context.js +186 -0
  59. package/.agents/scripts/plan-critics.js +227 -0
  60. package/.agents/scripts/plan-persist.js +383 -0
  61. package/.agents/scripts/pr-watch-with-update.js +8 -7
  62. package/.agents/scripts/run-lint.js +10 -11
  63. package/.agents/scripts/story-plan.js +19 -7
  64. package/.agents/scripts/sync-claude-commands.js +10 -5
  65. package/.agents/skills/core/knowledge-transfer/SKILL.md +11 -7
  66. package/.agents/workflows/audit-documentation.md +5 -7
  67. package/.agents/workflows/audit-lighthouse.md +1 -0
  68. package/.agents/workflows/audit-security.md +1 -0
  69. package/.agents/workflows/audit-to-stories.md +16 -5
  70. package/.agents/workflows/git-deliver.md +76 -243
  71. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  72. package/.agents/workflows/helpers/code-review.md +7 -6
  73. package/.agents/workflows/helpers/deliver-epic.md +5 -4
  74. package/.agents/workflows/helpers/plan-epic-reference.md +109 -96
  75. package/.agents/workflows/helpers/plan-epic.md +246 -999
  76. package/.agents/workflows/helpers/plan-story.md +14 -14
  77. package/.agents/workflows/helpers/scope-triage-gate.md +6 -4
  78. package/.agents/workflows/helpers/single-story-deliver.md +5 -4
  79. package/.agents/workflows/helpers/worktree-lifecycle.md +9 -7
  80. package/.agents/workflows/mandrel-update.md +144 -466
  81. package/.agents/workflows/plan.md +73 -143
  82. package/docs/CHANGELOG.md +13 -0
  83. package/lib/cli/registry.js +21 -3
  84. package/package.json +1 -1
  85. package/.agents/schemas/loop-unit.schema.json +0 -70
  86. package/.agents/scripts/assert-branch.js +0 -81
  87. package/.agents/scripts/check-loop-units.js +0 -204
  88. package/.agents/scripts/detect-merges.js +0 -111
  89. package/.agents/scripts/git-pr-quality-gate.js +0 -205
  90. package/.agents/scripts/git-rebase-and-resolve.js +0 -234
  91. package/.agents/scripts/hierarchy-gate.js +0 -192
  92. package/.agents/scripts/hydrate-context.js +0 -179
  93. package/.agents/scripts/lib/loop-units/validate-loop-unit.js +0 -197
  94. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/cli.js +0 -167
  95. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +0 -69
  96. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +0 -86
  97. package/.agents/scripts/loc-delta.js +0 -205
  98. package/.agents/scripts/render-manifest.js +0 -143
  99. package/.agents/scripts/run-audit-suite.js +0 -97
  100. package/.agents/scripts/select-audits.js +0 -155
  101. package/.agents/scripts/update-mutation-baseline.js +0 -189
  102. package/.agents/workflows/explain.md +0 -118
  103. package/.agents/workflows/git-merge-pr.md +0 -377
  104. package/.agents/workflows/helpers/epic-plan-decompose.md +0 -22
  105. package/.agents/workflows/helpers/epic-plan-spec.md +0 -22
  106. package/.agents/workflows/loops/README.md +0 -65
  107. package/.agents/workflows/loops/fix-failing-tests.md +0 -74
  108. package/.agents/workflows/loops/nightly-audit.md +0 -81
  109. package/.agents/workflows/loops/watch-ci.md +0 -68
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ /* node:coverage ignore file */
3
+
4
+ /**
5
+ * plan-context.js — step 1 of the collapsed `/plan` pipeline (Epic #4474,
6
+ * M3 PR2): the single emit-context CLI.
7
+ *
8
+ * Folds the retired 12-phase pipeline's two emit-context halves plus
9
+ * the three previously-no-CLI library calls
10
+ * (`findSimilarOpenEpics`, clarity scoring, re-plan detection) into ONE
11
+ * stdout-pure JSON envelope. The PR7 cutover retired the delegate CLIs —
12
+ * this is the only emit-context surface.
13
+ *
14
+ * Two entry forms (exactly one is required):
15
+ *
16
+ * --epic <id> Existing-Epic mode. Envelope carries `epic`,
17
+ * `clarity` (Epic Clarity Gate rubric), `replan`
18
+ * (already-planned signals) and `planState`.
19
+ *
20
+ * --one-pager <path> Ideation mode — the Epic does not exist yet
21
+ * (creation moves to the persist half). Envelope
22
+ * carries `onePager` and `duplicates[]` (cross-Epic
23
+ * dup search). No clarity score: the ideation path
24
+ * is definitionally clear.
25
+ *
26
+ * Flags:
27
+ * --pretty Pretty-print the JSON envelope.
28
+ * --full-context Bypass the planning-context budget (unbounded body).
29
+ *
30
+ * stdout is reserved for the JSON envelope (Story #2278 discipline):
31
+ * `routeAllOutputToStderr()` runs before any pipeline code so a captured
32
+ * file is unconditionally parseable by `JSON.parse`.
33
+ *
34
+ * Exit codes:
35
+ * 0 — envelope emitted.
36
+ * 1 — fatal error (see stderr).
37
+ */
38
+
39
+ // Fail-fast if the framework's runtime deps are not installed — must be the
40
+ // first import so the check runs before any third-party-importing sibling
41
+ // module is evaluated (Story #3432).
42
+ import './lib/runtime-deps/ensure-installed.js';
43
+ import { parseArgs } from 'node:util';
44
+ import { runAsCli } from './lib/cli-utils.js';
45
+ import {
46
+ resolveConfig,
47
+ validateOrchestrationConfig,
48
+ } from './lib/config-resolver.js';
49
+ import { routeAllOutputToStderr } from './lib/Logger.js';
50
+ import { buildPlanContext } from './lib/orchestration/plan-context.js';
51
+ import { recordPlanInvocation } from './lib/orchestration/plan-metrics.js';
52
+ import { createProvider } from './lib/provider-factory.js';
53
+
54
+ /**
55
+ * Build the envelope and write it to `stdout` as a single JSON line
56
+ * (or pretty-printed with --pretty). Exported for tests: the stdout-purity
57
+ * test injects a fake provider and a capture stream and asserts the
58
+ * captured output is exactly one `JSON.parse`-able payload.
59
+ *
60
+ * @param {{
61
+ * mode: 'epic'|'one-pager',
62
+ * epicId?: number,
63
+ * onePagerPath?: string,
64
+ * onePagerContent?: string,
65
+ * provider: object,
66
+ * config: object,
67
+ * settings: object,
68
+ * fullContext?: boolean,
69
+ * pretty?: boolean,
70
+ * cwd?: string,
71
+ * stdout?: { write: (chunk: string) => void },
72
+ * }} args
73
+ * @returns {Promise<object>} the emitted envelope.
74
+ */
75
+ export async function emitPlanContext({
76
+ mode,
77
+ epicId,
78
+ onePagerPath,
79
+ onePagerContent,
80
+ provider,
81
+ config,
82
+ settings,
83
+ fullContext = false,
84
+ pretty = false,
85
+ cwd,
86
+ stdout = process.stdout,
87
+ }) {
88
+ const envelope = await buildPlanContext({
89
+ mode,
90
+ epicId,
91
+ onePagerPath,
92
+ onePagerContent,
93
+ provider,
94
+ config,
95
+ settings,
96
+ fullContext,
97
+ cwd,
98
+ });
99
+ const json = pretty
100
+ ? JSON.stringify(envelope, null, 2)
101
+ : JSON.stringify(envelope);
102
+ stdout.write(`${json}\n`);
103
+ return envelope;
104
+ }
105
+
106
+ async function main() {
107
+ const { values } = parseArgs({
108
+ options: {
109
+ epic: { type: 'string' },
110
+ 'one-pager': { type: 'string' },
111
+ pretty: { type: 'boolean', default: false },
112
+ 'full-context': { type: 'boolean', default: false },
113
+ },
114
+ strict: true,
115
+ });
116
+
117
+ const hasEpic = typeof values.epic === 'string' && values.epic.length > 0;
118
+ const hasOnePager =
119
+ typeof values['one-pager'] === 'string' && values['one-pager'].length > 0;
120
+ if (hasEpic === hasOnePager) {
121
+ throw new Error(
122
+ 'Pass exactly one of --epic <id> or --one-pager <path>. ' +
123
+ '(--epic: existing-Epic mode; --one-pager: ideation mode.)',
124
+ );
125
+ }
126
+
127
+ let epicId;
128
+ if (hasEpic) {
129
+ epicId = Number.parseInt(values.epic, 10);
130
+ if (!Number.isInteger(epicId)) {
131
+ throw new Error(
132
+ `--epic must be a numeric issue id (got "${values.epic}").`,
133
+ );
134
+ }
135
+ }
136
+
137
+ // stdout is reserved for the JSON envelope: flip every Logger sink that
138
+ // could land on stdout to stderr BEFORE any pipeline code runs
139
+ // (Story #2278 — the same stdout-purity guarantee the retired pipeline
140
+ // gives; this CLI is emit-only so the flip is unconditional).
141
+ routeAllOutputToStderr();
142
+
143
+ let config;
144
+ let settings;
145
+ try {
146
+ config = resolveConfig();
147
+ // `settings` retains the legacy bag shape `buildAuthoringContext` and
148
+ // friends consume: `{ baseBranch, paths, planning, docsContextFiles }`.
149
+ settings = {
150
+ baseBranch: config.project?.baseBranch,
151
+ paths: config.project?.paths,
152
+ planning: config.planning,
153
+ docsContextFiles: config.project?.docsContextFiles,
154
+ };
155
+ validateOrchestrationConfig(config);
156
+ } catch (err) {
157
+ throw new Error(`Config schema validation failed:\n${err.message}`);
158
+ }
159
+ const provider = createProvider(config);
160
+
161
+ // Plan-metrics ledger (#4474 PR1): stamp entry/exit + mode so the folded
162
+ // emit surface is measured against the 12-phase baseline. One-pager mode
163
+ // has no Epic yet, so the record routes to the standalone stream
164
+ // (epicId null) exactly like `story-plan.js`.
165
+ await recordPlanInvocation(
166
+ {
167
+ cli: 'plan-context',
168
+ mode: hasEpic ? 'epic' : 'one-pager',
169
+ epicId: hasEpic ? epicId : null,
170
+ config,
171
+ },
172
+ () =>
173
+ emitPlanContext({
174
+ mode: hasEpic ? 'epic' : 'one-pager',
175
+ epicId,
176
+ onePagerPath: hasOnePager ? values['one-pager'] : undefined,
177
+ provider,
178
+ config,
179
+ settings,
180
+ fullContext: values['full-context'],
181
+ pretty: values.pretty,
182
+ }),
183
+ );
184
+ }
185
+
186
+ runAsCli(import.meta.url, main, { source: 'plan-context' });
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * plan-critics.js — deterministic dispatch gate for the conditional
5
+ * author-step critics of the collapsed /plan flow (Epic #4474 PR6,
6
+ * design §4).
7
+ *
8
+ * Runs between authoring and gate #2, entirely git-local (zero GitHub
9
+ * calls): reads the authored artifacts, evaluates the risk/size dispatch
10
+ * conditions for the consolidation (8.3) and pre-mortem (8.5) critics via
11
+ * `lib/orchestration/plan-critic-conditions.js`, and emits one JSON
12
+ * verdict on stdout. The workflow dispatches a fresh-context sub-agent
13
+ * ONLY for critics with `dispatch: true`; every skip decision is appended
14
+ * to the plan-metrics ledger (`kind: "critic-skip"`, with reasons) so
15
+ * under-firing is auditable — the persist validators remain unchanged
16
+ * hard gates regardless of what this gate decides.
17
+ *
18
+ * Conditions (design §4 / §6 PR6):
19
+ * - Consolidation: the existing deterministic precondition
20
+ * (`evaluateConsolidationPrecondition`) says dispatch AND (the draft
21
+ * has > 5 stories OR a confirmed divergence from the Tech Spec's
22
+ * Delivery Slicing table). Skipped outright in the single-delivery
23
+ * shape (no tickets exist to consolidate).
24
+ * - Pre-mortem: risk verdict overall level is high, OR ticket count is
25
+ * at least half `maxTickets`, OR any `planning.riskHeuristics` phrase
26
+ * matches the plan text (case-insensitive substring over tech spec +
27
+ * tickets + risk summary).
28
+ *
29
+ * Modes:
30
+ * --epic <id> Artifact paths default to the per-Epic temp tree
31
+ * (`temp/epic-<id>/techspec.md`, `risk-verdict.json`,
32
+ * `tickets.json`); skip records land on the
33
+ * per-Epic plan-metrics ledger.
34
+ * explicit paths Ideation mode: pass --tech-spec/--risk-verdict
35
+ * (and --tickets when fan-out) explicitly; skip
36
+ * records land on the standalone ledger stream.
37
+ *
38
+ * Output (stdout-pure JSON):
39
+ * { epicId, consolidation: { critic, dispatch, reasons },
40
+ * premortem: { critic, dispatch, reasons } }
41
+ *
42
+ * Exit codes: 0 — verdict emitted (dispatch decisions are data, not
43
+ * failures); 1 — fatal error (unreadable/invalid artifacts, bad args).
44
+ */
45
+
46
+ // Fail-fast if the framework's runtime deps are not installed — must be the
47
+ // first import so the check runs before any third-party-importing sibling
48
+ // module is evaluated (Story #3432).
49
+ import './lib/runtime-deps/ensure-installed.js';
50
+ import { readFile } from 'node:fs/promises';
51
+ import { parseArgs } from 'node:util';
52
+
53
+ import { runAsCli } from './lib/cli-utils.js';
54
+ import { epicArtifactPath } from './lib/config/temp-paths.js';
55
+ import {
56
+ getLimits,
57
+ resolveConfig,
58
+ validateOrchestrationConfig,
59
+ } from './lib/config-resolver.js';
60
+ import { routeAllOutputToStderr } from './lib/Logger.js';
61
+ import { loadRiskVerdict } from './lib/orchestration/epic-plan-spec/phases/risk-verdict.js';
62
+ import {
63
+ evaluateConsolidationDispatch,
64
+ evaluatePremortemDispatch,
65
+ } from './lib/orchestration/plan-critic-conditions.js';
66
+ import {
67
+ appendCriticSkip,
68
+ recordPlanInvocation,
69
+ } from './lib/orchestration/plan-metrics.js';
70
+
71
+ const USAGE =
72
+ 'Usage: plan-critics.js (--epic <EpicId> | --tech-spec <file> ' +
73
+ '--risk-verdict <file> [--tickets <file>]) [--pretty]';
74
+
75
+ /**
76
+ * Resolve the planning risk heuristics list from the canonical config
77
+ * block (same resolution `plan-context.js` and the decompose context use).
78
+ *
79
+ * @param {object} config
80
+ * @returns {string[]}
81
+ */
82
+ function resolveRiskHeuristics(config = {}) {
83
+ if (Array.isArray(config.planning?.riskHeuristics)) {
84
+ return config.planning.riskHeuristics;
85
+ }
86
+ return config.agentSettings?.planning?.riskHeuristics || [];
87
+ }
88
+
89
+ async function readOptional(filePath, { required }) {
90
+ try {
91
+ return await readFile(filePath, 'utf8');
92
+ } catch (err) {
93
+ if (!required && err?.code === 'ENOENT') return null;
94
+ throw new Error(`Cannot read ${filePath}: ${err.message}`);
95
+ }
96
+ }
97
+
98
+ async function main() {
99
+ const { values } = parseArgs({
100
+ options: {
101
+ epic: { type: 'string' },
102
+ 'tech-spec': { type: 'string' },
103
+ 'risk-verdict': { type: 'string' },
104
+ tickets: { type: 'string' },
105
+ pretty: { type: 'boolean', default: false },
106
+ },
107
+ strict: true,
108
+ });
109
+
110
+ let epicId = null;
111
+ if (values.epic !== undefined) {
112
+ epicId = Number.parseInt(values.epic, 10);
113
+ if (!Number.isInteger(epicId)) {
114
+ throw new Error(
115
+ `--epic must be a numeric issue id (got "${values.epic}").\n${USAGE}`,
116
+ );
117
+ }
118
+ }
119
+
120
+ // stdout is reserved for the JSON verdict — flip every Logger sink to
121
+ // stderr before any pipeline code runs (same guarantee plan-context.js
122
+ // gives its envelope).
123
+ routeAllOutputToStderr();
124
+
125
+ let config;
126
+ try {
127
+ config = resolveConfig();
128
+ validateOrchestrationConfig(config);
129
+ } catch (err) {
130
+ throw new Error(`Config schema validation failed:\n${err.message}`);
131
+ }
132
+
133
+ const fallback = (basename) =>
134
+ epicId === null ? undefined : epicArtifactPath(epicId, basename, config);
135
+ const techSpecPath = values['tech-spec'] ?? fallback('techspec.md');
136
+ const riskVerdictPath =
137
+ values['risk-verdict'] ?? fallback('risk-verdict.json');
138
+ const ticketsPath = values.tickets ?? fallback('tickets.json');
139
+ if (!techSpecPath || !riskVerdictPath) {
140
+ throw new Error(
141
+ `Missing artifact path(s): without --epic, explicit --tech-spec and --risk-verdict are required.\n${USAGE}`,
142
+ );
143
+ }
144
+
145
+ const verdict = await recordPlanInvocation(
146
+ { cli: 'plan-critics', mode: 'evaluate', epicId, config },
147
+ async () => {
148
+ const techSpecContent = await readOptional(techSpecPath, {
149
+ required: true,
150
+ });
151
+ const riskVerdict = loadRiskVerdict(riskVerdictPath);
152
+ // Tickets are shape-dependent: a single-delivery plan authors none.
153
+ // Required only when passed explicitly.
154
+ const ticketsRaw = ticketsPath
155
+ ? await readOptional(ticketsPath, {
156
+ required: values.tickets !== undefined,
157
+ })
158
+ : null;
159
+ let tickets = null;
160
+ if (ticketsRaw !== null) {
161
+ try {
162
+ tickets = JSON.parse(ticketsRaw);
163
+ } catch (err) {
164
+ throw new Error(
165
+ `Failed to parse tickets file "${ticketsPath}" as JSON: ${err.message}`,
166
+ );
167
+ }
168
+ if (!Array.isArray(tickets)) {
169
+ throw new Error(
170
+ `Tickets file "${ticketsPath}" must contain a JSON array.`,
171
+ );
172
+ }
173
+ }
174
+
175
+ const consolidation =
176
+ tickets === null
177
+ ? {
178
+ critic: 'consolidation',
179
+ dispatch: false,
180
+ reasons: [
181
+ 'single-delivery shape — no draft tickets exist to consolidate.',
182
+ ],
183
+ }
184
+ : evaluateConsolidationDispatch({
185
+ draftStories: tickets,
186
+ specText: techSpecContent,
187
+ });
188
+
189
+ const premortem = evaluatePremortemDispatch({
190
+ riskVerdict,
191
+ ticketCount: tickets?.length ?? 0,
192
+ maxTickets: getLimits(config).maxTickets,
193
+ riskHeuristics: resolveRiskHeuristics(config),
194
+ planText: [
195
+ techSpecContent,
196
+ ticketsRaw ?? '',
197
+ riskVerdict.summary ?? '',
198
+ ].join('\n'),
199
+ });
200
+
201
+ // Skip-audit trail (#4474 PR6): every non-dispatch is a ledger
202
+ // record. Best-effort — a failed append never fails the gate.
203
+ for (const decision of [consolidation, premortem]) {
204
+ if (!decision.dispatch) {
205
+ await appendCriticSkip(
206
+ {
207
+ critic: decision.critic,
208
+ reasons: decision.reasons,
209
+ cli: 'plan-critics',
210
+ epicId,
211
+ },
212
+ config,
213
+ );
214
+ }
215
+ }
216
+
217
+ return { epicId, consolidation, premortem };
218
+ },
219
+ );
220
+
221
+ const json = values.pretty
222
+ ? JSON.stringify(verdict, null, 2)
223
+ : JSON.stringify(verdict);
224
+ process.stdout.write(`${json}\n`);
225
+ }
226
+
227
+ runAsCli(import.meta.url, main, { source: 'plan-critics' });