@delegance/claude-autopilot 1.2.8 → 1.3.0
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 +45 -0
- package/package.json +3 -2
- package/presets/go/autopilot.config.yaml +1 -1
- package/presets/nextjs-supabase/autopilot.config.yaml +1 -1
- package/presets/python-fastapi/autopilot.config.yaml +1 -1
- package/presets/rails-postgres/autopilot.config.yaml +1 -1
- package/presets/t3/autopilot.config.yaml +1 -1
- package/src/adapters/loader.ts +5 -1
- package/src/adapters/review-engine/auto.ts +39 -0
- package/src/adapters/review-engine/claude.ts +126 -0
- package/src/cli/run.ts +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.8] — 2026-04-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- 8 new tests covering npm placeholder detection, pyproject.toml FastAPI detection, `resolveGitTouchedFiles` ignore list, deduplication, and status fallback — **136 total**
|
|
7
|
+
|
|
8
|
+
## [1.2.7] — 2026-04-21
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `autopilot run` now loads `.env.local` / `.env` at startup so `OPENAI_API_KEY` (and other env vars) are available without exporting them in the shell first
|
|
12
|
+
|
|
13
|
+
## [1.2.6] — 2026-04-21
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `skills/autopilot.md` included in npm package — install once, then `cp node_modules/@delegance/claude-autopilot/skills/autopilot.md .claude/skills/` to give Claude Code full context on when and how to invoke the CLI
|
|
17
|
+
|
|
18
|
+
## [1.2.5] — 2026-04-21
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `--version` / `-v` flag — prints package version and exits
|
|
22
|
+
- Built-in ignore list for git diff output: `node_modules/`, `dist/`, `build/`, `.next/`, `.nuxt/`, `out/`, `coverage/`, `.turbo/`, `.cache/`, `vendor/`, `__pycache__/`, `.venv/`, `venv/`, `target/`, `.gradle/` — prevents build artifact floods from polluting the changed-files list
|
|
23
|
+
|
|
24
|
+
## [1.2.4] — 2026-04-21
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- `autopilot init` is now deprecated — prints a notice and delegates to `autopilot setup`
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Removed superpowers plugin check from `doctor` — it was warning all external developers about a Delegance-internal tool they cannot install
|
|
31
|
+
|
|
32
|
+
## [1.2.3] — 2026-04-21
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- README rewrite: `setup` and `doctor` commands now prominent; config schema accurate; public API section added
|
|
36
|
+
|
|
37
|
+
## [1.2.2] — 2026-04-21
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- Hook install called from `setup` no longer double-prints stderr; added `silent` option to `runHook()` to suppress output when invoked programmatically
|
|
41
|
+
|
|
42
|
+
## [1.2.1] — 2026-04-21
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- `bin/autopilot.js` tsx resolution now checks the consumer's `node_modules/.bin/tsx` before falling back to PATH — fixes "tsx not found" on fresh installs
|
|
46
|
+
- npm default test placeholder (`echo "Error: no test specified" && exit 1`) is now detected and replaced with `npm test` instead of being used as the test command
|
|
47
|
+
|
|
3
48
|
## [1.2.0] — 2026-04-21
|
|
4
49
|
|
|
5
50
|
### Added
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Claude Code automation pipeline: spec
|
|
5
|
+
"description": "Claude Code automation pipeline: spec → plan → implement → validate → PR",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"claude",
|
|
8
8
|
"autopilot",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"autoregress": "tsx scripts/autoregress.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@anthropic-ai/sdk": "^0.90.0",
|
|
48
49
|
"ajv": "^8",
|
|
49
50
|
"dotenv": ">=16",
|
|
50
51
|
"js-yaml": "^4",
|
package/src/adapters/loader.ts
CHANGED
|
@@ -13,7 +13,11 @@ export interface LoadAdapterOptions {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const BUILTIN_PATHS: Record<IntegrationPoint, Record<string, string>> = {
|
|
16
|
-
'review-engine': {
|
|
16
|
+
'review-engine': {
|
|
17
|
+
codex: './review-engine/codex.ts',
|
|
18
|
+
claude: './review-engine/claude.ts',
|
|
19
|
+
auto: './review-engine/auto.ts',
|
|
20
|
+
},
|
|
17
21
|
'vcs-host': { github: './vcs-host/github.ts' },
|
|
18
22
|
'migration-runner': { supabase: './migration-runner/supabase.ts' },
|
|
19
23
|
'review-bot-parser': { cursor: './review-bot-parser/cursor.ts' },
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Capabilities } from '../base.ts';
|
|
2
|
+
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
3
|
+
import { AutopilotError } from '../../core/errors.ts';
|
|
4
|
+
|
|
5
|
+
// Priority order: ANTHROPIC_API_KEY → claude, OPENAI_API_KEY → codex
|
|
6
|
+
async function resolveAdapter(): Promise<ReviewEngine> {
|
|
7
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
8
|
+
const { claudeAdapter } = await import('./claude.ts');
|
|
9
|
+
return claudeAdapter;
|
|
10
|
+
}
|
|
11
|
+
if (process.env.OPENAI_API_KEY) {
|
|
12
|
+
const { codexAdapter } = await import('./codex.ts');
|
|
13
|
+
return codexAdapter;
|
|
14
|
+
}
|
|
15
|
+
throw new AutopilotError(
|
|
16
|
+
'No LLM API key found — set ANTHROPIC_API_KEY (recommended) or OPENAI_API_KEY to enable review',
|
|
17
|
+
{ code: 'auth', provider: 'auto' }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const autoAdapter: ReviewEngine = {
|
|
22
|
+
name: 'auto',
|
|
23
|
+
apiVersion: '1.0.0',
|
|
24
|
+
|
|
25
|
+
getCapabilities(): Capabilities {
|
|
26
|
+
return { structuredOutput: false, streaming: false, maxContextTokens: 200000, inlineComments: false };
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
estimateTokens(content: string): number {
|
|
30
|
+
return Math.ceil(content.length / 3.5);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async review(input: ReviewInput): Promise<ReviewOutput> {
|
|
34
|
+
const adapter = await resolveAdapter();
|
|
35
|
+
return adapter.review(input);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default autoAdapter;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { Finding } from '../../core/findings/types.ts';
|
|
3
|
+
import { AutopilotError } from '../../core/errors.ts';
|
|
4
|
+
import type { Capabilities } from '../base.ts';
|
|
5
|
+
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MODEL = 'claude-sonnet-4-6';
|
|
8
|
+
const MAX_OUTPUT_TOKENS = 4096;
|
|
9
|
+
|
|
10
|
+
// Cost per million tokens (USD) — sonnet-4-6 pricing
|
|
11
|
+
const COST_PER_M_INPUT = 3.0;
|
|
12
|
+
const COST_PER_M_OUTPUT = 15.0;
|
|
13
|
+
|
|
14
|
+
const SYSTEM_PROMPT_TEMPLATE = `You are a senior software architect reviewing code changes for quality, security, and correctness.
|
|
15
|
+
|
|
16
|
+
The codebase context:
|
|
17
|
+
{STACK}
|
|
18
|
+
|
|
19
|
+
Provide structured feedback in exactly this format:
|
|
20
|
+
|
|
21
|
+
## Review Summary
|
|
22
|
+
One paragraph overall assessment.
|
|
23
|
+
|
|
24
|
+
## Findings
|
|
25
|
+
|
|
26
|
+
For each finding, use this format:
|
|
27
|
+
### [CRITICAL|WARNING|NOTE] <short title>
|
|
28
|
+
<explanation>
|
|
29
|
+
**Suggestion:** <actionable fix>
|
|
30
|
+
|
|
31
|
+
Rules:
|
|
32
|
+
- CRITICAL: Blocks merge (security issues, data loss risks, broken contracts)
|
|
33
|
+
- WARNING: Should address before merging (logic errors, missing error handling, test gaps)
|
|
34
|
+
- NOTE: Improvement suggestion (style, performance, clarity)
|
|
35
|
+
- Maximum 10 findings, ranked by severity
|
|
36
|
+
- Be specific and constructive
|
|
37
|
+
- Reference the file and line when possible`;
|
|
38
|
+
|
|
39
|
+
export const claudeAdapter: ReviewEngine = {
|
|
40
|
+
name: 'claude',
|
|
41
|
+
apiVersion: '1.0.0',
|
|
42
|
+
|
|
43
|
+
getCapabilities(): Capabilities {
|
|
44
|
+
return { structuredOutput: false, streaming: false, maxContextTokens: 200000, inlineComments: false };
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
estimateTokens(content: string): number {
|
|
48
|
+
return Math.ceil(content.length / 3.5);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async review(input: ReviewInput): Promise<ReviewOutput> {
|
|
52
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
throw new AutopilotError('ANTHROPIC_API_KEY not set', { code: 'auth', provider: 'claude' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const model = (input.context as Record<string, unknown> | undefined)?.['model'] as string | undefined ?? DEFAULT_MODEL;
|
|
58
|
+
const stack = input.context?.stack ?? 'A web application — stack details unspecified.';
|
|
59
|
+
const systemPrompt = SYSTEM_PROMPT_TEMPLATE.replace('{STACK}', stack);
|
|
60
|
+
|
|
61
|
+
const client = new Anthropic({ apiKey });
|
|
62
|
+
let response: Anthropic.Message;
|
|
63
|
+
try {
|
|
64
|
+
response = await client.messages.create({
|
|
65
|
+
model,
|
|
66
|
+
max_tokens: MAX_OUTPUT_TOKENS,
|
|
67
|
+
system: systemPrompt,
|
|
68
|
+
messages: [{ role: 'user', content: `Please review the following:\n\n---\n\n${input.content}` }],
|
|
69
|
+
});
|
|
70
|
+
} catch (err) {
|
|
71
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
const isRateLimit = /rate.limit|429|overloaded/i.test(message);
|
|
73
|
+
const isAuth = /unauthorized|401|invalid.api.key|authentication/i.test(message);
|
|
74
|
+
throw new AutopilotError(`Claude review call failed: ${message}`, {
|
|
75
|
+
code: isAuth ? 'auth' : isRateLimit ? 'rate_limit' : 'transient_network',
|
|
76
|
+
provider: 'claude',
|
|
77
|
+
retryable: isRateLimit,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const rawOutput = response.content
|
|
82
|
+
.filter(b => b.type === 'text')
|
|
83
|
+
.map(b => (b as Anthropic.TextBlock).text)
|
|
84
|
+
.join('');
|
|
85
|
+
|
|
86
|
+
const costUSD = response.usage
|
|
87
|
+
? (response.usage.input_tokens / 1_000_000) * COST_PER_M_INPUT +
|
|
88
|
+
(response.usage.output_tokens / 1_000_000) * COST_PER_M_OUTPUT
|
|
89
|
+
: undefined;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
findings: parseClaudeOutput(rawOutput),
|
|
93
|
+
rawOutput,
|
|
94
|
+
usage: response.usage
|
|
95
|
+
? { input: response.usage.input_tokens, output: response.usage.output_tokens, costUSD }
|
|
96
|
+
: undefined,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default claudeAdapter;
|
|
102
|
+
|
|
103
|
+
function parseClaudeOutput(output: string): Finding[] {
|
|
104
|
+
const findings: Finding[] = [];
|
|
105
|
+
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
106
|
+
let match: RegExpExecArray | null;
|
|
107
|
+
while ((match = regex.exec(output)) !== null) {
|
|
108
|
+
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
109
|
+
const body = match[2]!.trim();
|
|
110
|
+
const titleEnd = body.indexOf('\n');
|
|
111
|
+
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
112
|
+
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
113
|
+
findings.push({
|
|
114
|
+
id: `claude-${findings.length}`,
|
|
115
|
+
source: 'review-engine',
|
|
116
|
+
severity,
|
|
117
|
+
category: 'claude-review',
|
|
118
|
+
file: '<unspecified>',
|
|
119
|
+
message: title,
|
|
120
|
+
suggestion,
|
|
121
|
+
protectedPath: false,
|
|
122
|
+
createdAt: new Date().toISOString(),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return findings;
|
|
126
|
+
}
|
package/src/cli/run.ts
CHANGED
|
@@ -107,13 +107,13 @@ export async function runCommand(options: RunCommandOptions = {}): Promise<numbe
|
|
|
107
107
|
return 0;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// Load review engine (optional — skip if no
|
|
110
|
+
// Load review engine (optional — skip gracefully if no API key configured)
|
|
111
111
|
let reviewEngine: ReviewEngine | undefined;
|
|
112
112
|
if (config.reviewEngine) {
|
|
113
113
|
const ref = typeof config.reviewEngine === 'string' ? config.reviewEngine : config.reviewEngine.adapter;
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
116
|
-
console.log(fmt('yellow', '\n [run]
|
|
114
|
+
const hasAnyKey = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
|
|
115
|
+
if (!hasAnyKey && (ref === 'auto' || ref === 'claude' || ref === 'codex')) {
|
|
116
|
+
console.log(fmt('yellow', '\n [run] No LLM API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY to enable review'));
|
|
117
117
|
} else {
|
|
118
118
|
try {
|
|
119
119
|
reviewEngine = await loadAdapter<ReviewEngine>({
|