@trim21/personal-pi-extensions 0.1.643 → 0.1.644
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/package.json +1 -1
- package/src/claude-code/files.ts +22 -36
- package/src/lib/file-reads.ts +61 -20
- package/src/opencode/files.ts +24 -37
package/package.json
CHANGED
package/src/claude-code/files.ts
CHANGED
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
createReadsState,
|
|
18
18
|
type FileSnapshot,
|
|
19
19
|
type ReadsState,
|
|
20
|
-
|
|
20
|
+
recordRead,
|
|
21
|
+
recordReads,
|
|
21
22
|
requireCurrentRead,
|
|
22
23
|
restoreReads,
|
|
23
24
|
snapshotOf,
|
|
@@ -247,13 +248,11 @@ export function registerFileTools(
|
|
|
247
248
|
{ type: "text", text: `Read image file [${imageMime}]` },
|
|
248
249
|
{ type: "image", data, mimeType: imageMime },
|
|
249
250
|
];
|
|
250
|
-
const
|
|
251
|
-
const key = await readStateKey(filePath);
|
|
252
|
-
state.reads.set(key, snapshot);
|
|
251
|
+
const reads = await recordRead(state, filePath, snapshotOf(image, false));
|
|
253
252
|
return {
|
|
254
253
|
content,
|
|
255
254
|
details: {
|
|
256
|
-
reads
|
|
255
|
+
reads,
|
|
257
256
|
pendant: {
|
|
258
257
|
subtitle: formatSubtitlePath(ctx.cwd, filePath),
|
|
259
258
|
title: "Read",
|
|
@@ -264,7 +263,6 @@ export function registerFileTools(
|
|
|
264
263
|
|
|
265
264
|
const offset = params.offset ?? 1;
|
|
266
265
|
const limit = params.limit;
|
|
267
|
-
const key = await readStateKey(filePath);
|
|
268
266
|
const buffer = await readFile(filePath);
|
|
269
267
|
|
|
270
268
|
if (isBinary(buffer.subarray(0, SAMPLE_BYTES))) {
|
|
@@ -285,8 +283,7 @@ export function registerFileTools(
|
|
|
285
283
|
`File content (${estimatedTokens} tokens) exceeds maximum allowed tokens (${MAX_READ_TOKENS}). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whole file.`,
|
|
286
284
|
);
|
|
287
285
|
}
|
|
288
|
-
const
|
|
289
|
-
state.reads.set(key, snapshot);
|
|
286
|
+
const reads = await recordRead(state, filePath, snapshotOf(buffer));
|
|
290
287
|
// 与 Edit / Write 同一条驻留路径:didOpen 后等待该文件的诊断并报告
|
|
291
288
|
const diagnostics = await getService().lspDiagnosticsForFile(filePath, ctx.cwd, {
|
|
292
289
|
notify: (message, level) => ctx.ui.notify(message, level),
|
|
@@ -300,7 +297,7 @@ export function registerFileTools(
|
|
|
300
297
|
},
|
|
301
298
|
],
|
|
302
299
|
details: {
|
|
303
|
-
reads
|
|
300
|
+
reads,
|
|
304
301
|
pendant: {
|
|
305
302
|
subtitle: formatSubtitlePath(
|
|
306
303
|
ctx.cwd,
|
|
@@ -384,9 +381,7 @@ export function registerFileTools(
|
|
|
384
381
|
await mkdir(dirname(filePath), { recursive: true });
|
|
385
382
|
}
|
|
386
383
|
await writeFile(filePath, newString, "utf8");
|
|
387
|
-
const
|
|
388
|
-
const key = await readStateKey(filePath);
|
|
389
|
-
state.reads.set(key, snapshot);
|
|
384
|
+
const reads = await recordRead(state, filePath, snapshotOf(newString));
|
|
390
385
|
const diff = generateDiffString("", convertLeadingTabsToSpaces(newString));
|
|
391
386
|
signal?.throwIfAborted();
|
|
392
387
|
const diagnostics = await getService().lspDiagnosticsForFile(filePath, ctx.cwd, {
|
|
@@ -399,7 +394,7 @@ export function registerFileTools(
|
|
|
399
394
|
diff: diff.diff,
|
|
400
395
|
patch: generateUnifiedPatch(filePath, "", convertLeadingTabsToSpaces(newString)),
|
|
401
396
|
firstChangedLine: diff.firstChangedLine,
|
|
402
|
-
reads
|
|
397
|
+
reads,
|
|
403
398
|
},
|
|
404
399
|
diagnostics,
|
|
405
400
|
];
|
|
@@ -436,8 +431,7 @@ export function registerFileTools(
|
|
|
436
431
|
"File is a Jupyter Notebook. Use the NotebookEditTool to edit this file.",
|
|
437
432
|
);
|
|
438
433
|
}
|
|
439
|
-
|
|
440
|
-
requireCurrentRead(state, key, filePath, content);
|
|
434
|
+
await requireCurrentRead(state, filePath, content);
|
|
441
435
|
await access(filePath, constants.R_OK | constants.W_OK);
|
|
442
436
|
signal?.throwIfAborted();
|
|
443
437
|
const original = content.toString("utf8");
|
|
@@ -450,8 +444,7 @@ export function registerFileTools(
|
|
|
450
444
|
signal,
|
|
451
445
|
});
|
|
452
446
|
await writeFile(filePath, restored, "utf8");
|
|
453
|
-
const
|
|
454
|
-
state.reads.set(key, snapshot);
|
|
447
|
+
const reads = await recordRead(state, filePath, snapshotOf(restored));
|
|
455
448
|
// patch/diff 仅供显示:前导 tab 转空格,避免 UI 渲染错位(对齐 Claude Code)
|
|
456
449
|
const diff = generateDiffString(
|
|
457
450
|
convertLeadingTabsToSpaces(original),
|
|
@@ -475,7 +468,7 @@ export function registerFileTools(
|
|
|
475
468
|
convertLeadingTabsToSpaces(restored),
|
|
476
469
|
),
|
|
477
470
|
firstChangedLine: diff.firstChangedLine,
|
|
478
|
-
reads
|
|
471
|
+
reads,
|
|
479
472
|
},
|
|
480
473
|
diagnostics,
|
|
481
474
|
];
|
|
@@ -527,13 +520,11 @@ export function registerFileTools(
|
|
|
527
520
|
[string, FileToolDetails, DiagnosticReport]
|
|
528
521
|
>(filePath, async () => {
|
|
529
522
|
let original: string | undefined;
|
|
530
|
-
let key: string | undefined;
|
|
531
523
|
try {
|
|
532
524
|
const value = await stat(filePath);
|
|
533
525
|
if (value.isFile()) {
|
|
534
526
|
const content = await readFile(filePath);
|
|
535
|
-
|
|
536
|
-
requireCurrentRead(state, key, filePath, content);
|
|
527
|
+
await requireCurrentRead(state, filePath, content);
|
|
537
528
|
original = content.toString("utf8");
|
|
538
529
|
}
|
|
539
530
|
} catch (error) {
|
|
@@ -552,10 +543,7 @@ export function registerFileTools(
|
|
|
552
543
|
});
|
|
553
544
|
await mkdir(dirname(filePath), { recursive: true });
|
|
554
545
|
await writeFile(filePath, params.content, "utf8");
|
|
555
|
-
const
|
|
556
|
-
// 新建文件:writeFile 之后 realpath 才能解析;覆盖写则复用上面的 key
|
|
557
|
-
const resolvedKey = key ?? (await readStateKey(filePath));
|
|
558
|
-
state.reads.set(resolvedKey, snapshot);
|
|
546
|
+
const reads = await recordRead(state, filePath, snapshotOf(params.content));
|
|
559
547
|
const diff = generateDiffString(original ?? "", params.content);
|
|
560
548
|
const text =
|
|
561
549
|
original === undefined
|
|
@@ -572,7 +560,7 @@ export function registerFileTools(
|
|
|
572
560
|
diff: diff.diff,
|
|
573
561
|
patch: generateUnifiedPatch(filePath, original ?? "", params.content),
|
|
574
562
|
firstChangedLine: diff.firstChangedLine,
|
|
575
|
-
reads
|
|
563
|
+
reads,
|
|
576
564
|
},
|
|
577
565
|
diagnostics,
|
|
578
566
|
];
|
|
@@ -639,16 +627,14 @@ export default function claudeCodeFileTools(
|
|
|
639
627
|
onEnabled: (pi, service) => {
|
|
640
628
|
registerLspRenameTool(pi, service, {
|
|
641
629
|
policy,
|
|
642
|
-
recordReads:
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
return reads;
|
|
651
|
-
},
|
|
630
|
+
recordReads: (applied) =>
|
|
631
|
+
recordReads(
|
|
632
|
+
state,
|
|
633
|
+
applied.map((fileEdit) => ({
|
|
634
|
+
path: fileEdit.path,
|
|
635
|
+
snapshot: snapshotOf(fileEdit.newText),
|
|
636
|
+
})),
|
|
637
|
+
),
|
|
652
638
|
});
|
|
653
639
|
registerLspInspectTools(pi, service);
|
|
654
640
|
},
|
package/src/lib/file-reads.ts
CHANGED
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
* - 工具名集合:claude-code 是大写 Read/Edit/Write,opencode 是小写;
|
|
7
7
|
* - 指纹来源:claude-code 手里已有内容,直接 snapshotOf(content);opencode 的
|
|
8
8
|
* read 是流式分页读取,整文件指纹用 fileDigest(path)。
|
|
9
|
+
*
|
|
10
|
+
* 记账 key 的解析与读写由本模块独占(readStateKey / recordRead / require*),
|
|
11
|
+
* 调用点只给路径与快照,不自己拼 details 里的 map。
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
14
|
import { createHash } from "node:crypto";
|
|
12
15
|
import { createReadStream } from "node:fs";
|
|
13
16
|
import { realpath } from "node:fs/promises";
|
|
17
|
+
import { basename, dirname, join } from "node:path";
|
|
14
18
|
|
|
15
19
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
16
20
|
import { type Static, Type } from "typebox";
|
|
@@ -72,32 +76,73 @@ export async function digestIfExists(filePath: string): Promise<string | undefin
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
/**
|
|
75
|
-
* 记账 key:解析 symlink
|
|
76
|
-
* old_string
|
|
79
|
+
* 记账 key:解析 symlink 后的真实路径。文件尚未落盘(Write 新建 / Edit 空
|
|
80
|
+
* old_string 创建)时向上找到最深的已存在祖先、解析它再把剩余路径段拼回去——
|
|
81
|
+
* 结果与文件落盘后 realpath(filePath) 相同,因此调用点不必关心算 key 的时机
|
|
82
|
+
* (写入路径尤其容易在创建分支上算早)。
|
|
77
83
|
*/
|
|
78
84
|
export async function readStateKey(filePath: string): Promise<string> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
const pending: string[] = [];
|
|
86
|
+
let current = filePath;
|
|
87
|
+
for (;;) {
|
|
88
|
+
try {
|
|
89
|
+
return join(await realpath(current), ...pending.toReversed());
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (!isMissingPath(error)) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const parent = dirname(current);
|
|
96
|
+
if (parent === current) {
|
|
97
|
+
// 一路到文件系统根仍不存在:无法解析,回退调用方给出的路径
|
|
83
98
|
return filePath;
|
|
84
99
|
}
|
|
85
|
-
|
|
100
|
+
pending.push(basename(current));
|
|
101
|
+
current = parent;
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
|
|
105
|
+
/**
|
|
106
|
+
* 记账「这些文件的内容即各自的 snapshot」:解析 key 写入 state,返回可直接放进
|
|
107
|
+
* 工具 details 的 reads 片段。
|
|
108
|
+
*
|
|
109
|
+
* 这一侧是对象而不是 Map:details 是 JSON(宿主按 `JsonValue` 写进 session
|
|
110
|
+
* JSONL),Map 序列化过去会变成 `{}`,记账就丢了;读取方向与之相对(
|
|
111
|
+
* `deserializeReads` 从同一个对象形状还原成 Map)。
|
|
112
|
+
*/
|
|
113
|
+
export async function recordReads(
|
|
114
|
+
state: ReadsState,
|
|
115
|
+
entries: readonly { path: string; snapshot: FileSnapshot }[],
|
|
116
|
+
): Promise<Record<string, FileSnapshot>> {
|
|
117
|
+
const reads: Record<string, FileSnapshot> = {};
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
const key = await readStateKey(entry.path);
|
|
120
|
+
state.reads.set(key, entry.snapshot);
|
|
121
|
+
reads[key] = entry.snapshot;
|
|
122
|
+
}
|
|
123
|
+
return reads;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** 单文件记账:`recordReads` 的单项形式。 */
|
|
127
|
+
export async function recordRead(
|
|
128
|
+
state: ReadsState,
|
|
129
|
+
filePath: string,
|
|
130
|
+
snapshot: FileSnapshot,
|
|
131
|
+
): Promise<Record<string, FileSnapshot>> {
|
|
132
|
+
return await recordReads(state, [{ path: filePath, snapshot }]);
|
|
133
|
+
}
|
|
134
|
+
|
|
89
135
|
function snapshotsEqual(left: FileSnapshot, right: FileSnapshot): boolean {
|
|
90
136
|
return left.digest === right.digest;
|
|
91
137
|
}
|
|
92
138
|
|
|
93
139
|
/** 校验「已读且未变」:未读过、非文本、指纹不符都拒绝写入。 */
|
|
94
|
-
export function requireCurrentRead(
|
|
140
|
+
export async function requireCurrentRead(
|
|
95
141
|
state: ReadsState,
|
|
96
|
-
key: string,
|
|
97
142
|
filePath: string,
|
|
98
143
|
currentContent: Uint8Array | string,
|
|
99
|
-
): void {
|
|
100
|
-
const readSnapshot = state.reads.get(
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const readSnapshot = state.reads.get(await readStateKey(filePath));
|
|
101
146
|
if (!readSnapshot) {
|
|
102
147
|
throw new Error("File has not been read yet. Read it first before writing to it.");
|
|
103
148
|
}
|
|
@@ -113,21 +158,17 @@ export function requireCurrentRead(
|
|
|
113
158
|
|
|
114
159
|
/**
|
|
115
160
|
* 过期校验:该文件已有读取记录时,要求磁盘上的当前指纹与记录一致,否则拒绝
|
|
116
|
-
*
|
|
161
|
+
* 写入;从未读过(没有记录)时直接放行,且不做整文件指纹计算。
|
|
117
162
|
*
|
|
118
163
|
* opencode 的 write 用这个:没读过的文件允许直接写,读过之后再被外部改动就必须
|
|
119
|
-
* 重新 read
|
|
164
|
+
* 重新 read。文件已不存在(指纹 undefined)同样算过期。
|
|
120
165
|
*/
|
|
121
|
-
export function requireUnchangedRead(
|
|
122
|
-
state
|
|
123
|
-
key: string,
|
|
124
|
-
currentDigest: string | undefined,
|
|
125
|
-
): void {
|
|
126
|
-
const readSnapshot = state.reads.get(key);
|
|
166
|
+
export async function requireUnchangedRead(state: ReadsState, filePath: string): Promise<void> {
|
|
167
|
+
const readSnapshot = state.reads.get(await readStateKey(filePath));
|
|
127
168
|
if (!readSnapshot) {
|
|
128
169
|
return;
|
|
129
170
|
}
|
|
130
|
-
if (
|
|
171
|
+
if (readSnapshot.digest !== (await digestIfExists(filePath))) {
|
|
131
172
|
throw new Error(
|
|
132
173
|
"File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.",
|
|
133
174
|
);
|
package/src/opencode/files.ts
CHANGED
|
@@ -38,11 +38,11 @@ import { Type } from "typebox";
|
|
|
38
38
|
|
|
39
39
|
import {
|
|
40
40
|
createReadsState,
|
|
41
|
-
digestIfExists,
|
|
42
41
|
fileDigest,
|
|
43
42
|
type FileSnapshot,
|
|
44
43
|
type ReadsState,
|
|
45
|
-
|
|
44
|
+
recordRead,
|
|
45
|
+
recordReads,
|
|
46
46
|
requireCurrentRead,
|
|
47
47
|
requireUnchangedRead,
|
|
48
48
|
restoreReads,
|
|
@@ -500,13 +500,11 @@ function registerReadTool(pi: ExtensionAPI, getService: () => LspService, state:
|
|
|
500
500
|
{ type: "text", text: "Image read successfully" },
|
|
501
501
|
{ type: "image", data: base64, mimeType },
|
|
502
502
|
];
|
|
503
|
-
const
|
|
504
|
-
const snapshot = snapshotOf(buffer, false);
|
|
505
|
-
state.reads.set(key, snapshot);
|
|
503
|
+
const reads = await recordRead(state, absolutePath, snapshotOf(buffer, false));
|
|
506
504
|
return {
|
|
507
505
|
content,
|
|
508
506
|
details: {
|
|
509
|
-
reads
|
|
507
|
+
reads,
|
|
510
508
|
pendant: { subtitle: formatSubtitlePath(ctx.cwd, absolutePath) },
|
|
511
509
|
},
|
|
512
510
|
};
|
|
@@ -521,7 +519,6 @@ function registerReadTool(pi: ExtensionAPI, getService: () => LspService, state:
|
|
|
521
519
|
|
|
522
520
|
// 记账指纹先于分页读取:文件若在读取期间被改动,指纹对应的是改动前的
|
|
523
521
|
// 内容,后续 edit 会要求重新 read(失败方向安全)。
|
|
524
|
-
const key = await readStateKey(absolutePath);
|
|
525
522
|
const snapshot: FileSnapshot = { digest: await fileDigest(absolutePath), textEditable: true };
|
|
526
523
|
|
|
527
524
|
const effectiveOffset = offset || 1;
|
|
@@ -555,7 +552,7 @@ function registerReadTool(pi: ExtensionAPI, getService: () => LspService, state:
|
|
|
555
552
|
outputText = `${header}${numbered}\n\n(End of file - total ${page.count} lines)${footer}`;
|
|
556
553
|
}
|
|
557
554
|
|
|
558
|
-
|
|
555
|
+
const reads = await recordRead(state, absolutePath, snapshot);
|
|
559
556
|
|
|
560
557
|
// opencode: 与 edit / write 同一条驻留路径:didOpen 后等待该文件的诊断并报告
|
|
561
558
|
const diagnostics = await getService().lspDiagnosticsForFile(absolutePath, ctx.cwd, {
|
|
@@ -572,7 +569,7 @@ function registerReadTool(pi: ExtensionAPI, getService: () => LspService, state:
|
|
|
572
569
|
content,
|
|
573
570
|
details: {
|
|
574
571
|
...details,
|
|
575
|
-
reads
|
|
572
|
+
reads,
|
|
576
573
|
pendant: {
|
|
577
574
|
subtitle: formatSubtitlePath(
|
|
578
575
|
ctx.cwd,
|
|
@@ -667,16 +664,14 @@ function registerEditTool(
|
|
|
667
664
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
668
665
|
signal?.throwIfAborted();
|
|
669
666
|
await writeFile(absolutePath, newString, "utf8");
|
|
670
|
-
// 新建文件无需先 read
|
|
671
|
-
const
|
|
672
|
-
const snapshot = snapshotOf(newString);
|
|
673
|
-
state.reads.set(key, snapshot);
|
|
667
|
+
// 新建文件无需先 read(无处可读),写后记账
|
|
668
|
+
const reads = await recordRead(state, absolutePath, snapshotOf(newString));
|
|
674
669
|
const diagnostics = await getService().lspDiagnosticsForFile(absolutePath, ctx.cwd, {
|
|
675
670
|
signal,
|
|
676
671
|
});
|
|
677
672
|
return [
|
|
678
673
|
"Edit applied successfully.",
|
|
679
|
-
{ diff: "", patch: "", firstChangedLine: 0, reads
|
|
674
|
+
{ diff: "", patch: "", firstChangedLine: 0, reads },
|
|
680
675
|
diagnostics,
|
|
681
676
|
] as const;
|
|
682
677
|
}
|
|
@@ -702,8 +697,7 @@ function registerEditTool(
|
|
|
702
697
|
|
|
703
698
|
// 指纹校验必须发生在写入之前:未 read 或外部已改动都拒绝编辑
|
|
704
699
|
const rawContent = await readFile(absolutePath, "utf8");
|
|
705
|
-
|
|
706
|
-
requireCurrentRead(state, key, absolutePath, rawContent);
|
|
700
|
+
await requireCurrentRead(state, absolutePath, rawContent);
|
|
707
701
|
|
|
708
702
|
const applied = applyEdit(rawContent, oldString, newString, replaceAll);
|
|
709
703
|
signal?.throwIfAborted();
|
|
@@ -715,8 +709,7 @@ function registerEditTool(
|
|
|
715
709
|
signal,
|
|
716
710
|
});
|
|
717
711
|
await writeFile(absolutePath, applied.finalContent, "utf8");
|
|
718
|
-
const
|
|
719
|
-
state.reads.set(key, snapshot);
|
|
712
|
+
const reads = await recordRead(state, absolutePath, snapshotOf(applied.finalContent));
|
|
720
713
|
|
|
721
714
|
const diffOld = normalizeToLF(applied.contentOld);
|
|
722
715
|
const diffNew = normalizeToLF(applied.contentNew);
|
|
@@ -731,7 +724,7 @@ function registerEditTool(
|
|
|
731
724
|
diff: diffResult.diff,
|
|
732
725
|
patch,
|
|
733
726
|
firstChangedLine: diffResult.firstChangedLine,
|
|
734
|
-
reads
|
|
727
|
+
reads,
|
|
735
728
|
},
|
|
736
729
|
diagnostics,
|
|
737
730
|
] as const;
|
|
@@ -811,12 +804,9 @@ function registerWriteTool(
|
|
|
811
804
|
async () => {
|
|
812
805
|
signal?.throwIfAborted();
|
|
813
806
|
|
|
814
|
-
const key = await readStateKey(absolutePath);
|
|
815
807
|
// write 不要求先读过;但若已有读取记录,磁盘内容必须仍是读取时的样子,
|
|
816
808
|
// 否则先重新 read(外部改动过的文件不让盲写覆盖)
|
|
817
|
-
|
|
818
|
-
requireUnchangedRead(state, key, await digestIfExists(absolutePath));
|
|
819
|
-
}
|
|
809
|
+
await requireUnchangedRead(state, absolutePath);
|
|
820
810
|
|
|
821
811
|
// opencode: desiredBom = source.bom || next.bom —— 保留原文件 BOM,
|
|
822
812
|
// 否则用新内容自带的 BOM。旧内容整读一次,既是 BOM 的来源,也是审批预览
|
|
@@ -842,14 +832,13 @@ function registerWriteTool(
|
|
|
842
832
|
await mkdir(dir, { recursive: true });
|
|
843
833
|
signal?.throwIfAborted();
|
|
844
834
|
await writeFile(absolutePath, finalContent, "utf8");
|
|
845
|
-
//
|
|
846
|
-
const
|
|
847
|
-
state.reads.set(key, snapshot);
|
|
835
|
+
// 写后记账:紧接着的 edit 不该再要求重新 read
|
|
836
|
+
const reads = await recordRead(state, absolutePath, snapshotOf(finalContent));
|
|
848
837
|
const diagnostics = await getService().lspDiagnosticsForFile(absolutePath, ctx.cwd, {
|
|
849
838
|
signal,
|
|
850
839
|
});
|
|
851
840
|
|
|
852
|
-
return ["Wrote file successfully.", { reads
|
|
841
|
+
return ["Wrote file successfully.", { reads }, diagnostics] as const;
|
|
853
842
|
},
|
|
854
843
|
);
|
|
855
844
|
|
|
@@ -906,16 +895,14 @@ export default function opencodeFileTools(
|
|
|
906
895
|
onEnabled: (pi, service) => {
|
|
907
896
|
registerLspRenameTool(pi, service, {
|
|
908
897
|
policy,
|
|
909
|
-
recordReads:
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
return reads;
|
|
918
|
-
},
|
|
898
|
+
recordReads: (applied) =>
|
|
899
|
+
recordReads(
|
|
900
|
+
state,
|
|
901
|
+
applied.map((fileEdit) => ({
|
|
902
|
+
path: fileEdit.path,
|
|
903
|
+
snapshot: snapshotOf(fileEdit.newText),
|
|
904
|
+
})),
|
|
905
|
+
),
|
|
919
906
|
});
|
|
920
907
|
registerLspInspectTools(pi, service);
|
|
921
908
|
},
|