pi-hashline-edit-pro 2.8.1 → 2.8.3
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 +15 -12
- package/index.ts +37 -8
- package/package.json +1 -1
- package/prompts/grep-guidelines.md +3 -3
- package/prompts/grep-snippet.md +1 -1
- package/prompts/grep.md +1 -1
- package/prompts/insert-guidelines.md +1 -1
- package/prompts/read-guidelines.md +1 -1
- package/prompts/replace.md +1 -1
- package/src/commit.ts +6 -2
- package/src/config.ts +15 -2
- package/src/constants.ts +1 -1
- package/src/grep.ts +200 -111
- package/src/hash-store/validation.ts +31 -0
- package/src/hash-store.ts +46 -2
- package/src/hashline/apply.ts +3 -3
- package/src/hashline/resolve.ts +52 -36
- package/src/read.ts +5 -3
- package/src/replace-undo.ts +6 -3
- package/src/replace.ts +2 -2
- package/src/served.ts +56 -27
- package/src/write-hook.ts +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/pi-hashline-edit-pro) [](https://www.npmjs.com/package/pi-hashline-edit-pro)
|
|
4
4
|
|
|
5
|
-
Anchor-based `read`, `replace`, `insert`, and `
|
|
5
|
+
Anchor-based `read`, `replace`, `insert`, and `anchor_grep` tools for [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent). Every line of a file gets a unique 3-character anchor, and you edit by anchor. There are no line numbers and no fuzzy matching, so edits land on the lines you meant.
|
|
6
6
|
|
|
7
7
|
Fork of [pi-hashline-edit](https://github.com/RimuruW/pi-hashline-edit) by RimuruW, extended with 3-character anchors and collision resolution.
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ Fork of [pi-hashline-edit](https://github.com/RimuruW/pi-hashline-edit) by Rimur
|
|
|
11
11
|
- `read` returns every line as `anchor│content`. The anchor is the line's address.
|
|
12
12
|
- `replace` targets a range of anchors, so edits land on the lines you meant.
|
|
13
13
|
- `insert` adds lines after or before a line by anchor: the anchor line is preserved and the new lines are applied literally, never deduplicated.
|
|
14
|
-
- `
|
|
14
|
+
- `anchor_grep` returns matching lines (and requested context) with `anchor│content` rows that are served like read output, so search results are immediately editable.
|
|
15
15
|
- Editing one part of a file leaves the anchors of the rest unchanged, so anchors from an earlier read stay valid across edits.
|
|
16
16
|
- After a `write` you get the new anchors. After a `replace` or `insert` you get the diff with the new anchors.
|
|
17
17
|
- The most recent replace or insert on a file can be reverted, even after a restart.
|
|
@@ -63,7 +63,7 @@ pi install /path/to/pi-hashline-edit-pro
|
|
|
63
63
|
|
|
64
64
|
Paged output ends with a continuation hint, for example `[Showing lines 1-50 of 120. Use offset=51 to continue.]`.
|
|
65
65
|
|
|
66
|
-
Lines up to 50KB are shown in full. A larger line is replaced by a marker that keeps the line's anchor: `anchor│[Line N is 2.2MB, exceeds 50KB; content not shown. Use bash: sed -n 'Np' <path> | head -c 51200]`. The marker is served like a normal row, so the whole line can still be replaced via that anchor; `
|
|
66
|
+
Lines up to 50KB are shown in full. A larger line is replaced by a marker that keeps the line's anchor: `anchor│[Line N is 2.2MB, exceeds 50KB; content not shown. Use bash: sed -n 'Np' <path> | head -c 51200]`. The marker is served like a normal row, so the whole line can still be replaced via that anchor; `anchor_grep` shows an anchored fragment around a match on such a line instead.
|
|
67
67
|
|
|
68
68
|
Edge cases:
|
|
69
69
|
|
|
@@ -125,15 +125,15 @@ Notes:
|
|
|
125
125
|
|
|
126
126
|
Notes:
|
|
127
127
|
|
|
128
|
-
- The anchor line must have been shown to you (read output, a post-edit diff row,
|
|
128
|
+
- The anchor line must have been shown to you (read output, a post-edit diff row, anchor_grep output, or stale-range feedback). The same verification as `replace` applies: a stale or unshown anchor is rejected with `[E_STALE_ANCHOR]`, `[E_AMBIGUOUS_ANCHOR]`, or `[E_RANGE_STALE]` and the retry needs no `read`.
|
|
129
129
|
- Lines are applied literally: nothing is removed, and a line that duplicates its neighbor is kept. `replace`'s boundary anti-duplication never runs for `insert`.
|
|
130
130
|
- To seed an empty file, read it and insert after the `anchor│` empty-line row.
|
|
131
131
|
- The same safety machinery as `replace` applies: undo is saved before the write (a failed write restores the previous undo record), line endings and BOMs survive, and an applied insert clears a pending boundary bypass.
|
|
132
132
|
- Inserting nothing (`lines: []`) reports a noop and leaves the file unchanged; inserted lines are never deduplicated.
|
|
133
133
|
|
|
134
|
-
## The
|
|
134
|
+
## The anchor_grep tool
|
|
135
135
|
|
|
136
|
-
`
|
|
136
|
+
`anchor_grep` replaces the built-in grep with an anchored search backed by ripgrep. While `anchor_grep` is enabled, the built-in grep is disabled; disabling `anchor_grep` restores it if it was active before the extension loaded. Every matching line (and each requested context line) is returned as `lineNumber │ anchor│content` — the `anchor│content` part is served exactly like `read` output, so you can target it with `replace`/`insert` without a separate `read`, while the line-number gutter and `=== path ===` header give filename and line for navigation (press Return to jump).
|
|
137
137
|
|
|
138
138
|
| Field | Description |
|
|
139
139
|
| --- | --- |
|
|
@@ -146,13 +146,14 @@ Notes:
|
|
|
146
146
|
| `limit` | Maximum number of matched lines to return (default: 100). |
|
|
147
147
|
|
|
148
148
|
Notes:
|
|
149
|
-
- Results are grouped per file under a `=== path ===` header; every shown row
|
|
150
|
-
- Directory searches
|
|
149
|
+
- Results are grouped per file under a `=== path ===` header; every shown row is `lineNumber │ anchor│content` where `anchor│content` is the anchor it would have in `read` output.
|
|
150
|
+
- Directory searches use ripgrep, respecting `.gitignore` (including parent directories); `.git` is always skipped, and hidden files are searched, so `node_modules`, `.tmp`, and `coverage` are skipped only when a `.gitignore` lists them. Binary, image, and oversized files are skipped silently.
|
|
151
151
|
- Regex patterns with backreferences, nested quantifiers, quantified alternation, or multiple variable quantifiers are rejected with `[E_UNSAFE_REGEX]` before any files are scanned; use `literal: true` when regex behavior is unnecessary.
|
|
152
|
-
- Output is capped at `limit` matched lines, 2000 rows, and 50KB of text (whichever comes first), with a hint naming the cap that cut results. A matched line longer than 500 bytes is shown as a fragment around the match with `...` marking the truncated sides, so the relevant part of the hit stays visible; a context line over 500 bytes is shown as its head with a trailing `...`. Fragments keep the line's anchor (long lines are hashed from their first 500 bytes) and are served like full rows, so a fragmented match is still editable with `replace` (which always replaces the whole line).
|
|
152
|
+
- Output is capped at `limit` matched lines, 2000 rows, and 50KB of text (whichever comes first), with a hint naming the cap that cut results. A matched line longer than 500 bytes is shown as a fragment around the match with `...` marking the truncated sides, so the relevant part of the hit stays visible; a context line over 500 bytes is shown as its head with a trailing `...`. Fragments keep the line's anchor (long lines are hashed from their first 500 bytes) and are served like full rows, so a fragmented match is still editable with `replace` (which always replaces the whole line).
|
|
153
153
|
- `file_path` works as an alias for `path`.
|
|
154
154
|
- Line endings and BOMs survive every edit. The file's line ending is detected from its first newline and restored on write; a file that mixes LF and CRLF (for example a WSL-edited file) is normalized to the first-seen ending.
|
|
155
155
|
- Files with multiple hard links (`nlink > 1`) are rewritten in place rather than via a temp-file rename, so every link keeps seeing the same content; that write is direct rather than atomic.
|
|
156
|
+
- The anchor_grep tool is enabled by default. Disable it with `/toggle-anchor-grep` (or set `anchorGrepEnabled` to `false` in the config file); the setting persists across sessions. When disabled, anchor_grep is removed from the model's toolset and the built-in grep is restored only if it was active before the extension loaded — a grep tool that was never enabled stays off.
|
|
156
157
|
|
|
157
158
|
## Undo
|
|
158
159
|
|
|
@@ -171,7 +172,7 @@ Notes:
|
|
|
171
172
|
Enabled by default. After a successful `write`, the extension reads the file and appends an `--- Auto-read (hashline anchors) ---` block to the result, so you get fresh `anchor│content` anchors without a separate `read` call.
|
|
172
173
|
|
|
173
174
|
- After `replace`, `insert`, and `undo_last_change`, the result shows the post-edit diff. The `+anchor│` and ` anchor│` rows carry the current anchors, so follow-up edits can anchor on the diff directly. The `-anchor│` rows show removed lines with their old anchors, so you can see exactly which anchors were deleted (those anchors are stale after the edit). When the context line touching a change is blank or whitespace-only, one more context line is shown in that direction, so the change stays anchored to visible content. Call `read` when you want the full file's anchors.
|
|
174
|
-
- Auto-read keeps the same 50KB / 2000-line budget as `read`. Lines over 50KB are shown as markers that keep the line's anchor (use `
|
|
175
|
+
- Auto-read keeps the same 50KB / 2000-line budget as `read`. Lines over 50KB are shown as markers that keep the line's anchor (use `anchor_grep` for a fragment around a match).
|
|
175
176
|
- Toggle at runtime with `/toggle-auto-read`; the setting persists across sessions.
|
|
176
177
|
|
|
177
178
|
## Tool result details
|
|
@@ -181,19 +182,21 @@ All five tools return machine-readable metadata in `details` alongside the model
|
|
|
181
182
|
- `read`: `details.truncation` (set when the output was truncated), `details.snapshotId` (a `v2|path|ino|mtime|ctime|size` fingerprint of the file), `details.nextOffset` (use as the next `offset`), and `details.metrics` with `truncated` and `next_offset`.
|
|
182
183
|
- `replace` and `insert`: `details.diff` (the post-edit diff, capped at 50KB with markers for oversized rows; `+HASH│` and ` HASH│` rows carry the current anchors), `details.patch` (a standard unified patch of the changes, for external tools, capped at 50KB like the diff), `details.patchTruncated` (true when the patch was cut to fit the cap and cannot be applied as-is), `details.firstChangedLine`, `details.snapshotId`, `details.classification` (`"noop"` when nothing changed), and `details.metrics`: `edits_attempted`, `edits_noop`, `warnings`, `classification` (`"applied"` or `"noop"`), `changed_lines` (`{ first, last }`), `added_lines`, `removed_lines`.
|
|
183
184
|
- `undo_last_change`: `details.diff` (the undo diff with the restored anchors), `details.patch` (a standard unified patch of the restored changes, capped at 50KB like `replace`), `details.patchTruncated` (true when the patch was cut), and `details.metrics` (same shape as `replace`).
|
|
184
|
-
- `
|
|
185
|
+
- `anchor_grep`: `details.metrics` with `matches` (matched lines found, capped at `limit`), `files`, and `truncated` (true when the row, byte, file-scan, or `limit` cap cut the results), plus `details.truncation` (the standard pi truncation report — `truncatedBy`, `totalLines`, `outputLines`, `maxLines`, `maxBytes`, … — when the output was cut) and `details.linesTruncated` (true when long lines were shown as fragments).
|
|
185
186
|
|
|
186
187
|
## Settings
|
|
187
188
|
|
|
188
189
|
| Command | Description |
|
|
189
190
|
| --- | --- |
|
|
190
191
|
| `/toggle-auto-read` | Toggle auto-read anchors after write and post-edit diffs after replace, insert, and undo_last_change. Persists across sessions. |
|
|
192
|
+
| `/toggle-anchor-grep` | Enable or disable the anchor_grep tool (the built-in grep is disabled while anchor_grep is on). Persists across sessions. |
|
|
191
193
|
|
|
192
194
|
Settings live in `~/.config/pi-hashline-edit-pro/config.json`, created automatically when a setting is toggled. On non-Windows platforms, the config directory honors `XDG_CONFIG_HOME` when set (falling back to `~/.config`); on Windows it always uses `~/.config`:
|
|
193
195
|
|
|
194
196
|
```json
|
|
195
197
|
{
|
|
196
|
-
"autoRead": true
|
|
198
|
+
"autoRead": true,
|
|
199
|
+
"anchorGrepEnabled": true
|
|
197
200
|
}
|
|
198
201
|
```
|
|
199
202
|
|
package/index.ts
CHANGED
|
@@ -12,15 +12,17 @@ import { MAX_HASH_LINES } from "./src/hashline";
|
|
|
12
12
|
import {
|
|
13
13
|
readConfig,
|
|
14
14
|
toggleAutoRead,
|
|
15
|
+
toggleAnchorGrep,
|
|
15
16
|
} from "./src/config";
|
|
16
17
|
import { loadHashStore, pruneMissing } from "./src/hash-store";
|
|
17
|
-
import { recordServedSafe, clearServed } from "./src/served";
|
|
18
|
+
import { recordServedSafe, clearServed, buildServedMap } from "./src/served";
|
|
18
19
|
import { clearBoundaryBypass } from "./src/boundary-bypass";
|
|
19
20
|
import { registerWriteHook } from "./src/write-hook";
|
|
20
21
|
import { readNormFile } from "./src/file-reader";
|
|
21
22
|
import { loadFileKindAndText } from "./src/file-kind";
|
|
22
23
|
import { resolveInCwd } from "./src/fs-write";
|
|
23
24
|
import { valAccess } from "./src/validation";
|
|
25
|
+
import { splitLines } from "./src/utils";
|
|
24
26
|
|
|
25
27
|
export default function (pi: ExtensionAPI): void {
|
|
26
28
|
regRead(pi);
|
|
@@ -32,19 +34,29 @@ export default function (pi: ExtensionAPI): void {
|
|
|
32
34
|
registerWriteHook(pi);
|
|
33
35
|
|
|
34
36
|
let autoRead = true;
|
|
37
|
+
let grepWasActive = false;
|
|
35
38
|
|
|
36
39
|
pi.on("session_start", async (_event, ctx) => {
|
|
37
40
|
const active = pi.getActiveTools();
|
|
41
|
+
grepWasActive = active.includes("grep");
|
|
38
42
|
pi.setActiveTools(active.filter((t) => t !== "edit"));
|
|
39
43
|
await initHasher();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
loadHashStore()
|
|
45
|
+
.then(store =>
|
|
46
|
+
pruneMissing(store).catch(err => {
|
|
47
|
+
console.error("Failed to prune hash store:", err);
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
.catch(err => {
|
|
51
|
+
console.error("Failed to load hash store:", err);
|
|
52
|
+
});
|
|
46
53
|
const config = await readConfig();
|
|
47
54
|
autoRead = config.autoRead;
|
|
55
|
+
pi.setActiveTools(
|
|
56
|
+
pi.getActiveTools().filter((t) =>
|
|
57
|
+
config.anchorGrepEnabled ? t !== "grep" : t !== "anchor_grep",
|
|
58
|
+
),
|
|
59
|
+
);
|
|
48
60
|
const debugValue = process.env.PI_HASHLINE_DEBUG;
|
|
49
61
|
if (debugValue === "1" || debugValue === "true") {
|
|
50
62
|
ctx.ui.notify(`Hashline Edit mode active`, "info");
|
|
@@ -60,6 +72,21 @@ export default function (pi: ExtensionAPI): void {
|
|
|
60
72
|
},
|
|
61
73
|
});
|
|
62
74
|
|
|
75
|
+
pi.registerCommand("toggle-anchor-grep", {
|
|
76
|
+
description: "Enable or disable the anchor_grep tool (the built-in grep is disabled while anchor_grep is on)",
|
|
77
|
+
handler: async (_args, ctx) => {
|
|
78
|
+
const enabled = await toggleAnchorGrep();
|
|
79
|
+
const active = pi.getActiveTools();
|
|
80
|
+
pi.setActiveTools(
|
|
81
|
+
enabled
|
|
82
|
+
? [...new Set([...active.filter((t) => t !== "grep"), "anchor_grep"])]
|
|
83
|
+
: [...new Set([...active.filter((t) => t !== "anchor_grep"), ...(grepWasActive ? ["grep"] : [])])],
|
|
84
|
+
);
|
|
85
|
+
const state = enabled ? "enabled" : "disabled";
|
|
86
|
+
ctx.ui.notify(`anchor_grep tool ${state}`, "info");
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
63
90
|
pi.on("tool_result", async (event, ctx) => {
|
|
64
91
|
if (event.isError) return;
|
|
65
92
|
|
|
@@ -95,7 +122,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
95
122
|
DEFAULT_MAX_BYTES,
|
|
96
123
|
DEFAULT_MAX_LINES,
|
|
97
124
|
);
|
|
98
|
-
|
|
125
|
+
const fileLines = splitLines(normalized);
|
|
126
|
+
const servedMap = buildServedMap(fileHashes, fileLines, preview.servedHashes);
|
|
127
|
+
await recordServedSafe(absolutePath, servedMap, "auto-read", new Set(fileHashes));
|
|
99
128
|
return {
|
|
100
129
|
content: [
|
|
101
130
|
...(event.content ?? []),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hashline-edit-pro",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hash-anchored read/replace/insert/grep tools for pi-coding-agent. Every line gets a unique 3-char hash (A-Za-z0-9) that stays stable across edits; stale or ambiguous anchors are rejected, never fuzzy-matched. Undo persists across restarts.",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
- `
|
|
2
|
-
- `
|
|
3
|
-
- `
|
|
1
|
+
- `anchor_grep`: every hit and `context` line comes back as `lineNumber │ anchor│content` — the `anchor│content` part is usable directly for `replace`/`insert` without a new `read`, while `lineNumber` enables jump-to-line.
|
|
2
|
+
- `anchor_grep`: uses ripgrep, respects `.gitignore`; use `path` for file or folder (default cwd), `glob` like `*.ts` to filter, `literal:true` for literal text, `context:N` for surrounding lines.
|
|
3
|
+
- `anchor_grep`: `.git` is always skipped; `node_modules`/`.tmp`/`coverage` are skipped only when a `.gitignore` lists them; binary/image files are skipped.
|
package/prompts/grep-snippet.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Search with `anchor│content` hits usable directly for `replace`/`insert`; use `literal`/`glob`/`context`
|
|
1
|
+
Search with `lineNumber │ anchor│content` hits usable directly for `replace`/`insert`; use `literal`/`glob`/`context`
|
package/prompts/grep.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Search text files for a pattern. Every hit and each `context` line is returned as `anchor│content`
|
|
1
|
+
Search text files for a pattern using ripgrep. Every hit and each `context` line is returned as `lineNumber │ anchor│content` — the `anchor│content` part is usable directly for `replace`/`insert` without a new `read`, while `lineNumber` and the `=== path ===` header give file and line for navigation. Respects `.gitignore` via ripgrep; `.git` is always skipped, and `node_modules`/`.tmp`/`coverage` are skipped only when a `.gitignore` lists them. Binary and image files are skipped silently. Matching lines longer than 500 bytes are shown as a fragment around the match with `...`, but the anchor is still valid for the whole line. If output says truncated, refine `pattern` or raise `limit` as hinted.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
- `insert`: `anchor` is bare `aB3` from `aB3│content` (never the content), direction `after` adds below, `before` adds above. Do not put the anchor line in `lines`; `[""]` is a blank line.
|
|
2
2
|
- `insert`: `lines` is bare content, one element per line, kept literally even if it duplicates neighbors — nothing is removed.
|
|
3
|
-
- `insert`: the anchor must have been shown by `read`, a post-edit diff (`+anchor│`/` anchor│`), or `
|
|
3
|
+
- `insert`: the anchor must have been shown by `read`, a post-edit diff (`+anchor│`/` anchor│`), or `anchor_grep`. For an empty file, `read` shows one `anchor│` empty row — insert `after` it.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
- `read`: call before `replace` when you need fresh anchors for a file.
|
|
2
|
-
- `read`: call again after an edit when you need anchors you do not have — post-edit diff `+anchor│`/` anchor│` rows and `
|
|
2
|
+
- `read`: call again after an edit when you need anchors you do not have — post-edit diff `+anchor│`/` anchor│` rows and `anchor_grep` hits are already fresh anchors for the changed range, so no new `read` needed for those lines.
|
package/prompts/replace.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Replace a range of lines (or a single line) in a text file, targeted by 3-character anchors from read output. Saw `aB3│content` in read/diff/
|
|
1
|
+
Replace a range of lines (or a single line) in a text file, targeted by 3-character anchors from read output. Saw `aB3│content` in read/diff/anchor_grep: `aB3` is the bare HASH before `│`, `aB3│content` is the full row. `remove_from` and `remove_to` are bare HASH only (e.g. `"aB3"`), never the full row or file content. `replacement_lines` is bare content without `│`, one string per line — use `[]` to delete. Example: read showed `aB3│old` and `kQm│old2`, to replace both use `{"remove_from":"aB3","remove_to":"kQm","replacement_lines":["new line 1","new line 2"]}`. Single line → same anchor for both.
|
package/src/commit.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { buildChanged, buildNoop, type RMeta, type TResult } from "./replace-res
|
|
|
4
4
|
import { saveUndo } from "./replace-undo";
|
|
5
5
|
import { safeSnapId } from "./file-reader";
|
|
6
6
|
import { writeAtomic } from "./fs-write";
|
|
7
|
-
import {
|
|
7
|
+
import { recordServedSafe, buildServedMap, servedHashesFromDiff } from "./served";
|
|
8
8
|
import { restoreEndings } from "./normalize";
|
|
9
|
+
import { splitLines } from "./utils";
|
|
9
10
|
|
|
10
11
|
export interface CommitMeta {
|
|
11
12
|
path: string;
|
|
@@ -102,7 +103,10 @@ export async function commitEdit(pipe: PipelineResult, meta: CommitMeta): Promis
|
|
|
102
103
|
};
|
|
103
104
|
const changed = buildChanged(successInput, meta.verb);
|
|
104
105
|
if (changed.details.diff) {
|
|
105
|
-
|
|
106
|
+
const diffHashes = servedHashesFromDiff(changed.details.diff);
|
|
107
|
+
const resultLines = splitLines(pipe.result);
|
|
108
|
+
const servedMap = buildServedMap(pipe.resultHashes, resultLines, diffHashes);
|
|
109
|
+
await recordServedSafe(mutationTargetPath, servedMap, "post-edit diff", new Set(pipe.resultHashes));
|
|
106
110
|
}
|
|
107
111
|
return changed;
|
|
108
112
|
}
|
package/src/config.ts
CHANGED
|
@@ -5,10 +5,12 @@ import { writeAtomic } from "./fs-write";
|
|
|
5
5
|
|
|
6
6
|
export interface Config {
|
|
7
7
|
autoRead: boolean;
|
|
8
|
+
anchorGrepEnabled: boolean;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const DEFAULT_CONFIG: Config = {
|
|
11
|
-
autoRead: true
|
|
12
|
+
autoRead: true,
|
|
13
|
+
anchorGrepEnabled: true
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
function parseConfig(content: string): Config {
|
|
@@ -17,7 +19,11 @@ function parseConfig(content: string): Config {
|
|
|
17
19
|
if (typeof autoRead !== "boolean") {
|
|
18
20
|
throw new Error("config.json must be an object with a boolean autoRead field");
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
const anchorGrepEnabled = isRec(parsed) ? parsed.anchorGrepEnabled : undefined;
|
|
23
|
+
return {
|
|
24
|
+
autoRead,
|
|
25
|
+
anchorGrepEnabled: typeof anchorGrepEnabled === "boolean" ? anchorGrepEnabled : DEFAULT_CONFIG.anchorGrepEnabled,
|
|
26
|
+
};
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
|
|
@@ -43,3 +49,10 @@ export async function toggleAutoRead(): Promise<boolean> {
|
|
|
43
49
|
await writeConfig(config);
|
|
44
50
|
return config.autoRead;
|
|
45
51
|
}
|
|
52
|
+
|
|
53
|
+
export async function toggleAnchorGrep(): Promise<boolean> {
|
|
54
|
+
const config = await readConfig();
|
|
55
|
+
config.anchorGrepEnabled = !config.anchorGrepEnabled;
|
|
56
|
+
await writeConfig(config);
|
|
57
|
+
return config.anchorGrepEnabled;
|
|
58
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -7,6 +7,6 @@ export const MAX_HASH_SOURCE_BYTES = 500;
|
|
|
7
7
|
export const MAX_GREP_LINE_BYTES = 500;
|
|
8
8
|
|
|
9
9
|
export const HASH_STORE_BUSY_TIMEOUT = 1000;
|
|
10
|
-
export const HASH_STORE_VERSION =
|
|
10
|
+
export const HASH_STORE_VERSION = 6;
|
|
11
11
|
export const NEW_CONTENT_NOT_ARRAY_MSG =
|
|
12
12
|
`[E_BAD_SHAPE] "replacement_lines" must be an array of strings, one per line (use [] to delete).`;
|