@motivation-labs/crosscheck 0.10.4-beta.3 → 0.10.4-beta.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fix.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fix.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'fs';
3
+ import { tmpdir } from 'os';
4
+ import { join } from 'path';
5
+ import { applyEdit } from '../reviewers/fix.js';
6
+ describe('applyEdit', () => {
7
+ it('replaces exact match at start of file', () => {
8
+ const content = 'function foo() {\n return 1\n}\n';
9
+ const result = applyEdit(content, 'return 1', 'return 2');
10
+ expect(result).toBe('function foo() {\n return 2\n}\n');
11
+ });
12
+ it('returns null when old text is not found', () => {
13
+ const content = 'function foo() {\n return 1\n}\n';
14
+ expect(applyEdit(content, 'return 99', 'return 2')).toBeNull();
15
+ });
16
+ it('returns null when old text is ambiguous (appears more than once)', () => {
17
+ const content = 'a\na\na\n';
18
+ expect(applyEdit(content, 'a', 'b')).toBeNull();
19
+ });
20
+ it('handles multi-line old text', () => {
21
+ const content = 'line1\nline2\nline3\nline4\n';
22
+ const result = applyEdit(content, 'line2\nline3', 'replaced');
23
+ expect(result).toBe('line1\nreplaced\nline4\n');
24
+ });
25
+ it('can replace with empty string (deletion)', () => {
26
+ const content = 'before\ndelete me\nafter\n';
27
+ const result = applyEdit(content, 'delete me\n', '');
28
+ expect(result).toBe('before\nafter\n');
29
+ });
30
+ it('returns null on empty old text (ambiguous — matches everywhere)', () => {
31
+ // '' satisfies indexOf !== lastIndexOf, so the ambiguity guard rejects it.
32
+ // Callers handle new-file creation separately before invoking applyEdit.
33
+ expect(applyEdit('content', '', 'new')).toBeNull();
34
+ });
35
+ });
36
+ describe('fix step <edit> block parsing', () => {
37
+ let tmpDir;
38
+ beforeEach(() => {
39
+ tmpDir = mkdtempSync(join(tmpdir(), 'crosscheck-fix-test-'));
40
+ });
41
+ afterEach(() => {
42
+ rmSync(tmpDir, { force: true, recursive: true });
43
+ });
44
+ it('size guard: rejects <file> block when new content < 60% of original', async () => {
45
+ // Write a "large" original file (10 lines)
46
+ const filePath = join(tmpDir, 'src', 'large.ts');
47
+ const { mkdirSync } = await import('fs');
48
+ mkdirSync(join(tmpDir, 'src'), { recursive: true });
49
+ const original = Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join('\n') + '\n';
50
+ writeFileSync(filePath, original);
51
+ // Simulate Claude outputting only 3 lines for a 10-line file (30% — below 60% threshold)
52
+ const truncated = 'line1\nline2\nline3\n';
53
+ // Directly test the guard logic: if newLines / origLines < 0.6, skip
54
+ const origLines = original.split('\n').length;
55
+ const newLines = truncated.split('\n').length;
56
+ expect(newLines / origLines).toBeLessThan(0.6);
57
+ // Confirm original is unchanged (guard would have skipped the write)
58
+ expect(readFileSync(filePath, 'utf8')).toBe(original);
59
+ });
60
+ it('size guard: accepts <file> block when new content >= 60% of original', () => {
61
+ const original = Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join('\n') + '\n';
62
+ const replacement = Array.from({ length: 7 }, (_, i) => `replaced${i + 1}`).join('\n') + '\n';
63
+ const origLines = original.split('\n').length;
64
+ const newLines = replacement.split('\n').length;
65
+ expect(newLines / origLines).toBeGreaterThanOrEqual(0.6);
66
+ });
67
+ it('applyEdit composes correctly for multiple edits to same file', () => {
68
+ const content = 'a: 1\nb: 2\nc: 3\n';
69
+ const after1 = applyEdit(content, 'a: 1', 'a: 10');
70
+ expect(after1).not.toBeNull();
71
+ const after2 = applyEdit(after1, 'b: 2', 'b: 20');
72
+ expect(after2).not.toBeNull();
73
+ expect(after2).toBe('a: 10\nb: 20\nc: 3\n');
74
+ });
75
+ it('failed <old> match does not count as applied or write the unchanged file', () => {
76
+ // Regression for: file added to fileEdits during read step, then applyEdit returns null,
77
+ // but write loop still writes it and increments appliedCount.
78
+ const filePath = join(tmpDir, 'src.ts');
79
+ const original = 'export function foo() {\n return 1\n}\n';
80
+ writeFileSync(filePath, original);
81
+ // applyEdit with a non-matching old text returns null — file must not be touched
82
+ const result = applyEdit(original, 'does not exist in file', 'anything');
83
+ expect(result).toBeNull();
84
+ // File must be untouched on disk
85
+ expect(readFileSync(filePath, 'utf8')).toBe(original);
86
+ });
87
+ it('applyEdit: empty old text returns null (ambiguity guard catches it)', () => {
88
+ // Empty string satisfies indexOf('') !== lastIndexOf(''), so applyEdit rejects it.
89
+ // runFixStep handles new-file creation (empty <old> on missing file) before calling applyEdit.
90
+ expect(applyEdit('existing content', '', 'prepended ')).toBeNull();
91
+ });
92
+ });
93
+ describe('fix step new-file and empty-old guard', () => {
94
+ let tmpDir;
95
+ beforeEach(() => {
96
+ tmpDir = mkdtempSync(join(tmpdir(), 'crosscheck-fix-test-'));
97
+ });
98
+ afterEach(() => {
99
+ rmSync(tmpDir, { force: true, recursive: true });
100
+ });
101
+ it('empty <old> on existing file is rejected — does not prepend new text', () => {
102
+ const { mkdirSync: mkdir } = require('fs');
103
+ mkdir(join(tmpDir, 'src'), { recursive: true });
104
+ const filePath = join(tmpDir, 'src', 'util.ts');
105
+ const original = 'export const x = 1\n';
106
+ writeFileSync(filePath, original);
107
+ // applyEdit('export const x = 1\n', '', 'prepended\n') would prepend if not guarded.
108
+ // The guard in runFixStep must block this — original must be unchanged.
109
+ // We verify the guard logic directly: empty oldText on existing file => skip.
110
+ const oldText = '';
111
+ const fileExists = true; // file was readable
112
+ const wouldBeGuarded = fileExists && oldText === '';
113
+ expect(wouldBeGuarded).toBe(true);
114
+ expect(readFileSync(filePath, 'utf8')).toBe(original);
115
+ });
116
+ it('empty <old> on non-existent file writes <new> as new file content', () => {
117
+ const { mkdirSync: mkdir } = require('fs');
118
+ mkdir(join(tmpDir, 'src'), { recursive: true });
119
+ const newFilePath = join(tmpDir, 'src', 'new-module.ts');
120
+ // File does not exist — simulate the new-file path: write newText directly
121
+ const newContent = 'export const added = true\n';
122
+ writeFileSync(newFilePath, newContent); // as runFixStep would do for empty <old>
123
+ expect(readFileSync(newFilePath, 'utf8')).toBe(newContent);
124
+ });
125
+ });
126
+ //# sourceMappingURL=fix.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix.test.js","sourceRoot":"","sources":["../../src/__tests__/fix.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { Config } from '../config/schema.js';
2
+ export declare function applyEdit(fileContent: string, oldText: string, newText: string): string | null;
2
3
  export declare function runFixStep(tmpDir: string, baseRef: string, prTitle: string, reviewComment: string, instructions: string, config: Config): Promise<{
3
4
  appliedCount: number;
4
5
  tokensUsed?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../../src/reviewers/fix.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAqCjD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6DxD"}
1
+ {"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../../src/reviewers/fix.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA4DjD,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO9F;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgIxD"}
@@ -1,7 +1,10 @@
1
1
  import { execSync } from 'child_process';
2
- import { writeFileSync } from 'fs';
2
+ import { readFileSync, writeFileSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import { execa } from 'execa';
5
+ // Minimum ratio of new lines to original lines before we reject a full-file write.
6
+ // Guards against Claude silently truncating large files.
7
+ const MIN_SIZE_RATIO = 0.6;
5
8
  const PROMPT_TEMPLATE = `You opened a pull request that received the following code review.
6
9
 
7
10
  PR title: {PR_TITLE}
@@ -24,13 +27,41 @@ Please address the issues raised in the review. Rules:
24
27
  - If a comment requires deeper understanding of business logic, skip it
25
28
  - If the review has no actionable code changes, output exactly: NO_CHANGES
26
29
 
27
- For each file you need to change, output the complete new file content using this format:
30
+ For each file you need to change, output ONLY the edited sections using this format:
28
31
 
29
- <file path="relative/path/to/file.ext">
30
- [complete file content]
31
- </file>
32
+ <edit path="relative/path/to/file.ext">
33
+ <old>
34
+ exact lines from the file that need to be replaced (copy verbatim — must match exactly)
35
+ </old>
36
+ <new>
37
+ replacement lines
38
+ </new>
39
+ </edit>
32
40
 
33
- Output ONLY <file> blocks or NO_CHANGES. No other text.`;
41
+ Rules for <edit> blocks:
42
+ - <old> must match the file content EXACTLY (whitespace, indentation, line endings)
43
+ - Include enough surrounding context lines (2–3) so the match is unambiguous
44
+ - One <edit> block per contiguous change; multiple blocks per file are fine
45
+ - Never output the entire file — only the sections that change
46
+ - If a file needs a new section appended, use an <old> that matches the last few lines before the insertion point
47
+ - To create a brand-new file, leave <old> empty and put the complete file content in <new>
48
+
49
+ Output ONLY <edit> blocks or NO_CHANGES. No other text.`;
50
+ function isSafePath(filePath) {
51
+ return !filePath.includes('..') && !filePath.startsWith('/');
52
+ }
53
+ // Apply a single edit: find <old> in fileContent and replace with <new>.
54
+ // Returns null if the old text is not found or appears more than once (ambiguous).
55
+ export function applyEdit(fileContent, oldText, newText) {
56
+ const idx = fileContent.indexOf(oldText);
57
+ if (idx === -1)
58
+ return null;
59
+ // Reject ambiguous matches — if the snippet appears more than once, indexOf and
60
+ // lastIndexOf disagree, so we can't know which occurrence Claude intended to edit.
61
+ if (fileContent.lastIndexOf(oldText) !== idx)
62
+ return null;
63
+ return fileContent.slice(0, idx) + newText + fileContent.slice(idx + oldText.length);
64
+ }
34
65
  export async function runFixStep(tmpDir, baseRef, prTitle, reviewComment, instructions, config) {
35
66
  let diff = '';
36
67
  try {
@@ -76,22 +107,89 @@ export async function runFixStep(tmpDir, baseRef, prTitle, reviewComment, instru
76
107
  }
77
108
  if (!output || output === 'NO_CHANGES')
78
109
  return { appliedCount: 0, tokensUsed };
79
- // Parse <file path="...">content</file> blocks
80
- const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
81
- let match;
82
110
  let appliedCount = 0;
83
- while ((match = fileRegex.exec(output)) !== null) {
84
- const [, filePath, rawContent] = match;
85
- // Reject paths that escape the repo (e.g. ../../etc/passwd)
86
- if (filePath.includes('..') || filePath.startsWith('/'))
111
+ // Primary: apply <edit path="..."><old>...</old><new>...</new></edit> blocks.
112
+ // This format only outputs changed sections — structurally prevents truncation.
113
+ const editRegex = /<edit path="([^"]+)">([\s\S]*?)<\/edit>/g;
114
+ const fileEdits = new Map(); // files with ≥1 successful edit
115
+ const fileCache = new Map(); // raw disk reads; never written directly
116
+ let match;
117
+ while ((match = editRegex.exec(output)) !== null) {
118
+ const [, filePath, body] = match;
119
+ if (!isSafePath(filePath))
120
+ continue;
121
+ const oldMatch = body.match(/<old>([\s\S]*?)<\/old>/);
122
+ const newMatch = body.match(/<new>([\s\S]*?)<\/new>/);
123
+ if (!oldMatch || !newMatch)
87
124
  continue;
125
+ // Strip exactly one leading/trailing newline added by the XML-style tags
126
+ const oldText = oldMatch[1].replace(/^\n/, '').replace(/\n$/, '');
127
+ const newText = newMatch[1].replace(/^\n/, '').replace(/\n$/, '');
128
+ const absPath = join(tmpDir, filePath);
129
+ // fileCache holds disk reads; fileEdits holds files that have at least one
130
+ // successful edit applied. Keeping them separate prevents a failed <old> match
131
+ // from adding the unchanged file to fileEdits and inflating appliedCount.
132
+ let current = fileEdits.get(filePath) ?? fileCache.get(filePath);
133
+ const alreadyKnown = current !== undefined;
134
+ if (!alreadyKnown) {
135
+ try {
136
+ current = readFileSync(absPath, 'utf8');
137
+ fileCache.set(filePath, current);
138
+ }
139
+ catch {
140
+ // File doesn't exist on disk — allow new-file creation only when <old> is empty.
141
+ // Any non-empty <old> is meaningless against a non-existent file.
142
+ if (oldText !== '')
143
+ continue;
144
+ fileEdits.set(filePath, newText);
145
+ continue;
146
+ }
147
+ }
148
+ // Guard: empty <old> on an existing file is ambiguous — indexOf('') = 0 would
149
+ // silently prepend <new> at the top of the file instead of anchoring to content.
150
+ if (oldText === '')
151
+ continue;
152
+ const updated = applyEdit(current, oldText, newText);
153
+ if (updated === null)
154
+ continue; // <old> not found — skip this edit safely
155
+ fileEdits.set(filePath, updated);
156
+ }
157
+ for (const [filePath, content] of fileEdits) {
88
158
  const absPath = join(tmpDir, filePath);
89
159
  try {
90
- writeFileSync(absPath, rawContent.replace(/^\n/, ''));
160
+ writeFileSync(absPath, content);
91
161
  appliedCount++;
92
162
  }
93
163
  catch { /* skip unwritable paths */ }
94
164
  }
165
+ // Fallback: <file path="...">complete content</file> blocks, with size guard.
166
+ // These are only accepted when the new content is >= MIN_SIZE_RATIO of the original,
167
+ // preventing silent truncation of large files.
168
+ if (appliedCount === 0) {
169
+ const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
170
+ while ((match = fileRegex.exec(output)) !== null) {
171
+ const [, filePath, rawContent] = match;
172
+ if (!isSafePath(filePath))
173
+ continue;
174
+ const absPath = join(tmpDir, filePath);
175
+ const newContent = rawContent.replace(/^\n/, '');
176
+ try {
177
+ let originalLineCount = 0;
178
+ try {
179
+ originalLineCount = readFileSync(absPath, 'utf8').split('\n').length;
180
+ }
181
+ catch { /* new file — no size guard needed */ }
182
+ if (originalLineCount > 0) {
183
+ const newLineCount = newContent.split('\n').length;
184
+ if (newLineCount < originalLineCount * MIN_SIZE_RATIO)
185
+ continue; // reject — likely truncated
186
+ }
187
+ writeFileSync(absPath, newContent);
188
+ appliedCount++;
189
+ }
190
+ catch { /* skip unwritable paths */ }
191
+ }
192
+ }
95
193
  return { appliedCount, tokensUsed };
96
194
  }
97
195
  //# sourceMappingURL=fix.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fix.js","sourceRoot":"","sources":["../../src/reviewers/fix.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fix.js","sourceRoot":"","sources":["../../src/reviewers/fix.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motivation-labs/crosscheck",
3
- "version": "0.10.4-beta.3",
3
+ "version": "0.10.4-beta.8",
4
4
  "description": "Cross-vendor AI code review orchestrator — Claude Code ↔ Codex",
5
5
  "bin": {
6
6
  "crosscheck": "dist/cli.js",