pi-hashline-edit-pro 4.3.8 → 4.3.9

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/README.md CHANGED
@@ -89,7 +89,7 @@ Single line: use the same anchor for `remove_from` and `remove_to`. `replace_fro
89
89
 
90
90
  The request is checked before any file I/O, so a bad request never touches the file.
91
91
 
92
- Common copy-paste slips are fixed automatically: a leftover `anchor│` prefix in `replacement_lines` or the anchor fields (a prefix of 4 to 5 letters before `│`, for example `abde│`), diff-preview rows pasted into the replacement, and a boundary line pasted twice are reported as warnings, while a reversed range, stringified array text (even with a trailing JS method call, for example `[…].map(s => s)`), and embedded newlines are corrected silently. New lines that re-include a block adjacent to the range are stripped when that block is unique in the file. The whole run is stripped as one unit, so re-including an unchanged block next to the range never duplicates it. Boundary dedup has three modes in `/hashline-config`: `on` strips with a warning, `off` applies edits literally, and `strict` rejects the edit with `[E_BOUNDARY_STRICT]` when any replacement line would be stripped.
92
+ Common copy-paste slips are fixed automatically: a leftover `anchor│` prefix in `replacement_lines` or the anchor fields (a prefix of 4 to 5 letters before `│`, for example `abde│`), diff-preview rows pasted into the replacement, and a boundary line pasted twice are reported as warnings, while a reversed range, stringified array text (even with a trailing JS method call, for example `[…].map(s => s)`), and embedded newlines are corrected silently. With Boundary dedup `on`, new lines that re-include a block adjacent to the range are stripped when that block is unique in the file, and the whole run is stripped as one unit, so re-including an unchanged block next to the range never duplicates it. Boundary dedup has three modes in `/hashline-config` and is `off` by default: `on` strips with a warning, `strict` rejects the edit with `[E_BOUNDARY_STRICT]` when any replacement line would be stripped, and `off` applies edits literally.
93
93
  Content containing a NUL byte (`U+0000`) is rejected with `[E_BAD_SHAPE]` before any file I/O: writing it would make the file binary, so use an empty replacement to delete. This applies to `replace`'s `replacement_lines` and `insert`'s `lines`.
94
94
 
95
95
  Every line in the removed range must match what was last shown to you. The extension records the `anchor│content` rows it serves (`read` output, `anchor_grep` output, the auto-read block after `write`, the `+anchor│` and ` anchor│` rows of post-edit diffs, the current-range rows of `[E_RANGE_STALE]` feedback, and the context rows of stale-anchor feedback) and verifies the whole range against that record before writing. A line that changed on disk since it was shown, or an anchor that is not owned in this session, refuses the edit with `[E_RANGE_STALE]` or `[E_STALE_ANCHOR]` and returns the current range with fresh anchors, so the retry needs no `read`. An owned anchor enters the served record when its row is shown (after a restart, restored ownership counts as shown), so a file with no owned anchors cannot be edited by anchor at all; call `read` first. An owned line that was never shown — for example beyond an auto-read preview's truncation cap — is refused with `[E_RANGE_STALE]` and returns the current range, so the retry still needs no `read`.
@@ -197,7 +197,7 @@ Settings live in `~/.config/pi-hashline-edit-pro/config.json`, created when a se
197
197
  "anchorGrepEnabled": true,
198
198
  "requirePath": false,
199
199
  "strictInput": false,
200
- "boundaryDedupMode": "on",
200
+ "boundaryDedupMode": "off",
201
201
  "diffContextLines": 1
202
202
  }
203
203
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-hashline-edit-pro",
3
- "version": "4.3.8",
3
+ "version": "4.3.9",
4
4
  "type": "module",
5
5
  "description": "Hash-anchored read/replace/insert/grep tools for pi-coding-agent. Every line gets a unique 4-char tokenizer-friendly anchor that stays stable across edits; stale or ambiguous anchors are rejected, never fuzzy-matched. Undo persists across restarts.",
6
6
  "main": "index.ts",
package/src/config-ui.ts CHANGED
@@ -25,7 +25,7 @@ export function configRows(config: Config): ConfigRow[] {
25
25
  { key: "anchorGrepEnabled", label: "Anchor grep", hint: "anchor_grep tool (builtin grep off while on)", enabled: config.anchorGrepEnabled === true },
26
26
  { key: "requirePath", label: "Require path", hint: "replace + insert need path (RPC visibility)", enabled: config.requirePath === true },
27
27
  { key: "strictInput", label: "Strict input", hint: "Reject auto-fixable slips instead of warnings", enabled: config.strictInput === true },
28
- { key: "boundaryDedupMode", label: "Boundary dedup", hint: "Strip edge lines: on, strict (reject), off (literal)", enabled: (config.boundaryDedupMode ?? "on") !== "off", mode: config.boundaryDedupMode ?? "on", cycle: ["on", "strict", "off"] },
28
+ { key: "boundaryDedupMode", label: "Boundary dedup", hint: "Strip edge lines: on, strict (reject), off (literal)", enabled: (config.boundaryDedupMode ?? "off") !== "off", mode: config.boundaryDedupMode ?? "off", cycle: ["on", "strict", "off"] },
29
29
  ];
30
30
  }
31
31
 
package/src/config.ts CHANGED
@@ -29,7 +29,7 @@ const DEFAULT_CONFIG: Config = {
29
29
  autoReadAllIgnore: [],
30
30
  requirePath: false,
31
31
  strictInput: false,
32
- boundaryDedupMode: "on",
32
+ boundaryDedupMode: "off",
33
33
  diffContextLines: DEFAULT_DIFF_CONTEXT_LINES
34
34
  };
35
35
 
@@ -39,7 +39,7 @@ function parseBoundaryDedupMode(mode: unknown, legacy: unknown): BoundaryDedupMo
39
39
  if (mode === "on" || mode === "strict" || mode === "off") return mode;
40
40
  if (legacy === true) return "on";
41
41
  if (legacy === false) return "off";
42
- return DEFAULT_CONFIG.boundaryDedupMode ?? "on";
42
+ return DEFAULT_CONFIG.boundaryDedupMode ?? "off";
43
43
  }
44
44
 
45
45
  function parseAutoReadAllMode(value: unknown): AutoReadAllMode {
@@ -227,10 +227,10 @@ export async function cycleAutoReadAllMode(): Promise<AutoReadAllMode> {
227
227
  export const toggleRequirePath = (): Promise<boolean> => toggleFlag("requirePath");
228
228
  export const toggleStrictInput = (): Promise<boolean> => toggleFlag("strictInput");
229
229
  export async function cycleBoundaryDedupMode(): Promise<BoundaryDedupMode> {
230
- let next: BoundaryDedupMode = "on";
230
+ let next: BoundaryDedupMode = "off";
231
231
  await updateConfig((c) => {
232
- const current = c.boundaryDedupMode ?? "on";
233
- next = BOUNDARY_DEDUP_MODES[(BOUNDARY_DEDUP_MODES.indexOf(current) + 1) % BOUNDARY_DEDUP_MODES.length] ?? "on";
232
+ const current = c.boundaryDedupMode ?? "off";
233
+ next = BOUNDARY_DEDUP_MODES[(BOUNDARY_DEDUP_MODES.indexOf(current) + 1) % BOUNDARY_DEDUP_MODES.length] ?? "off";
234
234
  c.boundaryDedupMode = next;
235
235
  });
236
236
  return next;
@@ -19,7 +19,7 @@ export interface EditToolFlags {
19
19
  export const DEFAULT_EDIT_FLAGS: EditToolFlags = {
20
20
  requirePath: false,
21
21
  strictInput: false,
22
- boundaryDedupMode: "on",
22
+ boundaryDedupMode: "off",
23
23
  autoRead: true,
24
24
  autoReadAllActive: false
25
25
  };
@@ -29,7 +29,7 @@ export async function currentEditFlags(): Promise<EditToolFlags> {
29
29
  return {
30
30
  requirePath: config.requirePath === true,
31
31
  strictInput: config.strictInput === true,
32
- boundaryDedupMode: config.boundaryDedupMode ?? "on",
32
+ boundaryDedupMode: config.boundaryDedupMode ?? "off",
33
33
  autoRead: config.autoRead !== false,
34
34
  autoReadAllActive: (config.autoReadAll ?? "off") !== "off"
35
35
  };
@@ -57,6 +57,9 @@ export function withReplacePrompts(base: { description: string; snippet: string;
57
57
  descriptionParts.push("Strict-input mode is on: auto-fixable slips are rejected instead of fixed with warnings.");
58
58
  guidelines.push("`replace`: strict-input is on: auto-fixable slips are rejected instead of fixed.");
59
59
  }
60
+ if (flags.boundaryDedupMode !== "on") {
61
+ guidelines = guidelines.filter((guideline) => !guideline.includes("deduplicated automatically"));
62
+ }
60
63
  if (flags.boundaryDedupMode === "off") {
61
64
  descriptionParts.push("Boundary dedup is off: edits apply literally.");
62
65
  guidelines.push("`replace`: boundary dedup is off: edits apply literally.");
@@ -170,7 +173,7 @@ export async function throwIfStrictInput(warnings: string[]): Promise<void> {
170
173
  }
171
174
 
172
175
  export async function getBoundaryDedupMode(): Promise<BoundaryDedupMode> {
173
- return (await readConfig()).boundaryDedupMode ?? "on";
176
+ return (await readConfig()).boundaryDedupMode ?? "off";
174
177
  }
175
178
 
176
179
  export function editRenderCallWrapper(
package/src/replace.ts CHANGED
@@ -324,7 +324,7 @@ export function buildToolDef(flags: EditToolFlags = DEFAULT_EDIT_FLAGS): ToolDef
324
324
  signal,
325
325
  hedit: built.edit,
326
326
  extraWarnings: built.warnings,
327
- skipBoundaryDedup: false,
327
+ skipBoundaryDedup: dedupMode === "off",
328
328
  strictBoundaryDedup,
329
329
  });
330
330
  });