@delegance/claude-autopilot 5.2.1 → 5.5.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 +97 -0
- package/README.md +49 -17
- package/dist/src/adapters/council/claude.js +2 -1
- package/dist/src/adapters/council/openai.js +2 -1
- 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 +13 -0
- package/dist/src/adapters/deploy/index.js +45 -0
- package/dist/src/adapters/deploy/types.d.ts +157 -0
- package/dist/src/adapters/deploy/types.js +15 -0
- package/dist/src/adapters/deploy/vercel.d.ts +127 -0
- package/dist/src/adapters/deploy/vercel.js +446 -0
- package/dist/src/adapters/review-engine/claude.js +2 -1
- package/dist/src/adapters/review-engine/codex.js +2 -1
- 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/costs.js +4 -2
- package/dist/src/cli/deploy.d.ts +71 -0
- package/dist/src/cli/deploy.js +514 -0
- package/dist/src/cli/index.js +91 -3
- package/dist/src/cli/pr.js +8 -2
- package/dist/src/cli/preflight.js +76 -1
- package/dist/src/core/config/schema.d.ts +34 -0
- package/dist/src/core/config/schema.js +18 -0
- package/dist/src/core/config/types.d.ts +6 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +1 -0
- package/dist/src/core/migrate/detector-rules.js +6 -0
- package/dist/src/core/migrate/schema-validator.js +7 -0
- package/dist/src/core/persist/cost-log.js +8 -0
- package/package.json +8 -5
- package/scripts/autoregress.ts +2 -1
- package/skills/migrate/SKILL.md +193 -47
package/dist/src/cli/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import { runMigrateV4 } from "./migrate-v4.js";
|
|
|
30
30
|
import { runMigrateDoctor } from "./migrate-doctor.js";
|
|
31
31
|
import { initMigrate, NoMigrationToolDetectedError } from "./init-migrate.js";
|
|
32
32
|
import { dispatch as runMigrateDispatch } from "../core/migrate/dispatcher.js";
|
|
33
|
+
import { runDeploy, runDeployRollback, runDeployStatus } from "./deploy.js";
|
|
33
34
|
import { findPackageRoot } from "./_pkg-root.js";
|
|
34
35
|
import { GuardrailError } from "../core/errors.js";
|
|
35
36
|
// Format unhandled errors as a one-line user-facing message instead of dumping a
|
|
@@ -159,8 +160,8 @@ These are aliases for the flat subcommands; they still work without the 'advance
|
|
|
159
160
|
}
|
|
160
161
|
args.shift(); // drop 'advanced'
|
|
161
162
|
}
|
|
162
|
-
const SUBCOMMANDS = ['init', 'run', 'scan', 'report', 'explain', 'ignore', 'ci', 'pr', 'fix', 'costs', 'watch', 'hook', 'autoregress', 'baseline', 'triage', 'lsp', 'worker', 'mcp', 'test-gen', 'pr-desc', 'doctor', 'preflight', 'setup', 'council', 'migrate-v4', 'migrate', 'migrate-doctor', 'brainstorm', 'help', '--help', '-h'];
|
|
163
|
-
const VALUE_FLAGS = ['base', 'config', 'files', 'format', 'output', 'debounce', 'ask', 'focus', 'fail-on', 'note', 'reason', 'expires', 'profile', 'severity', 'prompt', 'context-file', 'path'];
|
|
163
|
+
const SUBCOMMANDS = ['init', 'run', 'scan', 'report', 'explain', 'ignore', 'ci', 'pr', 'fix', 'costs', 'watch', 'hook', 'autoregress', 'baseline', 'triage', 'lsp', 'worker', 'mcp', 'test-gen', 'pr-desc', 'doctor', 'preflight', 'setup', 'council', 'migrate-v4', 'migrate', 'migrate-doctor', 'deploy', 'brainstorm', 'help', '--help', '-h'];
|
|
164
|
+
const VALUE_FLAGS = ['base', 'config', 'files', 'format', 'output', 'debounce', 'ask', 'focus', 'fail-on', 'note', 'reason', 'expires', 'profile', 'severity', 'prompt', 'context-file', 'path', 'adapter', 'ref', 'sha'];
|
|
164
165
|
// Bare invocation — no subcommand, no flags → show welcome guide
|
|
165
166
|
if (args.length === 0) {
|
|
166
167
|
const hasKey = !!(process.env.ANTHROPIC_API_KEY || process.env.GEMINI_API_KEY ||
|
|
@@ -257,6 +258,7 @@ Commands:
|
|
|
257
258
|
init Scaffold guardrail.config.yaml + auto-detect migrate stack (writes .autopilot/stack.md)
|
|
258
259
|
migrate Run database migrations via the stack-aware dispatcher
|
|
259
260
|
migrate doctor Validate .autopilot/stack.md and skill manifests (alias: migrate-doctor)
|
|
261
|
+
deploy Deploy via configured adapter (vercel | generic) — also: rollback, status
|
|
260
262
|
setup Auto-detect stack, write config, install pre-push hook
|
|
261
263
|
doctor Check prerequisites (alias: preflight)
|
|
262
264
|
preflight Check prerequisites (alias: doctor)
|
|
@@ -319,6 +321,21 @@ Options (migrate):
|
|
|
319
321
|
|
|
320
322
|
Options (migrate doctor / migrate-doctor):
|
|
321
323
|
--fix Apply auto-fixable mutations (legacy stack.md, skills/migrate/, schema_version)
|
|
324
|
+
|
|
325
|
+
Options (deploy):
|
|
326
|
+
--adapter <vercel|generic> Override deploy.adapter from config
|
|
327
|
+
--config <path> Path to config file
|
|
328
|
+
--ref <ref> Git ref (branch / tag) to deploy
|
|
329
|
+
--sha <commit> Specific commit SHA to deploy
|
|
330
|
+
--watch Stream build logs to stderr in real time (Vercel only)
|
|
331
|
+
--to <deploy-id> Target deploy ID for 'deploy rollback'
|
|
332
|
+
--pr <n> Post upserting deploy summary comment on the PR
|
|
333
|
+
|
|
334
|
+
Subcommands (deploy):
|
|
335
|
+
deploy Deploy via configured adapter
|
|
336
|
+
deploy rollback Roll back to previous prod deploy
|
|
337
|
+
deploy rollback --to <id> Roll back to a specific deploy
|
|
338
|
+
deploy status Show current prod + last 5 builds
|
|
322
339
|
`);
|
|
323
340
|
}
|
|
324
341
|
switch (subcommand) {
|
|
@@ -638,7 +655,23 @@ switch (subcommand) {
|
|
|
638
655
|
break;
|
|
639
656
|
}
|
|
640
657
|
case 'mcp': {
|
|
641
|
-
|
|
658
|
+
let runMcp;
|
|
659
|
+
try {
|
|
660
|
+
({ runMcp } = await import("./mcp.js"));
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
const code = err.code;
|
|
664
|
+
const msg = err.message ?? String(err);
|
|
665
|
+
// The mcp module imports @modelcontextprotocol/sdk at the top — if the
|
|
666
|
+
// package was installed with --omit=optional the dynamic import surfaces
|
|
667
|
+
// ERR_MODULE_NOT_FOUND naming the SDK. Translate to a friendly hint.
|
|
668
|
+
if ((code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') && /modelcontextprotocol/.test(msg)) {
|
|
669
|
+
console.error('\x1b[31m[claude-autopilot] mcp subcommand requires @modelcontextprotocol/sdk\x1b[0m');
|
|
670
|
+
console.error(' install: npm install @modelcontextprotocol/sdk');
|
|
671
|
+
process.exit(1);
|
|
672
|
+
}
|
|
673
|
+
throw err;
|
|
674
|
+
}
|
|
642
675
|
const configPath = flag('config');
|
|
643
676
|
await runMcp({ cwd: process.cwd(), configPath });
|
|
644
677
|
break;
|
|
@@ -705,6 +738,61 @@ switch (subcommand) {
|
|
|
705
738
|
await runMigrateDoctorCLI();
|
|
706
739
|
break;
|
|
707
740
|
}
|
|
741
|
+
case 'deploy': {
|
|
742
|
+
const config = flag('config');
|
|
743
|
+
const adapterArg = flag('adapter');
|
|
744
|
+
if (adapterArg && !['vercel', 'generic'].includes(adapterArg)) {
|
|
745
|
+
console.error(`\x1b[31m[claude-autopilot] --adapter must be "vercel" or "generic"\x1b[0m`);
|
|
746
|
+
process.exit(1);
|
|
747
|
+
}
|
|
748
|
+
// Phase 3 — `deploy rollback` and `deploy status` subverbs. The first
|
|
749
|
+
// non-flag positional after `deploy` selects the verb. The historic
|
|
750
|
+
// `claude-autopilot deploy` (no subverb) keeps calling runDeploy.
|
|
751
|
+
const subverb = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
752
|
+
if (subverb === 'rollback') {
|
|
753
|
+
const to = flag('to');
|
|
754
|
+
const code = await runDeployRollback({
|
|
755
|
+
configPath: config,
|
|
756
|
+
adapterOverride: adapterArg,
|
|
757
|
+
to,
|
|
758
|
+
});
|
|
759
|
+
process.exit(code);
|
|
760
|
+
}
|
|
761
|
+
if (subverb === 'status') {
|
|
762
|
+
const code = await runDeployStatus({
|
|
763
|
+
configPath: config,
|
|
764
|
+
adapterOverride: adapterArg,
|
|
765
|
+
});
|
|
766
|
+
process.exit(code);
|
|
767
|
+
}
|
|
768
|
+
if (subverb !== undefined) {
|
|
769
|
+
console.error(`\x1b[31m[claude-autopilot] unknown deploy subverb "${subverb}" — valid: rollback, status\x1b[0m`);
|
|
770
|
+
process.exit(1);
|
|
771
|
+
}
|
|
772
|
+
const ref = flag('ref');
|
|
773
|
+
const commitSha = flag('sha');
|
|
774
|
+
const watch = boolFlag('watch');
|
|
775
|
+
const prRaw = flag('pr');
|
|
776
|
+
let prNum;
|
|
777
|
+
if (prRaw !== undefined) {
|
|
778
|
+
const n = parseInt(prRaw, 10);
|
|
779
|
+
if (Number.isNaN(n) || n <= 0) {
|
|
780
|
+
console.error(`\x1b[31m[claude-autopilot] --pr must be a positive integer, got "${prRaw}"\x1b[0m`);
|
|
781
|
+
process.exit(1);
|
|
782
|
+
}
|
|
783
|
+
prNum = n;
|
|
784
|
+
}
|
|
785
|
+
const code = await runDeploy({
|
|
786
|
+
configPath: config,
|
|
787
|
+
adapterOverride: adapterArg,
|
|
788
|
+
ref,
|
|
789
|
+
commitSha,
|
|
790
|
+
watch,
|
|
791
|
+
pr: prNum,
|
|
792
|
+
});
|
|
793
|
+
process.exit(code);
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
708
796
|
case 'brainstorm': {
|
|
709
797
|
// `brainstorm` is the front of the pipeline and is implemented as a Claude
|
|
710
798
|
// Code skill (superpowers:brainstorming → autopilot), not a standalone CLI.
|
package/dist/src/cli/pr.js
CHANGED
|
@@ -25,9 +25,15 @@ function gitFetch(remote, ref, cwd) {
|
|
|
25
25
|
export async function runPr(options = {}) {
|
|
26
26
|
const cwd = options.cwd ?? process.cwd();
|
|
27
27
|
const configPath = options.configPath ?? path.join(cwd, 'guardrail.config.yaml');
|
|
28
|
+
// 5.2.2 — pr previously hard-failed when guardrail.config.yaml was missing
|
|
29
|
+
// ("not found at <path>"). The first-run UX surprised users running
|
|
30
|
+
// `claude-autopilot pr <n>` on a fresh repo: setup hadn't been invoked, and
|
|
31
|
+
// the error didn't say to invoke it. Now matches `run`'s behavior — defer
|
|
32
|
+
// config-loading to the underlying runCommand, which auto-detects stack +
|
|
33
|
+
// testCommand when the file is missing.
|
|
28
34
|
if (!fs.existsSync(configPath)) {
|
|
29
|
-
console.
|
|
30
|
-
|
|
35
|
+
console.log(fmt('dim', `[pr] guardrail.config.yaml not found — auto-detecting from stack signals.`));
|
|
36
|
+
console.log(fmt('dim', ` Run \`claude-autopilot setup\` first to commit a config.`));
|
|
31
37
|
}
|
|
32
38
|
// Resolve PR number
|
|
33
39
|
let prNumber = options.prNumber;
|
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { runSafe } from "../core/shell.js";
|
|
6
6
|
import { detectLLMKey, loadEnvFile, LLM_KEY_NAMES } from "../core/detect/llm-key.js";
|
|
7
7
|
import { findPackageRoot } from "./_pkg-root.js";
|
|
8
|
+
import { isSdkInstalled } from "../adapters/sdk-loader.js";
|
|
8
9
|
const PASS = '\x1b[32m✓\x1b[0m';
|
|
9
10
|
const FAIL = '\x1b[31m✗\x1b[0m';
|
|
10
11
|
const WARN = '\x1b[33m!\x1b[0m';
|
|
@@ -29,6 +30,39 @@ function skillRoots() {
|
|
|
29
30
|
roots.push(path.join(home, '.claude', 'plugins'));
|
|
30
31
|
return roots.filter(p => fs.existsSync(p));
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Reads `deploy.adapter` from guardrail.config.yaml without pulling in the
|
|
35
|
+
* full config loader (which validates against a JSON schema and would noise
|
|
36
|
+
* up the doctor output). Returns `null` if the config or field is absent.
|
|
37
|
+
*/
|
|
38
|
+
function readDeployAdapter(configPath) {
|
|
39
|
+
if (!fs.existsSync(configPath))
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
const text = fs.readFileSync(configPath, 'utf8');
|
|
43
|
+
// Match `deploy:` block then look for `adapter:` two lines down. Cheap
|
|
44
|
+
// line-based parse — avoids js-yaml dependency in the doctor path.
|
|
45
|
+
const lines = text.split(/\r?\n/);
|
|
46
|
+
let inDeploy = false;
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (/^deploy\s*:\s*$/.test(line)) {
|
|
49
|
+
inDeploy = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (inDeploy && /^\S/.test(line))
|
|
53
|
+
inDeploy = false; // dedented out of block
|
|
54
|
+
if (inDeploy) {
|
|
55
|
+
const m = line.match(/^\s+adapter\s*:\s*['"]?([a-zA-Z0-9_-]+)['"]?\s*$/);
|
|
56
|
+
if (m)
|
|
57
|
+
return m[1] ?? null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
32
66
|
export function findMissingSuperpowersSkills() {
|
|
33
67
|
// Traverse each root once, collect all discovered skill names, then diff against
|
|
34
68
|
// the required set. Previous implementation did N × roots separate recursive walks.
|
|
@@ -173,7 +207,48 @@ export async function runDoctor() {
|
|
|
173
207
|
? 'git user.name / user.email not set — commits will fail.'
|
|
174
208
|
: undefined,
|
|
175
209
|
});
|
|
176
|
-
// 9.
|
|
210
|
+
// 9. LLM SDK install state — surfaces which providers are usable. After
|
|
211
|
+
// the v5.5 lazy-load refactor, three SDKs moved to optionalDependencies;
|
|
212
|
+
// `--omit=optional` users may have removed them. This check shows users
|
|
213
|
+
// which providers will work without `npm install <sdk>`.
|
|
214
|
+
const sdkChecks = [
|
|
215
|
+
{ pkg: '@anthropic-ai/sdk', provider: 'claude' },
|
|
216
|
+
{ pkg: 'openai', provider: 'openai/codex' },
|
|
217
|
+
{ pkg: '@google/generative-ai', provider: 'gemini' },
|
|
218
|
+
];
|
|
219
|
+
const installed = [];
|
|
220
|
+
const missing = [];
|
|
221
|
+
for (const { pkg } of sdkChecks) {
|
|
222
|
+
if (await isSdkInstalled(pkg))
|
|
223
|
+
installed.push(pkg);
|
|
224
|
+
else
|
|
225
|
+
missing.push(pkg);
|
|
226
|
+
}
|
|
227
|
+
checks.push({
|
|
228
|
+
name: `LLM SDKs installed (${installed.length}/${sdkChecks.length})`,
|
|
229
|
+
result: installed.length === 0 ? 'fail' : missing.length > 0 ? 'warn' : 'pass',
|
|
230
|
+
message: installed.length === 0
|
|
231
|
+
? `No LLM SDKs found. Install at least one: ${sdkChecks.map(s => s.pkg).join(', ')}`
|
|
232
|
+
: missing.length > 0
|
|
233
|
+
? `Missing (run npm install <pkg> to enable): ${missing.join(', ')}`
|
|
234
|
+
: undefined,
|
|
235
|
+
});
|
|
236
|
+
// 10. Vercel deploy adapter auth (Phase 6 of v5.4 spec). Detects
|
|
237
|
+
// `deploy.adapter: vercel` in guardrail.config.yaml and verifies the
|
|
238
|
+
// auth token is set. Warn-not-fail because users without deploy
|
|
239
|
+
// configured don't care.
|
|
240
|
+
const deployAdapter = readDeployAdapter(configYaml);
|
|
241
|
+
if (deployAdapter === 'vercel') {
|
|
242
|
+
const token = process.env.VERCEL_TOKEN ?? envVars['VERCEL_TOKEN'];
|
|
243
|
+
checks.push({
|
|
244
|
+
name: 'VERCEL_TOKEN (deploy adapter: vercel)',
|
|
245
|
+
result: token ? 'pass' : 'warn',
|
|
246
|
+
message: token
|
|
247
|
+
? undefined
|
|
248
|
+
: 'deploy.adapter is "vercel" but VERCEL_TOKEN is not set. Generate one at https://vercel.com/account/tokens',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// 11. Superpowers plugin — required for pipeline phases, optional for review-only use
|
|
177
252
|
const missingSkills = findMissingSuperpowersSkills();
|
|
178
253
|
const allSkillsFound = missingSkills.length === 0;
|
|
179
254
|
checks.push({
|
|
@@ -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: {
|
|
@@ -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: {
|
|
@@ -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,6 +93,11 @@ 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>;
|
|
@@ -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';
|
|
2
2
|
export interface GuardrailErrorOptions {
|
|
3
3
|
code: ErrorCode;
|
|
4
4
|
retryable?: boolean;
|
package/dist/src/core/errors.js
CHANGED
|
@@ -3,6 +3,7 @@ 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,
|
|
6
7
|
};
|
|
7
8
|
export class GuardrailError extends Error {
|
|
8
9
|
code;
|
|
@@ -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({
|
|
@@ -3,6 +3,14 @@ import * as path from 'node:path';
|
|
|
3
3
|
const CACHE_DIR = '.guardrail-cache';
|
|
4
4
|
const LOG_FILE = 'costs.jsonl';
|
|
5
5
|
export function appendCostLog(cwd, entry) {
|
|
6
|
+
// Skip no-op entries that only pollute the report — runs that didn't
|
|
7
|
+
// actually invoke an LLM (dry-runs, no-findings paths, "no code files at
|
|
8
|
+
// path" early returns). Without this filter, randai's costs.jsonl picked up
|
|
9
|
+
// 6 zero-token zero-duration entries from setup-flow scans, drowning the
|
|
10
|
+
// 4 real review entries in `claude-autopilot costs` output.
|
|
11
|
+
if (entry.inputTokens === 0 && entry.outputTokens === 0 && entry.costUSD === 0) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
6
14
|
// Cost log is observability, not a contract. A failed write (read-only FS,
|
|
7
15
|
// full disk, permission error) must NEVER block the caller — every callsite
|
|
8
16
|
// calls this *after* its primary output is emitted, and a throw here would
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "5.2
|
|
3
|
+
"version": "5.5.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Autonomous development pipeline for Claude Code: brainstorm → spec → plan → implement → migrate → validate → PR → review → merge. Multi-model, local-first, every phase a skill you can intervene in.",
|
|
6
6
|
"keywords": [
|
|
@@ -58,18 +58,21 @@
|
|
|
58
58
|
"autoregress": "tsx scripts/autoregress.ts"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@anthropic-ai/sdk": "^0.91.1",
|
|
62
|
-
"@google/generative-ai": "^0.24.1",
|
|
63
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
64
61
|
"ajv": "^8",
|
|
62
|
+
"ajv-formats": "^3.0.1",
|
|
65
63
|
"dotenv": ">=16",
|
|
66
64
|
"js-yaml": "^4",
|
|
67
65
|
"minimatch": ">=9",
|
|
68
|
-
"openai": ">=4",
|
|
69
66
|
"proper-lockfile": "^4.1.2",
|
|
70
67
|
"shell-quote": "^1.8.3",
|
|
71
68
|
"tsx": ">=4"
|
|
72
69
|
},
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"@anthropic-ai/sdk": "^0.91.1",
|
|
72
|
+
"@google/generative-ai": "^0.24.1",
|
|
73
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
74
|
+
"openai": ">=4"
|
|
75
|
+
},
|
|
73
76
|
"devDependencies": {
|
|
74
77
|
"@types/js-yaml": "^4",
|
|
75
78
|
"@types/node": "^25",
|
package/scripts/autoregress.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as os from 'node:os';
|
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { selectSnapshots } from './snapshots/impact-selector.ts';
|
|
9
|
-
import
|
|
9
|
+
import { loadOpenAI } from '../src/adapters/sdk-loader.ts';
|
|
10
10
|
import { buildImportMap } from './snapshots/import-scanner.ts';
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -230,6 +230,7 @@ async function cmdGenerate(args: string[]): Promise<number> {
|
|
|
230
230
|
|
|
231
231
|
console.log(`[autoregress generate] generating snapshots for ${srcFiles.length} file(s)`);
|
|
232
232
|
|
|
233
|
+
const OpenAI = await loadOpenAI();
|
|
233
234
|
const client = new OpenAI({ apiKey });
|
|
234
235
|
let sourceCommit = 'unknown';
|
|
235
236
|
try {
|