@delegance/claude-autopilot 1.7.2 → 1.8.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/package.json +1 -1
- package/src/adapters/review-engine/claude.ts +2 -27
- package/src/adapters/review-engine/codex.ts +2 -27
- package/src/adapters/review-engine/gemini.ts +2 -27
- package/src/adapters/review-engine/openai-compatible.ts +2 -27
- package/src/adapters/review-engine/parse-output.ts +48 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type { Finding } from '../../core/findings/types.ts';
|
|
3
2
|
import { AutopilotError } from '../../core/errors.ts';
|
|
4
3
|
import type { Capabilities } from '../base.ts';
|
|
5
4
|
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
5
|
+
import { parseReviewOutput } from './parse-output.ts';
|
|
6
6
|
|
|
7
7
|
const DEFAULT_MODEL = 'claude-opus-4-7';
|
|
8
8
|
const MAX_OUTPUT_TOKENS = 4096;
|
|
@@ -90,7 +90,7 @@ export const claudeAdapter: ReviewEngine = {
|
|
|
90
90
|
: undefined;
|
|
91
91
|
|
|
92
92
|
return {
|
|
93
|
-
findings:
|
|
93
|
+
findings: parseReviewOutput(rawOutput, 'claude'),
|
|
94
94
|
rawOutput,
|
|
95
95
|
usage: response.usage
|
|
96
96
|
? { input: response.usage.input_tokens, output: response.usage.output_tokens, costUSD }
|
|
@@ -100,28 +100,3 @@ export const claudeAdapter: ReviewEngine = {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
export default claudeAdapter;
|
|
103
|
-
|
|
104
|
-
function parseClaudeOutput(output: string): Finding[] {
|
|
105
|
-
const findings: Finding[] = [];
|
|
106
|
-
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
107
|
-
let match: RegExpExecArray | null;
|
|
108
|
-
while ((match = regex.exec(output)) !== null) {
|
|
109
|
-
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
110
|
-
const body = match[2]!.trim();
|
|
111
|
-
const titleEnd = body.indexOf('\n');
|
|
112
|
-
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
113
|
-
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
114
|
-
findings.push({
|
|
115
|
-
id: `claude-${findings.length}`,
|
|
116
|
-
source: 'review-engine',
|
|
117
|
-
severity,
|
|
118
|
-
category: 'claude-review',
|
|
119
|
-
file: '<unspecified>',
|
|
120
|
-
message: title,
|
|
121
|
-
suggestion,
|
|
122
|
-
protectedPath: false,
|
|
123
|
-
createdAt: new Date().toISOString(),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return findings;
|
|
127
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import
|
|
2
|
+
import { parseReviewOutput } from './parse-output.ts';
|
|
3
3
|
import { AutopilotError } from '../../core/errors.ts';
|
|
4
4
|
import type { Capabilities } from '../base.ts';
|
|
5
5
|
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
@@ -74,7 +74,7 @@ export const codexAdapter: ReviewEngine = {
|
|
|
74
74
|
|
|
75
75
|
const rawOutput = response.output_text ?? '';
|
|
76
76
|
return {
|
|
77
|
-
findings:
|
|
77
|
+
findings: parseReviewOutput(rawOutput, 'codex'),
|
|
78
78
|
rawOutput,
|
|
79
79
|
usage: response.usage ? { input: response.usage.input_tokens, output: response.usage.output_tokens } : undefined,
|
|
80
80
|
};
|
|
@@ -82,28 +82,3 @@ export const codexAdapter: ReviewEngine = {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export default codexAdapter;
|
|
85
|
-
|
|
86
|
-
function parseCodexOutput(output: string): Finding[] {
|
|
87
|
-
const findings: Finding[] = [];
|
|
88
|
-
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
89
|
-
let match: RegExpExecArray | null;
|
|
90
|
-
while ((match = regex.exec(output)) !== null) {
|
|
91
|
-
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
92
|
-
const body = match[2]!.trim();
|
|
93
|
-
const titleEnd = body.indexOf('\n');
|
|
94
|
-
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
95
|
-
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
96
|
-
findings.push({
|
|
97
|
-
id: `codex-${findings.length}`,
|
|
98
|
-
source: 'review-engine',
|
|
99
|
-
severity,
|
|
100
|
-
category: 'codex-review',
|
|
101
|
-
file: '<unspecified>',
|
|
102
|
-
message: title,
|
|
103
|
-
suggestion,
|
|
104
|
-
protectedPath: false,
|
|
105
|
-
createdAt: new Date().toISOString(),
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
return findings;
|
|
109
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
2
|
-
import
|
|
2
|
+
import { parseReviewOutput } from './parse-output.ts';
|
|
3
3
|
import { AutopilotError } from '../../core/errors.ts';
|
|
4
4
|
import type { Capabilities } from '../base.ts';
|
|
5
5
|
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
@@ -95,7 +95,7 @@ export const geminiAdapter: ReviewEngine = {
|
|
|
95
95
|
: undefined;
|
|
96
96
|
|
|
97
97
|
return {
|
|
98
|
-
findings:
|
|
98
|
+
findings: parseReviewOutput(rawOutput, 'gemini'),
|
|
99
99
|
rawOutput,
|
|
100
100
|
usage: usage
|
|
101
101
|
? { input: usage.promptTokenCount, output: usage.candidatesTokenCount, costUSD }
|
|
@@ -105,28 +105,3 @@ export const geminiAdapter: ReviewEngine = {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
export default geminiAdapter;
|
|
108
|
-
|
|
109
|
-
function parseGeminiOutput(output: string): Finding[] {
|
|
110
|
-
const findings: Finding[] = [];
|
|
111
|
-
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
112
|
-
let match: RegExpExecArray | null;
|
|
113
|
-
while ((match = regex.exec(output)) !== null) {
|
|
114
|
-
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
115
|
-
const body = match[2]!.trim();
|
|
116
|
-
const titleEnd = body.indexOf('\n');
|
|
117
|
-
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
118
|
-
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
119
|
-
findings.push({
|
|
120
|
-
id: `gemini-${findings.length}`,
|
|
121
|
-
source: 'review-engine',
|
|
122
|
-
severity,
|
|
123
|
-
category: 'gemini-review',
|
|
124
|
-
file: '<unspecified>',
|
|
125
|
-
message: title,
|
|
126
|
-
suggestion,
|
|
127
|
-
protectedPath: false,
|
|
128
|
-
createdAt: new Date().toISOString(),
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
return findings;
|
|
132
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import
|
|
2
|
+
import { parseReviewOutput } from './parse-output.ts';
|
|
3
3
|
import { AutopilotError } from '../../core/errors.ts';
|
|
4
4
|
import type { Capabilities } from '../base.ts';
|
|
5
5
|
import type { ReviewEngine, ReviewInput, ReviewOutput } from './types.ts';
|
|
@@ -90,7 +90,7 @@ export const openaiCompatibleAdapter: ReviewEngine = {
|
|
|
90
90
|
|
|
91
91
|
const rawOutput = response.choices[0]?.message.content ?? '';
|
|
92
92
|
return {
|
|
93
|
-
findings:
|
|
93
|
+
findings: parseReviewOutput(rawOutput, 'openai-compatible'),
|
|
94
94
|
rawOutput,
|
|
95
95
|
usage: response.usage
|
|
96
96
|
? { input: response.usage.prompt_tokens, output: response.usage.completion_tokens }
|
|
@@ -100,28 +100,3 @@ export const openaiCompatibleAdapter: ReviewEngine = {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
export default openaiCompatibleAdapter;
|
|
103
|
-
|
|
104
|
-
function parseOutput(output: string): Finding[] {
|
|
105
|
-
const findings: Finding[] = [];
|
|
106
|
-
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
107
|
-
let match: RegExpExecArray | null;
|
|
108
|
-
while ((match = regex.exec(output)) !== null) {
|
|
109
|
-
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
110
|
-
const body = match[2]!.trim();
|
|
111
|
-
const titleEnd = body.indexOf('\n');
|
|
112
|
-
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
113
|
-
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
114
|
-
findings.push({
|
|
115
|
-
id: `openai-compatible-${findings.length}`,
|
|
116
|
-
source: 'review-engine',
|
|
117
|
-
severity,
|
|
118
|
-
category: 'openai-compatible-review',
|
|
119
|
-
file: '<unspecified>',
|
|
120
|
-
message: title,
|
|
121
|
-
suggestion,
|
|
122
|
-
protectedPath: false,
|
|
123
|
-
createdAt: new Date().toISOString(),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return findings;
|
|
127
|
-
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Finding } from '../../core/findings/types.ts';
|
|
2
|
+
|
|
3
|
+
// Matches "path/to/file.ts:42", "`path/to/file.ts`", or bare filenames with common extensions
|
|
4
|
+
const FILE_REF = /(?:`([^`]+\.[a-z]{1,6})`|(\b[\w./\-]+\.[a-z]{1,6})(?::(\d+))?)/;
|
|
5
|
+
|
|
6
|
+
function extractFileRef(text: string): { file: string; line?: number } {
|
|
7
|
+
const m = text.match(FILE_REF);
|
|
8
|
+
if (!m) return { file: '<unspecified>' };
|
|
9
|
+
const raw = (m[1] ?? m[2])!;
|
|
10
|
+
// Skip version strings (v1.2.3) and bare dotfile extensions with no path separator
|
|
11
|
+
if (/^v?\d/.test(raw) || (!raw.includes('/') && raw.startsWith('.') && raw.split('.').length === 2)) {
|
|
12
|
+
return { file: '<unspecified>' };
|
|
13
|
+
}
|
|
14
|
+
const line = m[3] ? parseInt(m[3], 10) : undefined;
|
|
15
|
+
return { file: raw, line };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses the structured [CRITICAL|WARNING|NOTE] markdown format
|
|
20
|
+
* produced by all review engine adapters. Extracts file:line references
|
|
21
|
+
* from the finding body when present.
|
|
22
|
+
*/
|
|
23
|
+
export function parseReviewOutput(output: string, idPrefix: string): Finding[] {
|
|
24
|
+
const findings: Finding[] = [];
|
|
25
|
+
const regex = /### \[(CRITICAL|WARNING|NOTE)\]\s*(.+?)(?=\n### \[|## Review Summary|$)/gs;
|
|
26
|
+
let match: RegExpExecArray | null;
|
|
27
|
+
while ((match = regex.exec(output)) !== null) {
|
|
28
|
+
const severity = match[1]!.toLowerCase() as Finding['severity'];
|
|
29
|
+
const body = match[2]!.trim();
|
|
30
|
+
const titleEnd = body.indexOf('\n');
|
|
31
|
+
const title = (titleEnd > 0 ? body.slice(0, titleEnd) : body).trim();
|
|
32
|
+
const suggestion = body.match(/\*\*Suggestion:\*\*\s*(.+)/s)?.[1]?.trim();
|
|
33
|
+
const { file, line } = extractFileRef(body);
|
|
34
|
+
findings.push({
|
|
35
|
+
id: `${idPrefix}-${findings.length}`,
|
|
36
|
+
source: 'review-engine',
|
|
37
|
+
severity,
|
|
38
|
+
category: 'review-engine',
|
|
39
|
+
file,
|
|
40
|
+
line,
|
|
41
|
+
message: title,
|
|
42
|
+
suggestion,
|
|
43
|
+
protectedPath: false,
|
|
44
|
+
createdAt: new Date().toISOString(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return findings;
|
|
48
|
+
}
|