@delegance/claude-autopilot 5.5.2 → 7.2.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/CHANGELOG.md +1776 -6
- package/README.md +65 -1
- package/bin/_launcher.js +38 -23
- package/dist/src/adapters/council/openai.js +12 -6
- package/dist/src/adapters/deploy/_http.d.ts +43 -0
- package/dist/src/adapters/deploy/_http.js +99 -0
- package/dist/src/adapters/deploy/fly.d.ts +206 -0
- package/dist/src/adapters/deploy/fly.js +696 -0
- package/dist/src/adapters/deploy/index.d.ts +2 -0
- package/dist/src/adapters/deploy/index.js +33 -0
- package/dist/src/adapters/deploy/render.d.ts +181 -0
- package/dist/src/adapters/deploy/render.js +550 -0
- package/dist/src/adapters/deploy/types.d.ts +67 -3
- package/dist/src/adapters/deploy/vercel.d.ts +17 -1
- package/dist/src/adapters/deploy/vercel.js +29 -49
- package/dist/src/adapters/pricing.d.ts +36 -0
- package/dist/src/adapters/pricing.js +40 -0
- package/dist/src/adapters/review-engine/codex.js +10 -7
- package/dist/src/cli/autopilot.d.ts +75 -0
- package/dist/src/cli/autopilot.js +750 -0
- package/dist/src/cli/brainstorm.d.ts +23 -0
- package/dist/src/cli/brainstorm.js +131 -0
- package/dist/src/cli/costs.d.ts +15 -1
- package/dist/src/cli/costs.js +99 -10
- package/dist/src/cli/dashboard/index.d.ts +5 -0
- package/dist/src/cli/dashboard/index.js +49 -0
- package/dist/src/cli/dashboard/login.d.ts +22 -0
- package/dist/src/cli/dashboard/login.js +260 -0
- package/dist/src/cli/dashboard/logout.d.ts +12 -0
- package/dist/src/cli/dashboard/logout.js +45 -0
- package/dist/src/cli/dashboard/status.d.ts +30 -0
- package/dist/src/cli/dashboard/status.js +65 -0
- package/dist/src/cli/dashboard/upload.d.ts +16 -0
- package/dist/src/cli/dashboard/upload.js +48 -0
- package/dist/src/cli/deploy.d.ts +3 -3
- package/dist/src/cli/deploy.js +34 -9
- package/dist/src/cli/engine-flag-deprecation.d.ts +14 -0
- package/dist/src/cli/engine-flag-deprecation.js +20 -0
- package/dist/src/cli/fix.d.ts +18 -0
- package/dist/src/cli/fix.js +105 -11
- package/dist/src/cli/help-text.d.ts +52 -0
- package/dist/src/cli/help-text.js +416 -0
- package/dist/src/cli/implement.d.ts +91 -0
- package/dist/src/cli/implement.js +196 -0
- package/dist/src/cli/index.d.ts +2 -1
- package/dist/src/cli/index.js +774 -245
- package/dist/src/cli/json-envelope.d.ts +187 -0
- package/dist/src/cli/json-envelope.js +270 -0
- package/dist/src/cli/json-mode.d.ts +33 -0
- package/dist/src/cli/json-mode.js +201 -0
- package/dist/src/cli/migrate.d.ts +111 -0
- package/dist/src/cli/migrate.js +305 -0
- package/dist/src/cli/plan.d.ts +81 -0
- package/dist/src/cli/plan.js +149 -0
- package/dist/src/cli/pr.d.ts +106 -0
- package/dist/src/cli/pr.js +191 -19
- package/dist/src/cli/preflight.js +26 -0
- package/dist/src/cli/review.d.ts +27 -0
- package/dist/src/cli/review.js +126 -0
- package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
- package/dist/src/cli/runs-watch-renderer.js +275 -0
- package/dist/src/cli/runs-watch.d.ts +41 -0
- package/dist/src/cli/runs-watch.js +395 -0
- package/dist/src/cli/runs.d.ts +122 -0
- package/dist/src/cli/runs.js +902 -0
- package/dist/src/cli/scaffold.d.ts +39 -0
- package/dist/src/cli/scaffold.js +287 -0
- package/dist/src/cli/scan.d.ts +93 -0
- package/dist/src/cli/scan.js +166 -40
- package/dist/src/cli/setup.d.ts +30 -0
- package/dist/src/cli/setup.js +137 -0
- package/dist/src/cli/spec.d.ts +66 -0
- package/dist/src/cli/spec.js +132 -0
- package/dist/src/cli/validate.d.ts +29 -0
- package/dist/src/cli/validate.js +131 -0
- package/dist/src/core/config/schema.d.ts +9 -0
- package/dist/src/core/config/schema.js +7 -0
- package/dist/src/core/config/types.d.ts +11 -0
- package/dist/src/core/council/runner.d.ts +10 -1
- package/dist/src/core/council/runner.js +25 -3
- package/dist/src/core/council/types.d.ts +7 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +11 -0
- package/dist/src/core/logging/redaction.d.ts +13 -0
- package/dist/src/core/logging/redaction.js +20 -0
- package/dist/src/core/migrate/schema-validator.js +15 -1
- package/dist/src/core/phases/static-rules.d.ts +5 -1
- package/dist/src/core/phases/static-rules.js +2 -5
- package/dist/src/core/run-state/budget.d.ts +88 -0
- package/dist/src/core/run-state/budget.js +141 -0
- package/dist/src/core/run-state/cli-internal.d.ts +21 -0
- package/dist/src/core/run-state/cli-internal.js +174 -0
- package/dist/src/core/run-state/events.d.ts +59 -0
- package/dist/src/core/run-state/events.js +512 -0
- package/dist/src/core/run-state/lock.d.ts +61 -0
- package/dist/src/core/run-state/lock.js +206 -0
- package/dist/src/core/run-state/phase-context.d.ts +60 -0
- package/dist/src/core/run-state/phase-context.js +108 -0
- package/dist/src/core/run-state/phase-registry.d.ts +137 -0
- package/dist/src/core/run-state/phase-registry.js +162 -0
- package/dist/src/core/run-state/phase-runner.d.ts +80 -0
- package/dist/src/core/run-state/phase-runner.js +447 -0
- package/dist/src/core/run-state/provider-readback.d.ts +130 -0
- package/dist/src/core/run-state/provider-readback.js +426 -0
- package/dist/src/core/run-state/replay-decision.d.ts +69 -0
- package/dist/src/core/run-state/replay-decision.js +144 -0
- package/dist/src/core/run-state/resolve-engine.d.ts +45 -0
- package/dist/src/core/run-state/resolve-engine.js +74 -0
- package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
- package/dist/src/core/run-state/resume-preflight.js +116 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +69 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.js +193 -0
- package/dist/src/core/run-state/runs.d.ts +57 -0
- package/dist/src/core/run-state/runs.js +288 -0
- package/dist/src/core/run-state/snapshot.d.ts +14 -0
- package/dist/src/core/run-state/snapshot.js +114 -0
- package/dist/src/core/run-state/state.d.ts +40 -0
- package/dist/src/core/run-state/state.js +164 -0
- package/dist/src/core/run-state/types.d.ts +284 -0
- package/dist/src/core/run-state/types.js +19 -0
- package/dist/src/core/run-state/ulid.d.ts +11 -0
- package/dist/src/core/run-state/ulid.js +95 -0
- package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
- package/dist/src/core/schema-alignment/extractor/index.js +2 -2
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
- package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
- package/dist/src/core/schema-alignment/git-history.js +53 -0
- package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
- package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
- package/dist/src/dashboard/auto-upload.d.ts +26 -0
- package/dist/src/dashboard/auto-upload.js +107 -0
- package/dist/src/dashboard/config.d.ts +22 -0
- package/dist/src/dashboard/config.js +109 -0
- package/dist/src/dashboard/upload/canonical.d.ts +3 -0
- package/dist/src/dashboard/upload/canonical.js +16 -0
- package/dist/src/dashboard/upload/chain.d.ts +9 -0
- package/dist/src/dashboard/upload/chain.js +27 -0
- package/dist/src/dashboard/upload/snapshot.d.ts +23 -0
- package/dist/src/dashboard/upload/snapshot.js +66 -0
- package/dist/src/dashboard/upload/uploader.d.ts +54 -0
- package/dist/src/dashboard/upload/uploader.js +330 -0
- package/package.json +19 -3
- package/scripts/autoregress.ts +1 -1
- package/scripts/test-runner.mjs +4 -0
- package/skills/claude-autopilot.md +1 -1
- package/skills/make-interfaces-feel-better/SKILL.md +104 -0
- package/skills/simplify-ui/SKILL.md +103 -0
- package/skills/ui/SKILL.md +117 -0
- package/skills/ui-ux-pro-max/SKILL.md +90 -0
package/dist/src/cli/index.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { runCommand } from "./run.js";
|
|
14
14
|
import { runWatch } from "./watch.js";
|
|
15
15
|
import { runSetup } from "./setup.js";
|
|
16
|
+
import { runScaffold } from "./scaffold.js";
|
|
16
17
|
import { runDoctor } from "./preflight.js";
|
|
17
18
|
import { runCi } from "./ci.js";
|
|
18
19
|
import { runFix } from "./fix.js";
|
|
@@ -29,10 +30,12 @@ import { runCouncilCmd } from "./council.js";
|
|
|
29
30
|
import { runMigrateV4 } from "./migrate-v4.js";
|
|
30
31
|
import { runMigrateDoctor } from "./migrate-doctor.js";
|
|
31
32
|
import { initMigrate, NoMigrationToolDetectedError } from "./init-migrate.js";
|
|
32
|
-
import {
|
|
33
|
+
import { runMigrate } from "./migrate.js";
|
|
33
34
|
import { runDeploy, runDeployRollback, runDeployStatus } from "./deploy.js";
|
|
34
35
|
import { findPackageRoot } from "./_pkg-root.js";
|
|
35
36
|
import { GuardrailError } from "../core/errors.js";
|
|
37
|
+
import { buildHelpText, buildCommandHelpText } from "./help-text.js";
|
|
38
|
+
import { runUnderJsonMode } from "./json-envelope.js";
|
|
36
39
|
// Format unhandled errors as a one-line user-facing message instead of dumping a
|
|
37
40
|
// Node stack trace. Auth/network failures are by far the most common path here
|
|
38
41
|
// (bad/missing API key, rate limit, network blip) and surfacing the raw stack
|
|
@@ -106,6 +109,21 @@ const REVIEW_VERBS = new Set(['run', 'scan', 'ci', 'fix', 'baseline', 'explain',
|
|
|
106
109
|
// `detector` is a library used by setup/run, not a CLI subcommand — leave it out.
|
|
107
110
|
const ADVANCED_VERBS = new Set(['lsp', 'mcp', 'worker', 'autoregress', 'test-gen', 'hook', 'ignore']);
|
|
108
111
|
if (args[0] === 'review') {
|
|
112
|
+
// v6.0.4 — `review` is BOTH a grouping prefix (legacy alpha.2) AND a flat
|
|
113
|
+
// verb (the new engine-wrapped `runReview`). Disambiguate based on args[1]:
|
|
114
|
+
//
|
|
115
|
+
// - missing → grouping-prefix help banner (legacy V16)
|
|
116
|
+
// - --help / -h → grouping-prefix help banner (legacy)
|
|
117
|
+
// - in REVIEW_VERBS → grouping prefix (shift, route to flat handler)
|
|
118
|
+
// - other flag (`--engine`,
|
|
119
|
+
// `--config`, etc.) → flat-verb invocation; let `case 'review':` handle it
|
|
120
|
+
// - anything else → reject with legacy "not a review-phase verb"
|
|
121
|
+
//
|
|
122
|
+
// The "missing → prefix help" branch preserves the V16 v4-compat test
|
|
123
|
+
// (`claude-autopilot review` alone prints the review-phase verb list);
|
|
124
|
+
// users who want the v6 flat-verb behavior must pass at least one flag
|
|
125
|
+
// (e.g. `--engine`, `--config`, `--context`). `help review` continues to
|
|
126
|
+
// surface the flat-verb Options block via buildCommandHelpText.
|
|
109
127
|
const sub = args[1];
|
|
110
128
|
if (!sub || sub === '--help' || sub === '-h') {
|
|
111
129
|
console.log(`
|
|
@@ -123,16 +141,25 @@ Review-phase verbs:
|
|
|
123
141
|
|
|
124
142
|
These are aliases for the flat subcommands — \`claude-autopilot run\` and
|
|
125
143
|
\`claude-autopilot review run\` are equivalent.
|
|
144
|
+
|
|
145
|
+
The v6 \`review\` phase verb (engine-wrap shell) is invoked with any flag
|
|
146
|
+
present, e.g. \`claude-autopilot review --engine\`. See
|
|
147
|
+
\`claude-autopilot help review\` for its options.
|
|
126
148
|
`);
|
|
127
149
|
process.exit(0);
|
|
128
150
|
}
|
|
129
|
-
if (
|
|
151
|
+
if (sub.startsWith('--')) {
|
|
152
|
+
// Flat-verb invocation — fall through; do not shift.
|
|
153
|
+
}
|
|
154
|
+
else if (!REVIEW_VERBS.has(sub)) {
|
|
130
155
|
console.error(`\x1b[31m[claude-autopilot] "${sub}" is not a review-phase verb.\x1b[0m`);
|
|
131
156
|
console.error(`\x1b[2m Valid: ${[...REVIEW_VERBS].join(', ')}\x1b[0m`);
|
|
132
157
|
console.error(`\x1b[2m Did you mean: claude-autopilot ${sub} ...?\x1b[0m`);
|
|
133
158
|
process.exit(1);
|
|
134
159
|
}
|
|
135
|
-
|
|
160
|
+
else {
|
|
161
|
+
args.shift(); // drop 'review', leave the flat subcommand at args[0]
|
|
162
|
+
}
|
|
136
163
|
}
|
|
137
164
|
if (args[0] === 'advanced') {
|
|
138
165
|
const sub = args[1];
|
|
@@ -160,8 +187,17 @@ These are aliases for the flat subcommands; they still work without the 'advance
|
|
|
160
187
|
}
|
|
161
188
|
args.shift(); // drop 'advanced'
|
|
162
189
|
}
|
|
163
|
-
|
|
164
|
-
|
|
190
|
+
// `internal` is a hidden verb (v6 Phase 2): markdown-driven skills shell out
|
|
191
|
+
// to it to append typed events. Deliberately not in HELP_GROUPS / HELP_VERBS,
|
|
192
|
+
// not advertised in the welcome banner. Documented only via
|
|
193
|
+
// `claude-autopilot internal --help`.
|
|
194
|
+
//
|
|
195
|
+
// `runs` (plural) is the v6 Phase 3 umbrella verb — its sub-verbs (list, show,
|
|
196
|
+
// gc, delete, doctor) are dispatched inside its case block. The singular
|
|
197
|
+
// `run resume` form is handled BEFORE the default `run` -> review dispatch
|
|
198
|
+
// kicks in (see disambiguation block just below).
|
|
199
|
+
const SUBCOMMANDS = ['init', 'run', 'runs', 'scan', 'report', 'explain', 'ignore', 'ci', 'pr', 'fix', 'costs', 'watch', 'hook', 'autoregress', 'baseline', 'triage', 'lsp', 'worker', 'mcp', 'test-gen', 'pr-desc', 'doctor', 'preflight', 'setup', 'council', 'migrate-v4', 'migrate', 'migrate-doctor', 'deploy', 'brainstorm', 'spec', 'plan', 'implement', 'review', 'validate', 'autopilot', 'internal', 'help', '--help', '-h'];
|
|
200
|
+
const VALUE_FLAGS = ['base', 'config', 'files', 'format', 'output', 'debounce', 'ask', 'focus', 'fail-on', 'note', 'reason', 'expires', 'profile', 'severity', 'prompt', 'context-file', 'path', 'adapter', 'ref', 'sha', 'spec', 'context', 'mode', 'phases', 'budget'];
|
|
165
201
|
// Bare invocation — no subcommand, no flags → show welcome guide
|
|
166
202
|
if (args.length === 0) {
|
|
167
203
|
const hasKey = !!(process.env.ANTHROPIC_API_KEY || process.env.GEMINI_API_KEY ||
|
|
@@ -197,8 +233,16 @@ Run \x1b[36mclaude-autopilot --help\x1b[0m for full command reference.
|
|
|
197
233
|
`);
|
|
198
234
|
process.exit(0);
|
|
199
235
|
}
|
|
200
|
-
// Detect first non-flag arg as subcommand, default to 'run'
|
|
201
|
-
|
|
236
|
+
// Detect first non-flag arg as subcommand, default to 'run'.
|
|
237
|
+
//
|
|
238
|
+
// v6 Phase 3 disambiguation: `run resume <id>` is a v6 verb; the bare `run`
|
|
239
|
+
// remains the legacy review-phase entry point. We rewrite the head to a
|
|
240
|
+
// synthetic 'run-resume' subcommand so the existing 'run' case keeps doing
|
|
241
|
+
// `runReview` and we don't need to special-case it inside the review path.
|
|
242
|
+
let subcommand = (args[0] && !args[0].startsWith('--')) ? args[0] : 'run';
|
|
243
|
+
if (subcommand === 'run' && args[1] === 'resume') {
|
|
244
|
+
subcommand = 'run-resume';
|
|
245
|
+
}
|
|
202
246
|
/** Returns value for --name <value>. Exits if value is missing (next token is another flag or absent). */
|
|
203
247
|
function flag(name) {
|
|
204
248
|
const idx = args.indexOf(`--${name}`);
|
|
@@ -214,129 +258,106 @@ function flag(name) {
|
|
|
214
258
|
function boolFlag(name) {
|
|
215
259
|
return args.includes(`--${name}`);
|
|
216
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* v7.0 — `--no-engine` removed; `--engine` becomes a no-op shim with a
|
|
263
|
+
* one-shot per-process deprecation warning to stderr (codex pass-3
|
|
264
|
+
* NOTE #2). The engine is unconditionally on.
|
|
265
|
+
*
|
|
266
|
+
* Behavior:
|
|
267
|
+
* - `--no-engine` → exit 1 with `invalid_config` and a removal hint.
|
|
268
|
+
* - `--engine` → emit one stderr deprecation line per process; return.
|
|
269
|
+
* - neither → no-op; return.
|
|
270
|
+
*/
|
|
271
|
+
let __engineDeprecationWarned = false;
|
|
272
|
+
import { ENGINE_FLAG_DEPRECATION_MESSAGE, ENGINE_OFF_REMOVED_MESSAGE, ENGINE_OFF_ENV_REMOVED_MESSAGE, } from "./engine-flag-deprecation.js";
|
|
273
|
+
function parseEngineCliFlag() {
|
|
274
|
+
if (args.includes('--no-engine')) {
|
|
275
|
+
console.error(`\x1b[31m[claude-autopilot] invalid_config: ${ENGINE_OFF_REMOVED_MESSAGE}\x1b[0m`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
if (args.includes('--engine') && !__engineDeprecationWarned) {
|
|
279
|
+
__engineDeprecationWarned = true;
|
|
280
|
+
process.stderr.write(`${ENGINE_FLAG_DEPRECATION_MESSAGE}\n`);
|
|
281
|
+
}
|
|
282
|
+
checkEngineOffEnvDeprecation();
|
|
283
|
+
// v7.0 — engine is always on; the resolver ignores cliEngine. We
|
|
284
|
+
// return undefined so `cliEngine` never gets spread into the
|
|
285
|
+
// resolver opts (keeps call sites identical and source-compatible).
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
// Test seam: reset the per-process deprecation latch so tests that drive
|
|
289
|
+
// the flag multiple times in one process can exercise the "warn once"
|
|
290
|
+
// behavior repeatably.
|
|
291
|
+
export function _resetEngineDeprecationLatchForTests() {
|
|
292
|
+
__engineDeprecationWarned = false;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* v7.0 — `CLAUDE_AUTOPILOT_ENGINE=off` is softer than `--no-engine`:
|
|
296
|
+
* emit a one-shot warning + return undefined so the engine remains on.
|
|
297
|
+
* Per spec: env vars in CI are sticky and silently breaking every
|
|
298
|
+
* v6.x → v7 upgrade in CI on day one would burn user trust.
|
|
299
|
+
*/
|
|
300
|
+
let __engineEnvOffWarned = false;
|
|
301
|
+
function checkEngineOffEnvDeprecation() {
|
|
302
|
+
const raw = process.env.CLAUDE_AUTOPILOT_ENGINE;
|
|
303
|
+
if (!raw)
|
|
304
|
+
return;
|
|
305
|
+
const normalized = raw.trim().toLowerCase();
|
|
306
|
+
if (normalized === 'off' || normalized === 'false' || normalized === '0' || normalized === 'no') {
|
|
307
|
+
if (!__engineEnvOffWarned) {
|
|
308
|
+
__engineEnvOffWarned = true;
|
|
309
|
+
process.stderr.write(`${ENGINE_OFF_ENV_REMOVED_MESSAGE}\n`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
export function _resetEngineEnvOffLatchForTests() {
|
|
314
|
+
__engineEnvOffWarned = false;
|
|
315
|
+
}
|
|
217
316
|
/**
|
|
218
317
|
* Run the migrate-doctor with shared CLI formatting and exit handling.
|
|
219
318
|
*
|
|
220
319
|
* Both `migrate doctor` (two-word) and `migrate-doctor` (single-verb alias)
|
|
221
320
|
* resolve to this helper to keep their behavior locked together.
|
|
321
|
+
*
|
|
322
|
+
* Phase 5: also handles --json (envelope on stdout, no human banner).
|
|
222
323
|
*/
|
|
223
324
|
async function runMigrateDoctorCLI() {
|
|
224
325
|
const fix = args.includes('--fix');
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
326
|
+
const json = args.includes('--json');
|
|
327
|
+
let docResult = null;
|
|
328
|
+
const code = await runUnderJsonMode({
|
|
329
|
+
command: 'migrate-doctor',
|
|
330
|
+
active: json,
|
|
331
|
+
payload: () => docResult ? {
|
|
332
|
+
results: docResult.results,
|
|
333
|
+
mutations: docResult.mutations ?? [],
|
|
334
|
+
migrationReportPath: docResult.migrationReportPath,
|
|
335
|
+
allOk: docResult.allOk,
|
|
336
|
+
} : {},
|
|
337
|
+
statusFor: exit => exit === 0 ? 'pass' : 'fail',
|
|
338
|
+
}, async () => {
|
|
339
|
+
docResult = await runMigrateDoctor({ repoRoot: process.cwd(), fix });
|
|
340
|
+
for (const r of docResult.results) {
|
|
341
|
+
const mark = r.result.ok ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
342
|
+
console.log(`${mark} ${r.name}${r.result.message ? ` — ${r.result.message}` : ''}`);
|
|
343
|
+
if (!r.result.ok && r.result.fixHint) {
|
|
344
|
+
console.log(` \x1b[2mhint: ${r.result.fixHint}\x1b[0m`);
|
|
345
|
+
}
|
|
231
346
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
347
|
+
if (docResult.mutations && docResult.mutations.length > 0) {
|
|
348
|
+
console.log(`\n\x1b[1mFixes applied:\x1b[0m`);
|
|
349
|
+
for (const m of docResult.mutations)
|
|
350
|
+
console.log(` - ${m}`);
|
|
351
|
+
}
|
|
352
|
+
if (docResult.migrationReportPath) {
|
|
353
|
+
console.log(`\n\x1b[2mMigration report: ${docResult.migrationReportPath}\x1b[0m`);
|
|
354
|
+
}
|
|
355
|
+
return docResult.allOk ? 0 : 1;
|
|
356
|
+
});
|
|
357
|
+
process.exit(code);
|
|
242
358
|
}
|
|
243
359
|
function printUsage() {
|
|
244
|
-
|
|
245
|
-
Usage: claude-autopilot <command> [options] (legacy alias: guardrail)
|
|
246
|
-
|
|
247
|
-
Commands:
|
|
248
|
-
run Review git-changed files (default)
|
|
249
|
-
scan Review any path — no git required
|
|
250
|
-
report Render cached findings as a markdown report
|
|
251
|
-
explain Deep-dive explanation + remediation for a specific finding
|
|
252
|
-
ignore Interactively add findings to .guardrail-ignore
|
|
253
|
-
watch Watch for file changes and re-run on each save
|
|
254
|
-
pr Review a specific PR by number (auto-detects if on PR branch)
|
|
255
|
-
fix Auto-fix cached findings using the configured LLM
|
|
256
|
-
costs Show per-run cost summary
|
|
257
|
-
ci Opinionated CI entrypoint (post comments + SARIF)
|
|
258
|
-
init Scaffold guardrail.config.yaml + auto-detect migrate stack (writes .autopilot/stack.md)
|
|
259
|
-
migrate Run database migrations via the stack-aware dispatcher
|
|
260
|
-
migrate doctor Validate .autopilot/stack.md and skill manifests (alias: migrate-doctor)
|
|
261
|
-
deploy Deploy via configured adapter (vercel | generic) — also: rollback, status
|
|
262
|
-
setup Auto-detect stack, write config, install pre-push hook
|
|
263
|
-
doctor Check prerequisites (alias: preflight)
|
|
264
|
-
preflight Check prerequisites (alias: doctor)
|
|
265
|
-
hook Install / remove the pre-push git hook
|
|
266
|
-
baseline Manage the committed findings baseline (create|update|show|delete)
|
|
267
|
-
triage Mark individual findings as accepted/dismissed
|
|
268
|
-
pr-desc Generate a PR title / summary / test plan from the current diff
|
|
269
|
-
council Multi-model review — dispatch the diff to N models and synthesize consensus
|
|
270
|
-
mcp MCP server for Claude / ChatGPT integration
|
|
271
|
-
autoregress Snapshot regression tests (run|diff|update|generate)
|
|
272
|
-
lsp Language server — publishes findings as LSP diagnostics (stdin/stdout)
|
|
273
|
-
worker Persistent review daemon for multi-terminal parallel usage (start|stop|status)
|
|
274
|
-
test-gen Detect uncovered exports and generate test cases using the LLM
|
|
275
|
-
|
|
276
|
-
Options (run):
|
|
277
|
-
--base <ref> Git base ref for diff (default: HEAD~1)
|
|
278
|
-
--config <path> Path to config file (default: ./guardrail.config.yaml)
|
|
279
|
-
--files <a,b,c> Explicit comma-separated file list (skips git detection)
|
|
280
|
-
--dry-run Show what would run without executing
|
|
281
|
-
--diff Send git diff hunks instead of full files (~70% fewer tokens)
|
|
282
|
-
--delta Only report findings new since last run (suppress pre-existing)
|
|
283
|
-
--inline-comments Post per-line review comments on the PR diff
|
|
284
|
-
--post-comments Post/update a summary comment on the open PR
|
|
285
|
-
--format <text|sarif> Output format (default: text)
|
|
286
|
-
--output <path> Output file path (required with --format sarif)
|
|
287
|
-
|
|
288
|
-
Options (scan):
|
|
289
|
-
<path> [path...] Files or directories to scan (or --all for entire codebase)
|
|
290
|
-
--all Scan entire codebase
|
|
291
|
-
--ask <question> Targeted question to inject into the LLM review prompt
|
|
292
|
-
--focus <type> security | logic | performance (default: all)
|
|
293
|
-
--dry-run List files that would be scanned without running
|
|
294
|
-
--config <path> Path to config file
|
|
295
|
-
|
|
296
|
-
Options (pr):
|
|
297
|
-
<number> PR number to review (optional if on a PR branch)
|
|
298
|
-
--no-post-comments Skip posting/updating PR summary comment
|
|
299
|
-
--no-inline-comments Skip posting per-line inline annotations
|
|
300
|
-
--config <path> Path to config file
|
|
301
|
-
|
|
302
|
-
Options (fix):
|
|
303
|
-
--severity <critical|warning|all> Which findings to fix (default: critical)
|
|
304
|
-
--dry-run Preview fixes without writing files
|
|
305
|
-
--config <path> Path to config file
|
|
306
|
-
|
|
307
|
-
Options (watch):
|
|
308
|
-
--config <path> Path to config file (default: ./guardrail.config.yaml)
|
|
309
|
-
--debounce <ms> Debounce delay in ms (default: 300)
|
|
310
|
-
|
|
311
|
-
Options (autoregress):
|
|
312
|
-
--all Run/diff all snapshots
|
|
313
|
-
--since <ref> Git ref for changed-files detection
|
|
314
|
-
--snapshot <slug> Target a single snapshot
|
|
315
|
-
--files <a,b,c> Explicit file list for generate (skips git detection)
|
|
316
|
-
|
|
317
|
-
Options (migrate):
|
|
318
|
-
--env <name> Target environment from .autopilot/stack.md (default: dev)
|
|
319
|
-
--dry-run Run skill in dry-run mode (no side effects)
|
|
320
|
-
--yes Required to apply prod migrations in CI
|
|
321
|
-
|
|
322
|
-
Options (migrate doctor / migrate-doctor):
|
|
323
|
-
--fix Apply auto-fixable mutations (legacy stack.md, skills/migrate/, schema_version)
|
|
324
|
-
|
|
325
|
-
Options (deploy):
|
|
326
|
-
--adapter <vercel|generic> Override deploy.adapter from config
|
|
327
|
-
--config <path> Path to config file
|
|
328
|
-
--ref <ref> Git ref (branch / tag) to deploy
|
|
329
|
-
--sha <commit> Specific commit SHA to deploy
|
|
330
|
-
--watch Stream build logs to stderr in real time (Vercel only)
|
|
331
|
-
--to <deploy-id> Target deploy ID for 'deploy rollback'
|
|
332
|
-
--pr <n> Post upserting deploy summary comment on the PR
|
|
333
|
-
|
|
334
|
-
Subcommands (deploy):
|
|
335
|
-
deploy Deploy via configured adapter
|
|
336
|
-
deploy rollback Roll back to previous prod deploy
|
|
337
|
-
deploy rollback --to <id> Roll back to a specific deploy
|
|
338
|
-
deploy status Show current prod + last 5 builds
|
|
339
|
-
`);
|
|
360
|
+
process.stdout.write(buildHelpText());
|
|
340
361
|
}
|
|
341
362
|
switch (subcommand) {
|
|
342
363
|
case 'scan': {
|
|
@@ -349,72 +370,113 @@ switch (subcommand) {
|
|
|
349
370
|
}
|
|
350
371
|
const dryRun = boolFlag('dry-run');
|
|
351
372
|
const all = boolFlag('all');
|
|
373
|
+
const json = boolFlag('json');
|
|
374
|
+
// v6.0.1 — engine knob. CLI flag wins; env / config / default resolved
|
|
375
|
+
// inside runScan once it's loaded the config file.
|
|
376
|
+
const cliEngine = parseEngineCliFlag();
|
|
352
377
|
// Remaining non-flag args after 'scan' are paths
|
|
353
378
|
const targets = args.slice(1).filter(a => !a.startsWith('--') && a !== ask && a !== focusArg && a !== config);
|
|
354
|
-
const code = await runScan({
|
|
379
|
+
const code = await runUnderJsonMode({ command: 'scan', active: json }, () => runScan({
|
|
355
380
|
configPath: config,
|
|
356
381
|
targets: targets.length > 0 ? targets : undefined,
|
|
357
382
|
all,
|
|
358
383
|
ask,
|
|
359
384
|
focus: focusArg,
|
|
360
385
|
dryRun,
|
|
361
|
-
|
|
386
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
387
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
388
|
+
}));
|
|
362
389
|
process.exit(code);
|
|
363
390
|
break;
|
|
364
391
|
}
|
|
365
392
|
case 'init': {
|
|
366
393
|
// `init` and `setup` are aliases. Keep both supported — no nag banner.
|
|
367
394
|
const force = args.includes('--force');
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
395
|
+
const json = boolFlag('json');
|
|
396
|
+
const code = await runUnderJsonMode({ command: 'init', active: json }, async () => {
|
|
397
|
+
await runSetup({ force });
|
|
398
|
+
// After the existing init/setup logic, sniff for a migration tool and write
|
|
399
|
+
// .autopilot/stack.md. Non-interactive: high-confidence single matches are
|
|
400
|
+
// auto-selected; ambiguity / no-match downgrades to a TODO 'none@1' shape so
|
|
401
|
+
// we don't block the user. (Interactive prompts come from the autopilot skill,
|
|
402
|
+
// not the CLI.)
|
|
403
|
+
try {
|
|
404
|
+
const result = await initMigrate({
|
|
405
|
+
repoRoot: process.cwd(),
|
|
406
|
+
force,
|
|
407
|
+
});
|
|
408
|
+
for (const ws of result.workspaces) {
|
|
409
|
+
const rel = ws.workspace === process.cwd() ? '.' : ws.workspace;
|
|
410
|
+
console.log(`\x1b[2m[init-migrate] ${ws.action} ${rel}/.autopilot/stack.md (skill: ${ws.skill})\x1b[0m`);
|
|
411
|
+
}
|
|
382
412
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
413
|
+
catch (err) {
|
|
414
|
+
if (err instanceof NoMigrationToolDetectedError) {
|
|
415
|
+
// No high-confidence match — fall back to skipMigrate shape so the user
|
|
416
|
+
// can edit it later. This matches the auto-detection contract documented
|
|
417
|
+
// in the v5.2.0 CHANGELOG.
|
|
418
|
+
try {
|
|
419
|
+
await initMigrate({
|
|
420
|
+
repoRoot: process.cwd(),
|
|
421
|
+
force,
|
|
422
|
+
skipMigrate: true,
|
|
423
|
+
});
|
|
424
|
+
console.log(`\x1b[33m[init-migrate] No migration tool detected — wrote 'none@1' stack.md (edit .autopilot/stack.md to configure)\x1b[0m`);
|
|
425
|
+
}
|
|
426
|
+
catch (fallbackErr) {
|
|
427
|
+
console.error(`\x1b[31m[init-migrate] failed: ${fallbackErr.message}\x1b[0m`);
|
|
428
|
+
return 1;
|
|
429
|
+
}
|
|
396
430
|
}
|
|
397
|
-
|
|
398
|
-
console.error(`\x1b[31m[init-migrate] failed: ${
|
|
431
|
+
else {
|
|
432
|
+
console.error(`\x1b[31m[init-migrate] failed: ${err.message}\x1b[0m`);
|
|
433
|
+
return 1;
|
|
399
434
|
}
|
|
400
435
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
436
|
+
return 0;
|
|
437
|
+
});
|
|
438
|
+
if (json)
|
|
439
|
+
process.exit(code);
|
|
405
440
|
break;
|
|
406
441
|
}
|
|
407
442
|
case 'doctor':
|
|
408
443
|
case 'preflight': {
|
|
409
|
-
const
|
|
410
|
-
|
|
444
|
+
const json = boolFlag('json');
|
|
445
|
+
let docResult = null;
|
|
446
|
+
const code = await runUnderJsonMode({
|
|
447
|
+
command: subcommand,
|
|
448
|
+
active: json,
|
|
449
|
+
payload: () => docResult ? {
|
|
450
|
+
blockers: docResult.blockers,
|
|
451
|
+
warnings: docResult.warnings,
|
|
452
|
+
} : {},
|
|
453
|
+
}, async () => {
|
|
454
|
+
docResult = await runDoctor();
|
|
455
|
+
return docResult.blockers > 0 ? 1 : 0;
|
|
456
|
+
});
|
|
457
|
+
process.exit(code);
|
|
411
458
|
break;
|
|
412
459
|
}
|
|
413
460
|
case 'help':
|
|
414
461
|
case '--help':
|
|
415
|
-
case '-h':
|
|
462
|
+
case '-h': {
|
|
463
|
+
// `claude-autopilot help <command>` — focused per-command help. Falls back
|
|
464
|
+
// to the full two-level listing with an "unknown command" notice + exit 1
|
|
465
|
+
// when the named verb isn't documented.
|
|
466
|
+
const target = args[1];
|
|
467
|
+
if (target && !target.startsWith('-')) {
|
|
468
|
+
const focused = buildCommandHelpText(target);
|
|
469
|
+
if (focused !== null) {
|
|
470
|
+
process.stdout.write(focused);
|
|
471
|
+
process.exit(0);
|
|
472
|
+
}
|
|
473
|
+
process.stderr.write(`\x1b[31m[claude-autopilot] unknown command: "${target}"\x1b[0m\n`);
|
|
474
|
+
process.stdout.write(buildHelpText());
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
416
477
|
printUsage();
|
|
417
478
|
break;
|
|
479
|
+
}
|
|
418
480
|
case 'watch': {
|
|
419
481
|
const config = flag('config');
|
|
420
482
|
const debounceArg = flag('debounce');
|
|
@@ -452,7 +514,8 @@ switch (subcommand) {
|
|
|
452
514
|
process.exit(1);
|
|
453
515
|
}
|
|
454
516
|
const newOnly = boolFlag('new-only');
|
|
455
|
-
const
|
|
517
|
+
const json = boolFlag('json');
|
|
518
|
+
const code = await runUnderJsonMode({ command: 'run', active: json }, () => runCommand({
|
|
456
519
|
base,
|
|
457
520
|
configPath: config,
|
|
458
521
|
files: filesArg ? filesArg.split(',').map(f => f.trim()) : undefined,
|
|
@@ -466,7 +529,7 @@ switch (subcommand) {
|
|
|
466
529
|
format: formatArg,
|
|
467
530
|
outputPath,
|
|
468
531
|
skipReview: staticOnly,
|
|
469
|
-
});
|
|
532
|
+
}));
|
|
470
533
|
process.exit(code);
|
|
471
534
|
break;
|
|
472
535
|
}
|
|
@@ -479,7 +542,8 @@ switch (subcommand) {
|
|
|
479
542
|
const diff = boolFlag('diff');
|
|
480
543
|
const newOnly = boolFlag('new-only');
|
|
481
544
|
const failOnArg = flag('fail-on');
|
|
482
|
-
const
|
|
545
|
+
const json = boolFlag('json');
|
|
546
|
+
const code = await runUnderJsonMode({ command: 'ci', active: json }, () => runCi({
|
|
483
547
|
configPath: config,
|
|
484
548
|
base,
|
|
485
549
|
sarifOutput: outputPath,
|
|
@@ -488,7 +552,7 @@ switch (subcommand) {
|
|
|
488
552
|
diff,
|
|
489
553
|
newOnly,
|
|
490
554
|
failOn: failOnArg,
|
|
491
|
-
});
|
|
555
|
+
}));
|
|
492
556
|
process.exit(code);
|
|
493
557
|
break;
|
|
494
558
|
}
|
|
@@ -497,21 +561,35 @@ switch (subcommand) {
|
|
|
497
561
|
const sub = args[1] ?? 'show';
|
|
498
562
|
const note = flag('note');
|
|
499
563
|
const config = flag('config');
|
|
500
|
-
const
|
|
564
|
+
const json = boolFlag('json');
|
|
565
|
+
const code = await runUnderJsonMode({ command: `baseline ${sub}`, active: json }, () => rb(sub, { cwd: process.cwd(), note, baselinePath: config }));
|
|
501
566
|
process.exit(code);
|
|
502
567
|
break;
|
|
503
568
|
}
|
|
504
569
|
case 'pr': {
|
|
570
|
+
// v6.0.9 — engine-wrap shell for the `pr` pipeline phase. Side-effecting
|
|
571
|
+
// (posts/updates a PR comment + inline review comments via the `gh` CLI
|
|
572
|
+
// inside runCommand). Declared `idempotent: false, hasSideEffects: true`
|
|
573
|
+
// with a `github-pr` externalRef recorded before the inner pipeline
|
|
574
|
+
// runs. See the long declaration note in src/cli/pr.ts for the
|
|
575
|
+
// per-call breakdown of what `gh` mutations happen and why the
|
|
576
|
+
// declaration matches the v6 spec table.
|
|
505
577
|
const config = flag('config');
|
|
506
578
|
const noPostComments = boolFlag('no-post-comments');
|
|
507
579
|
const noInlineComments = boolFlag('no-inline-comments');
|
|
580
|
+
const json = boolFlag('json');
|
|
508
581
|
const prNumber = args.slice(1).find(a => !a.startsWith('--') && /^\d+$/.test(a));
|
|
509
|
-
|
|
582
|
+
// v6.0.9 — engine knob. CLI flag wins; env / config / default resolved
|
|
583
|
+
// inside runPr once it's loaded the config file.
|
|
584
|
+
const cliEngine = parseEngineCliFlag();
|
|
585
|
+
const code = await runUnderJsonMode({ command: 'pr', active: json }, () => runPr({
|
|
510
586
|
configPath: config,
|
|
511
587
|
prNumber,
|
|
512
588
|
noPostComments,
|
|
513
589
|
noInlineComments,
|
|
514
|
-
|
|
590
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
591
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
592
|
+
}));
|
|
515
593
|
process.exit(code);
|
|
516
594
|
break;
|
|
517
595
|
}
|
|
@@ -542,19 +620,26 @@ switch (subcommand) {
|
|
|
542
620
|
}
|
|
543
621
|
const dryRun = boolFlag('dry-run');
|
|
544
622
|
const noVerify = boolFlag('no-verify');
|
|
545
|
-
const
|
|
623
|
+
const json = boolFlag('json');
|
|
624
|
+
// v6.0.2 — engine knob. CLI flag wins; env / config / default resolved
|
|
625
|
+
// inside runFix once it's loaded the config file.
|
|
626
|
+
const cliEngine = parseEngineCliFlag();
|
|
627
|
+
const code = await runUnderJsonMode({ command: 'fix', active: json }, () => runFix({
|
|
546
628
|
configPath: config,
|
|
547
629
|
severity: severityArg,
|
|
548
630
|
dryRun,
|
|
549
631
|
noVerify,
|
|
550
|
-
|
|
632
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
633
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
634
|
+
}));
|
|
551
635
|
process.exit(code);
|
|
552
636
|
break;
|
|
553
637
|
}
|
|
554
638
|
case 'triage': {
|
|
555
639
|
const sub = args[1];
|
|
556
640
|
const rest = args.slice(2);
|
|
557
|
-
const
|
|
641
|
+
const json = boolFlag('json');
|
|
642
|
+
const code = await runUnderJsonMode({ command: `triage${sub ? ` ${sub}` : ''}`, active: json }, () => runTriage(sub, rest));
|
|
558
643
|
process.exit(code);
|
|
559
644
|
break;
|
|
560
645
|
}
|
|
@@ -563,15 +648,16 @@ switch (subcommand) {
|
|
|
563
648
|
const base = flag('base');
|
|
564
649
|
const dryRun = boolFlag('dry-run');
|
|
565
650
|
const verify = boolFlag('verify');
|
|
651
|
+
const json = boolFlag('json');
|
|
566
652
|
const targets = args.slice(1).filter(a => !a.startsWith('--') && a !== config && a !== base);
|
|
567
|
-
const code = await runTestGen({
|
|
653
|
+
const code = await runUnderJsonMode({ command: 'test-gen', active: json }, () => runTestGen({
|
|
568
654
|
cwd: process.cwd(),
|
|
569
655
|
configPath: config,
|
|
570
656
|
targets: targets.length > 0 ? targets : undefined,
|
|
571
657
|
base,
|
|
572
658
|
dryRun,
|
|
573
659
|
verify,
|
|
574
|
-
});
|
|
660
|
+
}));
|
|
575
661
|
process.exit(code);
|
|
576
662
|
break;
|
|
577
663
|
}
|
|
@@ -581,12 +667,18 @@ switch (subcommand) {
|
|
|
581
667
|
const base = baseIdx !== -1 ? args[baseIdx + 1] : undefined;
|
|
582
668
|
const outputIdx = args.indexOf('--output');
|
|
583
669
|
const output = outputIdx !== -1 ? args[outputIdx + 1] : undefined;
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
670
|
+
const json = boolFlag('json');
|
|
671
|
+
const code = await runUnderJsonMode({ command: 'pr-desc', active: json }, async () => {
|
|
672
|
+
await runPrDesc({
|
|
673
|
+
base,
|
|
674
|
+
post: args.includes('--post'),
|
|
675
|
+
yes: args.includes('--yes'),
|
|
676
|
+
output,
|
|
677
|
+
});
|
|
678
|
+
return 0;
|
|
589
679
|
});
|
|
680
|
+
if (json)
|
|
681
|
+
process.exit(code);
|
|
590
682
|
break;
|
|
591
683
|
}
|
|
592
684
|
case 'lsp': {
|
|
@@ -595,22 +687,217 @@ switch (subcommand) {
|
|
|
595
687
|
}
|
|
596
688
|
case 'costs': {
|
|
597
689
|
const { runCosts } = await import("./costs.js");
|
|
598
|
-
const
|
|
690
|
+
const json = boolFlag('json');
|
|
691
|
+
const config = flag('config');
|
|
692
|
+
// v6.0.2 — engine knob. CLI flag wins; env / config / default resolved
|
|
693
|
+
// inside runCosts once it's loaded the config file.
|
|
694
|
+
const cliEngine = parseEngineCliFlag();
|
|
695
|
+
const code = await runUnderJsonMode({ command: 'costs', active: json }, () => runCosts({
|
|
696
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
697
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
698
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
699
|
+
}));
|
|
700
|
+
process.exit(code);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
case 'plan': {
|
|
704
|
+
// v6.0.4 — engine-wrap shell for the `plan` pipeline phase. The actual
|
|
705
|
+
// LLM-driven planning content is produced by the Claude Code
|
|
706
|
+
// superpowers:writing-plans skill; this CLI verb provides a
|
|
707
|
+
// checkpointable phase shell so v6 pipeline runs can record a `plan`
|
|
708
|
+
// entry. Mirrors the costs/scan/fix dispatcher shape.
|
|
709
|
+
const { runPlan } = await import("./plan.js");
|
|
710
|
+
const json = boolFlag('json');
|
|
711
|
+
const config = flag('config');
|
|
712
|
+
const specPath = flag('spec');
|
|
713
|
+
const outputPath = flag('output');
|
|
714
|
+
const cliEngine = parseEngineCliFlag();
|
|
715
|
+
const code = await runUnderJsonMode({ command: 'plan', active: json }, () => runPlan({
|
|
716
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
717
|
+
...(specPath !== undefined ? { specPath } : {}),
|
|
718
|
+
...(outputPath !== undefined ? { outputPath } : {}),
|
|
719
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
720
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
721
|
+
}));
|
|
722
|
+
process.exit(code);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
case 'review': {
|
|
726
|
+
// v6.0.4 — engine-wrap shell for the `review` pipeline phase. The actual
|
|
727
|
+
// LLM-driven review content is produced by the Claude Code review skills
|
|
728
|
+
// (`/review`, `/review-2pass`, `pr-review-toolkit:review-pr`). PR-side
|
|
729
|
+
// comment posting lives in `claude-autopilot pr --inline-comments` /
|
|
730
|
+
// `--post-comments`; this verb does not post anywhere. See the long
|
|
731
|
+
// deviation note in src/cli/review.ts for the idempotent / hasSideEffects
|
|
732
|
+
// declaration rationale.
|
|
733
|
+
const { runReview } = await import("./review.js");
|
|
734
|
+
const json = boolFlag('json');
|
|
735
|
+
const config = flag('config');
|
|
736
|
+
const context = flag('context');
|
|
737
|
+
const outputPath = flag('output');
|
|
738
|
+
const cliEngine = parseEngineCliFlag();
|
|
739
|
+
const code = await runUnderJsonMode({ command: 'review', active: json }, () => runReview({
|
|
740
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
741
|
+
...(context !== undefined ? { context } : {}),
|
|
742
|
+
...(outputPath !== undefined ? { outputPath } : {}),
|
|
743
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
744
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
745
|
+
}));
|
|
746
|
+
process.exit(code);
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
case 'validate': {
|
|
750
|
+
// v6.0.5 — engine-wrap shell for the `validate` pipeline phase. The
|
|
751
|
+
// actual validation pipeline (static checks, auto-fix, tests, Codex
|
|
752
|
+
// review with auto-fix, bugbot triage) lives in the Claude Code
|
|
753
|
+
// `/validate` skill; this verb provides a checkpointable phase shell so
|
|
754
|
+
// v6 pipeline runs can record a `validate` entry. Mirrors the
|
|
755
|
+
// plan / review dispatcher shape. See the long deviation note in
|
|
756
|
+
// src/cli/validate.ts for the externalRefs / sarif-artifact
|
|
757
|
+
// declaration rationale.
|
|
758
|
+
const { runValidate } = await import("./validate.js");
|
|
759
|
+
const json = boolFlag('json');
|
|
760
|
+
const config = flag('config');
|
|
761
|
+
const context = flag('context');
|
|
762
|
+
const outputPath = flag('output');
|
|
763
|
+
const cliEngine = parseEngineCliFlag();
|
|
764
|
+
const code = await runUnderJsonMode({ command: 'validate', active: json }, () => runValidate({
|
|
765
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
766
|
+
...(context !== undefined ? { context } : {}),
|
|
767
|
+
...(outputPath !== undefined ? { outputPath } : {}),
|
|
768
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
769
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
770
|
+
}));
|
|
771
|
+
process.exit(code);
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
case 'implement': {
|
|
775
|
+
// v6.0.7 — engine-wrap shell for the `implement` pipeline phase. The
|
|
776
|
+
// actual implement loop (read plan → dispatch subagents one per plan
|
|
777
|
+
// phase via `subagent-driven-development` → write code → run tests →
|
|
778
|
+
// commit → optionally push via `commit-push-pr`) lives in the Claude
|
|
779
|
+
// Code `claude-autopilot` skill; this verb provides a checkpointable
|
|
780
|
+
// phase shell so v6 pipeline runs can record an `implement` entry.
|
|
781
|
+
// Mirrors the plan / review / validate dispatcher shape. See the long
|
|
782
|
+
// deviation note in src/cli/implement.ts for the idempotent /
|
|
783
|
+
// hasSideEffects / git-remote-push declaration rationale.
|
|
784
|
+
const { runImplement } = await import("./implement.js");
|
|
785
|
+
const json = boolFlag('json');
|
|
786
|
+
const config = flag('config');
|
|
787
|
+
const context = flag('context');
|
|
788
|
+
const plan = flag('plan');
|
|
789
|
+
const outputPath = flag('output');
|
|
790
|
+
const cliEngine = parseEngineCliFlag();
|
|
791
|
+
const code = await runUnderJsonMode({ command: 'implement', active: json }, () => runImplement({
|
|
792
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
793
|
+
...(context !== undefined ? { context } : {}),
|
|
794
|
+
...(plan !== undefined ? { plan } : {}),
|
|
795
|
+
...(outputPath !== undefined ? { outputPath } : {}),
|
|
796
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
797
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
798
|
+
}));
|
|
599
799
|
process.exit(code);
|
|
600
800
|
break;
|
|
601
801
|
}
|
|
802
|
+
case 'autopilot': {
|
|
803
|
+
// v6.2.0 — multi-phase orchestrator. One runId across all phases.
|
|
804
|
+
// Engine-on REQUIRED (rejected at pre-flight if --no-engine / env=off
|
|
805
|
+
// / config=false). v6.2.0 ships --mode=full (scan → spec → plan →
|
|
806
|
+
// implement); v6.2.1 extends to scan → spec → plan → implement →
|
|
807
|
+
// migrate → pr; v6.2.2 adds the `--json` outer envelope.
|
|
808
|
+
const { runAutopilot, runAutopilotWithJsonEnvelope } = await import("./autopilot.js");
|
|
809
|
+
const json = boolFlag('json');
|
|
810
|
+
const modeArg = flag('mode');
|
|
811
|
+
if (modeArg !== undefined && modeArg !== 'full') {
|
|
812
|
+
// In --json mode emit the spec envelope instead of stderr text so
|
|
813
|
+
// CI consumers get a deterministic shape even on this synchronous
|
|
814
|
+
// pre-run validation failure.
|
|
815
|
+
if (json) {
|
|
816
|
+
const { writeAutopilotEnvelope } = await import("./json-envelope.js");
|
|
817
|
+
writeAutopilotEnvelope({
|
|
818
|
+
runId: null,
|
|
819
|
+
status: 'failed',
|
|
820
|
+
exitCode: 1,
|
|
821
|
+
phases: [],
|
|
822
|
+
totalCostUSD: 0,
|
|
823
|
+
durationMs: 0,
|
|
824
|
+
errorCode: 'invalid_config',
|
|
825
|
+
errorMessage: `--mode "${modeArg}" not supported (use --mode=full)`,
|
|
826
|
+
});
|
|
827
|
+
process.exit(1);
|
|
828
|
+
}
|
|
829
|
+
console.error(`\x1b[31m[claude-autopilot] invalid_config: --mode "${modeArg}" not supported (use --mode=full)\x1b[0m`);
|
|
830
|
+
console.error(`\x1b[2m --mode=fix and --mode=review land in v6.2.x+; use --phases=<csv> for custom lists\x1b[0m`);
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
const phasesArg = flag('phases');
|
|
834
|
+
const phases = phasesArg
|
|
835
|
+
? phasesArg.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
836
|
+
: undefined;
|
|
837
|
+
const budgetRaw = flag('budget');
|
|
838
|
+
let budgetUSD;
|
|
839
|
+
if (budgetRaw !== undefined) {
|
|
840
|
+
const parsed = Number.parseFloat(budgetRaw);
|
|
841
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
842
|
+
if (json) {
|
|
843
|
+
const { writeAutopilotEnvelope } = await import("./json-envelope.js");
|
|
844
|
+
writeAutopilotEnvelope({
|
|
845
|
+
runId: null,
|
|
846
|
+
status: 'failed',
|
|
847
|
+
exitCode: 1,
|
|
848
|
+
phases: [],
|
|
849
|
+
totalCostUSD: 0,
|
|
850
|
+
durationMs: 0,
|
|
851
|
+
errorCode: 'invalid_config',
|
|
852
|
+
errorMessage: `--budget must be a positive number, got "${budgetRaw}"`,
|
|
853
|
+
});
|
|
854
|
+
process.exit(1);
|
|
855
|
+
}
|
|
856
|
+
console.error(`\x1b[31m[claude-autopilot] invalid_config: --budget must be a positive number, got "${budgetRaw}"\x1b[0m`);
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
budgetUSD = parsed;
|
|
860
|
+
}
|
|
861
|
+
const cliEngine = parseEngineCliFlag();
|
|
862
|
+
const noUpload = boolFlag('no-upload');
|
|
863
|
+
if (json) {
|
|
864
|
+
const exitCode = await runAutopilotWithJsonEnvelope({
|
|
865
|
+
cwd: process.cwd(),
|
|
866
|
+
mode: 'full',
|
|
867
|
+
...(phases !== undefined ? { phases } : {}),
|
|
868
|
+
...(budgetUSD !== undefined ? { budgetUSD } : {}),
|
|
869
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
870
|
+
...(noUpload ? { noUpload: true } : {}),
|
|
871
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
872
|
+
});
|
|
873
|
+
process.exit(exitCode);
|
|
874
|
+
}
|
|
875
|
+
const result = await runAutopilot({
|
|
876
|
+
cwd: process.cwd(),
|
|
877
|
+
mode: 'full',
|
|
878
|
+
...(phases !== undefined ? { phases } : {}),
|
|
879
|
+
...(budgetUSD !== undefined ? { budgetUSD } : {}),
|
|
880
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
881
|
+
...(noUpload ? { noUpload: true } : {}),
|
|
882
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
883
|
+
});
|
|
884
|
+
process.exit(result.exitCode);
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
602
887
|
case 'report': {
|
|
603
888
|
const outputPath = flag('output');
|
|
604
889
|
const trend = boolFlag('trend');
|
|
605
|
-
const
|
|
890
|
+
const json = boolFlag('json');
|
|
891
|
+
const code = await runUnderJsonMode({ command: 'report', active: json }, () => runReport({ output: outputPath, trend }));
|
|
606
892
|
process.exit(code);
|
|
607
893
|
break;
|
|
608
894
|
}
|
|
609
895
|
case 'explain': {
|
|
610
896
|
const config = flag('config');
|
|
897
|
+
const json = boolFlag('json');
|
|
611
898
|
// Target is the first non-flag arg after 'explain'
|
|
612
899
|
const target = args.slice(1).find(a => !a.startsWith('--'));
|
|
613
|
-
const code = await runExplain({ configPath: config, target });
|
|
900
|
+
const code = await runUnderJsonMode({ command: 'explain', active: json }, () => runExplain({ configPath: config, target }));
|
|
614
901
|
process.exit(code);
|
|
615
902
|
break;
|
|
616
903
|
}
|
|
@@ -624,11 +911,17 @@ switch (subcommand) {
|
|
|
624
911
|
case 'setup': {
|
|
625
912
|
const force = args.includes('--force');
|
|
626
913
|
const profileArg = flag('profile');
|
|
914
|
+
const json = boolFlag('json');
|
|
627
915
|
if (profileArg && !['security-strict', 'team', 'solo'].includes(profileArg)) {
|
|
628
916
|
console.error(`\x1b[31m[claude-autopilot] --profile must be "security-strict", "team", or "solo"\x1b[0m`);
|
|
629
917
|
process.exit(1);
|
|
630
918
|
}
|
|
631
|
-
await
|
|
919
|
+
const code = await runUnderJsonMode({ command: 'setup', active: json }, async () => {
|
|
920
|
+
await runSetup({ force, profile: profileArg });
|
|
921
|
+
return 0;
|
|
922
|
+
});
|
|
923
|
+
if (json)
|
|
924
|
+
process.exit(code);
|
|
632
925
|
break;
|
|
633
926
|
}
|
|
634
927
|
case 'worker': {
|
|
@@ -638,19 +931,33 @@ switch (subcommand) {
|
|
|
638
931
|
process.exit(code);
|
|
639
932
|
break;
|
|
640
933
|
}
|
|
934
|
+
case 'scaffold': {
|
|
935
|
+
// v7.2.0 — `claude-autopilot scaffold --from-spec <path>`
|
|
936
|
+
const fromSpec = flag('from-spec');
|
|
937
|
+
const dryRun = boolFlag('dry-run');
|
|
938
|
+
if (!fromSpec) {
|
|
939
|
+
console.error(`\x1b[31m[claude-autopilot] scaffold requires --from-spec <path>\x1b[0m`);
|
|
940
|
+
console.error(` Example: claude-autopilot scaffold --from-spec docs/specs/foo.md`);
|
|
941
|
+
process.exit(1);
|
|
942
|
+
}
|
|
943
|
+
await runScaffold({ specPath: fromSpec, dryRun });
|
|
944
|
+
process.exit(0);
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
641
947
|
case 'council': {
|
|
642
948
|
const config = flag('config');
|
|
643
949
|
const prompt = flag('prompt');
|
|
644
950
|
const contextFile = flag('context-file');
|
|
645
951
|
const dryRun = boolFlag('dry-run');
|
|
646
952
|
const noSynthesize = boolFlag('no-synthesize');
|
|
647
|
-
const
|
|
953
|
+
const json = boolFlag('json');
|
|
954
|
+
const code = await runUnderJsonMode({ command: 'council', active: json }, () => runCouncilCmd({
|
|
648
955
|
prompt,
|
|
649
956
|
contextFile,
|
|
650
957
|
configPath: config,
|
|
651
958
|
dryRun,
|
|
652
959
|
noSynthesize,
|
|
653
|
-
});
|
|
960
|
+
}));
|
|
654
961
|
process.exit(code);
|
|
655
962
|
break;
|
|
656
963
|
}
|
|
@@ -677,11 +984,12 @@ switch (subcommand) {
|
|
|
677
984
|
break;
|
|
678
985
|
}
|
|
679
986
|
case 'migrate-v4': {
|
|
680
|
-
const
|
|
987
|
+
const json = boolFlag('json');
|
|
988
|
+
const code = await runUnderJsonMode({ command: 'migrate-v4', active: json }, () => runMigrateV4({
|
|
681
989
|
cwd: flag('path') ?? process.cwd(),
|
|
682
990
|
write: boolFlag('write'),
|
|
683
991
|
undo: boolFlag('undo'),
|
|
684
|
-
});
|
|
992
|
+
}));
|
|
685
993
|
process.exit(code);
|
|
686
994
|
break;
|
|
687
995
|
}
|
|
@@ -695,41 +1003,49 @@ switch (subcommand) {
|
|
|
695
1003
|
break;
|
|
696
1004
|
}
|
|
697
1005
|
// Plain `migrate [--env <name>] [--dry-run] [--yes]` → dispatcher.
|
|
1006
|
+
// v6.0.8: routed through `runMigrate` (src/cli/migrate.ts) which
|
|
1007
|
+
// wraps the dispatcher in a `RunPhase<MigrateInput, MigrateOutput>`
|
|
1008
|
+
// with `--engine` / `--no-engine` precedence. Engine-off is byte-for-
|
|
1009
|
+
// byte identical to v6.0.7 — same dispatch shape, same render lines.
|
|
698
1010
|
const envName = flag('env') ?? 'dev';
|
|
699
1011
|
const dryRun = boolFlag('dry-run');
|
|
700
1012
|
const yesFlag = boolFlag('yes');
|
|
701
|
-
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1013
|
+
const json = boolFlag('json');
|
|
1014
|
+
const cliEngine = parseEngineCliFlag();
|
|
1015
|
+
// Capture migrate result in an outer ref so the wrapper's payload
|
|
1016
|
+
// callback can surface its structured fields in --json mode.
|
|
1017
|
+
let migrateResult = null;
|
|
1018
|
+
const code = await runUnderJsonMode({
|
|
1019
|
+
command: 'migrate',
|
|
1020
|
+
active: json,
|
|
1021
|
+
payload: () => migrateResult ? {
|
|
1022
|
+
migrate: {
|
|
1023
|
+
status: migrateResult.status,
|
|
1024
|
+
reasonCode: migrateResult.reasonCode,
|
|
1025
|
+
appliedMigrations: migrateResult.appliedMigrations,
|
|
1026
|
+
nextActions: migrateResult.nextActions,
|
|
1027
|
+
},
|
|
1028
|
+
...(migrateResult.nextActions.length > 0 ? { nextActions: migrateResult.nextActions } : {}),
|
|
1029
|
+
} : {},
|
|
1030
|
+
statusFor: exit => {
|
|
1031
|
+
if (!migrateResult)
|
|
1032
|
+
return exit === 0 ? 'pass' : 'fail';
|
|
1033
|
+
return migrateResult.status === 'applied' || migrateResult.status === 'skipped' ? 'pass' : 'fail';
|
|
1034
|
+
},
|
|
1035
|
+
}, async () => {
|
|
1036
|
+
const out = await runMigrate({
|
|
1037
|
+
cwd: process.cwd(),
|
|
1038
|
+
env: envName,
|
|
1039
|
+
dryRun,
|
|
1040
|
+
yesFlag,
|
|
1041
|
+
nonInteractive: json || !process.stdin.isTTY,
|
|
1042
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
1043
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
1044
|
+
});
|
|
1045
|
+
migrateResult = out.result;
|
|
1046
|
+
return out.exitCode;
|
|
722
1047
|
});
|
|
723
|
-
|
|
724
|
-
const color = ok ? '\x1b[32m' : '\x1b[31m';
|
|
725
|
-
console.log(`${color}[migrate] status=${result.status} reason=${result.reasonCode}\x1b[0m`);
|
|
726
|
-
if (result.appliedMigrations.length > 0) {
|
|
727
|
-
console.log(` applied: ${result.appliedMigrations.join(', ')}`);
|
|
728
|
-
}
|
|
729
|
-
if (result.nextActions.length > 0) {
|
|
730
|
-
console.log(` next: ${result.nextActions.join('; ')}`);
|
|
731
|
-
}
|
|
732
|
-
process.exit(ok ? 0 : 1);
|
|
1048
|
+
process.exit(code);
|
|
733
1049
|
break;
|
|
734
1050
|
}
|
|
735
1051
|
case 'migrate-doctor': {
|
|
@@ -741,28 +1057,33 @@ switch (subcommand) {
|
|
|
741
1057
|
case 'deploy': {
|
|
742
1058
|
const config = flag('config');
|
|
743
1059
|
const adapterArg = flag('adapter');
|
|
744
|
-
|
|
745
|
-
|
|
1060
|
+
// Keep this list in sync with `DeployConfig.adapter` in
|
|
1061
|
+
// src/adapters/deploy/types.ts and the factory in
|
|
1062
|
+
// src/adapters/deploy/index.ts.
|
|
1063
|
+
const ADAPTER_NAMES = ['vercel', 'fly', 'render', 'generic'];
|
|
1064
|
+
if (adapterArg && !ADAPTER_NAMES.includes(adapterArg)) {
|
|
1065
|
+
console.error(`\x1b[31m[claude-autopilot] --adapter must be one of: ${ADAPTER_NAMES.join(', ')}\x1b[0m`);
|
|
746
1066
|
process.exit(1);
|
|
747
1067
|
}
|
|
748
1068
|
// Phase 3 — `deploy rollback` and `deploy status` subverbs. The first
|
|
749
1069
|
// non-flag positional after `deploy` selects the verb. The historic
|
|
750
1070
|
// `claude-autopilot deploy` (no subverb) keeps calling runDeploy.
|
|
751
1071
|
const subverb = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
1072
|
+
const json = boolFlag('json');
|
|
752
1073
|
if (subverb === 'rollback') {
|
|
753
1074
|
const to = flag('to');
|
|
754
|
-
const code = await runDeployRollback({
|
|
1075
|
+
const code = await runUnderJsonMode({ command: 'deploy rollback', active: json }, () => runDeployRollback({
|
|
755
1076
|
configPath: config,
|
|
756
1077
|
adapterOverride: adapterArg,
|
|
757
1078
|
to,
|
|
758
|
-
});
|
|
1079
|
+
}));
|
|
759
1080
|
process.exit(code);
|
|
760
1081
|
}
|
|
761
1082
|
if (subverb === 'status') {
|
|
762
|
-
const code = await runDeployStatus({
|
|
1083
|
+
const code = await runUnderJsonMode({ command: 'deploy status', active: json }, () => runDeployStatus({
|
|
763
1084
|
configPath: config,
|
|
764
1085
|
adapterOverride: adapterArg,
|
|
765
|
-
});
|
|
1086
|
+
}));
|
|
766
1087
|
process.exit(code);
|
|
767
1088
|
}
|
|
768
1089
|
if (subverb !== undefined) {
|
|
@@ -782,42 +1103,250 @@ switch (subcommand) {
|
|
|
782
1103
|
}
|
|
783
1104
|
prNum = n;
|
|
784
1105
|
}
|
|
785
|
-
const code = await runDeploy({
|
|
1106
|
+
const code = await runUnderJsonMode({ command: 'deploy', active: json }, () => runDeploy({
|
|
786
1107
|
configPath: config,
|
|
787
1108
|
adapterOverride: adapterArg,
|
|
788
1109
|
ref,
|
|
789
1110
|
commitSha,
|
|
790
1111
|
watch,
|
|
791
1112
|
pr: prNum,
|
|
792
|
-
});
|
|
1113
|
+
}));
|
|
793
1114
|
process.exit(code);
|
|
794
1115
|
break;
|
|
795
1116
|
}
|
|
796
1117
|
case 'brainstorm': {
|
|
797
|
-
// `brainstorm` is
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1118
|
+
// v6.0.3 — `brainstorm` is wrapped through `runPhase`. Engine-off path
|
|
1119
|
+
// is byte-for-byte identical to v6.0.2 (advisory print pointing at the
|
|
1120
|
+
// Claude Code skill); engine-on path creates a run dir + emits
|
|
1121
|
+
// run.start/phase.start/phase.success/run.complete events. See
|
|
1122
|
+
// src/cli/brainstorm.ts for the deviation rationale on
|
|
1123
|
+
// `idempotent: true` vs. the spec table's `idempotent: no`.
|
|
1124
|
+
const { runBrainstorm } = await import("./brainstorm.js");
|
|
1125
|
+
const json = boolFlag('json');
|
|
1126
|
+
const config = flag('config');
|
|
1127
|
+
const cliEngine = parseEngineCliFlag();
|
|
1128
|
+
if (json) {
|
|
1129
|
+
// --json mode: surface the resume hint via nextActions, not a banner.
|
|
1130
|
+
// Mirror the v6.0.2 envelope shape so existing consumers (the
|
|
1131
|
+
// json-channel-discipline test, MCP wrappers) don't break. The phase
|
|
1132
|
+
// body itself runs in silent mode — engine-on still produces
|
|
1133
|
+
// run-state artifacts; engine-off short-circuits to 0.
|
|
1134
|
+
const code = await runUnderJsonMode({
|
|
1135
|
+
command: 'brainstorm',
|
|
1136
|
+
active: true,
|
|
1137
|
+
payload: () => ({
|
|
1138
|
+
note: 'brainstorm is a Claude Code skill, not a CLI subcommand',
|
|
1139
|
+
nextActions: [
|
|
1140
|
+
'Invoke /brainstorm from Claude Code for interactive spec writing',
|
|
1141
|
+
'Then /autopilot to run the full pipeline from an approved spec',
|
|
1142
|
+
],
|
|
1143
|
+
}),
|
|
1144
|
+
}, () => runBrainstorm({
|
|
1145
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
1146
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
1147
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
1148
|
+
__silent: true,
|
|
1149
|
+
}));
|
|
1150
|
+
process.exit(code);
|
|
1151
|
+
}
|
|
1152
|
+
const code = await runBrainstorm({
|
|
1153
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
1154
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
1155
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
1156
|
+
});
|
|
1157
|
+
process.exit(code);
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
case 'spec': {
|
|
1161
|
+
// v6.0.3 — `spec` is wrapped through `runPhase`. Same shape as
|
|
1162
|
+
// brainstorm: engine-off prints an advisory pointing at the Claude
|
|
1163
|
+
// Code skill; engine-on creates a run dir + emits lifecycle events.
|
|
1164
|
+
// Like brainstorm, the deviation from the spec table's
|
|
1165
|
+
// `idempotent: no` is justified inline at the top of src/cli/spec.ts.
|
|
1166
|
+
const { runSpec } = await import("./spec.js");
|
|
1167
|
+
const json = boolFlag('json');
|
|
1168
|
+
const config = flag('config');
|
|
1169
|
+
const cliEngine = parseEngineCliFlag();
|
|
1170
|
+
if (json) {
|
|
1171
|
+
const code = await runUnderJsonMode({
|
|
1172
|
+
command: 'spec',
|
|
1173
|
+
active: true,
|
|
1174
|
+
payload: () => ({
|
|
1175
|
+
note: 'spec is a Claude Code skill, not a CLI subcommand',
|
|
1176
|
+
nextActions: [
|
|
1177
|
+
'Approve a brainstorm output, then invoke /autopilot from Claude Code',
|
|
1178
|
+
'The autopilot skill writes the implementation plan + executes the pipeline',
|
|
1179
|
+
],
|
|
1180
|
+
}),
|
|
1181
|
+
}, () => runSpec({
|
|
1182
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
1183
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
1184
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
1185
|
+
__silent: true,
|
|
1186
|
+
}));
|
|
1187
|
+
process.exit(code);
|
|
1188
|
+
}
|
|
1189
|
+
const code = await runSpec({
|
|
1190
|
+
...(config !== undefined ? { configPath: config } : {}),
|
|
1191
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
1192
|
+
envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
|
|
1193
|
+
});
|
|
1194
|
+
process.exit(code);
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1197
|
+
case 'runs': {
|
|
1198
|
+
// v6 Phase 3 — umbrella verb. Sub-verbs: list, show, gc, delete, doctor.
|
|
1199
|
+
const sub = args[1];
|
|
1200
|
+
const json = boolFlag('json');
|
|
1201
|
+
const cwd = process.cwd();
|
|
1202
|
+
if (!sub || sub === '--help' || sub === '-h' || sub === 'help') {
|
|
1203
|
+
const focused = (await import("./help-text.js")).buildCommandHelpText('runs');
|
|
1204
|
+
process.stdout.write(focused ?? buildHelpText());
|
|
1205
|
+
process.exit(0);
|
|
1206
|
+
}
|
|
1207
|
+
const { runRunsList, runRunsShow, runRunsGc, runRunsDelete, runRunsDoctor, } = await import("./runs.js");
|
|
1208
|
+
let result;
|
|
1209
|
+
switch (sub) {
|
|
1210
|
+
case 'list': {
|
|
1211
|
+
result = await runRunsList({ cwd, status: flag('status'), json });
|
|
1212
|
+
break;
|
|
1213
|
+
}
|
|
1214
|
+
case 'show': {
|
|
1215
|
+
const events = boolFlag('events');
|
|
1216
|
+
const tailRaw = flag('events-tail');
|
|
1217
|
+
const eventsTail = tailRaw ? parseInt(tailRaw, 10) : undefined;
|
|
1218
|
+
// Filter out value-flag *values* — without this, `runs show
|
|
1219
|
+
// --events-tail 5 <ULID>` would resolve runId to '5'. Same pattern
|
|
1220
|
+
// as the scan case above. Caught by Cursor Bugbot on PR #88
|
|
1221
|
+
// (MEDIUM).
|
|
1222
|
+
const runId = args.slice(2).find(a => !a.startsWith('--') && a !== tailRaw);
|
|
1223
|
+
result = await runRunsShow({
|
|
1224
|
+
runId: runId ?? '',
|
|
1225
|
+
cwd,
|
|
1226
|
+
events,
|
|
1227
|
+
...(eventsTail !== undefined ? { eventsTail } : {}),
|
|
1228
|
+
json,
|
|
1229
|
+
});
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
case 'gc': {
|
|
1233
|
+
const dryRun = boolFlag('dry-run');
|
|
1234
|
+
const yes = boolFlag('yes');
|
|
1235
|
+
const olderRaw = flag('older-than-days');
|
|
1236
|
+
const olderThanDays = olderRaw ? parseInt(olderRaw, 10) : undefined;
|
|
1237
|
+
result = await runRunsGc({
|
|
1238
|
+
cwd,
|
|
1239
|
+
dryRun,
|
|
1240
|
+
yes,
|
|
1241
|
+
...(olderThanDays !== undefined ? { olderThanDays } : {}),
|
|
1242
|
+
json,
|
|
1243
|
+
});
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
case 'delete': {
|
|
1247
|
+
const runId = args.slice(2).find(a => !a.startsWith('--'));
|
|
1248
|
+
const force = boolFlag('force');
|
|
1249
|
+
result = await runRunsDelete({ runId: runId ?? '', cwd, force, json });
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
case 'doctor': {
|
|
1253
|
+
const runId = args.slice(2).find(a => !a.startsWith('--'));
|
|
1254
|
+
const fix = boolFlag('fix');
|
|
1255
|
+
result = await runRunsDoctor({
|
|
1256
|
+
cwd,
|
|
1257
|
+
...(runId ? { runId } : {}),
|
|
1258
|
+
fix,
|
|
1259
|
+
json,
|
|
1260
|
+
});
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1263
|
+
case 'watch': {
|
|
1264
|
+
// v6.1 — `runs watch <id>` tails events.ndjson with a live cost meter.
|
|
1265
|
+
// The other umbrella verbs use a unified `RunsCliResult` shape so the
|
|
1266
|
+
// dispatcher can treat them uniformly; `runs-watch.ts` returns the
|
|
1267
|
+
// same shape.
|
|
1268
|
+
const sinceRaw = flag('since');
|
|
1269
|
+
const since = sinceRaw !== undefined ? parseInt(sinceRaw, 10) : undefined;
|
|
1270
|
+
if (sinceRaw !== undefined && (Number.isNaN(since) || since < 0)) {
|
|
1271
|
+
process.stderr.write(`\x1b[31m[claude-autopilot] --since must be a non-negative integer\x1b[0m\n`);
|
|
1272
|
+
process.exit(1);
|
|
1273
|
+
}
|
|
1274
|
+
const noFollow = boolFlag('no-follow');
|
|
1275
|
+
const noColor = boolFlag('no-color');
|
|
1276
|
+
// Filter the value-flag value out of the positional lookup —
|
|
1277
|
+
// matches the same defensive pattern used in `runs show` (Bugbot
|
|
1278
|
+
// PR #88 MEDIUM). Without this, `runs watch --since 5 <ULID>`
|
|
1279
|
+
// would resolve runId to "5".
|
|
1280
|
+
const runId = args.slice(2).find(a => !a.startsWith('--') && a !== sinceRaw);
|
|
1281
|
+
const { runRunsWatch } = await import("./runs-watch.js");
|
|
1282
|
+
result = await runRunsWatch({
|
|
1283
|
+
runId: runId ?? '',
|
|
1284
|
+
cwd,
|
|
1285
|
+
...(since !== undefined ? { since } : {}),
|
|
1286
|
+
noFollow,
|
|
1287
|
+
json,
|
|
1288
|
+
noColor,
|
|
1289
|
+
});
|
|
1290
|
+
break;
|
|
1291
|
+
}
|
|
1292
|
+
default: {
|
|
1293
|
+
process.stderr.write(`\x1b[31m[claude-autopilot] runs: unknown sub-verb "${sub}" — valid: list, show, gc, delete, doctor, watch\x1b[0m\n`);
|
|
1294
|
+
process.exit(1);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
for (const line of result.stdout)
|
|
1298
|
+
process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1299
|
+
for (const line of result.stderr)
|
|
1300
|
+
process.stderr.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1301
|
+
process.exit(result.exit);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
case 'run-resume': {
|
|
1305
|
+
// v6 Phase 3 — `run resume <id>`. Lookup-only: identifies the next phase
|
|
1306
|
+
// and decision rationale. Actual phase execution wires in Phase 6+.
|
|
1307
|
+
// The synthetic 'run-resume' subcommand was set above by the
|
|
1308
|
+
// disambiguation block; args[0]==='run', args[1]==='resume', args[2] is
|
|
1309
|
+
// optional run id.
|
|
1310
|
+
const json = boolFlag('json');
|
|
1311
|
+
const fromPhase = flag('from-phase') ?? flag('from');
|
|
1312
|
+
// Filter value-flag values out of positional lookup — see same comment
|
|
1313
|
+
// in the `runs show` case above. (Bugbot MEDIUM, PR #88.)
|
|
1314
|
+
const runId = args.slice(2).find(a => !a.startsWith('--') && a !== fromPhase);
|
|
1315
|
+
const { runRunResume } = await import("./runs.js");
|
|
1316
|
+
const result = await runRunResume({
|
|
1317
|
+
runId: runId ?? '',
|
|
1318
|
+
cwd: process.cwd(),
|
|
1319
|
+
...(fromPhase ? { fromPhase } : {}),
|
|
1320
|
+
json,
|
|
1321
|
+
});
|
|
1322
|
+
for (const line of result.stdout)
|
|
1323
|
+
process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1324
|
+
for (const line of result.stderr)
|
|
1325
|
+
process.stderr.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1326
|
+
process.exit(result.exit);
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
case 'internal': {
|
|
1330
|
+
// v6 Phase 2 — hidden verb. Markdown skills shell out to append typed
|
|
1331
|
+
// events into a run's events.ndjson. NOT advertised in the main help.
|
|
1332
|
+
const { runInternalCli } = await import("../core/run-state/cli-internal.js");
|
|
1333
|
+
const result = await runInternalCli({
|
|
1334
|
+
args: args.slice(1),
|
|
1335
|
+
cwd: process.cwd(),
|
|
1336
|
+
});
|
|
1337
|
+
for (const line of result.stdout)
|
|
1338
|
+
process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1339
|
+
for (const line of result.stderr)
|
|
1340
|
+
process.stderr.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
1341
|
+
process.exit(result.exit);
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
case 'dashboard': {
|
|
1345
|
+
// v7.0 Phase 2.3 — hosted dashboard verbs.
|
|
1346
|
+
// claude-autopilot dashboard {login,logout,status,upload <runId>}
|
|
1347
|
+
const { runDashboardVerb } = await import("./dashboard/index.js");
|
|
1348
|
+
const exit = await runDashboardVerb({ argv: args.slice(1) });
|
|
1349
|
+
process.exit(exit);
|
|
821
1350
|
break;
|
|
822
1351
|
}
|
|
823
1352
|
default:
|