@agnishc/edb-diff-files 0.6.1 → 0.8.2
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 +4 -0
- package/package.json +1 -1
- package/src/index.ts +26 -40
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -56,29 +56,29 @@ class FilesViewComponent {
|
|
|
56
56
|
|
|
57
57
|
private handleListInput(data: string): void {
|
|
58
58
|
const n = this.entries.length;
|
|
59
|
-
if (
|
|
59
|
+
if (data === "j" || matchesKey(data, "down")) {
|
|
60
60
|
this.cursor = n > 0 ? Math.min(this.cursor + 1, n - 1) : 0;
|
|
61
|
-
this.
|
|
62
|
-
} else if (
|
|
61
|
+
this.rerender();
|
|
62
|
+
} else if (data === "k" || matchesKey(data, "up")) {
|
|
63
63
|
this.cursor = Math.max(0, this.cursor - 1);
|
|
64
|
-
this.
|
|
65
|
-
} else if (matchesKey(data, "
|
|
64
|
+
this.rerender();
|
|
65
|
+
} else if (matchesKey(data, "enter")) {
|
|
66
66
|
if (this.entries[this.cursor]) {
|
|
67
67
|
this.mode = "diff";
|
|
68
68
|
this.diffScroll = 0;
|
|
69
|
-
this.
|
|
69
|
+
this.rerender();
|
|
70
70
|
}
|
|
71
|
-
} else if (
|
|
71
|
+
} else if (data === "o") {
|
|
72
72
|
const entry = this.entries[this.cursor];
|
|
73
73
|
if (entry) {
|
|
74
74
|
this.statusMsg = this.openFileInEditor(entry.path);
|
|
75
|
-
this.
|
|
75
|
+
this.rerender();
|
|
76
76
|
}
|
|
77
|
-
} else if (
|
|
77
|
+
} else if (data === "f") {
|
|
78
78
|
const idx = FILTER_CYCLE.indexOf(this.filterMode);
|
|
79
79
|
this.filterMode = FILTER_CYCLE[(idx + 1) % FILTER_CYCLE.length]!;
|
|
80
80
|
this.cursor = 0;
|
|
81
|
-
this.
|
|
81
|
+
this.rerender();
|
|
82
82
|
} else if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
83
83
|
this.onClose();
|
|
84
84
|
}
|
|
@@ -88,33 +88,32 @@ class FilesViewComponent {
|
|
|
88
88
|
const viewH = this.viewportHeight();
|
|
89
89
|
const maxScroll = Math.max(0, this.currentDiffLines().length - viewH);
|
|
90
90
|
|
|
91
|
-
if (
|
|
91
|
+
if (data === "j" || matchesKey(data, "down")) {
|
|
92
92
|
this.diffScroll = Math.min(this.diffScroll + 1, maxScroll);
|
|
93
|
-
this.
|
|
94
|
-
} else if (
|
|
93
|
+
this.rerender();
|
|
94
|
+
} else if (data === "k" || matchesKey(data, "up")) {
|
|
95
95
|
this.diffScroll = Math.max(0, this.diffScroll - 1);
|
|
96
|
-
this.
|
|
96
|
+
this.rerender();
|
|
97
97
|
} else if (matchesKey(data, "ctrl+d")) {
|
|
98
98
|
this.diffScroll = Math.min(this.diffScroll + Math.floor(viewH / 2), maxScroll);
|
|
99
|
-
this.
|
|
99
|
+
this.rerender();
|
|
100
100
|
} else if (matchesKey(data, "ctrl+u")) {
|
|
101
101
|
this.diffScroll = Math.max(0, this.diffScroll - Math.floor(viewH / 2));
|
|
102
|
-
this.
|
|
103
|
-
} else if (
|
|
102
|
+
this.rerender();
|
|
103
|
+
} else if (data === "o") {
|
|
104
104
|
const entry = this.entries[this.cursor];
|
|
105
105
|
if (entry) {
|
|
106
106
|
this.statusMsg = this.openFileInEditor(entry.path);
|
|
107
|
-
this.
|
|
107
|
+
this.rerender();
|
|
108
108
|
}
|
|
109
|
-
} else if (matchesKey(data, "escape") ||
|
|
109
|
+
} else if (matchesKey(data, "escape") || data === "q") {
|
|
110
110
|
this.mode = "list";
|
|
111
111
|
this.statusMsg = "";
|
|
112
|
-
this.
|
|
112
|
+
this.rerender();
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
private openFileInEditor(filePath: string): string {
|
|
117
|
-
// Use same logic as pi core: respect $VISUAL / $EDITOR
|
|
118
117
|
const editorCmd = process.env.VISUAL || process.env.EDITOR;
|
|
119
118
|
if (!editorCmd) {
|
|
120
119
|
this.statusMsg = "No editor configured (set $VISUAL or $EDITOR)";
|
|
@@ -122,19 +121,15 @@ class FilesViewComponent {
|
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
try {
|
|
125
|
-
// Stop TUI to release terminal for interactive editor
|
|
126
124
|
this.tui.stop();
|
|
127
125
|
|
|
128
|
-
// Split by space to support editor arguments (e.g., "code --wait")
|
|
129
126
|
const [editor, ...editorArgs] = editorCmd.split(" ");
|
|
130
127
|
|
|
131
|
-
// Spawn editor synchronously with inherited stdio for interactive editing
|
|
132
128
|
const result = spawnSync(editor, [...editorArgs, filePath], {
|
|
133
129
|
stdio: "inherit",
|
|
134
130
|
shell: process.platform === "win32",
|
|
135
131
|
});
|
|
136
132
|
|
|
137
|
-
// On non-zero exit, show status
|
|
138
133
|
if (result.status !== 0) {
|
|
139
134
|
this.statusMsg = `Editor exited with status ${result.status}`;
|
|
140
135
|
} else {
|
|
@@ -143,9 +138,7 @@ class FilesViewComponent {
|
|
|
143
138
|
} catch {
|
|
144
139
|
this.statusMsg = "Failed to open editor";
|
|
145
140
|
} finally {
|
|
146
|
-
// Restart TUI
|
|
147
141
|
this.tui.start();
|
|
148
|
-
// Force full re-render since external editor uses alternate screen
|
|
149
142
|
this.tui.requestRender(true);
|
|
150
143
|
}
|
|
151
144
|
|
|
@@ -176,7 +169,6 @@ class FilesViewComponent {
|
|
|
176
169
|
private renderList(width: number): string[] {
|
|
177
170
|
const { entries, theme: th } = this;
|
|
178
171
|
const visible = this.filteredEntries();
|
|
179
|
-
// Clamp cursor to visible range
|
|
180
172
|
const cursor = Math.min(this.cursor, Math.max(0, visible.length - 1));
|
|
181
173
|
const lines: string[] = [];
|
|
182
174
|
|
|
@@ -318,7 +310,6 @@ class FilesViewComponent {
|
|
|
318
310
|
}
|
|
319
311
|
|
|
320
312
|
private viewportHeight(): number {
|
|
321
|
-
// Terminal rows minus header (≈5) and footer (≈4) lines
|
|
322
313
|
return Math.max(5, (process.stdout.rows ?? 40) - 9);
|
|
323
314
|
}
|
|
324
315
|
|
|
@@ -326,6 +317,12 @@ class FilesViewComponent {
|
|
|
326
317
|
this.cachedWidth = undefined;
|
|
327
318
|
this.cachedLines = undefined;
|
|
328
319
|
}
|
|
320
|
+
|
|
321
|
+
/** Invalidate cache and request TUI re-render. */
|
|
322
|
+
private rerender(): void {
|
|
323
|
+
this.invalidate();
|
|
324
|
+
this.tui.requestRender();
|
|
325
|
+
}
|
|
329
326
|
}
|
|
330
327
|
|
|
331
328
|
// ─── Extension ───────────────────────────────────────────────────────────────
|
|
@@ -374,7 +371,6 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
374
371
|
}
|
|
375
372
|
|
|
376
373
|
// ── write/edit tool_call ──────────────────────────────────────────────
|
|
377
|
-
// Record file paths the agent is about to touch
|
|
378
374
|
pi.on("tool_call", async (event: any, _ctx: any) => {
|
|
379
375
|
if (event.toolName === "write" || event.toolName === "edit") {
|
|
380
376
|
const fp = event.input?.path ?? event.input?.file_path ?? "";
|
|
@@ -394,8 +390,6 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
394
390
|
return;
|
|
395
391
|
}
|
|
396
392
|
|
|
397
|
-
// ── bash tool_call ────────────────────────────────────────────────
|
|
398
|
-
// Snapshot git state before the bash command runs
|
|
399
393
|
if (event.toolName === "bash" && inGit) {
|
|
400
394
|
const snapshot = getGitDiff(config.cwd);
|
|
401
395
|
bashSnapshots.set(event.toolCallId, snapshot);
|
|
@@ -403,7 +397,6 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
403
397
|
});
|
|
404
398
|
|
|
405
399
|
// ── bash tool_result ────────────────────────────────────────────────
|
|
406
|
-
// Diff current state vs snapshot to find new changes
|
|
407
400
|
pi.on("tool_result", async (event: any, _ctx: any) => {
|
|
408
401
|
if (event.toolName !== "bash") return;
|
|
409
402
|
if (event.isError) return;
|
|
@@ -414,14 +407,11 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
414
407
|
|
|
415
408
|
const after = getGitDiff(config.cwd);
|
|
416
409
|
|
|
417
|
-
// Find entries in after that are new or changed compared to before
|
|
418
410
|
const newEntries: GitDiffSnapshot = new Map();
|
|
419
411
|
for (const [path, changeType] of after) {
|
|
420
412
|
if (!before.has(path)) {
|
|
421
|
-
// New entry — file wasn't in the before snapshot
|
|
422
413
|
newEntries.set(path, changeType);
|
|
423
414
|
} else if (before.get(path) !== changeType) {
|
|
424
|
-
// Changed type (e.g., was M, now A because the file was deleted and recreated)
|
|
425
415
|
newEntries.set(path, changeType);
|
|
426
416
|
}
|
|
427
417
|
}
|
|
@@ -443,11 +433,9 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
443
433
|
: entries.filter((e: FileEntry) => e.changeType !== ChangeType.Deleted);
|
|
444
434
|
|
|
445
435
|
const lines: string[] = [];
|
|
446
|
-
// Flash header — briefly shows how many files changed this turn
|
|
447
436
|
lines.push(
|
|
448
437
|
`${colors.fgCreated}↯ ${addedCount} ${addedCount === 1 ? "file" : "files"} added this turn${colors.rst}`,
|
|
449
438
|
);
|
|
450
|
-
// Entries up to maxLines − 1 (the flash line takes one slot)
|
|
451
439
|
const shown = visible.slice(0, Math.max(1, config.maxLines - 1));
|
|
452
440
|
for (const e of shown) {
|
|
453
441
|
const prefix =
|
|
@@ -471,7 +459,6 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
471
459
|
});
|
|
472
460
|
|
|
473
461
|
// ── turn_end ────────────────────────────────────────────────────────
|
|
474
|
-
// Flash widget briefly when new files appear, then settle to normal.
|
|
475
462
|
pi.on("turn_end", async (_event: any, ctx: any) => {
|
|
476
463
|
if (tracker.size === 0) return;
|
|
477
464
|
|
|
@@ -494,7 +481,6 @@ export default function diffFilesExtension(pi: any): void {
|
|
|
494
481
|
});
|
|
495
482
|
|
|
496
483
|
// ── session_start ───────────────────────────────────────────────────
|
|
497
|
-
// Reset state for new sessions
|
|
498
484
|
pi.on("session_start", async (_event: any, ctx: any) => {
|
|
499
485
|
tracker.clear();
|
|
500
486
|
bashSnapshots.clear();
|