@delegance/claude-autopilot 2.5.0 → 5.0.0-alpha.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 +63 -0
- package/README.md +169 -106
- package/bin/_launcher.js +77 -0
- package/bin/claude-autopilot.js +3 -0
- package/bin/guardrail.js +3 -0
- package/package.json +23 -9
- package/presets/generic/guardrail.config.yaml +35 -0
- package/presets/generic/stack.md +40 -0
- package/presets/nextjs-supabase/{autopilot.config.yaml → guardrail.config.yaml} +7 -0
- package/scripts/autoregress.ts +27 -11
- package/skills/autopilot/SKILL.md +170 -0
- package/skills/claude-autopilot.md +80 -0
- package/skills/guardrail.md +39 -0
- package/skills/migrate/SKILL.md +83 -0
- package/src/adapters/council/claude.ts +41 -0
- package/src/adapters/council/openai.ts +40 -0
- package/src/adapters/council/types.ts +7 -0
- package/src/adapters/loader.ts +7 -7
- package/src/adapters/review-engine/auto.ts +2 -2
- package/src/adapters/review-engine/claude.ts +9 -11
- package/src/adapters/review-engine/codex.ts +9 -11
- package/src/adapters/review-engine/gemini.ts +9 -11
- package/src/adapters/review-engine/openai-compatible.ts +10 -12
- package/src/adapters/review-engine/parse-output.ts +32 -6
- package/src/adapters/review-engine/prompt-builder.ts +19 -0
- package/src/adapters/review-engine/types.ts +1 -1
- package/src/adapters/vcs-host/commit-status.ts +39 -0
- package/src/adapters/vcs-host/github.ts +2 -2
- package/src/cli/baseline.ts +125 -0
- package/src/cli/ci.ts +11 -8
- package/src/cli/costs.ts +2 -2
- package/src/cli/council.ts +96 -0
- package/src/cli/detector.ts +21 -5
- package/src/cli/explain.ts +197 -0
- package/src/cli/fix.ts +173 -111
- package/src/cli/hook.ts +72 -27
- package/src/cli/ignore-helper.ts +116 -0
- package/src/cli/index.ts +355 -31
- package/src/cli/init.ts +12 -12
- package/src/cli/lsp.ts +200 -0
- package/src/cli/mcp.ts +206 -0
- package/src/cli/pr-comment.ts +5 -5
- package/src/cli/pr-desc.ts +168 -0
- package/src/cli/pr-review-comments.ts +3 -3
- package/src/cli/pr.ts +76 -0
- package/src/cli/preflight.ts +109 -32
- package/src/cli/report.ts +186 -0
- package/src/cli/run.ts +140 -36
- package/src/cli/scan.ts +233 -0
- package/src/cli/setup.ts +121 -15
- package/src/cli/test-gen.ts +125 -0
- package/src/cli/triage.ts +137 -0
- package/src/cli/watch.ts +52 -31
- package/src/cli/worker.ts +109 -0
- package/src/core/cache/review-cache.ts +2 -2
- package/src/core/chunking/index.ts +2 -2
- package/src/core/config/loader.ts +10 -10
- package/src/core/config/preset-resolver.ts +6 -6
- package/src/core/config/schema.ts +103 -2
- package/src/core/config/types.ts +57 -2
- package/src/core/council/config.ts +71 -0
- package/src/core/council/context.ts +17 -0
- package/src/core/council/runner.ts +83 -0
- package/src/core/council/types.ts +45 -0
- package/src/core/detect/llm-key.ts +89 -0
- package/src/core/detect/workspaces.ts +103 -0
- package/src/core/errors.ts +4 -4
- package/src/core/fix/generator.ts +149 -0
- package/src/core/ignore/index.ts +4 -4
- package/src/core/mcp/concurrency.ts +16 -0
- package/src/core/mcp/handlers/fix-finding.ts +126 -0
- package/src/core/mcp/handlers/get-capabilities.ts +62 -0
- package/src/core/mcp/handlers/get-findings.ts +36 -0
- package/src/core/mcp/handlers/review-diff.ts +65 -0
- package/src/core/mcp/handlers/scan-files.ts +65 -0
- package/src/core/mcp/handlers/validate-fix.ts +41 -0
- package/src/core/mcp/run-store.ts +85 -0
- package/src/core/mcp/workspace.ts +35 -0
- package/src/core/persist/baseline.ts +112 -0
- package/src/core/persist/cost-log.ts +1 -1
- package/src/core/persist/findings-cache.ts +1 -1
- package/src/core/persist/triage.ts +112 -0
- package/src/core/phases/static-rules.ts +18 -5
- package/src/core/pipeline/review-phase.ts +65 -26
- package/src/core/pipeline/run.ts +42 -10
- package/src/core/runtime/lock.ts +2 -2
- package/src/core/runtime/state.ts +2 -2
- package/src/core/schema-alignment/detector.ts +59 -0
- package/src/core/schema-alignment/extractor/index.ts +24 -0
- package/src/core/schema-alignment/extractor/prisma.ts +21 -0
- package/src/core/schema-alignment/extractor/sql.ts +99 -0
- package/src/core/schema-alignment/llm-check.ts +91 -0
- package/src/core/schema-alignment/scanner.ts +107 -0
- package/src/core/schema-alignment/types.ts +43 -0
- package/src/core/shell.ts +3 -3
- package/src/core/static-rules/registry.ts +17 -8
- package/src/core/static-rules/rules/brand-tokens.ts +145 -0
- package/src/core/static-rules/rules/hardcoded-secrets.ts +27 -1
- package/src/core/static-rules/rules/insecure-redirect.ts +67 -0
- package/src/core/static-rules/rules/missing-auth.ts +70 -0
- package/src/core/static-rules/rules/schema-alignment.ts +132 -0
- package/src/core/static-rules/rules/sql-injection.ts +71 -0
- package/src/core/static-rules/rules/ssrf.ts +63 -0
- package/src/core/static-rules/tailwind-extractor.ts +38 -0
- package/src/core/test-gen/coverage-analyzer.ts +93 -0
- package/src/core/test-gen/framework-detector.ts +21 -0
- package/src/core/test-gen/test-writer.ts +33 -0
- package/src/core/ui/design-context-loader.ts +87 -0
- package/src/core/worker/client.ts +46 -0
- package/src/core/worker/lockfile.ts +38 -0
- package/src/core/worker/server.ts +81 -0
- package/src/formatters/junit.ts +52 -0
- package/src/formatters/sarif.ts +2 -2
- package/src/index.ts +1 -2
- package/tests/snapshots/baselines/src-formatters-sarif.json +4 -4
- package/tests/snapshots/index.json +3 -3
- package/tests/snapshots/src-formatters-sarif.snap.ts +1 -1
- package/tests/snapshots/src-snapshots-impact-selector.snap.ts +3 -3
- package/tests/snapshots/src-snapshots-import-scanner.snap.ts +3 -3
- package/tests/snapshots/src-snapshots-serializer.snap.ts +2 -2
- package/bin/autopilot.js +0 -20
- package/skills/autopilot.md +0 -157
- /package/presets/go/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
- /package/presets/python-fastapi/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
- /package/presets/rails-postgres/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
- /package/presets/t3/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
- /package/{src → scripts}/snapshots/impact-selector.ts +0 -0
- /package/{src → scripts}/snapshots/import-scanner.ts +0 -0
- /package/{src → scripts}/snapshots/serializer.ts +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as net from 'node:net';
|
|
3
|
+
import type { GuardrailConfig } from '../config/types.ts';
|
|
4
|
+
import type { Finding } from '../findings/types.ts';
|
|
5
|
+
|
|
6
|
+
export interface WorkerServerOptions {
|
|
7
|
+
cwd: string;
|
|
8
|
+
onReview: (files: string[], config: GuardrailConfig) => Promise<{ findings: Finding[]; usage?: { costUSD: number } }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface WorkerServer {
|
|
12
|
+
port: number;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function getRandomPort(): Promise<number> {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const srv = net.createServer();
|
|
19
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
20
|
+
const addr = srv.address() as net.AddressInfo;
|
|
21
|
+
srv.close(() => resolve(addr.port));
|
|
22
|
+
});
|
|
23
|
+
srv.on('error', reject);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function startWorkerServer(opts: WorkerServerOptions): Promise<WorkerServer> {
|
|
28
|
+
const port = await getRandomPort();
|
|
29
|
+
let jobsProcessed = 0;
|
|
30
|
+
const startedAt = Date.now();
|
|
31
|
+
|
|
32
|
+
const server = http.createServer(async (req, res) => {
|
|
33
|
+
if (req.method === 'GET' && req.url === '/status') {
|
|
34
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
35
|
+
res.end(JSON.stringify({
|
|
36
|
+
pid: process.pid, port, jobsProcessed,
|
|
37
|
+
queueDepth: 0,
|
|
38
|
+
uptimeMs: Date.now() - startedAt,
|
|
39
|
+
}));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (req.method === 'POST' && req.url === '/stop') {
|
|
44
|
+
res.writeHead(200);
|
|
45
|
+
res.end('{"ok":true}');
|
|
46
|
+
setImmediate(() => server.close());
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (req.method === 'POST' && req.url === '/review') {
|
|
51
|
+
let body = '';
|
|
52
|
+
req.on('data', (chunk: Buffer) => { body += chunk.toString(); });
|
|
53
|
+
req.on('end', async () => {
|
|
54
|
+
try {
|
|
55
|
+
const { files, config } = JSON.parse(body) as { files: string[]; config: GuardrailConfig };
|
|
56
|
+
const result = await opts.onReview(files, config);
|
|
57
|
+
jobsProcessed++;
|
|
58
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
59
|
+
res.end(JSON.stringify(result));
|
|
60
|
+
} catch (err) {
|
|
61
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
62
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
res.writeHead(404);
|
|
69
|
+
res.end('Not found');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await new Promise<void>((resolve, reject) => {
|
|
73
|
+
server.listen(port, '127.0.0.1', () => resolve());
|
|
74
|
+
server.on('error', reject);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
port,
|
|
79
|
+
close: () => new Promise<void>(resolve => server.close(() => resolve())),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { RunResult } from '../core/pipeline/run.ts';
|
|
2
|
+
|
|
3
|
+
function escapeXml(s: string): string {
|
|
4
|
+
return s
|
|
5
|
+
.replace(/&/g, '&')
|
|
6
|
+
.replace(/</g, '<')
|
|
7
|
+
.replace(/>/g, '>')
|
|
8
|
+
.replace(/"/g, '"')
|
|
9
|
+
.replace(/'/g, ''');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function toJUnit(result: RunResult, opts: { suiteName?: string } = {}): string {
|
|
13
|
+
const name = opts.suiteName ?? 'guardrail';
|
|
14
|
+
const findings = result.allFindings;
|
|
15
|
+
const failures = findings.filter(f => f.severity === 'critical').length;
|
|
16
|
+
const total = findings.length;
|
|
17
|
+
const time = (result.durationMs / 1000).toFixed(3);
|
|
18
|
+
|
|
19
|
+
const lines: string[] = [
|
|
20
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
21
|
+
`<testsuites name="${escapeXml(name)}" tests="${total}" failures="${failures}" time="${time}">`,
|
|
22
|
+
` <testsuite name="${escapeXml(name)}" tests="${total}" failures="${failures}" errors="0" time="${time}">`,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
if (findings.length === 0) {
|
|
26
|
+
lines.push(` <testcase name="no findings" classname="${escapeXml(name)}" />`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const f of findings) {
|
|
30
|
+
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
31
|
+
const testName = escapeXml(`[${f.severity.toUpperCase()}] ${f.category} — ${loc}`);
|
|
32
|
+
const classname = escapeXml(f.file.replace(/\//g, '.').replace(/\.[tj]sx?$/, ''));
|
|
33
|
+
const body = escapeXml(f.message + (f.suggestion ? `\n${f.suggestion}` : ''));
|
|
34
|
+
|
|
35
|
+
if (f.severity === 'critical') {
|
|
36
|
+
lines.push(
|
|
37
|
+
` <testcase name="${testName}" classname="${classname}">`,
|
|
38
|
+
` <failure type="${escapeXml(f.category)}" message="${escapeXml(f.message)}">${body}</failure>`,
|
|
39
|
+
` </testcase>`,
|
|
40
|
+
);
|
|
41
|
+
} else {
|
|
42
|
+
lines.push(
|
|
43
|
+
` <testcase name="${testName}" classname="${classname}">`,
|
|
44
|
+
` <system-out>${body}</system-out>`,
|
|
45
|
+
` </testcase>`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
lines.push(' </testsuite>', '</testsuites>');
|
|
51
|
+
return lines.join('\n');
|
|
52
|
+
}
|
package/src/formatters/sarif.ts
CHANGED
|
@@ -91,9 +91,9 @@ export function toSarif(
|
|
|
91
91
|
runs: [{
|
|
92
92
|
tool: {
|
|
93
93
|
driver: {
|
|
94
|
-
name: '
|
|
94
|
+
name: 'guardrail',
|
|
95
95
|
version: opts.toolVersion,
|
|
96
|
-
informationUri: 'https://github.com/axledbetter/
|
|
96
|
+
informationUri: 'https://github.com/axledbetter/guardrail',
|
|
97
97
|
rules: [...rulesMap.values()],
|
|
98
98
|
},
|
|
99
99
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export type { Finding, Severity, FindingSource } from './core/findings/types.js';
|
|
2
2
|
export type { RunResult, RunInput, PhaseResult } from './core/pipeline/run.js';
|
|
3
|
-
export type {
|
|
4
|
-
export { normalizeSnapshot } from './snapshots/serializer.js';
|
|
3
|
+
export type { GuardrailConfig, AdapterRef, AdapterReference } from './core/config/types.js';
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
{
|
|
14
14
|
"tool": {
|
|
15
15
|
"driver": {
|
|
16
|
-
"name": "
|
|
16
|
+
"name": "guardrail",
|
|
17
17
|
"version": "9.9.9",
|
|
18
|
-
"informationUri": "https://github.com/axledbetter/
|
|
18
|
+
"informationUri": "https://github.com/axledbetter/guardrail",
|
|
19
19
|
"rules": [
|
|
20
20
|
{
|
|
21
21
|
"id": "no-secrets",
|
|
@@ -137,9 +137,9 @@
|
|
|
137
137
|
{
|
|
138
138
|
"tool": {
|
|
139
139
|
"driver": {
|
|
140
|
-
"name": "
|
|
140
|
+
"name": "guardrail",
|
|
141
141
|
"version": "1.2.3",
|
|
142
|
-
"informationUri": "https://github.com/axledbetter/
|
|
142
|
+
"informationUri": "https://github.com/axledbetter/guardrail",
|
|
143
143
|
"rules": [
|
|
144
144
|
{
|
|
145
145
|
"id": "cat-a",
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"src/formatters/sarif.ts"
|
|
4
4
|
],
|
|
5
5
|
"tests/snapshots/src-snapshots-impact-selector.snap.ts": [
|
|
6
|
-
"
|
|
6
|
+
"scripts/snapshots/impact-selector.ts"
|
|
7
7
|
],
|
|
8
8
|
"tests/snapshots/src-snapshots-import-scanner.snap.ts": [
|
|
9
|
-
"
|
|
9
|
+
"scripts/snapshots/import-scanner.ts"
|
|
10
10
|
],
|
|
11
11
|
"tests/snapshots/src-snapshots-serializer.snap.ts": [
|
|
12
|
-
"
|
|
12
|
+
"scripts/snapshots/serializer.ts"
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
10
10
|
import * as path from 'node:path';
|
|
11
11
|
|
|
12
12
|
import { normalizeSarifUri, toSarif } from '../../src/formatters/sarif.ts';
|
|
13
|
-
import { normalizeSnapshot } from '../../
|
|
13
|
+
import { normalizeSnapshot } from '../../scripts/snapshots/serializer.ts';
|
|
14
14
|
|
|
15
15
|
const SLUG = 'src-formatters-sarif';
|
|
16
16
|
const baselineRaw =
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @snapshot-for:
|
|
1
|
+
// @snapshot-for: scripts/snapshots/impact-selector.ts
|
|
2
2
|
// @generated-at: 2026-04-21T17:42:06.431Z
|
|
3
3
|
// @source-commit: d207869
|
|
4
4
|
// @generator-version: 1.0.0-alpha.6
|
|
@@ -9,8 +9,8 @@ import assert from 'node:assert/strict';
|
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
import * as path from 'node:path';
|
|
11
11
|
|
|
12
|
-
import { selectSnapshots } from '../../
|
|
13
|
-
import { normalizeSnapshot } from '../../
|
|
12
|
+
import { selectSnapshots } from '../../scripts/snapshots/impact-selector.ts';
|
|
13
|
+
import { normalizeSnapshot } from '../../scripts/snapshots/serializer.ts';
|
|
14
14
|
|
|
15
15
|
const SLUG = 'src-snapshots-impact-selector';
|
|
16
16
|
const baselineRaw = process.env.CAPTURE_BASELINE === '1' ? '{}' : fs.readFileSync(fileURLToPath(new URL('./baselines/src-snapshots-impact-selector.json', import.meta.url)), 'utf8');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @snapshot-for:
|
|
1
|
+
// @snapshot-for: scripts/snapshots/import-scanner.ts
|
|
2
2
|
// @generated-at: 2026-04-21T17:42:06.431Z
|
|
3
3
|
// @source-commit: d207869
|
|
4
4
|
// @generator-version: 1.0.0-alpha.6
|
|
@@ -9,8 +9,8 @@ import assert from 'node:assert/strict';
|
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
import * as path from 'node:path';
|
|
11
11
|
|
|
12
|
-
import { buildImportMap } from '../../
|
|
13
|
-
import { normalizeSnapshot } from '../../
|
|
12
|
+
import { buildImportMap } from '../../scripts/snapshots/import-scanner.ts';
|
|
13
|
+
import { normalizeSnapshot } from '../../scripts/snapshots/serializer.ts';
|
|
14
14
|
|
|
15
15
|
const SLUG = 'src-snapshots-import-scanner';
|
|
16
16
|
void SLUG;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @snapshot-for:
|
|
1
|
+
// @snapshot-for: scripts/snapshots/serializer.ts
|
|
2
2
|
// @generated-at: 2026-04-21T17:42:06.431Z
|
|
3
3
|
// @source-commit: d207869
|
|
4
4
|
// @generator-version: 1.0.0-alpha.6
|
|
@@ -8,7 +8,7 @@ import { describe, it } from 'node:test';
|
|
|
8
8
|
import assert from 'node:assert/strict';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
import * as path from 'node:path';
|
|
11
|
-
import { normalizeSnapshot } from '../../
|
|
11
|
+
import { normalizeSnapshot } from '../../scripts/snapshots/serializer.ts';
|
|
12
12
|
|
|
13
13
|
const SLUG = 'src-snapshots-serializer';
|
|
14
14
|
const baselineRaw = process.env.CAPTURE_BASELINE === '1' ? '{}' : fs.readFileSync(fileURLToPath(new URL('./baselines/src-snapshots-serializer.json', import.meta.url)), 'utf8');
|
package/bin/autopilot.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
|
-
import * as path from 'node:path';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const entrypoint = path.resolve(__dirname, '..', 'src', 'cli', 'index.ts');
|
|
9
|
-
|
|
10
|
-
// Locate tsx: own node_modules (dev) → consumer's node_modules/.bin → PATH
|
|
11
|
-
function findTsx() {
|
|
12
|
-
const own = path.resolve(__dirname, '..', 'node_modules', '.bin', 'tsx');
|
|
13
|
-
if (fs.existsSync(own)) return own;
|
|
14
|
-
const consumer = path.resolve(__dirname, '..', '..', '..', '.bin', 'tsx');
|
|
15
|
-
if (fs.existsSync(consumer)) return consumer;
|
|
16
|
-
return 'tsx';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const result = spawnSync(findTsx(), [entrypoint, ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
20
|
-
process.exit(result.status ?? 1);
|
package/skills/autopilot.md
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: autopilot
|
|
3
|
-
description: Run the @delegance/claude-autopilot code review pipeline — static rules, LLM review, snapshot regression. Use before any PR or after completing a feature.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# autopilot — Code Review Pipeline
|
|
7
|
-
|
|
8
|
-
Runs static rules, optional LLM review, and impact-aware snapshot regression on git-changed files. Auto-detects stack, protected paths, and test command from the project. Outputs findings inline and optionally as SARIF for GitHub Code Scanning.
|
|
9
|
-
|
|
10
|
-
## When to Use
|
|
11
|
-
|
|
12
|
-
- Before creating a PR: `npx autopilot run --base main`
|
|
13
|
-
- Inside CI: `npx autopilot ci` (one-command, posts PR comment + SARIF)
|
|
14
|
-
- Dev loop: `npx autopilot watch`
|
|
15
|
-
- First setup: `npx autopilot setup && npx autopilot doctor`
|
|
16
|
-
|
|
17
|
-
## Prerequisites
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npx autopilot doctor # checks Node 22+, tsx, gh CLI, API key, git config
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Commands
|
|
24
|
-
|
|
25
|
-
### `run` — pipeline on git-changed files
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npx autopilot run # diff HEAD~1 (default)
|
|
29
|
-
npx autopilot run --base main # diff against branch
|
|
30
|
-
npx autopilot run --files src/a.ts,src/b.ts # explicit files
|
|
31
|
-
npx autopilot run --dry-run # show what would run
|
|
32
|
-
npx autopilot run --post-comments # post/update summary on open PR
|
|
33
|
-
npx autopilot run --format sarif --output autopilot.sarif
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### `ci` — opinionated CI entrypoint
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx autopilot ci # base=GITHUB_BASE_REF, post-comments=true, sarif written
|
|
40
|
-
npx autopilot ci --base develop
|
|
41
|
-
npx autopilot ci --no-post-comments
|
|
42
|
-
npx autopilot ci --output results.sarif
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Equivalent to `run --base <ref> --post-comments --format sarif --output <path>`. Base ref resolves from `GITHUB_BASE_REF` → `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` → `HEAD~1`.
|
|
46
|
-
|
|
47
|
-
### `setup` — zero-prompt first run
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npx autopilot setup # auto-detect stack, write config, install hook
|
|
51
|
-
npx autopilot setup --force # overwrite existing config
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Auto-detects: Go, Rails, FastAPI, T3, Next.js+Supabase. Runs doctor at end.
|
|
55
|
-
|
|
56
|
-
### `watch` — dev loop
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
npx autopilot watch
|
|
60
|
-
npx autopilot watch --debounce 500
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### `autoregress` — snapshot regression
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
npx autopilot autoregress generate # create baselines for changed files
|
|
67
|
-
npx autopilot autoregress run # run impact-selected snapshots
|
|
68
|
-
npx autopilot autoregress run --all # run all snapshots
|
|
69
|
-
npx autopilot autoregress diff # show diffs vs baselines
|
|
70
|
-
npx autopilot autoregress update # overwrite baselines after intentional change
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### `hook` — pre-push git hook
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
npx autopilot hook install
|
|
77
|
-
npx autopilot hook uninstall
|
|
78
|
-
npx autopilot hook status
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## LLM Review Adapters
|
|
82
|
-
|
|
83
|
-
Configure via `reviewEngine.adapter` in `autopilot.config.yaml`:
|
|
84
|
-
|
|
85
|
-
| Adapter | Key env var | Notes |
|
|
86
|
-
|---------|-------------|-------|
|
|
87
|
-
| `auto` | any | Picks available provider; prefers the one already used in code |
|
|
88
|
-
| `claude` | `ANTHROPIC_API_KEY` | Claude Opus 4.7 |
|
|
89
|
-
| `gemini` | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | Gemini 2.5 Pro, 1M context |
|
|
90
|
-
| `codex` | `OPENAI_API_KEY` | gpt-5.3-codex via responses API |
|
|
91
|
-
| `openai-compatible` | configurable | Any OpenAI-API endpoint (Groq, Ollama, Together) |
|
|
92
|
-
|
|
93
|
-
`auto` priority order: Anthropic → Gemini → OpenAI → Groq. When multiple keys are present, `auto` scans the project source files and prefers the provider already referenced most.
|
|
94
|
-
|
|
95
|
-
## Auto-Detection
|
|
96
|
-
|
|
97
|
-
When config fields are absent, `autopilot run` fills them in automatically:
|
|
98
|
-
|
|
99
|
-
- **stack** — parsed from `package.json`, `go.mod`, `Cargo.toml`, `requirements.txt`, `Gemfile`; injected into review prompt
|
|
100
|
-
- **protectedPaths** — migration dirs (`data/deltas/`, `migrations/`, `prisma/migrations/`, etc.), schema files, infra dirs (`terraform/`, `.github/workflows/`)
|
|
101
|
-
- **testCommand** — re-detected at run time from project files; set `testCommand: null` to disable explicitly
|
|
102
|
-
- **git context** — branch + last commit injected as `Change context: branch: X | last commit: Y`
|
|
103
|
-
|
|
104
|
-
Detection lines are printed dim after the file count: `auto-detected: stack: Next.js + Supabase | protected: data/deltas/** ...`
|
|
105
|
-
|
|
106
|
-
## Interpreting Results
|
|
107
|
-
|
|
108
|
-
**Exit code 0** — pass or warnings only. Safe to proceed.
|
|
109
|
-
**Exit code 1** — blocking findings. Fix before merging.
|
|
110
|
-
|
|
111
|
-
Finding severities: `critical` blocks merge, `warning` should fix, `note` informational.
|
|
112
|
-
|
|
113
|
-
PR comment (via `--post-comments` or `ci`): status badge, phase table, critical/warning findings, cost footer. Edits existing comment on re-runs (tracked via `<!-- autopilot-review -->` marker).
|
|
114
|
-
|
|
115
|
-
SARIF output: upload with `github/codeql-action/upload-sarif@v3` for inline PR diff annotations. Also auto-emits `::error`/`::warning` annotations when `GITHUB_ACTIONS=true`.
|
|
116
|
-
|
|
117
|
-
## Config (`autopilot.config.yaml`)
|
|
118
|
-
|
|
119
|
-
```yaml
|
|
120
|
-
configVersion: 1
|
|
121
|
-
reviewEngine:
|
|
122
|
-
adapter: auto # auto, claude, gemini, codex, openai-compatible
|
|
123
|
-
testCommand: npm test # null to disable
|
|
124
|
-
protectedPaths: # auto-detected if omitted
|
|
125
|
-
- data/deltas/**
|
|
126
|
-
- .github/workflows/**
|
|
127
|
-
staticRules:
|
|
128
|
-
- hardcoded-secrets
|
|
129
|
-
- npm-audit
|
|
130
|
-
- package-lock-sync
|
|
131
|
-
- console-log
|
|
132
|
-
- todo-fixme
|
|
133
|
-
- large-file
|
|
134
|
-
- missing-tests
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Preset defaults at: `node_modules/@delegance/claude-autopilot/presets/<name>/autopilot.config.yaml`
|
|
138
|
-
|
|
139
|
-
## GitHub Actions
|
|
140
|
-
|
|
141
|
-
```yaml
|
|
142
|
-
- uses: axledbetter/claude-autopilot/.github/actions/ci@main
|
|
143
|
-
with:
|
|
144
|
-
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} # or openai/gemini/groq
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Runs `npx autopilot ci`, uploads SARIF, annotates PR diff. All API key inputs optional — whichever is set gets used by `auto`.
|
|
148
|
-
|
|
149
|
-
## Integration with Development Pipeline
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
# After implementing feature
|
|
153
|
-
npx autopilot run --base main
|
|
154
|
-
|
|
155
|
-
# If findings → fix → re-run (max 3 iterations)
|
|
156
|
-
# If clean → push → create PR
|
|
157
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|