Skip to content

Commit 85c667d

Browse files
committed
...
1 parent 8a87c6c commit 85c667d

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

src/commands/context.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- `replace.js` enforces `g` flag and supports dry-run reporting.
2424
- `escapeString.js` centralises string coercion, enabling new built-ins without touching `loop.js` internals.
2525
- `preapproval.js` validation now guards against risky shells/flags with dedicated unit coverage for new heuristics.
26+
- `edit.js` and `replace.js` now return the full updated file contents (with headings) in their stdout payloads, letting the LLM consume changes without issuing a follow-up read.
2627

2728
## Risks / Gaps
2829

src/commands/replace.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,37 @@ function validateFiles(files) {
4444
});
4545
}
4646

47-
function buildResultSummary(results, dryRun) {
47+
function buildResultOutput(results, dryRun) {
4848
const totalMatches = results.reduce((acc, item) => acc + item.matches, 0);
4949
const touchedFiles = results.filter((item) => item.matches > 0).length;
5050

51-
const lines = [`Total matches: ${totalMatches}`, `Files with changes: ${touchedFiles}`];
51+
const summaryLines = [
52+
`Total matches: ${totalMatches}`,
53+
`Files with changes: ${touchedFiles}`,
54+
];
5255

5356
results.forEach((item) => {
54-
lines.push(`${item.path}: ${item.matches} matches${dryRun ? ' (dry-run)' : ''}`);
57+
summaryLines.push(`${item.path}: ${item.matches} matches${dryRun ? ' (dry-run)' : ''}`);
5558
});
5659

5760
if (dryRun) {
58-
lines.push('Dry-run: no files were modified.');
61+
summaryLines.push('Dry-run: no files were modified.');
5962
}
6063

61-
return lines.join('\n');
64+
const detailSections = results.map((item) => {
65+
const status = dryRun
66+
? item.matches > 0
67+
? `Preview ${item.path}`
68+
: `Preview (no matches) ${item.path}`
69+
: item.changed
70+
? `Updated ${item.path}`
71+
: `Unchanged ${item.path}`;
72+
73+
const header = `--- ${item.path}`;
74+
return `${status}\n\n${header}\n${item.content}`;
75+
});
76+
77+
return summaryLines.concat('', detailSections).join('\n');
6278
}
6379

6480
export function runReplace(spec, cwd = '.') {
@@ -101,22 +117,29 @@ export function runReplace(spec, cwd = '.') {
101117
return replacement;
102118
});
103119

104-
if (!dryRun && matches > 0 && replaced !== original) {
120+
const relOutputPath = path.relative(process.cwd(), absPath);
121+
const wouldChange = matches > 0 && replaced !== original;
122+
123+
if (!dryRun && wouldChange) {
105124
try {
106125
fs.writeFileSync(absPath, replaced, { encoding });
107126
} catch (err) {
108127
throw new Error(`Unable to write file: ${absPath}${err.message}`);
109128
}
110129
}
111130

131+
const finalContent = dryRun ? replaced : wouldChange ? replaced : original;
132+
112133
results.push({
113-
path: path.relative(process.cwd(), absPath),
134+
path: relOutputPath,
114135
matches,
136+
content: finalContent,
137+
changed: !dryRun && wouldChange,
115138
});
116139
}
117140

118141
return {
119-
stdout: buildResultSummary(results, dryRun),
142+
stdout: buildResultOutput(results, dryRun),
120143
stderr: '',
121144
exit_code: 0,
122145
killed: false,

tests/unit/replaceCommand.test.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,16 @@ describe('runReplace', () => {
2323
dir,
2424
);
2525

26+
const relOutput = path.relative(process.cwd(), file);
27+
2628
expect(result.exit_code).toBe(0);
2729
expect(fs.readFileSync(file, 'utf8')).toBe('bar and bar again');
30+
expect(result.stdout).toContain('Total matches: 2');
31+
expect(result.stdout).toContain('Files with changes: 1');
32+
expect(result.stdout).toContain(`${relOutput}: 2 matches`);
33+
expect(result.stdout).toContain(`Updated ${relOutput}`);
34+
expect(result.stdout).toContain(`--- ${relOutput}`);
35+
expect(result.stdout).toContain('bar and bar again');
2836
});
2937

3038
test('supports dry-run without modifying files', async () => {
@@ -42,8 +50,14 @@ describe('runReplace', () => {
4250
dir,
4351
);
4452

53+
const relOutput = path.relative(process.cwd(), file);
54+
4555
expect(result.exit_code).toBe(0);
46-
expect(result.stdout).toMatch(/Dry-run/);
56+
expect(result.stdout).toContain('Dry-run: no files were modified.');
57+
expect(result.stdout).toContain(`${relOutput}: 1 matches (dry-run)`);
58+
expect(result.stdout).toContain(`Preview ${relOutput}`);
59+
expect(result.stdout).toContain(`--- ${relOutput}`);
60+
expect(result.stdout).toContain('hello universe');
4761
expect(fs.readFileSync(file, 'utf8')).toBe('hello world');
4862
});
4963

0 commit comments

Comments
 (0)