@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
|
@@ -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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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;
|
package/dist/src/core/errors.js
CHANGED
|
@@ -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
|
-
|
|
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[],
|
|
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
|
|
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,
|
|
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
|