@trim21/personal-pi-extensions 0.1.539 → 0.1.541
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 +2 -2
- package/src/claude-code/edit.md +1 -0
- package/src/claude-code/files.ts +24 -15
- package/src/claude-code/write.md +1 -0
- package/src/lib/lsp/lsp.ts +6 -23
- package/src/opencode/files.ts +16 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trim21/personal-pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.541",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
|
|
6
6
|
"keywords": [
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"prettier --write"
|
|
94
94
|
]
|
|
95
95
|
},
|
|
96
|
-
"packageManager": "pnpm@11.
|
|
96
|
+
"packageManager": "pnpm@11.27.0",
|
|
97
97
|
"engines": {
|
|
98
98
|
"node": ">=24.14"
|
|
99
99
|
},
|
package/src/claude-code/edit.md
CHANGED
|
@@ -4,6 +4,7 @@ Performs exact string replacements in files.
|
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
6
|
|
|
7
|
+
- You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.
|
|
7
8
|
- When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: line number + tab. Everything after that is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.
|
|
8
9
|
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
|
|
9
10
|
- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
|
package/src/claude-code/files.ts
CHANGED
|
@@ -199,9 +199,8 @@ async function readStateKey(filePath: string): Promise<string> {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* 不强制先 Read;存在已读快照时校验文本可编辑且 digest 一致。
|
|
202
|
+
* 校验「已读且未变」。key 与 currentContent 由调用方提供:调用方每次工具调用
|
|
203
|
+
* 只 realpath / readFile 一次,避免重复 IO。
|
|
205
204
|
*/
|
|
206
205
|
function requireCurrentRead(
|
|
207
206
|
state: ClaudeCodeState,
|
|
@@ -210,7 +209,9 @@ function requireCurrentRead(
|
|
|
210
209
|
currentContent: Uint8Array,
|
|
211
210
|
): void {
|
|
212
211
|
const readSnapshot = state.reads.get(key);
|
|
213
|
-
if (!readSnapshot)
|
|
212
|
+
if (!readSnapshot) {
|
|
213
|
+
throw new Error("File has not been read yet. Read it first before writing to it.");
|
|
214
|
+
}
|
|
214
215
|
if (!readSnapshot.textEditable) {
|
|
215
216
|
throw new Error(`Cannot edit or overwrite a binary file with a text tool: ${filePath}`);
|
|
216
217
|
}
|
|
@@ -331,18 +332,25 @@ export function registerFileTools(
|
|
|
331
332
|
}
|
|
332
333
|
const snapshot = snapshotOf(buffer);
|
|
333
334
|
state.reads.set(key, snapshot);
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
335
|
+
// 与 Edit / Write 同一条驻留路径:didOpen 后等待该文件的诊断并报告
|
|
336
|
+
const {
|
|
337
|
+
text: diagnosticText,
|
|
338
|
+
errorCount,
|
|
339
|
+
warningCount,
|
|
340
|
+
} = await getService().lspDiagnosticsForFile(filePath, ctx.cwd, {
|
|
341
|
+
notify: (message, level) => ctx.ui.notify(message, level),
|
|
342
|
+
});
|
|
340
343
|
return {
|
|
341
|
-
content: [
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: appendLspDiagnosticText(formatted.text, diagnosticText, errorCount),
|
|
348
|
+
},
|
|
349
|
+
],
|
|
342
350
|
details: {
|
|
343
351
|
reads: { [key]: snapshot },
|
|
344
352
|
pendant: {
|
|
345
|
-
subtitle: formatSubtitlePath(ctx.cwd, filePath),
|
|
353
|
+
subtitle: formatSubtitlePath(ctx.cwd, filePath, errorCount, warningCount),
|
|
346
354
|
title: "Read",
|
|
347
355
|
} satisfies ToolPendant,
|
|
348
356
|
},
|
|
@@ -355,6 +363,7 @@ export function registerFileTools(
|
|
|
355
363
|
label: "Edit",
|
|
356
364
|
description: [
|
|
357
365
|
"Performs exact string replacements in files.",
|
|
366
|
+
"You must use Read on the file before editing it.",
|
|
358
367
|
"old_string must match exactly and must be unique unless replace_all is true.",
|
|
359
368
|
"This tool does not use regular expressions or fuzzy matching.",
|
|
360
369
|
].join("\n"),
|
|
@@ -570,7 +579,7 @@ export function registerFileTools(
|
|
|
570
579
|
description: [
|
|
571
580
|
"Writes a file to the local filesystem.",
|
|
572
581
|
"This tool overwrites an existing file with the full content provided.",
|
|
573
|
-
"Prefer Edit for partial changes.",
|
|
582
|
+
"If the file exists, you must use Read first. Prefer Edit for partial changes.",
|
|
574
583
|
].join("\n"),
|
|
575
584
|
promptSnippet: "Create or overwrite files",
|
|
576
585
|
promptGuidelines: [WRITE_PROMPT],
|
|
@@ -693,8 +702,8 @@ export default function claudeCodeFileTools(pi: ExtensionAPI, options?: LspServi
|
|
|
693
702
|
const state = createClaudeCodeState();
|
|
694
703
|
|
|
695
704
|
// LSP 专属工具(lsp-rename / inspect 族)仅在 lsp.json 存在 enabled 服务器时
|
|
696
|
-
// 注册(session_start
|
|
697
|
-
//
|
|
705
|
+
// 注册(session_start 校验后);本工具集跟踪 read-before-write 状态,rename
|
|
706
|
+
// 落盘的文件要标记为已读并随 details 持久化(restoreFileReads 依赖 details.reads)。
|
|
698
707
|
const manager = createLspManager(
|
|
699
708
|
pi,
|
|
700
709
|
{
|
package/src/claude-code/write.md
CHANGED
|
@@ -5,6 +5,7 @@ Writes a file to the local filesystem.
|
|
|
5
5
|
Usage:
|
|
6
6
|
|
|
7
7
|
- This tool will overwrite the existing file if there is one at the provided path.
|
|
8
|
+
- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.
|
|
8
9
|
- Prefer the Edit tool for modifying existing files — it only sends the diff. Only use this tool to create new files or for complete rewrites.
|
|
9
10
|
- NEVER create documentation files (*.md) or README files unless explicitly requested by the User.
|
|
10
11
|
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
|
package/src/lib/lsp/lsp.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 的 id 直接忽略;
|
|
17
17
|
* - client 按 (root, serverID) 缓存,并发 spawn 去重,启动失败记入
|
|
18
18
|
* broken(冷却期内跳过,冷却过后下次触碰自动重试)并主动 notify;
|
|
19
|
-
* - 工具只与 touchFile /
|
|
19
|
+
* - 工具只与 touchFile / diagnostics / lspDiagnosticsForFile 三个方法打交道;通知回调按请求传入。
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { readFileSync } from "node:fs";
|
|
@@ -397,9 +397,8 @@ export interface LspService {
|
|
|
397
397
|
diagnostics?: "document" | "full",
|
|
398
398
|
options?: LspRequestOptions,
|
|
399
399
|
): Promise<void>;
|
|
400
|
-
/** read 用:只发文件事件通知服务器磁盘上有该文件,不驻留、不等诊断。 */
|
|
401
|
-
notifyFile(file: string, cwd: string, options?: LspRequestOptions): Promise<void>;
|
|
402
400
|
diagnostics(): Promise<Record<string, Diagnostic[]>>;
|
|
401
|
+
/** read / edit / write 用:等待文档诊断并返回该文件的 ERROR / WARN 报告与数量。 */
|
|
403
402
|
lspDiagnosticsForFile(
|
|
404
403
|
file: string,
|
|
405
404
|
cwd: string,
|
|
@@ -736,7 +735,7 @@ export function createLspService(
|
|
|
736
735
|
/**
|
|
737
736
|
* 记录一次启动失败:进入 broken(冷却期内跳过)、渲染 status,并按节流
|
|
738
737
|
* 间隔主动 notify。错误上报优先走会话级 sessionNotify——不依赖触发请求
|
|
739
|
-
* 恰好携带 notify(否则
|
|
738
|
+
* 恰好携带 notify(否则 opencode 工具集等不带请求级 notify 的通道会把失败吞掉);未注入
|
|
740
739
|
* 会话通知时退回请求级 notify 兜底。
|
|
741
740
|
*/
|
|
742
741
|
function reportStartupFailure(
|
|
@@ -881,7 +880,7 @@ export function createLspService(
|
|
|
881
880
|
|
|
882
881
|
/**
|
|
883
882
|
* 打开文档让服务器索引 / 产出诊断。diagnostics 传 "document" 时最多等 5s,
|
|
884
|
-
* "full" 最多等 10s
|
|
883
|
+
* "full" 最多等 10s;不传则只 didOpen 不等待。
|
|
885
884
|
*/
|
|
886
885
|
async function touchFile(
|
|
887
886
|
file: string,
|
|
@@ -923,23 +922,8 @@ export function createLspService(
|
|
|
923
922
|
}
|
|
924
923
|
|
|
925
924
|
/**
|
|
926
|
-
* read
|
|
927
|
-
*
|
|
928
|
-
*/
|
|
929
|
-
async function notifyFile(file: string, cwd: string, options?: LspRequestOptions): Promise<void> {
|
|
930
|
-
const clients = await getClients(file, cwd, options?.notify);
|
|
931
|
-
await Promise.all(
|
|
932
|
-
clients.map((client) =>
|
|
933
|
-
client.notify.watchedFiles([{ path: file, type: "changed", isDirectory: false }]),
|
|
934
|
-
),
|
|
935
|
-
).catch(() => {
|
|
936
|
-
// 文件事件通知失败不影响读取
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* edit/write 用:等待文档诊断并返回该文件的 ERROR / WARN 报告(text 空串表示无此类诊断)
|
|
942
|
-
* 与数量。内部所有 LSP 失败都会被吞掉,不干扰写操作本身。
|
|
925
|
+
* read / edit / write 用:等待文档诊断并返回该文件的 ERROR / WARN 报告(text 空串表示无此类诊断)
|
|
926
|
+
* 与数量。内部所有 LSP 失败都会被吞掉,不干扰读取或写操作本身。
|
|
943
927
|
*/
|
|
944
928
|
async function lspDiagnosticsForFile(
|
|
945
929
|
file: string,
|
|
@@ -1184,7 +1168,6 @@ export function createLspService(
|
|
|
1184
1168
|
|
|
1185
1169
|
return {
|
|
1186
1170
|
touchFile,
|
|
1187
|
-
notifyFile,
|
|
1188
1171
|
diagnostics,
|
|
1189
1172
|
lspDiagnosticsForFile,
|
|
1190
1173
|
rename,
|
package/src/opencode/files.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 对齐官方 v1(packages/opencode/src/tool/{read,edit,write}.ts):
|
|
9
9
|
* - read:流式分行(LF / CRLF / CR)、每行 `N: ` 行号前缀、单行 2000
|
|
10
|
-
* 字符截断、1 起始 offset
|
|
10
|
+
* 字符截断、1 起始 offset、目录排序;读取后等待并报告该文档的诊断。
|
|
11
11
|
* 不接 PDF、不接 <system-reminder>;图片 magic 检测保留。
|
|
12
12
|
* - edit:匹配引擎 + 把 old/new 转到文件换行后再替换;写后等待文档诊断。
|
|
13
13
|
* - write:BOM 保留(source.bom || next.bom);写后同 edit 的诊断输出。
|
|
@@ -431,20 +431,26 @@ function registerReadTool(pi: ExtensionAPI, getService: () => LspService): void
|
|
|
431
431
|
outputText = `${header}${numbered}\n\n(End of file - total ${page.count} lines)${footer}`;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
434
|
+
// opencode: 与 edit / write 同一条驻留路径:didOpen 后等待该文件的诊断并报告
|
|
435
|
+
const {
|
|
436
|
+
text: diagnosticText,
|
|
437
|
+
errorCount,
|
|
438
|
+
warningCount,
|
|
439
|
+
} = await getService().lspDiagnosticsForFile(absolutePath, ctx.cwd, { signal });
|
|
440
|
+
content = [
|
|
441
|
+
{
|
|
442
|
+
type: "text",
|
|
443
|
+
text: appendLspDiagnosticText(outputText, diagnosticText, errorCount),
|
|
444
|
+
},
|
|
445
|
+
];
|
|
442
446
|
|
|
443
447
|
return {
|
|
444
448
|
content,
|
|
445
449
|
details: {
|
|
446
450
|
...details,
|
|
447
|
-
pendant: {
|
|
451
|
+
pendant: {
|
|
452
|
+
subtitle: formatSubtitlePath(ctx.cwd, absolutePath, errorCount, warningCount),
|
|
453
|
+
},
|
|
448
454
|
},
|
|
449
455
|
};
|
|
450
456
|
},
|