@delegance/claude-autopilot 6.2.2 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +886 -0
  2. package/README.md +10 -1
  3. package/bin/_launcher.js +38 -23
  4. package/dist/src/cli/autopilot.d.ts +4 -0
  5. package/dist/src/cli/autopilot.js +15 -0
  6. package/dist/src/cli/dashboard/index.d.ts +5 -0
  7. package/dist/src/cli/dashboard/index.js +49 -0
  8. package/dist/src/cli/dashboard/login.d.ts +22 -0
  9. package/dist/src/cli/dashboard/login.js +260 -0
  10. package/dist/src/cli/dashboard/logout.d.ts +12 -0
  11. package/dist/src/cli/dashboard/logout.js +45 -0
  12. package/dist/src/cli/dashboard/status.d.ts +30 -0
  13. package/dist/src/cli/dashboard/status.js +65 -0
  14. package/dist/src/cli/dashboard/upload.d.ts +16 -0
  15. package/dist/src/cli/dashboard/upload.js +48 -0
  16. package/dist/src/cli/engine-flag-deprecation.d.ts +14 -0
  17. package/dist/src/cli/engine-flag-deprecation.js +20 -0
  18. package/dist/src/cli/help-text.d.ts +1 -1
  19. package/dist/src/cli/help-text.js +44 -28
  20. package/dist/src/cli/index.d.ts +2 -1
  21. package/dist/src/cli/index.js +72 -17
  22. package/dist/src/cli/scaffold.d.ts +39 -0
  23. package/dist/src/cli/scaffold.js +287 -0
  24. package/dist/src/cli/setup.d.ts +30 -0
  25. package/dist/src/cli/setup.js +137 -0
  26. package/dist/src/core/run-state/events.js +10 -2
  27. package/dist/src/core/run-state/resolve-engine.d.ts +26 -81
  28. package/dist/src/core/run-state/resolve-engine.js +39 -155
  29. package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +5 -9
  30. package/dist/src/core/run-state/run-phase-with-lifecycle.js +26 -19
  31. package/dist/src/core/run-state/state.d.ts +1 -1
  32. package/dist/src/core/run-state/types.d.ts +8 -2
  33. package/dist/src/core/run-state/types.js +8 -2
  34. package/dist/src/dashboard/auto-upload.d.ts +26 -0
  35. package/dist/src/dashboard/auto-upload.js +107 -0
  36. package/dist/src/dashboard/config.d.ts +22 -0
  37. package/dist/src/dashboard/config.js +109 -0
  38. package/dist/src/dashboard/upload/canonical.d.ts +3 -0
  39. package/dist/src/dashboard/upload/canonical.js +16 -0
  40. package/dist/src/dashboard/upload/chain.d.ts +9 -0
  41. package/dist/src/dashboard/upload/chain.js +27 -0
  42. package/dist/src/dashboard/upload/snapshot.d.ts +23 -0
  43. package/dist/src/dashboard/upload/snapshot.js +66 -0
  44. package/dist/src/dashboard/upload/uploader.d.ts +54 -0
  45. package/dist/src/dashboard/upload/uploader.js +330 -0
  46. package/package.json +18 -3
  47. package/scripts/test-runner.mjs +4 -0
@@ -0,0 +1,48 @@
1
+ // `claude-autopilot dashboard upload <runId>` — manual upload wrapper.
2
+ //
3
+ // Locates run dir at <homeDir>/runs/<runId>, calls uploadRun() directly,
4
+ // and prints the result. Intended for resuming interrupted auto-uploads.
5
+ import * as path from 'node:path';
6
+ import { promises as fs } from 'node:fs';
7
+ import { readConfig, getConfigDir } from "../../dashboard/config.js";
8
+ import { uploadRun } from "../../dashboard/upload/uploader.js";
9
+ export async function runDashboardUpload(opts) {
10
+ const cfg = await readConfig();
11
+ if (!cfg) {
12
+ if (!opts.silent) {
13
+ process.stderr.write(`[autopilot] not logged in. Run: claude-autopilot dashboard login\n`);
14
+ }
15
+ return { ok: false, notLoggedIn: true };
16
+ }
17
+ const runsDir = opts.runsDir ?? path.join(getConfigDir(), 'runs');
18
+ const runDir = path.join(runsDir, opts.runId);
19
+ try {
20
+ await fs.access(runDir);
21
+ }
22
+ catch {
23
+ if (!opts.silent) {
24
+ process.stderr.write(`[autopilot] run dir not found: ${runDir}\n`);
25
+ }
26
+ return { ok: false, runDirMissing: true, runDir };
27
+ }
28
+ const uploadOpts = {
29
+ apiKey: cfg.apiKey,
30
+ ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),
31
+ ...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
32
+ ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
33
+ };
34
+ const res = await uploadRun(opts.runId, runDir, uploadOpts);
35
+ if (!opts.silent) {
36
+ if (res.ok && res.url) {
37
+ process.stdout.write(`[autopilot] uploaded to ${res.url}\n`);
38
+ }
39
+ else if (res.ok && res.skipped) {
40
+ process.stdout.write(`[autopilot] skipping upload — events.ndjson is empty\n`);
41
+ }
42
+ else {
43
+ process.stderr.write(`[autopilot] upload failed: ${res.error}\n`);
44
+ }
45
+ }
46
+ return { ...res, runDir };
47
+ }
48
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1,14 @@
1
+ /** Banner emitted to stderr (once per process) when `--engine` is
2
+ * passed in v7.0+. The flag is preserved as a no-op shim; the engine
3
+ * is unconditionally on. Codex pass-3 NOTE #2. */
4
+ export declare const ENGINE_FLAG_DEPRECATION_MESSAGE = "[deprecation] --engine is a no-op in v7.0+ (engine is always on). Drop the flag from your scripts.";
5
+ /** Banner emitted to stderr when `--no-engine` is passed in v7.0+. The
6
+ * dispatcher rejects with `invalid_config` exit 1 immediately after. */
7
+ export declare const ENGINE_OFF_REMOVED_MESSAGE = "[deprecation] --no-engine was removed in v7.0. The engine is always on. See docs/v7/breaking-changes.md.";
8
+ /** Banner emitted to stderr (once per process) when
9
+ * `CLAUDE_AUTOPILOT_ENGINE` is set to an off-style value. The env value
10
+ * is otherwise ignored — softer than the `--no-engine` rejection
11
+ * because env vars in CI are sticky and silently breaking every
12
+ * v6.x → v7 upgrade in CI on day one would burn user trust. */
13
+ export declare const ENGINE_OFF_ENV_REMOVED_MESSAGE = "[deprecation] CLAUDE_AUTOPILOT_ENGINE=off has no effect in v7.0+ (engine is always on). Unset the env var.";
14
+ //# sourceMappingURL=engine-flag-deprecation.d.ts.map
@@ -0,0 +1,20 @@
1
+ // src/cli/engine-flag-deprecation.ts
2
+ //
3
+ // v7.0 — exported deprecation constants for the engine-flag removal.
4
+ // Pulled out of src/cli/index.ts so tests can import them without
5
+ // triggering the dispatcher's top-level switch (which exits the
6
+ // process on module load).
7
+ /** Banner emitted to stderr (once per process) when `--engine` is
8
+ * passed in v7.0+. The flag is preserved as a no-op shim; the engine
9
+ * is unconditionally on. Codex pass-3 NOTE #2. */
10
+ export const ENGINE_FLAG_DEPRECATION_MESSAGE = '[deprecation] --engine is a no-op in v7.0+ (engine is always on). Drop the flag from your scripts.';
11
+ /** Banner emitted to stderr when `--no-engine` is passed in v7.0+. The
12
+ * dispatcher rejects with `invalid_config` exit 1 immediately after. */
13
+ export const ENGINE_OFF_REMOVED_MESSAGE = '[deprecation] --no-engine was removed in v7.0. The engine is always on. See docs/v7/breaking-changes.md.';
14
+ /** Banner emitted to stderr (once per process) when
15
+ * `CLAUDE_AUTOPILOT_ENGINE` is set to an off-style value. The env value
16
+ * is otherwise ignored — softer than the `--no-engine` rejection
17
+ * because env vars in CI are sticky and silently breaking every
18
+ * v6.x → v7 upgrade in CI on day one would burn user trust. */
19
+ export const ENGINE_OFF_ENV_REMOVED_MESSAGE = '[deprecation] CLAUDE_AUTOPILOT_ENGINE=off has no effect in v7.0+ (engine is always on). Unset the env var.';
20
+ //# sourceMappingURL=engine-flag-deprecation.js.map
@@ -42,7 +42,7 @@ export declare const HELP_OPTIONS: Record<string, string>;
42
42
  * support varies; v6.0.1 wires them into `scan` first, additional verbs land
43
43
  * in subsequent v6.0.x point releases per docs/v6/wrapping-pipeline-phases.md).
44
44
  */
45
- export declare const GLOBAL_FLAGS_BLOCK = "Global flags:\n --json Emit a structured JSON envelope on stdout (most verbs)\n --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)\n --no-engine Force the legacy stateless code path (overrides config / env)\n Precedence: CLI > env (CLAUDE_AUTOPILOT_ENGINE) > config (engine.enabled) > built-in default\n v6.0.1: wired for `scan`. v6.0.2: wired for `fix` and `costs`. v6.0.3: wired for `brainstorm` and `spec`. v6.0.4: wired for `plan` and `review`. v6.0.5: wired for `validate`. v6.0.7: wired for `implement`. v6.0.8: wired for `migrate`. v6.0.9: wired for `pr`. ALL 10 phases now wrapped \u2014 v6.0.x feature-complete.";
45
+ export declare const GLOBAL_FLAGS_BLOCK = "Global flags:\n --json Emit a structured JSON envelope on stdout (most verbs)\n --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning\n v7.0 removed the engine-off opt-out flag and code path entirely.\n CLAUDE_AUTOPILOT_ENGINE=off is now ignored (warns once per process)\n instead of disabling the engine. See docs/v7/breaking-changes.md.";
46
46
  /** Build the full two-level help text. Returned as a string so tests can assert against it without spawning. */
47
47
  export declare function buildHelpText(): string;
48
48
  /** Build help text for a single verb. Returns null if the verb is unknown. */
@@ -28,6 +28,7 @@ export const HELP_GROUPS = [
28
28
  verbs: [
29
29
  { verb: 'init', summary: 'Scaffold guardrail.config.yaml + auto-detect migrate stack (writes .autopilot/stack.md)' },
30
30
  { verb: 'setup', summary: 'Auto-detect stack, write config, install pre-push hook' },
31
+ { verb: 'scaffold', summary: 'Scaffold project skeleton from a spec markdown (--from-spec <path>)' },
31
32
  { verb: 'autopilot', summary: 'Multi-phase orchestrator — run scan → spec → plan → implement under one runId (v6.2.0)' },
32
33
  { verb: 'brainstorm', summary: 'Pipeline entry point (Claude Code skill — see /brainstorm)' },
33
34
  { verb: 'spec', summary: 'Spec-writing pointer (Claude Code skill — see /brainstorm)' },
@@ -87,6 +88,13 @@ export const HELP_GROUPS = [
87
88
  { verb: 'runs', summary: 'Inspect run state: runs list | show <id> | watch <id> | resume <id> | gc | delete <id> | doctor' },
88
89
  ],
89
90
  },
91
+ {
92
+ name: 'Dashboard',
93
+ tagline: 'hosted product — login, status, manual upload (v7.0 Phase 2.3)',
94
+ verbs: [
95
+ { verb: 'dashboard', summary: 'Manage hosted dashboard auth + uploads: dashboard {login,logout,status,upload <runId>}' },
96
+ ],
97
+ },
90
98
  {
91
99
  name: 'Advanced',
92
100
  tagline: 'server / experimental — hidden from welcome screen',
@@ -125,15 +133,13 @@ export const HELP_OPTIONS = {
125
133
  --focus <type> security | logic | performance (default: all)
126
134
  --dry-run List files that would be scanned without running
127
135
  --config <path> Path to config file
128
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
129
- --no-engine Force the legacy stateless code path (overrides config / env)`,
136
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning`,
130
137
  pr: `Options (pr):
131
138
  <number> PR number to review (optional if on a PR branch)
132
139
  --no-post-comments Skip posting/updating PR summary comment
133
140
  --no-inline-comments Skip posting per-line inline annotations
134
141
  --config <path> Path to config file
135
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
136
- --no-engine Force the legacy stateless code path (overrides config / env)
142
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
137
143
 
138
144
  Note: pr is side-effecting — it posts/updates a PR comment and inline
139
145
  review comments via the gh CLI. Engine-on records a github-pr
@@ -144,16 +150,13 @@ export const HELP_OPTIONS = {
144
150
  --severity <critical|warning|all> Which findings to fix (default: critical)
145
151
  --dry-run Preview fixes without writing files
146
152
  --config <path> Path to config file
147
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
148
- --no-engine Force the legacy stateless code path (overrides config / env)`,
153
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning`,
149
154
  costs: `Options (costs):
150
155
  --config <path> Path to config file
151
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
152
- --no-engine Force the legacy stateless code path (overrides config / env)`,
156
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning`,
153
157
  brainstorm: `Options (brainstorm):
154
158
  --config <path> Path to config file
155
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
156
- --no-engine Force the legacy stateless code path (overrides config / env)
159
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
157
160
  --json Emit a structured JSON envelope on stdout
158
161
 
159
162
  Note: brainstorm is primarily a Claude Code skill (/brainstorm). The CLI
@@ -161,8 +164,7 @@ export const HELP_OPTIONS = {
161
164
  snapshot (state.json + events.ndjson) for pipeline introspection.`,
162
165
  spec: `Options (spec):
163
166
  --config <path> Path to config file
164
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
165
- --no-engine Force the legacy stateless code path (overrides config / env)
167
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
166
168
  --json Emit a structured JSON envelope on stdout
167
169
 
168
170
  Note: spec is primarily a Claude Code skill (entered via /brainstorm). The
@@ -172,20 +174,17 @@ export const HELP_OPTIONS = {
172
174
  --spec <path> Spec file the planner should read (optional)
173
175
  --output <path> Where to write the plan markdown (default: .guardrail-cache/plans/<ts>-plan.md)
174
176
  --config <path> Path to config file
175
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
176
- --no-engine Force the legacy stateless code path (overrides config / env)`,
177
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning`,
177
178
  review: `Options (review):
178
179
  --context <text> Optional context note injected into the review log
179
180
  --output <path> Where to write the review log (default: .guardrail-cache/reviews/<ts>-review.md)
180
181
  --config <path> Path to config file
181
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
182
- --no-engine Force the legacy stateless code path (overrides config / env)`,
182
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning`,
183
183
  validate: `Options (validate):
184
184
  --context <text> Optional context note injected into the validate log
185
185
  --output <path> Where to write the validate log (default: .guardrail-cache/validate/<ts>-validate.md)
186
186
  --config <path> Path to config file
187
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
188
- --no-engine Force the legacy stateless code path (overrides config / env)
187
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
189
188
 
190
189
  Note: validate is primarily a Claude Code skill (/validate). The CLI verb
191
190
  is an engine-wrap shell; engine-on still produces a run-state
@@ -195,8 +194,7 @@ export const HELP_OPTIONS = {
195
194
  --mode <full> Pipeline mode (v6.2.0 ships 'full' only — scan → spec → plan → implement)
196
195
  --phases <a,b,c> Explicit phase list (comma-separated; overrides --mode)
197
196
  --budget <usd> Run-scope budget cap (USD). Cross-phase — actualSoFar accumulates across phases.
198
- --engine Run under the v6 Run State Engine (REQUIRED engine-off is rejected)
199
- --no-engine REJECTED — orchestrator requires engine-on (rejected at pre-flight, exit 1)
197
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
200
198
 
201
199
  Behavior: drives N pipeline phases under ONE runId so a \`runs watch <id>\`
202
200
  window covers the whole pipeline. Sequential — no parallel
@@ -215,13 +213,32 @@ export const HELP_OPTIONS = {
215
213
  claude-autopilot autopilot
216
214
  claude-autopilot autopilot --budget 25
217
215
  claude-autopilot autopilot --phases=scan,spec,plan`,
216
+ dashboard: `Options (dashboard):
217
+ login Open browser, mint API key via loopback callback
218
+ logout Revoke server-side, delete local config
219
+ status Show login state + memberships + last upload
220
+ upload <runId> Manually upload a run (resume an interrupted auto-upload)
221
+
222
+ Auto-upload: after \`dashboard login\`, every engine-on autopilot run
223
+ uploads to autopilot.dev when run.complete fires. Failure prints a
224
+ resume command and never fails the run. Opt out per-run with
225
+ \`--no-upload\` or globally with \`CLAUDE_AUTOPILOT_UPLOAD=off\`.
226
+
227
+ Env:
228
+ AUTOPILOT_DASHBOARD_BASE_URL Override base URL (default https://autopilot.dev)
229
+ CLAUDE_AUTOPILOT_HOME Override config dir (default ~/.claude-autopilot)
230
+ CLAUDE_AUTOPILOT_UPLOAD=off Skip auto-upload at run.complete
231
+
232
+ Examples:
233
+ claude-autopilot dashboard login
234
+ claude-autopilot dashboard status
235
+ claude-autopilot dashboard upload 01HFGB...`,
218
236
  implement: `Options (implement):
219
237
  --context <text> Optional context note injected into the implement log
220
238
  --plan <path> Plan file the implement phase consumed (e.g. docs/plans/<slug>.md)
221
239
  --output <path> Where to write the implement log (default: .guardrail-cache/implement/<ts>-implement.md)
222
240
  --config <path> Path to config file
223
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
224
- --no-engine Force the legacy stateless code path (overrides config / env)
241
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
225
242
 
226
243
  Note: implement is primarily a Claude Code skill (claude-autopilot — reads
227
244
  plan, dispatches subagents per plan phase via subagent-driven-development,
@@ -248,8 +265,7 @@ export const HELP_OPTIONS = {
248
265
  --dry-run Run skill in dry-run mode (no side effects)
249
266
  --yes Required to apply prod migrations in CI
250
267
  --config <path> Path to config file (default: ./guardrail.config.yaml)
251
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
252
- --no-engine Force the legacy stateless code path (overrides config / env)
268
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
253
269
 
254
270
  Note: migrate is a side-effecting phase — re-running with --engine after a
255
271
  prior phase.success requires --force-replay (Phase 6+) because the
@@ -342,10 +358,10 @@ function padVerb(verb) {
342
358
  */
343
359
  export const GLOBAL_FLAGS_BLOCK = `Global flags:
344
360
  --json Emit a structured JSON envelope on stdout (most verbs)
345
- --engine Run under the v6 Run State Engine (writes .guardrail-cache/runs/<ulid>/)
346
- --no-engine Force the legacy stateless code path (overrides config / env)
347
- Precedence: CLI > env (CLAUDE_AUTOPILOT_ENGINE) > config (engine.enabled) > built-in default
348
- v6.0.1: wired for \`scan\`. v6.0.2: wired for \`fix\` and \`costs\`. v6.0.3: wired for \`brainstorm\` and \`spec\`. v6.0.4: wired for \`plan\` and \`review\`. v6.0.5: wired for \`validate\`. v6.0.7: wired for \`implement\`. v6.0.8: wired for \`migrate\`. v6.0.9: wired for \`pr\`. ALL 10 phases now wrapped — v6.0.x feature-complete.`;
361
+ --engine [v7.0 deprecated no-op] engine is always on; flag emits a warning
362
+ v7.0 removed the engine-off opt-out flag and code path entirely.
363
+ CLAUDE_AUTOPILOT_ENGINE=off is now ignored (warns once per process)
364
+ instead of disabling the engine. See docs/v7/breaking-changes.md.`;
349
365
  /** Build the full two-level help text. Returned as a string so tests can assert against it without spawning. */
350
366
  export function buildHelpText() {
351
367
  const lines = [];
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export declare function _resetEngineDeprecationLatchForTests(): void;
3
+ export declare function _resetEngineEnvOffLatchForTests(): void;
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -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";
@@ -258,30 +259,60 @@ function boolFlag(name) {
258
259
  return args.includes(`--${name}`);
259
260
  }
260
261
  /**
261
- * Parse the `--engine` / `--no-engine` flag pair into a tri-state.
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.
262
265
  *
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.
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
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";
271
273
  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`);
274
+ if (args.includes('--no-engine')) {
275
+ console.error(`\x1b[31m[claude-autopilot] invalid_config: ${ENGINE_OFF_REMOVED_MESSAGE}\x1b[0m`);
277
276
  process.exit(1);
278
277
  }
279
- if (on)
280
- return true;
281
- if (off)
282
- return false;
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).
283
286
  return undefined;
284
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
+ }
285
316
  /**
286
317
  * Run the migrate-doctor with shared CLI formatting and exit handling.
287
318
  *
@@ -828,6 +859,7 @@ switch (subcommand) {
828
859
  budgetUSD = parsed;
829
860
  }
830
861
  const cliEngine = parseEngineCliFlag();
862
+ const noUpload = boolFlag('no-upload');
831
863
  if (json) {
832
864
  const exitCode = await runAutopilotWithJsonEnvelope({
833
865
  cwd: process.cwd(),
@@ -835,6 +867,7 @@ switch (subcommand) {
835
867
  ...(phases !== undefined ? { phases } : {}),
836
868
  ...(budgetUSD !== undefined ? { budgetUSD } : {}),
837
869
  ...(cliEngine !== undefined ? { cliEngine } : {}),
870
+ ...(noUpload ? { noUpload: true } : {}),
838
871
  envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
839
872
  });
840
873
  process.exit(exitCode);
@@ -845,6 +878,7 @@ switch (subcommand) {
845
878
  ...(phases !== undefined ? { phases } : {}),
846
879
  ...(budgetUSD !== undefined ? { budgetUSD } : {}),
847
880
  ...(cliEngine !== undefined ? { cliEngine } : {}),
881
+ ...(noUpload ? { noUpload: true } : {}),
848
882
  envEngine: process.env.CLAUDE_AUTOPILOT_ENGINE,
849
883
  });
850
884
  process.exit(result.exitCode);
@@ -897,6 +931,19 @@ switch (subcommand) {
897
931
  process.exit(code);
898
932
  break;
899
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
+ }
900
947
  case 'council': {
901
948
  const config = flag('config');
902
949
  const prompt = flag('prompt');
@@ -1294,6 +1341,14 @@ switch (subcommand) {
1294
1341
  process.exit(result.exit);
1295
1342
  break;
1296
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);
1350
+ break;
1351
+ }
1297
1352
  default:
1298
1353
  console.error(`\x1b[31m[claude-autopilot] Unknown subcommand: "${subcommand}"\x1b[0m`);
1299
1354
  printUsage();
@@ -0,0 +1,39 @@
1
+ export interface ScaffoldOptions {
2
+ cwd?: string;
3
+ specPath: string;
4
+ /** When true, log what would happen but don't write anything. */
5
+ dryRun?: boolean;
6
+ }
7
+ export interface ScaffoldResult {
8
+ filesCreated: string[];
9
+ dirsCreated: string[];
10
+ filesSkippedExisting: string[];
11
+ packageJsonAction: 'created' | 'merged' | 'skipped-exists';
12
+ tsconfigAction: 'created' | 'skipped-exists' | 'skipped-no-ts';
13
+ }
14
+ interface ParsedFiles {
15
+ /** Raw paths extracted from the `## Files` section bullets. */
16
+ paths: string[];
17
+ /** Loosely-parsed package.json hints found anywhere in the section. */
18
+ packageHints: {
19
+ bin?: Record<string, string>;
20
+ type?: 'module' | 'commonjs';
21
+ dependencies?: Record<string, string>;
22
+ devDependencies?: Record<string, string>;
23
+ scripts?: Record<string, string>;
24
+ };
25
+ }
26
+ /**
27
+ * Parse the `## Files` (or `## files`) section of a spec markdown file.
28
+ * Tolerant: missing section returns `null`; malformed bullets are skipped
29
+ * silently. Returns extracted file paths + best-effort package-hint blob.
30
+ */
31
+ export declare function parseSpecFiles(markdown: string): ParsedFiles | null;
32
+ /**
33
+ * Build a minimal starter package.json. Caller passes in any explicit
34
+ * hints (parsed from spec); we layer Node 22 ESM defaults on top.
35
+ */
36
+ export declare function buildStarterPackageJson(projectName: string, hints: ParsedFiles['packageHints']): Record<string, unknown>;
37
+ export declare function runScaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
38
+ export {};
39
+ //# sourceMappingURL=scaffold.d.ts.map