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