@d3ara1n/pi-hashline-edit 0.1.0 → 0.1.1

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/src/pi/pi.test.ts CHANGED
@@ -3,25 +3,25 @@ import assert from "node:assert/strict";
3
3
  import { canonicalPath } from "./read-tool.ts";
4
4
  import { missingInputError } from "./edit-tool.ts";
5
5
 
6
- test("canonicalPath resolve 相对/绝对", () => {
6
+ test("canonicalPath resolves relative/absolute", () => {
7
7
  assert.equal(canonicalPath("/cwd", "foo.ts"), "/cwd/foo.ts");
8
8
  assert.equal(canonicalPath("/cwd", "./foo.ts"), "/cwd/foo.ts");
9
9
  assert.equal(canonicalPath("/cwd", "/abs/x.ts"), "/abs/x.ts");
10
10
  });
11
11
 
12
- test("missingInputError: edits 数组 → 明确告知不降级", () => {
12
+ test("missingInputError: edits array → explicit no-degradation message", () => {
13
13
  const msg = missingInputError("f.ts", { edits: [{ oldText: "a", newText: "b" }] });
14
14
  assert.ok(msg.includes("legacy"), msg);
15
15
  assert.ok(msg.includes("ONLY"), msg);
16
16
  assert.ok(msg.includes("f.ts"), msg);
17
17
  });
18
18
 
19
- test("missingInputError: 顶层 oldText/newText 也识别为旧格式", () => {
19
+ test("missingInputError: top-level oldText/newText also recognized as legacy", () => {
20
20
  const msg = missingInputError("f.ts", { oldText: "a", newText: "b" });
21
21
  assert.ok(msg.includes("legacy"));
22
22
  });
23
23
 
24
- test("missingInputError: 仅缺 input(非旧格式)", () => {
24
+ test("missingInputError: only input missing (not legacy)", () => {
25
25
  const msg = missingInputError("f.ts", {});
26
26
  assert.ok(msg.includes("missing"));
27
27
  assert.ok(!msg.includes("legacy"));
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Override read:文本文件输出「行号#hash│内容」并记录快照;
3
- * 非文本(图片/二进制)与读取错误透传内置 read,renderer 自动继承。
2
+ * Override read: text files output "lineNo#hash│content" and record a snapshot;
3
+ * non-text (images/binary) and read errors delegate to the built-in read, whose
4
+ * renderer is inherited automatically.
4
5
  *
5
6
  * @module pi-hashline-edit/pi
6
7
  */
@@ -14,12 +15,12 @@ import { getState, getSnapshot, recordSnapshot } from "./state.ts";
14
15
  const MAX_LINES = 2000;
15
16
  const MAX_BYTES = 256 * 1024;
16
17
 
17
- /** canonical path:read/edit 共用,保证 snapshot key 一致。 */
18
+ /** canonical path: shared by read/edit to keep the snapshot key consistent. */
18
19
  export function canonicalPath(cwd: string, p: string): string {
19
20
  return resolve(cwd, p);
20
21
  }
21
22
 
22
- /** 构造 read override(registerTool 的 ToolDefinition 片段)。 */
23
+ /** Build the read override (a ToolDefinition fragment for registerTool). */
23
24
  export function makeReadOverride(cwd: string) {
24
25
  const builtin = createReadTool(cwd);
25
26
 
@@ -36,8 +37,7 @@ export function makeReadOverride(cwd: string) {
36
37
  parameters: builtin.parameters,
37
38
 
38
39
  async execute(toolCallId: string, params: any, signal: AbortSignal | undefined, onUpdate: any) {
39
- // 未启用 → 完全透传内置
40
- // 未启用 或 用户已取消 → 透传内置(builtin 自行处理 abort)
40
+ // Not enabled OR user cancelled → delegate to the built-in (builtin handles abort itself)
41
41
  if (!getState().config.enabled || signal?.aborted) return builtin.execute(toolCallId, params, signal, onUpdate);
42
42
 
43
43
  const absPath = canonicalPath(cwd, params.path as string);
@@ -45,18 +45,18 @@ export function makeReadOverride(cwd: string) {
45
45
  try {
46
46
  buf = await readFile(absPath);
47
47
  } catch {
48
- // 读取错误 → 透传内置(它有完善的错误信息)
48
+ // read error → delegate to the built-in (it has polished error messages)
49
49
  return builtin.execute(toolCallId, params, signal, onUpdate);
50
50
  }
51
51
 
52
- // 二进制/图片检测(null 字节)→ 透传内置(内置用 file-type 处理图片)
52
+ // binary/image detection (null byte) → delegate to the built-in (it uses file-type for images)
53
53
  if (buf.includes(0)) return builtin.execute(toolCallId, params, signal, onUpdate);
54
54
 
55
55
  const text = buf.toString("utf-8");
56
56
  const allLines = splitLines(text);
57
57
  const totalLines = allLines.length;
58
58
 
59
- // 记录全文快照(edit 锚基于全文行号)
59
+ // record a full-file snapshot (edit anchors are based on full-file line numbers)
60
60
  const snap = recordSnapshot(absPath, text);
61
61
 
62
62
  // offset/limit
@@ -92,7 +92,7 @@ export function makeReadOverride(cwd: string) {
92
92
  };
93
93
  }
94
94
 
95
- /** 供 edit override 复用:取某 path 的已记录快照(按 canonical path)。 */
95
+ /** Reused by the edit override: look up a recorded snapshot for a path (by canonical path). */
96
96
  export function lookupSnapshot(cwd: string, p: string) {
97
97
  return getSnapshot(canonicalPath(cwd, p));
98
98
  }
package/src/pi/state.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Session 级状态:文件快照存储(LRU)+ 配置。
2
+ * Session-level state: file snapshot store (LRU) + config.
3
3
  *
4
- * globalThis 单例(规避 Bun module identity 问题,见仓库 AGENTS)。
5
- * 快照 LRU 驱逐(默认 64 个文件),防长会话内存无限增长——冷文件被新文件挤出。
6
- * read 记录、edit 校验;key 为 canonical 绝对路径。
7
- * config 放 state,session_start 重载。
4
+ * globalThis singleton (avoids Bun module-identity issues; see the repo AGENTS).
5
+ * Snapshot LRU eviction (default 64 files) prevents unbounded memory growth in
6
+ * long sessions — cold files are pushed out by new ones. read records; edit
7
+ * verifies; key is the canonical absolute path.
8
+ * config lives in state and is reloaded on session_start.
8
9
  *
9
10
  * @module pi-hashline-edit/pi
10
11
  */
@@ -15,11 +16,11 @@ import type { HashlineEditConfig } from "./config.ts";
15
16
 
16
17
  const GLOBAL_KEY = "__piHashlineEdit";
17
18
  const DEFAULT_CONFIG: HashlineEditConfig = { enabled: true, hashLen: 4 };
18
- /** 最多缓存的文件快照数;超出按 LRU 驱逐最久未访问的。 */
19
+ /** Maximum number of cached file snapshots; beyond this the least-recently-accessed is evicted. */
19
20
  const MAX_SNAPSHOTS = 64;
20
21
 
21
22
  export interface HashlineEditState {
22
- /** canonical path → 快照。LRU 顺序:Map 插入序,最近访问的在末尾。 */
23
+ /** canonical path → snapshot. LRU order: Map insertion order, most recently accessed at the end. */
23
24
  readonly snapshots: Map<string, FileSnapshot>;
24
25
  hashLen: number;
25
26
  config: HashlineEditConfig;
@@ -38,7 +39,7 @@ export function getState(): HashlineEditState {
38
39
  return state;
39
40
  }
40
41
 
41
- /** 写入快照并维持 LRU:移到末尾(最近使用),超限驱逐最旧(Map 首项)。 */
42
+ /** Write a snapshot and maintain LRU: move to the end (most recently used); evict the oldest (Map's first entry) when over the limit. */
42
43
  function touchAndEvict(map: Map<string, FileSnapshot>, path: string, snap: FileSnapshot): void {
43
44
  if (map.has(path)) map.delete(path);
44
45
  map.set(path, snap);
@@ -49,7 +50,7 @@ function touchAndEvict(map: Map<string, FileSnapshot>, path: string, snap: FileS
49
50
  }
50
51
  }
51
52
 
52
- /** 记录文件快照(read 时调用):算行 hash + LRU。 */
53
+ /** Record a file snapshot (called on read): compute line hashes + LRU bookkeeping. */
53
54
  export function recordSnapshot(canonicalPath: string, text: string): FileSnapshot {
54
55
  const state = getState();
55
56
  const snap = createSnapshot(canonicalPath, text, state.hashLen);
@@ -57,12 +58,12 @@ export function recordSnapshot(canonicalPath: string, text: string): FileSnapsho
57
58
  return snap;
58
59
  }
59
60
 
60
- /** 存入已算好的快照(edit 成功后更新),走 LRU。 */
61
+ /** Store an already-computed snapshot (updated after a successful edit), via LRU. */
61
62
  export function putSnapshot(canonicalPath: string, snap: FileSnapshot): void {
62
63
  touchAndEvict(getState().snapshots, canonicalPath, snap);
63
64
  }
64
65
 
65
- /** 取文件快照(edit 校验用);命中时移到末尾(LRU touch)。 */
66
+ /** Get a file snapshot (for edit verification); on a hit move it to the end (LRU touch). */
66
67
  export function getSnapshot(canonicalPath: string): FileSnapshot | undefined {
67
68
  const map = getState().snapshots;
68
69
  const snap = map.get(canonicalPath);
@@ -73,7 +74,7 @@ export function getSnapshot(canonicalPath: string): FileSnapshot | undefined {
73
74
  return snap;
74
75
  }
75
76
 
76
- /** 清空快照(session 重启时)。 */
77
+ /** Clear all snapshots (on session restart). */
77
78
  export function clearSnapshots(): void {
78
79
  getState().snapshots.clear();
79
80
  }