@nghyane/arcane 0.1.27 → 0.1.28
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 +8 -0
- package/package.json +1 -1
- package/src/patch/edit-tool.ts +12 -3
- package/src/patch/hashline.ts +11 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.1.28] - 2026-03-08
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fix edit tool: hashline delete missing `saveForUndo` causing unrecoverable file deletion
|
|
10
|
+
- Fix `any` types on `EditTool` class — replaced with `EditToolDetails`
|
|
11
|
+
- Fix `applyHashlineEdits` mutating caller's input array via splice
|
|
12
|
+
|
|
5
13
|
## [0.1.27] - 2026-03-08
|
|
6
14
|
|
|
7
15
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@nghyane/arcane",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.28",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/nghyane/arcane",
|
|
7
7
|
"author": "Can Bölük",
|
package/src/patch/edit-tool.ts
CHANGED
|
@@ -134,13 +134,21 @@ function mergeDiagnosticsWithWarnings(
|
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
export class EditTool implements AgentTool<TInput,
|
|
137
|
+
export class EditTool implements AgentTool<TInput, EditToolDetails, Theme> {
|
|
138
138
|
readonly name = "edit";
|
|
139
139
|
readonly label = "Edit";
|
|
140
140
|
readonly nonAbortable = true;
|
|
141
141
|
readonly concurrency = "exclusive";
|
|
142
|
-
readonly renderCall = editToolRenderer.renderCall as unknown as AgentTool<
|
|
143
|
-
|
|
142
|
+
readonly renderCall = editToolRenderer.renderCall as unknown as AgentTool<
|
|
143
|
+
TInput,
|
|
144
|
+
EditToolDetails,
|
|
145
|
+
Theme
|
|
146
|
+
>["renderCall"];
|
|
147
|
+
readonly renderResult = editToolRenderer.renderResult as unknown as AgentTool<
|
|
148
|
+
TInput,
|
|
149
|
+
EditToolDetails,
|
|
150
|
+
Theme
|
|
151
|
+
>["renderResult"];
|
|
144
152
|
|
|
145
153
|
readonly #allowFuzzy: boolean;
|
|
146
154
|
readonly #fuzzyThreshold: number;
|
|
@@ -252,6 +260,7 @@ export class EditTool implements AgentTool<TInput, any, Theme> {
|
|
|
252
260
|
|
|
253
261
|
if (deleteFile) {
|
|
254
262
|
if (await file.exists()) {
|
|
263
|
+
saveForUndo(absolutePath, await file.text());
|
|
255
264
|
await file.unlink();
|
|
256
265
|
}
|
|
257
266
|
invalidateFsScanAfterDelete(absolutePath);
|
package/src/patch/hashline.ts
CHANGED
|
@@ -592,7 +592,7 @@ function autocorrectEscapedTabs(lines: string[]): string[] {
|
|
|
592
592
|
*/
|
|
593
593
|
export function applyHashlineEdits(
|
|
594
594
|
content: string,
|
|
595
|
-
edits: HashlineEdit[],
|
|
595
|
+
edits: readonly HashlineEdit[],
|
|
596
596
|
): {
|
|
597
597
|
content: string;
|
|
598
598
|
firstChangedLine: number | undefined;
|
|
@@ -603,6 +603,8 @@ export function applyHashlineEdits(
|
|
|
603
603
|
return { content, firstChangedLine: undefined };
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
+
const mutableEdits: HashlineEdit[] = edits.map(e => ({ ...e, content: [...e.content] }));
|
|
607
|
+
|
|
606
608
|
const fileLines = content.split("\n");
|
|
607
609
|
const hadFinalNewline = content.endsWith("\n");
|
|
608
610
|
const originalFileLines = [...fileLines];
|
|
@@ -612,7 +614,7 @@ export function applyHashlineEdits(
|
|
|
612
614
|
const autocorrect = Bun.env.ARCANE_HL_AUTOCORRECT === "1";
|
|
613
615
|
|
|
614
616
|
const warnings: string[] = [];
|
|
615
|
-
for (const edit of
|
|
617
|
+
for (const edit of mutableEdits) {
|
|
616
618
|
const unicodeWarning = detectUnicodeEscapePlaceholders(edit.content);
|
|
617
619
|
if (unicodeWarning && !warnings.includes(unicodeWarning)) {
|
|
618
620
|
warnings.push(unicodeWarning);
|
|
@@ -622,7 +624,7 @@ export function applyHashlineEdits(
|
|
|
622
624
|
|
|
623
625
|
function collectExplicitlyTouchedLines(): Set<number> {
|
|
624
626
|
const touched = new Set<number>();
|
|
625
|
-
for (const edit of
|
|
627
|
+
for (const edit of mutableEdits) {
|
|
626
628
|
switch (edit.op) {
|
|
627
629
|
case "replace":
|
|
628
630
|
touched.add(edit.target.line);
|
|
@@ -652,7 +654,7 @@ export function applyHashlineEdits(
|
|
|
652
654
|
mismatches.push({ line: ref.line, expected: ref.hash, actual: actualHash });
|
|
653
655
|
return false;
|
|
654
656
|
}
|
|
655
|
-
for (const edit of
|
|
657
|
+
for (const edit of mutableEdits) {
|
|
656
658
|
switch (edit.op) {
|
|
657
659
|
case "replace": {
|
|
658
660
|
if (!validateRef(edit.target)) continue;
|
|
@@ -678,8 +680,8 @@ export function applyHashlineEdits(
|
|
|
678
680
|
}
|
|
679
681
|
const seenEditKeys = new Map<string, number>();
|
|
680
682
|
const dedupIndices = new Set<number>();
|
|
681
|
-
for (let i = 0; i <
|
|
682
|
-
const edit =
|
|
683
|
+
for (let i = 0; i < mutableEdits.length; i++) {
|
|
684
|
+
const edit = mutableEdits[i];
|
|
683
685
|
let lineKey: string;
|
|
684
686
|
switch (edit.op) {
|
|
685
687
|
case "replace":
|
|
@@ -697,13 +699,13 @@ export function applyHashlineEdits(
|
|
|
697
699
|
}
|
|
698
700
|
}
|
|
699
701
|
if (dedupIndices.size > 0) {
|
|
700
|
-
for (let i =
|
|
701
|
-
if (dedupIndices.has(i))
|
|
702
|
+
for (let i = mutableEdits.length - 1; i >= 0; i--) {
|
|
703
|
+
if (dedupIndices.has(i)) mutableEdits.splice(i, 1);
|
|
702
704
|
}
|
|
703
705
|
}
|
|
704
706
|
|
|
705
707
|
// Compute sort key (descending) — bottom-up application
|
|
706
|
-
const annotated =
|
|
708
|
+
const annotated = mutableEdits.map((edit, idx) => {
|
|
707
709
|
let sortLine: number;
|
|
708
710
|
let precedence: number;
|
|
709
711
|
switch (edit.op) {
|