@delegance/claude-autopilot 1.2.7 → 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 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.2.7",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
- "description": "Claude Code automation pipeline: spec \u2192 plan \u2192 implement \u2192 validate \u2192 PR",
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",
@@ -1,5 +1,5 @@
1
1
  configVersion: 1
2
- reviewEngine: { adapter: codex }
2
+ reviewEngine: { adapter: auto }
3
3
  vcsHost: { adapter: github }
4
4
  reviewBot: { adapter: cursor }
5
5
  protectedPaths:
@@ -1,5 +1,5 @@
1
1
  configVersion: 1
2
- reviewEngine: { adapter: codex }
2
+ reviewEngine: { adapter: auto }
3
3
  vcsHost: { adapter: github }
4
4
  migrationRunner: { adapter: supabase }
5
5
  reviewBot: { adapter: cursor }
@@ -1,5 +1,5 @@
1
1
  configVersion: 1
2
- reviewEngine: { adapter: codex }
2
+ reviewEngine: { adapter: auto }
3
3
  vcsHost: { adapter: github }
4
4
  reviewBot: { adapter: cursor }
5
5
  protectedPaths:
@@ -1,5 +1,5 @@
1
1
  configVersion: 1
2
- reviewEngine: { adapter: codex }
2
+ reviewEngine: { adapter: auto }
3
3
  vcsHost: { adapter: github }
4
4
  reviewBot: { adapter: cursor }
5
5
  protectedPaths:
@@ -1,5 +1,5 @@
1
1
  configVersion: 1
2
- reviewEngine: { adapter: codex }
2
+ reviewEngine: { adapter: auto }
3
3
  vcsHost: { adapter: github }
4
4
  reviewBot: { adapter: cursor }
5
5
  protectedPaths:
@@ -13,7 +13,11 @@ export interface LoadAdapterOptions {
13
13
  }
14
14
 
15
15
  const BUILTIN_PATHS: Record<IntegrationPoint, Record<string, string>> = {
16
- 'review-engine': { codex: './review-engine/codex.ts' },
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 OPENAI_API_KEY or not configured)
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 hasKey = !!(process.env.OPENAI_API_KEY);
115
- if (!hasKey && ref === 'codex') {
116
- console.log(fmt('yellow', '\n [run] OPENAI_API_KEY not setCodex review step will be skipped'));
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>({