@trim21/personal-pi-extensions 0.1.491 → 0.1.493
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/bwrap/runtime.ts +1 -6
- package/src/claude-code/common.ts +0 -4
- package/src/claude-code/files.ts +1 -20
- package/src/lib/markdown.ts +16 -0
- package/src/lib/write-guard.ts +7 -5
package/package.json
CHANGED
package/src/bwrap/runtime.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { throttle } from "lodash-es";
|
|
|
16
16
|
import { type TObject, Type } from "typebox";
|
|
17
17
|
|
|
18
18
|
import { type CommandSpec, parseCommand } from "../lib/cli.js";
|
|
19
|
+
import { fenceCodeBlock } from "../lib/markdown.js";
|
|
19
20
|
import { formatDisplayPath } from "../lib/path.js";
|
|
20
21
|
import {
|
|
21
22
|
type CheckboxAction,
|
|
@@ -144,12 +145,6 @@ function escapeHtml(text: string): string {
|
|
|
144
145
|
.replaceAll('"', """);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
function fenceCodeBlock(code: string): string {
|
|
148
|
-
const longestRun = Math.max(...(code.match(/`+/g)?.map((match) => match.length) ?? [0]));
|
|
149
|
-
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
150
|
-
return `${fence}\n${code}\n${fence}`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
148
|
/** 进度推送的节流间隔(对齐 pi 内置 bash 工具的 100ms)。 */
|
|
154
149
|
const BASH_UPDATE_THROTTLE_MS = 100;
|
|
155
150
|
/** 进度快照只保留尾部内容,避免大输出每 100ms 全量推给 TUI。 */
|
|
@@ -18,10 +18,6 @@ import { Value } from "typebox/value";
|
|
|
18
18
|
const fileSnapshotSchema = Type.Object({
|
|
19
19
|
digest: Type.String(),
|
|
20
20
|
textEditable: Type.Boolean(),
|
|
21
|
-
// 以下字段仅 Read 写入,供同范围重复读取 dedup;Edit/Write 不写,
|
|
22
|
-
// 覆盖记录后 offset 缺省 → 不再 dedup,强制重新 Read(对齐 CC readFileState)
|
|
23
|
-
offset: Type.Optional(Type.Number()),
|
|
24
|
-
limit: Type.Optional(Type.Number()),
|
|
25
21
|
});
|
|
26
22
|
|
|
27
23
|
export type FileSnapshot = Static<typeof fileSnapshotSchema>;
|
package/src/claude-code/files.ts
CHANGED
|
@@ -34,10 +34,6 @@ import { convertLeadingTabsToSpaces, findActualString, preserveQuoteStyle } from
|
|
|
34
34
|
|
|
35
35
|
const SAMPLE_BYTES = 4096;
|
|
36
36
|
|
|
37
|
-
/** 同范围重复读取、文件未变时返回的 stub(对齐 Claude Code 的 file_unchanged)。 */
|
|
38
|
-
export const FILE_UNCHANGED_STUB =
|
|
39
|
-
"File unchanged since last read. The content from the earlier Read tool_result in this conversation is still current — refer to that instead of re-reading.";
|
|
40
|
-
|
|
41
37
|
/** Read 全读时的文件大小上限(对齐 Claude Code 的 256KB)。 */
|
|
42
38
|
const MAX_READ_SIZE_BYTES = 0.25 * 1024 * 1024;
|
|
43
39
|
/** Read 输出 token 粗估上限(对齐 Claude Code 的 25K tokens;无 tokenizer,按 4 字符/token 估算)。 */
|
|
@@ -317,21 +313,6 @@ export function registerFileTools(
|
|
|
317
313
|
const key = await readStateKey(filePath);
|
|
318
314
|
const buffer = await readFile(filePath);
|
|
319
315
|
|
|
320
|
-
// 同范围 + checksum 未变 → 返回 stub 而非重发内容(对齐 CC readFileState;
|
|
321
|
-
// 复用 reads 里的 sha256,比 mtime 可靠,无时间片粒度问题)
|
|
322
|
-
const previous = state.reads.get(key);
|
|
323
|
-
if (
|
|
324
|
-
previous !== undefined &&
|
|
325
|
-
previous.offset !== undefined &&
|
|
326
|
-
previous.offset === offset &&
|
|
327
|
-
previous.limit === limit &&
|
|
328
|
-
snapshotsEqual(previous, snapshotOf(buffer))
|
|
329
|
-
) {
|
|
330
|
-
return {
|
|
331
|
-
content: [{ type: "text", text: FILE_UNCHANGED_STUB }],
|
|
332
|
-
details: { pendant: { subtitle: formatSubtitlePath(ctx.cwd, filePath) } },
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
316
|
if (isBinary(buffer.subarray(0, SAMPLE_BYTES)))
|
|
336
317
|
throw new Error(`Cannot read binary file: ${filePath}`);
|
|
337
318
|
// 全读(limit 未传)时受字节上限约束(对齐 Claude Code)
|
|
@@ -349,7 +330,7 @@ export function registerFileTools(
|
|
|
349
330
|
`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.`,
|
|
350
331
|
);
|
|
351
332
|
}
|
|
352
|
-
const snapshot =
|
|
333
|
+
const snapshot = snapshotOf(buffer);
|
|
353
334
|
state.reads.set(key, snapshot);
|
|
354
335
|
// LSP 文件事件通知是后台任务,失败不影响读取(read 不驻留文档)
|
|
355
336
|
void getService()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering helpers shared across extensions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wrap text in a fenced markdown code block.
|
|
7
|
+
*
|
|
8
|
+
* The fence uses one more backtick than the longest backtick run inside the
|
|
9
|
+
* content (min 3), so content containing markdown code fences (```) cannot
|
|
10
|
+
* close the block early and break rendering.
|
|
11
|
+
*/
|
|
12
|
+
export function fenceCodeBlock(code: string, lang = ""): string {
|
|
13
|
+
const longestRun = Math.max(...(code.match(/`+/g)?.map((match) => match.length) ?? [0]));
|
|
14
|
+
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
15
|
+
return `${fence}${lang}\n${code}\n${fence}`;
|
|
16
|
+
}
|
package/src/lib/write-guard.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { basename, isAbsolute, relative, sep } from "node:path";
|
|
|
19
19
|
import { generateUnifiedPatch } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
|
|
21
21
|
import { applyEdit, normalizeToLF } from "../opencode/edit-engine.js";
|
|
22
|
+
import { fenceCodeBlock } from "./markdown.js";
|
|
22
23
|
|
|
23
24
|
const ALWAYS_ALLOW = ["/tmp"];
|
|
24
25
|
const MAX_PREVIEW_LINES = 100;
|
|
@@ -41,11 +42,12 @@ function isPathAllowed(absolutePath: string, cwd: string): boolean {
|
|
|
41
42
|
/** Wrap patch text in a `diff` code block, truncating very large diffs. */
|
|
42
43
|
function wrapDiff(patch: string): string {
|
|
43
44
|
const lines = patch.split("\n");
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const body =
|
|
46
|
+
lines.length > MAX_PREVIEW_LINES
|
|
47
|
+
? `${lines.slice(0, MAX_PREVIEW_LINES).join("\n")}\n… (preview truncated to ${MAX_PREVIEW_LINES} lines)`
|
|
48
|
+
: patch;
|
|
49
|
+
// fenceCodeBlock 选更长的围栏,避免 patch 内容里的 ``` 提前闭合代码块
|
|
50
|
+
return fenceCodeBlock(body, "diff");
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/** The pending file change, described by the caller from already-parsed args. */
|