@delegance/claude-autopilot 5.0.7 → 5.0.8
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.
|
@@ -35,10 +35,20 @@ const FILE_REF = new RegExp(String.raw `(?:` +
|
|
|
35
35
|
String.raw `\x60([^\x60]+\.[a-z]{1,6})\x60` +
|
|
36
36
|
String.raw `|(\b[\w./\-]+\.` + CODE_EXT + String.raw `)(?::(\d+))?` +
|
|
37
37
|
String.raw `)`, 'i');
|
|
38
|
+
// Matches "line 42", "on line 42", "at line 42" — used as a fallback when the
|
|
39
|
+
// LLM mentions a line number separately from the file ref. Critical for `fix`:
|
|
40
|
+
// without a line, the fixer can't extract a code snippet, so findings without
|
|
41
|
+
// `line` got silently dropped from `fix --dry-run` (the path-only finding case
|
|
42
|
+
// was the most-cited demo torpedo from the 5.0.7 stress test).
|
|
43
|
+
const LINE_REF = /\b(?:on |at )?line\s+(\d+)\b/i;
|
|
38
44
|
function extractFileRef(text) {
|
|
39
45
|
const m = text.match(FILE_REF);
|
|
40
|
-
if (!m)
|
|
41
|
-
|
|
46
|
+
if (!m) {
|
|
47
|
+
// No file ref at all — but maybe the body still has "line N" prose we can
|
|
48
|
+
// surface. Caller treats file `<unspecified>` as a sentinel either way.
|
|
49
|
+
const lm = text.match(LINE_REF);
|
|
50
|
+
return lm ? { file: '<unspecified>', line: parseInt(lm[1], 10) } : { file: '<unspecified>' };
|
|
51
|
+
}
|
|
42
52
|
const raw = (m[1] ?? m[2]);
|
|
43
53
|
// Skip version strings (v1.2.3), bare dotfile extensions with no path
|
|
44
54
|
// separator, and known prose abbreviations that slipped through the regex
|
|
@@ -47,10 +57,16 @@ function extractFileRef(text) {
|
|
|
47
57
|
if (/^v?\d/.test(raw) ||
|
|
48
58
|
(!raw.includes('/') && raw.startsWith('.') && raw.split('.').length === 2) ||
|
|
49
59
|
/^(?:e\.g|i\.e|etc|vs|cf|al|U\.S|U\.K)$/i.test(raw)) {
|
|
50
|
-
|
|
60
|
+
const lm = text.match(LINE_REF);
|
|
61
|
+
return lm ? { file: '<unspecified>', line: parseInt(lm[1], 10) } : { file: '<unspecified>' };
|
|
51
62
|
}
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
// Prefer the colon-line from the file ref (`foo.ts:42`); fall back to a
|
|
64
|
+
// separately-mentioned line ("line 42") only when the file ref didn't carry one.
|
|
65
|
+
const colonLine = m[3] ? parseInt(m[3], 10) : undefined;
|
|
66
|
+
if (colonLine !== undefined)
|
|
67
|
+
return { file: raw, line: colonLine };
|
|
68
|
+
const lm = text.match(LINE_REF);
|
|
69
|
+
return lm ? { file: raw, line: parseInt(lm[1], 10) } : { file: raw };
|
|
54
70
|
}
|
|
55
71
|
// Accepts any of: `### [CRITICAL] title`, `### CRITICAL title`, `### **CRITICAL** title`,
|
|
56
72
|
// `### **[CRITICAL]** title`. Severity capture works across variants.
|
package/dist/src/cli/fix.js
CHANGED
|
@@ -38,8 +38,13 @@ export async function runFix(options = {}) {
|
|
|
38
38
|
console.log(fmt('yellow', '[fix] No cached findings — run `guardrail scan <path>` or `guardrail run` first.'));
|
|
39
39
|
return 0;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// Two gates:
|
|
42
|
+
// - "actionable": has a real file path. Surfaced in dry-run so the user sees
|
|
43
|
+
// findings even when the LLM didn't pin a line number.
|
|
44
|
+
// - "fixable": also has a line. The LLM-fix loop needs both to extract a
|
|
45
|
+
// code snippet around the finding location.
|
|
46
|
+
const actionable = findings.filter(f => {
|
|
47
|
+
if (!f.file || f.file === '<unspecified>' || f.file === '<pipeline>')
|
|
43
48
|
return false;
|
|
44
49
|
if (severityFilter === 'all')
|
|
45
50
|
return true;
|
|
@@ -47,8 +52,21 @@ export async function runFix(options = {}) {
|
|
|
47
52
|
return f.severity === 'critical';
|
|
48
53
|
return f.severity === 'critical' || f.severity === 'warning';
|
|
49
54
|
});
|
|
55
|
+
const fixable = actionable.filter(f => f.line && f.line > 0);
|
|
56
|
+
if (actionable.length === 0) {
|
|
57
|
+
console.log(fmt('yellow', `[fix] No actionable findings (severity=${severityFilter}, need file path).`));
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
50
60
|
if (fixable.length === 0) {
|
|
51
|
-
|
|
61
|
+
const verb = actionable.length === 1 ? 'has' : 'have';
|
|
62
|
+
const noun = actionable.length === 1 ? 'finding' : 'findings';
|
|
63
|
+
console.log(fmt('yellow', `[fix] ${actionable.length} ${noun} ${verb} file but no line — model output was line-less. Re-run scan with --ask "include line numbers" or run \`claude-autopilot run\` for richer extraction.`));
|
|
64
|
+
for (const f of actionable) {
|
|
65
|
+
const sev = f.severity === 'critical' ? fmt('red', 'CRITICAL')
|
|
66
|
+
: f.severity === 'warning' ? fmt('yellow', 'WARNING ')
|
|
67
|
+
: fmt('dim', 'NOTE ');
|
|
68
|
+
console.log(` [${sev}] ${fmt('dim', f.file)} ${f.message}`);
|
|
69
|
+
}
|
|
52
70
|
return 0;
|
|
53
71
|
}
|
|
54
72
|
const modeNote = options.dryRun ? ' (dry run)' : options.yes ? '' : ' (interactive — use --yes to skip prompts)';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
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": [
|