@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.
Files changed (130) hide show
  1. package/CHANGELOG.md +1027 -1
  2. package/README.md +104 -17
  3. package/dist/src/adapters/council/claude.js +2 -1
  4. package/dist/src/adapters/council/openai.js +14 -7
  5. package/dist/src/adapters/deploy/_http.d.ts +43 -0
  6. package/dist/src/adapters/deploy/_http.js +99 -0
  7. package/dist/src/adapters/deploy/fly.d.ts +206 -0
  8. package/dist/src/adapters/deploy/fly.js +696 -0
  9. package/dist/src/adapters/deploy/generic.d.ts +39 -0
  10. package/dist/src/adapters/deploy/generic.js +98 -0
  11. package/dist/src/adapters/deploy/index.d.ts +15 -0
  12. package/dist/src/adapters/deploy/index.js +78 -0
  13. package/dist/src/adapters/deploy/render.d.ts +181 -0
  14. package/dist/src/adapters/deploy/render.js +550 -0
  15. package/dist/src/adapters/deploy/types.d.ts +221 -0
  16. package/dist/src/adapters/deploy/types.js +15 -0
  17. package/dist/src/adapters/deploy/vercel.d.ts +143 -0
  18. package/dist/src/adapters/deploy/vercel.js +426 -0
  19. package/dist/src/adapters/pricing.d.ts +36 -0
  20. package/dist/src/adapters/pricing.js +40 -0
  21. package/dist/src/adapters/review-engine/claude.js +2 -1
  22. package/dist/src/adapters/review-engine/codex.js +12 -8
  23. package/dist/src/adapters/review-engine/gemini.js +2 -1
  24. package/dist/src/adapters/review-engine/openai-compatible.js +2 -1
  25. package/dist/src/adapters/sdk-loader.d.ts +15 -0
  26. package/dist/src/adapters/sdk-loader.js +77 -0
  27. package/dist/src/cli/autopilot.d.ts +71 -0
  28. package/dist/src/cli/autopilot.js +735 -0
  29. package/dist/src/cli/brainstorm.d.ts +23 -0
  30. package/dist/src/cli/brainstorm.js +131 -0
  31. package/dist/src/cli/costs.d.ts +15 -1
  32. package/dist/src/cli/costs.js +99 -10
  33. package/dist/src/cli/deploy.d.ts +71 -0
  34. package/dist/src/cli/deploy.js +539 -0
  35. package/dist/src/cli/fix.d.ts +18 -0
  36. package/dist/src/cli/fix.js +105 -11
  37. package/dist/src/cli/help-text.d.ts +52 -0
  38. package/dist/src/cli/help-text.js +400 -0
  39. package/dist/src/cli/implement.d.ts +91 -0
  40. package/dist/src/cli/implement.js +196 -0
  41. package/dist/src/cli/index.js +784 -222
  42. package/dist/src/cli/json-envelope.d.ts +187 -0
  43. package/dist/src/cli/json-envelope.js +270 -0
  44. package/dist/src/cli/json-mode.d.ts +33 -0
  45. package/dist/src/cli/json-mode.js +201 -0
  46. package/dist/src/cli/migrate.d.ts +111 -0
  47. package/dist/src/cli/migrate.js +305 -0
  48. package/dist/src/cli/plan.d.ts +81 -0
  49. package/dist/src/cli/plan.js +149 -0
  50. package/dist/src/cli/pr.d.ts +106 -0
  51. package/dist/src/cli/pr.js +191 -19
  52. package/dist/src/cli/preflight.js +102 -1
  53. package/dist/src/cli/review.d.ts +27 -0
  54. package/dist/src/cli/review.js +126 -0
  55. package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
  56. package/dist/src/cli/runs-watch-renderer.js +275 -0
  57. package/dist/src/cli/runs-watch.d.ts +41 -0
  58. package/dist/src/cli/runs-watch.js +395 -0
  59. package/dist/src/cli/runs.d.ts +122 -0
  60. package/dist/src/cli/runs.js +902 -0
  61. package/dist/src/cli/scan.d.ts +93 -0
  62. package/dist/src/cli/scan.js +166 -40
  63. package/dist/src/cli/spec.d.ts +66 -0
  64. package/dist/src/cli/spec.js +132 -0
  65. package/dist/src/cli/validate.d.ts +29 -0
  66. package/dist/src/cli/validate.js +131 -0
  67. package/dist/src/core/config/schema.d.ts +43 -0
  68. package/dist/src/core/config/schema.js +25 -0
  69. package/dist/src/core/config/types.d.ts +17 -0
  70. package/dist/src/core/council/runner.d.ts +10 -1
  71. package/dist/src/core/council/runner.js +25 -3
  72. package/dist/src/core/council/types.d.ts +7 -0
  73. package/dist/src/core/errors.d.ts +1 -1
  74. package/dist/src/core/errors.js +12 -0
  75. package/dist/src/core/logging/redaction.d.ts +13 -0
  76. package/dist/src/core/logging/redaction.js +20 -0
  77. package/dist/src/core/migrate/detector-rules.js +6 -0
  78. package/dist/src/core/migrate/schema-validator.js +22 -1
  79. package/dist/src/core/phases/static-rules.d.ts +5 -1
  80. package/dist/src/core/phases/static-rules.js +2 -5
  81. package/dist/src/core/run-state/budget.d.ts +88 -0
  82. package/dist/src/core/run-state/budget.js +141 -0
  83. package/dist/src/core/run-state/cli-internal.d.ts +21 -0
  84. package/dist/src/core/run-state/cli-internal.js +174 -0
  85. package/dist/src/core/run-state/events.d.ts +59 -0
  86. package/dist/src/core/run-state/events.js +504 -0
  87. package/dist/src/core/run-state/lock.d.ts +61 -0
  88. package/dist/src/core/run-state/lock.js +206 -0
  89. package/dist/src/core/run-state/phase-context.d.ts +60 -0
  90. package/dist/src/core/run-state/phase-context.js +108 -0
  91. package/dist/src/core/run-state/phase-registry.d.ts +137 -0
  92. package/dist/src/core/run-state/phase-registry.js +162 -0
  93. package/dist/src/core/run-state/phase-runner.d.ts +80 -0
  94. package/dist/src/core/run-state/phase-runner.js +447 -0
  95. package/dist/src/core/run-state/provider-readback.d.ts +130 -0
  96. package/dist/src/core/run-state/provider-readback.js +426 -0
  97. package/dist/src/core/run-state/replay-decision.d.ts +69 -0
  98. package/dist/src/core/run-state/replay-decision.js +144 -0
  99. package/dist/src/core/run-state/resolve-engine.d.ts +100 -0
  100. package/dist/src/core/run-state/resolve-engine.js +190 -0
  101. package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
  102. package/dist/src/core/run-state/resume-preflight.js +116 -0
  103. package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +73 -0
  104. package/dist/src/core/run-state/run-phase-with-lifecycle.js +186 -0
  105. package/dist/src/core/run-state/runs.d.ts +57 -0
  106. package/dist/src/core/run-state/runs.js +288 -0
  107. package/dist/src/core/run-state/snapshot.d.ts +14 -0
  108. package/dist/src/core/run-state/snapshot.js +114 -0
  109. package/dist/src/core/run-state/state.d.ts +40 -0
  110. package/dist/src/core/run-state/state.js +164 -0
  111. package/dist/src/core/run-state/types.d.ts +278 -0
  112. package/dist/src/core/run-state/types.js +13 -0
  113. package/dist/src/core/run-state/ulid.d.ts +11 -0
  114. package/dist/src/core/run-state/ulid.js +95 -0
  115. package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
  116. package/dist/src/core/schema-alignment/extractor/index.js +2 -2
  117. package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
  118. package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
  119. package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
  120. package/dist/src/core/schema-alignment/git-history.js +53 -0
  121. package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
  122. package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
  123. package/package.json +9 -5
  124. package/scripts/autoregress.ts +3 -2
  125. package/skills/claude-autopilot.md +1 -1
  126. package/skills/make-interfaces-feel-better/SKILL.md +104 -0
  127. package/skills/migrate/SKILL.md +193 -47
  128. package/skills/simplify-ui/SKILL.md +103 -0
  129. package/skills/ui/SKILL.md +117 -0
  130. package/skills/ui-ux-pro-max/SKILL.md +90 -0
@@ -0,0 +1,131 @@
1
+ import * as path from 'node:path';
2
+ import * as fs from 'node:fs';
3
+ import { loadConfig } from "../core/config/loader.js";
4
+ import { runPhaseWithLifecycle } from "../core/run-state/run-phase-with-lifecycle.js";
5
+ const C = {
6
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
7
+ green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m', red: '\x1b[31m',
8
+ };
9
+ const fmt = (c, t) => `${C[c]}${t}${C.reset}`;
10
+ export async function runValidate(options = {}) {
11
+ const cwd = options.cwd ?? process.cwd();
12
+ const configPath = options.configPath ?? path.join(cwd, 'guardrail.config.yaml');
13
+ let config = { configVersion: 1 };
14
+ if (fs.existsSync(configPath)) {
15
+ const loaded = await loadConfig(configPath);
16
+ if (loaded)
17
+ config = loaded;
18
+ }
19
+ // INTENTIONAL DEVIATION FROM THE SPEC TABLE (preserved in v6.0.6):
20
+ // the v6 spec (docs/specs/v6-run-state-engine.md, line 161) lists
21
+ // `validate` with `idempotent: yes, hasSideEffects: no,
22
+ // externalRefs: sarif-artifact`. This wrap declares
23
+ // `idempotent: true, hasSideEffects: false` (matches the spec) but
24
+ // does **not** plumb a `sarif-artifact` externalRef. The reasoning:
25
+ // the `validate` CLI verb is an engine-wrap shell pointing at the
26
+ // Claude Code `/validate` skill — it does not itself emit a SARIF
27
+ // artifact. SARIF emission lives in `claude-autopilot run --format
28
+ // sarif --output <path>` (a separate verb, see help-text.ts → `run`
29
+ // Options block). The `sarif-artifact` externalRef is local-only file
30
+ // output (no remote upload), so the engine doesn't need a readback
31
+ // rule for it on resume — `idempotent: true` covers replay safety. If
32
+ // a future PR adds SARIF emission directly to this verb (or moves the
33
+ // `--format sarif` flag here), the wrap can add an
34
+ // `ctx.emitExternalRef({ kind: 'sarif-artifact', id: '<path>',
35
+ // observedAt: ... })` call after the file write lands. Until then, no
36
+ // ledger entry is needed because there's nothing to read back from.
37
+ const context = options.context ?? null;
38
+ const outputPath = options.outputPath
39
+ ? path.resolve(cwd, options.outputPath)
40
+ : path.join(cwd, '.guardrail-cache', 'validate', `${new Date().toISOString().replace(/[:.]/g, '-')}-validate.md`);
41
+ const validateInput = { cwd, context, outputPath };
42
+ // The wrapped phase body — writes a validate log stub to disk. The actual
43
+ // validation work (static checks → auto-fix → tests → Codex review →
44
+ // bugbot triage) is produced by the Claude Code `/validate` skill.
45
+ // Engine-off callers invoke this directly via `executeValidatePhase()`;
46
+ // engine-on callers route through `runPhase()`.
47
+ const phase = {
48
+ name: 'validate',
49
+ // Re-running the validate verb against the same context writes the same
50
+ // log file. Engine treats local file writes as overwrite-style — same
51
+ // precedent as scan's findings-cache and review's review-log.
52
+ idempotent: true,
53
+ // Local file write only — no PR comment posting, no git push, no
54
+ // provider-side mutation, no SARIF upload. See the long deviation note
55
+ // above where the engine resolution is computed for the externalRefs
56
+ // rationale.
57
+ hasSideEffects: false,
58
+ run: async (input) => executeValidatePhase(input),
59
+ };
60
+ // v6.0.6 — lifecycle wiring lives in `runPhaseWithLifecycle`.
61
+ let output;
62
+ try {
63
+ const result = await runPhaseWithLifecycle({
64
+ cwd,
65
+ phase,
66
+ input: validateInput,
67
+ config,
68
+ cliEngine: options.cliEngine,
69
+ envEngine: options.envEngine,
70
+ runEngineOff: () => executeValidatePhase(validateInput),
71
+ });
72
+ output = result.output;
73
+ }
74
+ catch {
75
+ return 1;
76
+ }
77
+ return renderValidateOutput(output, validateInput);
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Phase body — write a validate log stub. Pure: no console output, no exit
81
+ // codes. Returns a JSON-serializable ValidateOutput so the engine can persist
82
+ // it as `result` on the phase snapshot. The actual validation work is
83
+ // produced by the Claude Code `/validate` skill; this CLI verb's job is to
84
+ // provide a checkpointable phase shell.
85
+ // ---------------------------------------------------------------------------
86
+ async function executeValidatePhase(input) {
87
+ const { context, outputPath } = input;
88
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
89
+ const lines = [
90
+ '# Validate',
91
+ '',
92
+ `Generated: ${new Date().toISOString()}`,
93
+ '',
94
+ context ? `Context: ${context}` : 'Context: (none provided)',
95
+ '',
96
+ '<!--',
97
+ 'This is the v6 engine-wrap stub for the `validate` phase. The actual',
98
+ 'validation work (static checks, auto-fix, tests, Codex review with',
99
+ 'auto-fix, bugbot triage) is produced by the Claude Code `/validate`',
100
+ 'skill. The CLI verb exists to provide a checkpointable phase shell so',
101
+ '`claude-autopilot runs show <id>` reflects a `validate` phase entry',
102
+ 'when the pipeline includes one. SARIF emission lives in',
103
+ '`claude-autopilot run --format sarif --output <path>` (a separate',
104
+ 'verb).',
105
+ '-->',
106
+ '',
107
+ ];
108
+ fs.writeFileSync(outputPath, lines.join('\n'), 'utf8');
109
+ return {
110
+ validateLogPath: outputPath,
111
+ context,
112
+ };
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Render — translate ValidateOutput back to a stdout summary + exit code.
116
+ // Lives outside the wrapped phase because it's pure presentation.
117
+ // ---------------------------------------------------------------------------
118
+ function renderValidateOutput(output, input) {
119
+ const { validateLogPath, context } = output;
120
+ const { cwd } = input;
121
+ console.log('');
122
+ console.log(fmt('bold', '[validate]') + ' ' + fmt('dim', context ? `context: ${context}` : 'no context provided'));
123
+ console.log(fmt('dim', ` → ${path.relative(cwd, validateLogPath)}`));
124
+ console.log('');
125
+ console.log(fmt('cyan', 'Note:') + fmt('dim', ' the validation pipeline lives in Claude Code (/validate skill —'));
126
+ console.log(fmt('dim', ' static checks, auto-fix, tests, Codex review, bugbot triage).'));
127
+ console.log(fmt('dim', ' SARIF emission lives in `claude-autopilot run --format sarif --output <path>`.'));
128
+ console.log('');
129
+ return 0;
130
+ }
131
+ //# sourceMappingURL=validate.js.map
@@ -206,6 +206,40 @@ export declare const GUARDRAIL_CONFIG_SCHEMA: {
206
206
  };
207
207
  readonly additionalProperties: false;
208
208
  };
209
+ readonly deploy: {
210
+ readonly type: "object";
211
+ readonly required: readonly ["adapter"];
212
+ readonly additionalProperties: false;
213
+ readonly properties: {
214
+ readonly adapter: {
215
+ readonly enum: readonly ["vercel", "generic"];
216
+ };
217
+ readonly project: {
218
+ readonly type: "string";
219
+ };
220
+ readonly team: {
221
+ readonly type: "string";
222
+ };
223
+ readonly target: {
224
+ readonly enum: readonly ["production", "preview"];
225
+ };
226
+ readonly deployCommand: {
227
+ readonly type: "string";
228
+ };
229
+ readonly watchBuildLogs: {
230
+ readonly type: "boolean";
231
+ };
232
+ readonly rollbackOn: {
233
+ readonly type: "array";
234
+ readonly items: {
235
+ readonly enum: readonly ["healthCheckFailure", "smokeTestFailure"];
236
+ };
237
+ };
238
+ readonly healthCheckUrl: {
239
+ readonly type: "string";
240
+ };
241
+ };
242
+ };
209
243
  readonly 'schema-alignment': {
210
244
  readonly type: "object";
211
245
  readonly properties: {
@@ -264,6 +298,15 @@ export declare const GUARDRAIL_CONFIG_SCHEMA: {
264
298
  readonly concurrency: {
265
299
  readonly type: "object";
266
300
  };
301
+ readonly engine: {
302
+ readonly type: "object";
303
+ readonly additionalProperties: false;
304
+ readonly properties: {
305
+ readonly enabled: {
306
+ readonly type: "boolean";
307
+ };
308
+ };
309
+ };
267
310
  readonly council: {
268
311
  readonly type: "object";
269
312
  readonly required: readonly ["models", "synthesizer"];
@@ -112,6 +112,24 @@ export const GUARDRAIL_CONFIG_SCHEMA = {
112
112
  },
113
113
  additionalProperties: false,
114
114
  },
115
+ deploy: {
116
+ type: 'object',
117
+ required: ['adapter'],
118
+ additionalProperties: false,
119
+ properties: {
120
+ adapter: { enum: ['vercel', 'generic'] },
121
+ project: { type: 'string' },
122
+ team: { type: 'string' },
123
+ target: { enum: ['production', 'preview'] },
124
+ deployCommand: { type: 'string' },
125
+ watchBuildLogs: { type: 'boolean' },
126
+ rollbackOn: {
127
+ type: 'array',
128
+ items: { enum: ['healthCheckFailure', 'smokeTestFailure'] },
129
+ },
130
+ healthCheckUrl: { type: 'string' },
131
+ },
132
+ },
115
133
  'schema-alignment': {
116
134
  type: 'object',
117
135
  properties: {
@@ -134,6 +152,13 @@ export const GUARDRAIL_CONFIG_SCHEMA = {
134
152
  cache: { type: 'object' },
135
153
  persistence: { type: 'object' },
136
154
  concurrency: { type: 'object' },
155
+ engine: {
156
+ type: 'object',
157
+ additionalProperties: false,
158
+ properties: {
159
+ enabled: { type: 'boolean' },
160
+ },
161
+ },
137
162
  council: {
138
163
  type: 'object',
139
164
  required: ['models', 'synthesizer'],
@@ -1,4 +1,5 @@
1
1
  import type { SchemaAlignmentConfig } from '../schema-alignment/types.ts';
2
+ import type { DeployConfig } from '../../adapters/deploy/types.ts';
2
3
  export interface AdapterReference {
3
4
  adapter: string;
4
5
  options?: Record<string, unknown>;
@@ -92,9 +93,25 @@ export interface GuardrailConfig {
92
93
  };
93
94
  };
94
95
  'schema-alignment'?: SchemaAlignmentConfig;
96
+ /**
97
+ * Deploy phase configuration. Optional — when absent, the deploy phase is a
98
+ * no-op. See `src/adapters/deploy/types.ts` for the full DeployConfig shape.
99
+ */
100
+ deploy?: DeployConfig;
95
101
  cache?: Record<string, unknown>;
96
102
  persistence?: Record<string, unknown>;
97
103
  concurrency?: Record<string, unknown>;
104
+ /**
105
+ * Run State Engine (v6) configuration. v6.0 ships the engine OFF by default
106
+ * to preserve v5.x behavior; v6.1+ flips the default to ON per
107
+ * `docs/specs/v6.1-default-flip.md`. The `engine.enabled` knob is the
108
+ * lowest-priority opt-in — env (`CLAUDE_AUTOPILOT_ENGINE`) and CLI flags
109
+ * (`--engine` / `--no-engine`) override it. See
110
+ * `src/core/run-state/resolve-engine.ts` for the precedence resolver.
111
+ */
112
+ engine?: {
113
+ enabled?: boolean;
114
+ };
98
115
  council?: {
99
116
  models: Array<{
100
117
  adapter: string;
@@ -8,5 +8,14 @@ export interface CouncilRunOutput {
8
8
  costUSD: number;
9
9
  };
10
10
  }
11
- export declare function runCouncil(config: CouncilConfig, adapters: CouncilAdapter[], synthesizer: CouncilAdapter, prompt: string, contextDoc: string): Promise<CouncilRunOutput>;
11
+ /** Phase 4 bounded recursion options. The current single-shot
12
+ * synthesizer never sets this; it's plumbed for future self-eat
13
+ * patterns where the synthesizer recursively calls runCouncil. */
14
+ export interface CouncilRunOptions {
15
+ /** Current recursion depth. Top-level callers omit this (default 0).
16
+ * A synthesizer that calls runCouncil internally MUST pass
17
+ * `currentDepth + 1` so the bound takes effect. */
18
+ currentDepth?: number;
19
+ }
20
+ export declare function runCouncil(config: CouncilConfig, adapters: CouncilAdapter[], synthesizer: CouncilAdapter, prompt: string, contextDoc: string, options?: CouncilRunOptions): Promise<CouncilRunOutput>;
12
21
  //# sourceMappingURL=runner.d.ts.map
@@ -32,8 +32,26 @@ async function consultWithTimeout(adapter, prompt, context, timeoutMs) {
32
32
  clearTimeout(timer);
33
33
  }
34
34
  }
35
- export async function runCouncil(config, adapters, synthesizer, prompt, contextDoc) {
35
+ export async function runCouncil(config, adapters, synthesizer, prompt, contextDoc, options = {}) {
36
36
  const run_id = crypto.randomUUID();
37
+ const currentDepth = options.currentDepth ?? 0;
38
+ // -- Recursion bound (Phase 4) ------------------------------------------
39
+ // Strict `>` so a maxDepth of N permits N nested self-calls — i.e.
40
+ // depth 0 (top-level) + N inner calls. Exceeding aborts with `partial`
41
+ // and never recurses deeper.
42
+ if (typeof config.councilMaxRecursionDepth === 'number'
43
+ && currentDepth > config.councilMaxRecursionDepth) {
44
+ return {
45
+ result: {
46
+ schema_version: 1,
47
+ run_id,
48
+ status: 'partial',
49
+ prompt,
50
+ responses: [],
51
+ },
52
+ usage: { inputTokens: 0, outputTokens: 0, costUSD: 0 },
53
+ };
54
+ }
37
55
  const context = windowContext(contextDoc, config.parallelInputMaxTokens);
38
56
  const responses = await Promise.all(adapters.map(a => consultWithTimeout(a, prompt, context, config.timeoutMs)));
39
57
  const aggregateUsage = (entries) => {
@@ -61,8 +79,12 @@ export async function runCouncil(config, adapters, synthesizer, prompt, contextD
61
79
  const responseSections = successful
62
80
  .map(r => `### ${r.label}\n${r.text}`)
63
81
  .join('\n\n');
64
- const synthesisDoc = `${contextDoc}\n\n---\n\n${responseSections}`;
65
- const synthesisCtx = windowContext(synthesisDoc, config.synthesisInputMaxTokens);
82
+ // Advisor responses go in synthesisPrompt only (structured form). The
83
+ // context the synthesizer sees is the original conversation document
84
+ // re-windowed for its own token budget — keeping responseSections out of
85
+ // it avoids duplicating them and also avoids letting large responses
86
+ // squeeze contextDoc out of synthesisInputMaxTokens.
87
+ const synthesisCtx = windowContext(contextDoc, config.synthesisInputMaxTokens);
66
88
  const synthesisPrompt = [
67
89
  `You have received responses from multiple technical advisors on the following question:\n\n## Original Question\n\n${prompt}`,
68
90
  `## Advisor Responses\n\n${responseSections}`,
@@ -10,6 +10,13 @@ export interface CouncilConfig {
10
10
  minSuccessfulResponses: number;
11
11
  parallelInputMaxTokens: number;
12
12
  synthesisInputMaxTokens: number;
13
+ /** Phase 4 (v6) — bounded recursion for the synthesizer. If set, every
14
+ * synthesizer self-call increments an internal depth counter; exceeding
15
+ * this value aborts the council with `partial` status (never deeper).
16
+ * Default: undefined (no bound; current single-shot synthesizer is
17
+ * unaffected — the bound only matters when the synthesizer itself
18
+ * recurses into runCouncil). */
19
+ councilMaxRecursionDepth?: number;
13
20
  }
14
21
  export type ModelResponseStatus = 'ok' | 'timeout' | 'error';
15
22
  export interface ModelResponse {
@@ -1,4 +1,4 @@
1
- export type ErrorCode = 'auth' | 'rate_limit' | 'transient_network' | 'invalid_config' | 'adapter_bug' | 'user_input' | 'budget_exceeded' | 'concurrency_lock' | 'superseded';
1
+ export type ErrorCode = 'auth' | 'rate_limit' | 'transient_network' | 'invalid_config' | 'adapter_bug' | 'user_input' | 'budget_exceeded' | 'concurrency_lock' | 'superseded' | 'no_previous_deploy' | 'not_found' | 'lock_held' | 'corrupted_state' | 'partial_write' | 'needs_human';
2
2
  export interface GuardrailErrorOptions {
3
3
  code: ErrorCode;
4
4
  retryable?: boolean;
@@ -3,6 +3,18 @@ const DEFAULT_RETRYABLE = {
3
3
  auth: false, rate_limit: true, transient_network: true, invalid_config: false,
4
4
  adapter_bug: false, user_input: false, budget_exceeded: false,
5
5
  concurrency_lock: false, superseded: false,
6
+ no_previous_deploy: false,
7
+ // 404 — caller-fixable (slug typo, wrong scope). Not retryable; the
8
+ // resource won't materialize on its own.
9
+ not_found: false,
10
+ // v6 Run State Engine — none retry automatically; takeover/recovery is an
11
+ // explicit user-driven decision (--force-takeover / --force).
12
+ lock_held: false,
13
+ corrupted_state: false,
14
+ partial_write: false,
15
+ // v6.2.1 — needs_human is by definition a stop-the-pipeline signal; the
16
+ // user (or `--force-replay`) decides whether to retry.
17
+ needs_human: false,
6
18
  };
7
19
  export class GuardrailError extends Error {
8
20
  code;
@@ -1,4 +1,17 @@
1
1
  export declare const DEFAULT_REDACTION_PATTERNS: readonly string[];
2
2
  export declare function applyRedaction(text: string, patterns: readonly string[]): string;
3
3
  export declare function containsSecret(text: string, patterns: readonly string[]): boolean;
4
+ /**
5
+ * Convenience wrapper around {@link applyRedaction} that defaults to the
6
+ * built-in {@link DEFAULT_REDACTION_PATTERNS} list and accepts an optional
7
+ * caller-supplied extension. Designed for adapter `output` fields and other
8
+ * "last N lines" surfaces where a pattern list is rarely available at the
9
+ * call site (the v5.6 spec § "Log redaction" requires this for all new
10
+ * adapters).
11
+ *
12
+ * Pass extra patterns when the caller has loaded
13
+ * `config.persistence.redactionPatterns`; otherwise omit the argument and
14
+ * the defaults handle the well-known token shapes.
15
+ */
16
+ export declare function redactLogLines(text: string, patterns?: readonly string[]): string;
4
17
  //# sourceMappingURL=redaction.d.ts.map
@@ -15,4 +15,24 @@ export function applyRedaction(text, patterns) {
15
15
  export function containsSecret(text, patterns) {
16
16
  return patterns.some(p => new RegExp(p).test(text));
17
17
  }
18
+ /**
19
+ * Convenience wrapper around {@link applyRedaction} that defaults to the
20
+ * built-in {@link DEFAULT_REDACTION_PATTERNS} list and accepts an optional
21
+ * caller-supplied extension. Designed for adapter `output` fields and other
22
+ * "last N lines" surfaces where a pattern list is rarely available at the
23
+ * call site (the v5.6 spec § "Log redaction" requires this for all new
24
+ * adapters).
25
+ *
26
+ * Pass extra patterns when the caller has loaded
27
+ * `config.persistence.redactionPatterns`; otherwise omit the argument and
28
+ * the defaults handle the well-known token shapes.
29
+ */
30
+ export function redactLogLines(text, patterns) {
31
+ if (!text)
32
+ return text;
33
+ const merged = patterns && patterns.length > 0
34
+ ? [...DEFAULT_REDACTION_PATTERNS, ...patterns]
35
+ : DEFAULT_REDACTION_PATTERNS;
36
+ return applyRedaction(text, merged);
37
+ }
18
38
  //# sourceMappingURL=redaction.js.map
@@ -38,6 +38,7 @@ export const DETECTION_RULES = [
38
38
  requireAll: ['prisma/schema.prisma'],
39
39
  excludeIf: ['prisma/migrations'],
40
40
  defaultSkill: 'migrate@1',
41
+ defaultCommand: { exec: 'prisma', args: ['db', 'push'] },
41
42
  promptOnSelect: true,
42
43
  },
43
44
  {
@@ -58,6 +59,7 @@ export const DETECTION_RULES = [
58
59
  requireAny: ['drizzle.config.ts', 'drizzle.config.js'],
59
60
  excludeIf: ['drizzle/migrations'],
60
61
  defaultSkill: 'migrate@1',
62
+ defaultCommand: { exec: 'drizzle-kit', args: ['push'] },
61
63
  promptOnSelect: true,
62
64
  },
63
65
  {
@@ -76,6 +78,9 @@ export const DETECTION_RULES = [
76
78
  confidence: 'high',
77
79
  requireAll: ['go.mod', 'migrate'],
78
80
  defaultSkill: 'migrate@1',
81
+ // Conventional invocation; users with non-standard layouts will edit
82
+ // the generated stack.md (e.g. different -path or DSN flag).
83
+ defaultCommand: { exec: 'migrate', args: ['-database', '$DATABASE_URL', '-path', 'migrations', 'up'] },
79
84
  promptOnSelect: false,
80
85
  },
81
86
  {
@@ -132,6 +137,7 @@ export const DETECTION_RULES = [
132
137
  requireAll: [],
133
138
  requireAny: ['ormconfig.json', 'ormconfig.ts', 'ormconfig.js', 'data-source.ts'],
134
139
  defaultSkill: 'migrate@1',
140
+ defaultCommand: { exec: 'typeorm', args: ['migration:run'] },
135
141
  promptOnSelect: true,
136
142
  },
137
143
  {
@@ -12,6 +12,7 @@
12
12
  import * as fs from 'node:fs';
13
13
  import * as path from 'node:path';
14
14
  import Ajv from 'ajv';
15
+ import addFormats from 'ajv-formats';
15
16
  import * as yaml from 'js-yaml';
16
17
  import { requirePackageRoot } from "../../cli/_pkg-root.js";
17
18
  // Resolve presets/ relative to the canonical package root so this works under
@@ -28,6 +29,12 @@ function loadStableIds() {
28
29
  }
29
30
  function buildValidator() {
30
31
  const ajv = new Ajv({ strict: false, allErrors: true });
32
+ // Register standard formats (date-time, uri, email, etc.) so the schema's
33
+ // `format: "date-time"` constraint is actually validated rather than warned-
34
+ // about-and-ignored on every CLI invocation. Pre-5.2.3 every command printed:
35
+ // "unknown format \"date-time\" ignored in schema at path #/.../detected_at"
36
+ // — twice, since the validator is built lazily in two paths.
37
+ addFormats(ajv);
31
38
  const stableIds = loadStableIds();
32
39
  // Custom keyword: validates that the value is one of the registered stable IDs.
33
40
  ajv.addKeyword({
@@ -47,7 +54,20 @@ function buildValidator() {
47
54
  }
48
55
  return ajv.compile(schema);
49
56
  }
50
- const validate = buildValidator();
57
+ // Lazy-init: previously the validator was built at module load, which meant
58
+ // every `claude-autopilot --version` (or any CLI invocation that imports the
59
+ // migrate module via dispatch chain) eagerly read presets/aliases.lock.json
60
+ // + presets/schemas/migrate.schema.json. Missing files crashed the entire
61
+ // CLI with a stack trace before the user-facing entry point even started.
62
+ // Rebuilding on first use also keeps tests that don't touch validation
63
+ // from paying the AJV compile cost. Caught by the tombstone-bin test
64
+ // (`does not leak a node stack trace when claude-autopilot is unreachable`).
65
+ let _validate;
66
+ function getValidator() {
67
+ if (_validate === undefined)
68
+ _validate = buildValidator();
69
+ return _validate;
70
+ }
51
71
  function commandsEqual(a, b) {
52
72
  return JSON.stringify(a) === JSON.stringify(b);
53
73
  }
@@ -92,6 +112,7 @@ export function validateStackMd(yamlSource) {
92
112
  }],
93
113
  };
94
114
  }
115
+ const validate = getValidator();
95
116
  const ok = validate(parsed);
96
117
  const schemaErrors = ok ? [] : ajvErrorsToValidationErrors(validate.errors ?? []);
97
118
  const crossFieldErrors = ok ? checkDevCommandReuse(parsed) : [];
@@ -1,10 +1,14 @@
1
1
  import type { Finding, FixAttempt, FixStatus } from '../findings/types.ts';
2
2
  import type { GuardrailConfig } from '../config/types.ts';
3
3
  import type { ReviewEngine } from '../../adapters/review-engine/types.ts';
4
+ export interface StaticRuleContext {
5
+ config?: GuardrailConfig;
6
+ engine?: ReviewEngine;
7
+ }
4
8
  export interface StaticRule {
5
9
  name: string;
6
10
  severity: 'critical' | 'warning' | 'note';
7
- check(touchedFiles: string[], config?: Record<string, unknown>): Promise<Finding[]>;
11
+ check(touchedFiles: string[], ctx?: StaticRuleContext): Promise<Finding[]>;
8
12
  autofix?(finding: Finding): Promise<FixStatus>;
9
13
  }
10
14
  export interface StaticRulesPhaseInput {
@@ -42,13 +42,10 @@ export async function runStaticRulesPhase(input) {
42
42
  return { phase: 'static-rules', status, findings: preFixFindings, fixAttempts, durationMs: Date.now() - start };
43
43
  }
44
44
  async function runAllChecks(rules, files, config, engine) {
45
- const ruleConfig = {
46
- ...(config ? config : {}),
47
- _engine: engine,
48
- };
45
+ const ctx = { config, engine };
49
46
  const all = [];
50
47
  for (const rule of rules)
51
- all.push(...(await rule.check(files, ruleConfig)));
48
+ all.push(...(await rule.check(files, ctx)));
52
49
  return all;
53
50
  }
54
51
  function findRuleForFinding(rules, finding) {
@@ -0,0 +1,88 @@
1
+ /** Default Layer 2 reserve when none is configured. Conservative — phases
2
+ * without an `estimateCost` are assumed to consume at least this much,
3
+ * which keeps the cap from "failing open" the moment a phase forgets to
4
+ * declare its cost shape. */
5
+ export declare const DEFAULT_CONSERVATIVE_PHASE_RESERVE_USD = 5;
6
+ export interface BudgetConfig {
7
+ /** Total run cap (USD). Hard stop. Required — phases that don't want
8
+ * budget enforcement should not pass a `BudgetConfig` at all. */
9
+ perRunUSD: number;
10
+ /** Per-phase cap (USD). Phases that haven't declared `estimateCost`
11
+ * still pay the conservativePhaseReserve under Layer 2. Optional. */
12
+ perPhaseUSD?: number;
13
+ /** Bounded recursion for council synthesizer. Wired in
14
+ * `src/core/council/runner.ts`; no effect inside `runPhase`. */
15
+ councilMaxRecursionDepth?: number;
16
+ /** Bounded autopilot self-eat rounds (per spec). Reserved field —
17
+ * consumed by the autopilot orchestrator, not the runner. */
18
+ bgAutopilotMaxRoundsPerSelfEat?: number;
19
+ /** Used by Layer 2 (mandatory runtime guard) when a phase has no
20
+ * `estimateCost` — represents the "we don't know how big this gets,
21
+ * reserve at least this much from the cap" floor. Defaults to
22
+ * `DEFAULT_CONSERVATIVE_PHASE_RESERVE_USD` when omitted. */
23
+ conservativePhaseReserveUSD?: number;
24
+ /** v6.2.0 — budget scope. `'phase'` (default) keeps the legacy
25
+ * per-phase semantics where each `runPhase` invocation reasons against
26
+ * its own phase budget (back-compat for single-phase wrappers like
27
+ * `runPhaseWithLifecycle`). `'run'` is the orchestrator's
28
+ * cross-phase mode: the actualSoFar reservoir already sums every
29
+ * prior `phase.cost` event in the run, so `perRunUSD` is policed
30
+ * monotonically against the WHOLE pipeline's spend.
31
+ *
32
+ * In practice the policy math is identical between the two scopes —
33
+ * Layer 1 + Layer 2 both consume `actualSoFarUSD` regardless. The
34
+ * scope flag exists so the `budget.check` event tells observers
35
+ * which mode produced the decision (so a CI dashboard can attribute
36
+ * a reject to "run scope" vs "single-phase scope") and so future
37
+ * policy changes (e.g. divergent perPhase reserves under run scope)
38
+ * have a place to land without an event-shape break.
39
+ *
40
+ * Per spec docs/specs/v6.2-multi-phase-orchestrator.md "Budget
41
+ * enforcement": `checkPhaseBudget` gains `scope: 'phase' | 'run'`
42
+ * (default 'phase' for back-compat). Orchestrator passes
43
+ * `scope: 'run'`; per-phase callers keep the default. */
44
+ scope?: 'phase' | 'run';
45
+ }
46
+ /** The decision the runner consumes. Mirrors the `budget.check` event
47
+ * payload one-to-one so wiring is trivial. */
48
+ export interface BudgetCheck {
49
+ decision: 'proceed' | 'pause' | 'hard-fail';
50
+ phase: string;
51
+ phaseIdx: number;
52
+ /** `estimate.high` from the phase's `estimateCost` if it returned a
53
+ * value; null when the phase doesn't implement estimateCost. */
54
+ estimatedHigh: number | null;
55
+ actualSoFar: number;
56
+ /** The reserve the policy deducted against `perRunUSD` for this phase
57
+ * (the larger of `estimate.high` and `conservativePhaseReserveUSD`). */
58
+ reserveApplied: number;
59
+ /** USD remaining under `perRunUSD` after `actualSoFar` + the larger of
60
+ * `estimatedHigh` and `reserveApplied`. May be negative on hard-fail. */
61
+ capRemaining: number;
62
+ reason: string;
63
+ /** v6.2.0 — which scope produced the decision. Echoes `BudgetConfig.scope`
64
+ * back into the `budget.check` event so observers can attribute
65
+ * cross-phase rejections to the orchestrator vs single-phase wrappers
66
+ * passing the legacy default. */
67
+ scope: 'phase' | 'run';
68
+ }
69
+ export interface CheckPhaseBudgetOpts {
70
+ budget: BudgetConfig;
71
+ phaseName: string;
72
+ phaseIdx: number;
73
+ /** What `RunPhase.estimateCost(input)` returned, or null if absent. */
74
+ estimatedCost: {
75
+ lowUSD: number;
76
+ highUSD: number;
77
+ } | null;
78
+ /** Sum of every prior `phase.cost` event in the run, in USD. */
79
+ actualSoFarUSD: number;
80
+ /** When true, a `pause` decision becomes `hard-fail` (CI / `--json`
81
+ * mode can't prompt for human approval). */
82
+ nonInteractive: boolean;
83
+ }
84
+ /** Policy decision for a single about-to-run phase. Pure — no IO. The
85
+ * caller (`runPhase`) is responsible for emitting the `budget.check`
86
+ * event with this payload and acting on the decision. */
87
+ export declare function checkPhaseBudget(opts: CheckPhaseBudgetOpts): BudgetCheck;
88
+ //# sourceMappingURL=budget.d.ts.map