@oh-my-pi/hashline 15.13.0 → 15.13.2
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/CHANGELOG.md +111 -34
- package/README.md +6 -6
- package/dist/types/block.d.ts +2 -2
- package/dist/types/format.d.ts +13 -10
- package/dist/types/input.d.ts +2 -2
- package/dist/types/messages.d.ts +33 -18
- package/dist/types/patcher.d.ts +3 -3
- package/dist/types/snapshots.d.ts +24 -3
- package/dist/types/types.d.ts +10 -10
- package/package.json +1 -1
- package/src/apply.ts +57 -3
- package/src/block.ts +16 -6
- package/src/format.ts +17 -14
- package/src/grammar.lark +7 -7
- package/src/input.ts +3 -3
- package/src/messages.ts +80 -26
- package/src/parser.ts +8 -8
- package/src/patcher.ts +26 -6
- package/src/prompt.md +39 -39
- package/src/snapshots.ts +40 -4
- package/src/tokenizer.ts +39 -36
- package/src/types.ts +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,79 +2,156 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.13.2] - 2026-06-15
|
|
6
|
+
|
|
5
7
|
### Breaking Changes
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
+
- Renamed all hashline DSL operators to concise abbreviated keywords:
|
|
10
|
+
- `replace` -> `SWAP`
|
|
11
|
+
- `delete` -> `DEL`
|
|
12
|
+
- `insert before`/`after`/`head`/`tail` -> `INS.PRE`/`POST`/`HEAD`/`TAIL`
|
|
13
|
+
- `replace_block` -> `SWAP.BLK`
|
|
14
|
+
- `delete_block` -> `DEL.BLK`
|
|
15
|
+
- `insert_after_block` -> `INS.BLK.POST`
|
|
16
|
+
|
|
17
|
+
## [15.13.1] - 2026-06-15
|
|
18
|
+
|
|
19
|
+
### Breaking Changes
|
|
20
|
+
|
|
21
|
+
- Rejected edits anchored to lines not displayed in the tagged read/search output, requiring unseen ranges to be re-read before reapplying
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Rejected `replace block`, `delete block`, and `insert after block` operations that resolve to a single line and instructed users to use the plain single-line form or anchor the true construct opener
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Auto-repaired one-sided multi-line boundary echoes by dropping delimiter-neutral duplicated boundary lines and emitted a boundary-echo warning
|
|
30
|
+
- Normalized cwd-relative hashline paths to forward-slash form on Windows.
|
|
31
|
+
- Parser now treats a leading `\` on inline payload bodies as the payload delimiter, matching standalone payload rows.
|
|
32
|
+
- Restored the warning emitted when escaped indented payload rows (`\\ TEXT`) are accepted as payload delimiters.
|
|
33
|
+
|
|
34
|
+
## [15.12.5] - 2026-06-13
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Fixed delimiter-balance boundary repair so it does not keep a deleted structural closer when the replacement payload already restates that closer.
|
|
39
|
+
|
|
40
|
+
## [15.12.0] - 2026-06-12
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Condensed all parser/applier/patcher error and warning messages: shorter wording, same diagnostic anchors (op names, line numbers, suggested fallback forms)
|
|
45
|
+
|
|
46
|
+
## [15.11.4] - 2026-06-12
|
|
9
47
|
|
|
10
48
|
### Added
|
|
11
49
|
|
|
12
50
|
- Added inward landing correction for `insert after block N:`: a body indented deeper than the block's closing line now slides back across the block's trailing closer lines and lands inside the block at its claimed depth, with a warning naming the landing line. Same conservative guards as the outward shift — comparable indentation only, closers only, abandoned when another hunk targets a crossed line; plain `insert after M:` stays literal
|
|
13
51
|
- Added closer-anchor lowering for `insert after block N:`: anchoring on a pure closing-delimiter line (where no block begins, so resolution previously failed the whole patch) now applies as plain `insert after N:` with a warning teaching the opener-only rule. `resolveBlockEdits` gained an `onWarning` callback; apply, preview, and patcher paths surface it on `warnings`
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Condensed the edit-tool prompt: one-line op definitions, 5–20-word rules, and a tighter `<critical>` recap; landing-correction mechanics are no longer described to the agent
|
|
56
|
+
|
|
57
|
+
## [15.11.1] - 2026-06-11
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- Fixed the `insert after block N:` prompt guidance so it explicitly says N must be the block opener, not the closing delimiter or last visible line, and points visible closing-line edits to plain `insert after M:`. ([#2292](https://github.com/can1357/oh-my-pi/issues/2292))
|
|
62
|
+
|
|
63
|
+
## [15.11.0] - 2026-06-10
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Block-unresolved errors (`replace block N:` / `delete block N` / `insert after block N:` failing to resolve a syntactic block) now append a numbered preview of the file around the anchor line — same `*`-marked context rows the hash-mismatch error shows — so the offending line is visible without a re-read
|
|
68
|
+
|
|
69
|
+
## [15.10.11] - 2026-06-10
|
|
70
|
+
|
|
71
|
+
### Breaking Changes
|
|
72
|
+
|
|
73
|
+
- Changed `BlockResolution.isDelete` to `BlockResolution.op` (`"replace" | "delete" | "insert_after"`) so resolutions can describe every block-anchored op
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
14
77
|
- Added `insert after block N:` patch syntax to insert body rows after the last line of the tree-sitter-resolved block beginning on line N, so a statement can be placed after a construct without counting to its closing line
|
|
15
78
|
- Added depth-guided landing correction for `insert after N:` hunks: a body indented shallower than its anchor line slides past the structural closer lines below the anchor until depth returns to the body's level, with a warning naming the final landing line. The shift never crosses content lines, skips incomparable indentation styles and pure-closer bodies, and is abandoned when another hunk targets a crossed line
|
|
16
79
|
- Added a global byte ceiling to `InMemorySnapshotStore` (`maxTotalBytes`, default 64 MiB): the cap was previously per-file only, so a session reading many large files retained up to 30 paths × 4 full-text versions indefinitely
|
|
17
|
-
- Added `maxAddedRunContext` option to control how many added lines are shown at each side of collapsed inserted runs, with `maxUnchangedRun` kept as a backward-compatible alias
|
|
18
|
-
- Added a `BlockResolution` type and surfaced resolved block spans on `ApplyResult.blockResolutions` / `PatchSectionResult.blockResolutions`. `resolveBlockEdits` now accepts an `onResolved` callback that reports each `replace block N:` / `delete block N` anchor's resolved `[start, end]` span (and whether it was a delete). Spans are surfaced only on the no-drift apply paths, where the resolved line numbers line up with the tag the caller read.
|
|
19
|
-
- Added `replace block N:` and `delete block N` patch syntax to replace or delete the entire syntactic block that begins on line N using tree-sitter-resolved spans
|
|
20
|
-
- Added `BlockResolver` support in `Patcher` and `PatchSection.applyTo`/`applyPartialTo` to wire language-specific block-resolution at apply time
|
|
21
|
-
- Added `resolveBlockEdits` and block edit type definitions to the package API for resolving deferred `replace block` / `delete block` edits
|
|
22
80
|
|
|
23
81
|
### Changed
|
|
24
82
|
|
|
25
|
-
- Condensed all parser/applier/patcher error and warning messages: shorter wording, same diagnostic anchors (op names, line numbers, suggested fallback forms)
|
|
26
|
-
- Condensed the edit-tool prompt: one-line op definitions, 5–20-word rules, and a tighter `<critical>` recap; landing-correction mechanics are no longer described to the agent
|
|
27
|
-
- Block-unresolved errors (`replace block N:` / `delete block N` / `insert after block N:` failing to resolve a syntactic block) now append a numbered preview of the file around the anchor line — same `*`-marked context rows the hash-mismatch error shows — so the offending line is visible without a re-read
|
|
28
83
|
- Trimmed the `replace block N:` ops entry in the patch prompt to grammar and pointing rules; the usage doctrine it duplicated stays in the rules section
|
|
29
84
|
- Changed `buildCompactDiffPreview` to treat blank rows as gap separators alongside `…` markers: separators never stack (removed lines omitted from the preview no longer leave two adjacent), and leading/trailing separators are trimmed
|
|
30
|
-
- Changed `buildCompactDiffPreview` to omit removed lines from the preview while preserving removal counts for offset tracking
|
|
31
|
-
- Changed `buildCompactDiffPreview` to collapse long contiguous added runs with a bare `…` marker, keeping only the first and last `maxAddedRunContext` lines visible (the surrounding line numbers convey how many were elided)
|
|
32
|
-
- Reworked the `edit` tool prompt (`prompt.md`): added a `replace block N` vs `replace N..M` decision rule, documented that a leading decorator/attribute/doc-comment is a separate node not swept into the block (point N at the first decorator line, or use `replace N..M` for a Rust-style `///` sibling comment), reframed the blast-radius guidance so "block replace" no longer reads as the dangerous option, and added a decorated-definition example.
|
|
33
85
|
|
|
34
86
|
### Fixed
|
|
35
87
|
|
|
36
|
-
- Normalized cwd-relative hashline paths to forward-slash form on Windows.
|
|
37
|
-
- Fixed delimiter-balance boundary repair so it does not keep a deleted structural closer when the replacement payload already restates that closer.
|
|
38
|
-
- Fixed the `insert after block N:` prompt guidance so it explicitly says N must be the block opener, not the closing delimiter or last visible line, and points visible closing-line edits to plain `insert after M:`. ([#2292](https://github.com/can1357/oh-my-pi/issues/2292))
|
|
39
88
|
- Fixed the boundary-echo repair stripping payload edges without the balance-neutrality guard its own documentation promised: in brace-heavy code where bare `}` lines repeat, a payload intentionally beginning/ending with lines identical to the range's neighbors had both edges silently dropped, writing content that differed from what was authored
|
|
40
89
|
- Fixed lenient bare-body handling silently mutating payloads: interior blank rows in an un-prefixed body were dropped outright, and a body of numeric-keyed literals (`1: "one"` dict/YAML shapes) satisfied the uniform line-prefix check and had its keys stripped from every line — blank rows are now preserved when proven interior, and the uniform strip refuses lone-literal remainders
|
|
41
90
|
- Fixed the multi-section "all-or-nothing" claim being false for write failures: commits run serially, so a mid-batch write error left earlier sections on disk while the thrown error said nothing — the error now lists exactly which sections were written and which were not
|
|
42
91
|
- Fixed `delete`/`replace` ranges ending on the phantom trailing line of a newline-terminated file silently stripping the file's final newline; such anchors are now rejected with guidance toward `N-1` / `insert tail:` (inserts there remain valid, and genuine empty last lines of unterminated files stay deletable)
|
|
43
|
-
- Fixed compact edit previews to omit deleted content, keep visible lines anchored to the current file, and collapse long inserted runs with a bare `…` elision marker.
|
|
44
|
-
- Fixed compact edit previews to render added/current lines without diff-prefix padding and normalize adjacent ASCII/Unicode elision markers to one `…`.
|
|
45
|
-
- Stripped read-output line-number prefixes (`N:`) from auto-piped bare body rows so that pasting `3:text` without a `+` prefix no longer injects `3:` as literal content. Stripping is applied only when *every* bare row in the hunk carries the prefix (the signature of a pasted snapshot) and removes at most one prefix per row, so a genuine body that merely starts with `digits:` (YAML port maps, timestamps) is left intact ([#1492](https://github.com/can1357/oh-my-pi/issues/1492)).
|
|
46
|
-
- Fixed missing-header diagnostics and copied-content prefix stripping to consistently teach and recognize 4-hex snapshot tags.
|
|
47
|
-
- Fixed delimiter-balance boundary repair to also drop a single duplicated structural opener (e.g. a restated `foo(` / `if (x) {` signature line surviving just above the range), not only duplicated closers. Zero-balance duplicates remain untouched.
|
|
48
|
-
- Fixed hashline replacements that accidentally restated unchanged lines above and below the selected range so they no longer duplicate both boundary lines ([#1664](https://github.com/can1357/oh-my-pi/issues/1664)).
|
|
49
|
-
|
|
50
|
-
## [15.13.0] - 2026-06-14
|
|
51
92
|
|
|
52
|
-
## [15.
|
|
93
|
+
## [15.10.5] - 2026-06-08
|
|
53
94
|
|
|
54
|
-
|
|
95
|
+
### Added
|
|
55
96
|
|
|
56
|
-
|
|
97
|
+
- Added `maxAddedRunContext` option to control how many added lines are shown at each side of collapsed inserted runs, with `maxUnchangedRun` kept as a backward-compatible alias
|
|
57
98
|
|
|
58
|
-
|
|
99
|
+
### Changed
|
|
59
100
|
|
|
60
|
-
|
|
101
|
+
- Changed `buildCompactDiffPreview` to omit removed lines from the preview while preserving removal counts for offset tracking
|
|
102
|
+
- Changed `buildCompactDiffPreview` to collapse long contiguous added runs with a bare `…` marker, keeping only the first and last `maxAddedRunContext` lines visible (the surrounding line numbers convey how many were elided)
|
|
61
103
|
|
|
62
|
-
|
|
104
|
+
### Fixed
|
|
63
105
|
|
|
64
|
-
|
|
106
|
+
- Fixed compact edit previews to omit deleted content, keep visible lines anchored to the current file, and collapse long inserted runs with a bare `…` elision marker.
|
|
107
|
+
- Fixed compact edit previews to render added/current lines without diff-prefix padding and normalize adjacent ASCII/Unicode elision markers to one `…`.
|
|
65
108
|
|
|
66
109
|
## [15.10.3] - 2026-06-08
|
|
67
110
|
|
|
111
|
+
### Added
|
|
112
|
+
|
|
113
|
+
- Added a `BlockResolution` type and surfaced resolved block spans on `ApplyResult.blockResolutions` / `PatchSectionResult.blockResolutions`. `resolveBlockEdits` now accepts an `onResolved` callback that reports each `replace block N:` / `delete block N` anchor's resolved `[start, end]` span (and whether it was a delete). Spans are surfaced only on the no-drift apply paths, where the resolved line numbers line up with the tag the caller read.
|
|
114
|
+
|
|
115
|
+
### Changed
|
|
116
|
+
|
|
117
|
+
- Reworked the `edit` tool prompt (`prompt.md`): added a `replace block N` vs `replace N..M` decision rule, documented that a leading decorator/attribute/doc-comment is a separate node not swept into the block (point N at the first decorator line, or use `replace N..M` for a Rust-style `///` sibling comment), reframed the blast-radius guidance so "block replace" no longer reads as the dangerous option, and added a decorated-definition example.
|
|
118
|
+
|
|
68
119
|
## [15.10.2] - 2026-06-08
|
|
69
120
|
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
- Stripped read-output line-number prefixes (`N:`) from auto-piped bare body rows so that pasting `3:text` without a `+` prefix no longer injects `3:` as literal content. Stripping is applied only when *every* bare row in the hunk carries the prefix (the signature of a pasted snapshot) and removes at most one prefix per row, so a genuine body that merely starts with `digits:` (YAML port maps, timestamps) is left intact ([#1492](https://github.com/can1357/oh-my-pi/issues/1492)).
|
|
124
|
+
|
|
70
125
|
## [15.9.67] - 2026-06-06
|
|
71
126
|
|
|
127
|
+
### Breaking Changes
|
|
128
|
+
|
|
129
|
+
- Changed hashline file section headers from `¶PATH#TAG` to `[PATH#TAG]` so model-authored edits use ASCII delimiters instead of a pilcrow sigil.
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
|
|
133
|
+
- Fixed missing-header diagnostics and copied-content prefix stripping to consistently teach and recognize 4-hex snapshot tags.
|
|
134
|
+
|
|
72
135
|
## [15.8.2] - 2026-06-03
|
|
73
136
|
|
|
137
|
+
### Fixed
|
|
138
|
+
|
|
139
|
+
- Fixed delimiter-balance boundary repair to also drop a single duplicated structural opener (e.g. a restated `foo(` / `if (x) {` signature line surviving just above the range), not only duplicated closers. Zero-balance duplicates remain untouched.
|
|
140
|
+
|
|
74
141
|
## [15.8.0] - 2026-06-02
|
|
75
142
|
|
|
143
|
+
### Fixed
|
|
144
|
+
|
|
145
|
+
- Fixed hashline replacements that accidentally restated unchanged lines above and below the selected range so they no longer duplicate both boundary lines ([#1664](https://github.com/can1357/oh-my-pi/issues/1664)).
|
|
146
|
+
|
|
76
147
|
## [15.7.0] - 2026-05-31
|
|
77
148
|
|
|
149
|
+
### Added
|
|
150
|
+
|
|
151
|
+
- Added `replace block N:` and `delete block N` patch syntax to replace or delete the entire syntactic block that begins on line N using tree-sitter-resolved spans
|
|
152
|
+
- Added `BlockResolver` support in `Patcher` and `PatchSection.applyTo`/`applyPartialTo` to wire language-specific block-resolution at apply time
|
|
153
|
+
- Added `resolveBlockEdits` and block edit type definitions to the package API for resolving deferred `replace block` / `delete block` edits
|
|
154
|
+
|
|
78
155
|
## [15.5.13] - 2026-05-29
|
|
79
156
|
|
|
80
157
|
### Breaking Changes
|
|
@@ -85,7 +162,7 @@
|
|
|
85
162
|
### Added
|
|
86
163
|
|
|
87
164
|
- Added `maxPaths` and `maxVersionsPerPath` options to `InMemorySnapshotStore` to bound tracked paths and per-path snapshot history
|
|
88
|
-
- Re-introduced balance-validated boundary repair in `applyEdits`. A replacement hunk (`replace N..M:` + body) is normalized so its payload preserves the deleted region's delimiter balance: when the body restates a closing delimiter that survives just outside the range (duplicate `}` / `);` / `]`) the echo is dropped, and when the range deletes a structural closer the body never restates (missing closer) the closer is spared instead of deleted. A repair fires only when one boundary operation drives the per-channel `()` / `[]` / `{}` imbalance to exactly zero while leaving surrounding text byte-identical (single-line ops are limited to pure structural-closer lines), so balance-preserving edits and intentional balanced duplicates are never touched. Bracket
|
|
165
|
+
- Re-introduced balance-validated boundary repair in `applyEdits`. A replacement hunk (`replace N..M:` + body) is normalized so its payload preserves the deleted region's delimiter balance: when the body restates a closing delimiter that survives just outside the range (duplicate `}` / `);` / `]`) the echo is dropped, and when the range deletes a structural closer the body never restates (missing closer) the closer is spared instead of deleted. A repair fires only when one boundary operation drives the per-channel `()` / `[]` / `{}` imbalance to exactly zero while leaving surrounding text byte-identical (single-line ops are limited to pure structural-closer lines), so balance-preserving edits and intentional balanced duplicates are never touched. Bracket counting skips strings, template literals, and comments. Each repair surfaces a `delimiter-balance` warning through `ApplyResult.warnings`.
|
|
89
166
|
|
|
90
167
|
### Changed
|
|
91
168
|
|
|
@@ -153,7 +230,7 @@
|
|
|
153
230
|
|
|
154
231
|
### Breaking Changes
|
|
155
232
|
|
|
156
|
-
-
|
|
233
|
+
- Changed hashline payload continuations from `+TEXT` to `\TEXT`; use `\` for an explicit blank payload line.
|
|
157
234
|
|
|
158
235
|
### Added
|
|
159
236
|
|
|
@@ -166,7 +243,7 @@
|
|
|
166
243
|
|
|
167
244
|
### Removed
|
|
168
245
|
|
|
169
|
-
- Removed
|
|
246
|
+
- Removed the `A-B!` / `A!` deletion operator. Use `A-B:` with the desired payload (or empty payload to blank the range) instead.
|
|
170
247
|
|
|
171
248
|
All notable changes to this package will be documented in this file.
|
|
172
249
|
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ await fs.writeText("hello.ts", before);
|
|
|
26
26
|
const tag = snapshots.record("hello.ts", before);
|
|
27
27
|
const patcher = new Patcher({ fs, snapshots });
|
|
28
28
|
const patch = Patch.parse(String.raw`[hello.ts#${tag}]
|
|
29
|
-
|
|
29
|
+
SWAP 1..1:
|
|
30
30
|
+const greeting = "hello";`);
|
|
31
31
|
const result = await patcher.apply(patch);
|
|
32
32
|
|
|
@@ -47,11 +47,11 @@ still matches the recorded content hash, and refusing or attempting
|
|
|
47
47
|
session-aware recovery on mismatch.
|
|
48
48
|
|
|
49
49
|
Inside a section:
|
|
50
|
-
- `
|
|
51
|
-
- `
|
|
52
|
-
- `
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
50
|
+
- `SWAP A..B:` — replace lines A..B with following `+TEXT` body rows.
|
|
51
|
+
- `SWAP.BLK A:` — replace the syntactic block beginning on line A.
|
|
52
|
+
- `DEL A..B` / `DEL.BLK A` — delete concrete lines or a resolved block.
|
|
53
|
+
- `INS.PRE A:` / `INS.POST A:` / `INS.HEAD:` / `INS.TAIL:` — insert following body rows.
|
|
54
|
+
- `INS.BLK.POST A:` — insert following body rows after the resolved block's last line.
|
|
55
55
|
- `+TEXT` — literal body row (use `+` alone for a blank line).
|
|
56
56
|
|
|
57
57
|
## Abstractions
|
package/dist/types/block.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface ResolveBlockEditsOptions {
|
|
|
6
6
|
* `blockUnresolvedMessage` error — used by the authoritative apply + final
|
|
7
7
|
* preview paths. `"drop"` silently skips the edit — used by the streaming
|
|
8
8
|
* preview, where a half-written file or transient parse error must not
|
|
9
|
-
* throw. Unresolvable `
|
|
9
|
+
* throw. Unresolvable `insert_after_block N:` edits never reach this: they
|
|
10
10
|
* are lowered to plain `insert after N:` with a warning.
|
|
11
11
|
*/
|
|
12
12
|
onUnresolved?: "throw" | "drop";
|
|
@@ -19,7 +19,7 @@ export interface ResolveBlockEditsOptions {
|
|
|
19
19
|
onResolved?: (resolution: BlockResolution) => void;
|
|
20
20
|
/**
|
|
21
21
|
* Invoked once per diagnostic produced while resolving — currently the
|
|
22
|
-
* `
|
|
22
|
+
* `insert_after_block N:` lowerings (closer anchor or unresolvable block).
|
|
23
23
|
* Hosts should surface these on the apply result's `warnings`.
|
|
24
24
|
*/
|
|
25
25
|
onWarning?: (message: string) => void;
|
package/dist/types/format.d.ts
CHANGED
|
@@ -10,22 +10,25 @@ export declare const HL_FILE_SUFFIX = "]";
|
|
|
10
10
|
/** Payload sigil for literal body rows. */
|
|
11
11
|
export declare const HL_PAYLOAD_REPLACE = "+";
|
|
12
12
|
/** Hunk-header keyword for concrete line replacement. */
|
|
13
|
-
export declare const HL_REPLACE_KEYWORD = "
|
|
14
|
-
/** Hunk-header sub-keyword: `replace block N:` resolves N to a tree-sitter block range. */
|
|
15
|
-
export declare const HL_BLOCK_KEYWORD = "block";
|
|
13
|
+
export declare const HL_REPLACE_KEYWORD = "SWAP";
|
|
16
14
|
/** Hunk-header keyword for concrete line deletion. */
|
|
17
|
-
export declare const HL_DELETE_KEYWORD = "
|
|
15
|
+
export declare const HL_DELETE_KEYWORD = "DEL";
|
|
18
16
|
/** Hunk-header keyword for insertion operations. */
|
|
19
|
-
export declare const HL_INSERT_KEYWORD = "
|
|
17
|
+
export declare const HL_INSERT_KEYWORD = "INS";
|
|
20
18
|
/** Insert position keyword for inserting before a concrete line. */
|
|
21
|
-
export declare const HL_INSERT_BEFORE = "
|
|
19
|
+
export declare const HL_INSERT_BEFORE = "PRE";
|
|
22
20
|
/** Insert position keyword for inserting after a concrete line. */
|
|
23
|
-
export declare const HL_INSERT_AFTER = "
|
|
21
|
+
export declare const HL_INSERT_AFTER = "POST";
|
|
24
22
|
/** Insert position keyword for inserting at the start of the file. */
|
|
25
|
-
export declare const HL_INSERT_HEAD = "
|
|
23
|
+
export declare const HL_INSERT_HEAD = "HEAD";
|
|
26
24
|
/** Insert position keyword for inserting at the end of the file. */
|
|
27
|
-
export declare const HL_INSERT_TAIL = "
|
|
28
|
-
/** Hunk-header
|
|
25
|
+
export declare const HL_INSERT_TAIL = "TAIL";
|
|
26
|
+
/** Hunk-header keyword: `SWAP.BLK N:` resolves N to a tree-sitter block range and replaces its span. */
|
|
27
|
+
export declare const HL_REPLACE_BLOCK_KEYWORD = "SWAP.BLK";
|
|
28
|
+
/** Hunk-header keyword: `DEL.BLK N` resolves N to a tree-sitter block range and deletes its span. */
|
|
29
|
+
export declare const HL_DELETE_BLOCK_KEYWORD = "DEL.BLK";
|
|
30
|
+
/** Hunk-header keyword: `INS.BLK.POST N:` inserts after the last line of the tree-sitter block at N. */
|
|
31
|
+
export declare const HL_INSERT_AFTER_BLOCK_KEYWORD = "INS.BLK.POST";
|
|
29
32
|
export declare const HL_HEADER_COLON = ":";
|
|
30
33
|
/** Separator between a hashline file path and its opaque snapshot tag. */
|
|
31
34
|
export declare const HL_FILE_HASH_SEP = "#";
|
package/dist/types/input.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare class PatchSection {
|
|
|
50
50
|
* method directly when you've already validated the file content and
|
|
51
51
|
* just want the result.
|
|
52
52
|
*
|
|
53
|
-
* `blockResolver` resolves any `
|
|
53
|
+
* `blockResolver` resolves any `replace_block N:` edits against `text`; an
|
|
54
54
|
* unresolvable block throws (this is the final, authoritative preview path).
|
|
55
55
|
*/
|
|
56
56
|
applyTo(text: string, blockResolver?: BlockResolver): ApplyResult;
|
|
@@ -61,7 +61,7 @@ export declare class PatchSection {
|
|
|
61
61
|
* empty-payload edit. Intended for incremental diff previews; the writer
|
|
62
62
|
* path should always use {@link applyTo}.
|
|
63
63
|
*
|
|
64
|
-
* `blockResolver` resolves any `
|
|
64
|
+
* `blockResolver` resolves any `replace_block N:` edits against `text`; an
|
|
65
65
|
* unresolvable block is silently dropped so a half-written file does not
|
|
66
66
|
* throw mid-stream.
|
|
67
67
|
*/
|
package/dist/types/messages.d.ts
CHANGED
|
@@ -17,51 +17,49 @@ export declare const END_PATCH_MARKER = "*** End Patch";
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const ABORT_MARKER = "*** Abort";
|
|
19
19
|
/** Two consecutive hunks targeted the exact same concrete range. */
|
|
20
|
-
export declare const REPLACE_PAIR_COALESCED_WARNING = "Two hunks targeted the same range; kept only the second. One `
|
|
21
|
-
/** Bare bodyless hunk followed by an overlapping concrete hunk. */
|
|
22
|
-
export declare const REPLACE_PAIR_COALESCED_OVERLAP_WARNING = "Dropped a bare hunk overlapped by the concrete hunk after it. One `replace N..M:` hunk per range \u2014 the body is the final content, never old+new.";
|
|
20
|
+
export declare const REPLACE_PAIR_COALESCED_WARNING = "Two hunks targeted the same range; kept only the second. One `SWAP N..M:` hunk per range \u2014 the body is the final content, never old+new.";
|
|
23
21
|
/** Bare body rows auto-converted to literal `+` rows. */
|
|
24
22
|
export declare const BARE_BODY_AUTO_PIPED_WARNING = "Auto-prefixed bare body row(s) with `+`. Body rows must be `+TEXT` literal lines.";
|
|
25
23
|
/** Unified-diff-style `-` row in a hunk body. */
|
|
26
24
|
export declare const MINUS_ROW_REJECTED = "`-` rows are not valid; the range already names the lines being changed. For a literal `-` line, write `+-\u2026`.";
|
|
27
25
|
/** Replace hunk with no body. */
|
|
28
|
-
export declare const EMPTY_REPLACE = "`
|
|
29
|
-
/** `
|
|
30
|
-
export declare const EMPTY_BLOCK = "`
|
|
26
|
+
export declare const EMPTY_REPLACE = "`SWAP N..M:` needs at least one `+TEXT` body row. To delete lines, use `DEL N..M`.";
|
|
27
|
+
/** `replace_block N:` hunk with no body. */
|
|
28
|
+
export declare const EMPTY_BLOCK = "`SWAP.BLK N:` needs at least one `+TEXT` body row. To delete a block, use `DEL.BLK N`.";
|
|
31
29
|
/**
|
|
32
30
|
* Block-anchored replace/delete could not resolve to a syntactic block
|
|
33
31
|
* (unsupported language, blank/out-of-range line, no node beginning on N, or
|
|
34
32
|
* parse error). Appends a {@link formatAnchoredContext} preview when
|
|
35
|
-
* `fileLines` is given. `
|
|
33
|
+
* `fileLines` is given. `insert_after_block N:` never reaches this — it is
|
|
36
34
|
* lowered to plain `insert after N:` instead (see
|
|
37
35
|
* {@link insertAfterBlockUnresolvedLoweredWarning}).
|
|
38
36
|
*/
|
|
39
37
|
export declare function blockUnresolvedMessage(line: number, op?: "replace" | "delete", fileLines?: readonly string[]): string;
|
|
40
38
|
/** Block-anchored edit reached a path with no {@link BlockResolver} wired in — a host-configuration bug. */
|
|
41
|
-
export declare const BLOCK_RESOLVER_UNAVAILABLE = "`
|
|
39
|
+
export declare const BLOCK_RESOLVER_UNAVAILABLE = "`SWAP.BLK`/`DEL.BLK`/`INS.BLK.POST` are not available here (no block resolver configured). Use a concrete line range.";
|
|
42
40
|
/**
|
|
43
|
-
* `
|
|
41
|
+
* `insert_after_block N:` anchored on a closing-delimiter line, lowered to
|
|
44
42
|
* plain `insert after N:` — the closer ends a block, and inserting after it
|
|
45
43
|
* is exactly what the plain form does.
|
|
46
44
|
*/
|
|
47
45
|
export declare function insertAfterBlockCloserLoweredWarning(line: number): string;
|
|
48
46
|
/**
|
|
49
|
-
* `
|
|
47
|
+
* `insert_after_block N:` anchor unresolvable (unsupported language, blank
|
|
50
48
|
* line, parse error, or no resolver), lowered to plain `insert after N:` —
|
|
51
49
|
* applying with a warning beats failing the patch.
|
|
52
50
|
*/
|
|
53
51
|
export declare function insertAfterBlockUnresolvedLoweredWarning(line: number): string;
|
|
54
52
|
/**
|
|
55
|
-
* Internal invariant: `applyEdits` received an unresolved `
|
|
53
|
+
* Internal invariant: `applyEdits` received an unresolved `replace_block N:`
|
|
56
54
|
* edit; `resolveBlockEdits` must run first. Wiring bug, not authored input.
|
|
57
55
|
*/
|
|
58
|
-
export declare const UNRESOLVED_BLOCK_INTERNAL = "internal error: unresolved `
|
|
56
|
+
export declare const UNRESOLVED_BLOCK_INTERNAL = "internal error: unresolved `SWAP.BLK` edit reached the applier (resolveBlockEdits was not run).";
|
|
59
57
|
/** Delete hunk received a body row. */
|
|
60
|
-
export declare const DELETE_TAKES_NO_BODY = "`
|
|
61
|
-
/** `
|
|
62
|
-
export declare const DELETE_BLOCK_TAKES_NO_BODY = "`
|
|
58
|
+
export declare const DELETE_TAKES_NO_BODY = "`DEL N..M` does not take body rows. Remove the body, or use `SWAP N..M:`.";
|
|
59
|
+
/** `delete_block N` hunk received a body row. */
|
|
60
|
+
export declare const DELETE_BLOCK_TAKES_NO_BODY = "`DEL.BLK N` does not take body rows. Remove the body, or use `SWAP.BLK N:`.";
|
|
63
61
|
/** Insert hunk with no body. */
|
|
64
|
-
export declare const EMPTY_INSERT = "`
|
|
62
|
+
export declare const EMPTY_INSERT = "`INS` needs at least one `+TEXT` body row.";
|
|
65
63
|
/**
|
|
66
64
|
* `insert after` body indented shallower than the anchor: the landing slid
|
|
67
65
|
* forward past trailing closer lines — the common "anchored on the last line
|
|
@@ -69,7 +67,7 @@ export declare const EMPTY_INSERT = "`insert` needs at least one `+TEXT` body ro
|
|
|
69
67
|
*/
|
|
70
68
|
export declare function afterInsertLandingShiftWarning(anchorLine: number, landingLine: number, crossed: number): string;
|
|
71
69
|
/**
|
|
72
|
-
* `
|
|
70
|
+
* `insert_after_block N:` body indented deeper than the block's closer: the
|
|
73
71
|
* landing was pulled inside the block — a deeper body almost always means
|
|
74
72
|
* "append inside the block's body".
|
|
75
73
|
*/
|
|
@@ -90,9 +88,26 @@ export declare const RECOVERY_SESSION_REPLAY_WARNING = "Recovered by replaying y
|
|
|
90
88
|
* Head/tail position is content-independent, so drift is non-fatal: apply
|
|
91
89
|
* onto live content and warn instead of hard-failing.
|
|
92
90
|
*/
|
|
93
|
-
export declare const HEADTAIL_DRIFT_WARNING = "Applied the `
|
|
91
|
+
export declare const HEADTAIL_DRIFT_WARNING = "Applied the `INS.HEAD:`/`INS.TAIL:` edit despite a stale snapshot tag (file changed since your read) \u2014 head/tail position is content-independent. Re-read if the drift was unexpected.";
|
|
94
92
|
/**
|
|
95
93
|
* Section omitted the mandatory snapshot tag. Shared by the apply
|
|
96
94
|
* ({@link Patcher.prepare}) and preview/diff paths so both stay in lockstep.
|
|
97
95
|
*/
|
|
98
96
|
export declare function missingSnapshotTagMessage(sectionPath: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* An anchored edit referenced lines the read that minted the cited tag never
|
|
99
|
+
* displayed (a partial range, or a structural summary that collapsed bodies).
|
|
100
|
+
* Editing lines you have not read is the off-by-memory failure that mangles
|
|
101
|
+
* files; reject and make the model re-read those exact lines first.
|
|
102
|
+
*/
|
|
103
|
+
export declare function unseenLinesMessage(sectionPath: string, unseenLines: readonly number[], tag: string): string;
|
|
104
|
+
/** Op kind of a deferred block edit, for {@link blockSingleLineMessage}. */
|
|
105
|
+
export type BlockOp = "replace" | "delete" | "insert_after";
|
|
106
|
+
/**
|
|
107
|
+
* A `replace_block`/`delete_block`/`insert_after_block` anchor resolved to a
|
|
108
|
+
* single line — almost always a bare statement the model mis-anchored, not a
|
|
109
|
+
* multi-line construct. The plain op is unambiguous for one line; the block
|
|
110
|
+
* form only earns its keep when it spares counting a closing line you cannot
|
|
111
|
+
* see. Reject and point at both fixes.
|
|
112
|
+
*/
|
|
113
|
+
export declare function blockSingleLineMessage(line: number, op: BlockOp): string;
|
package/dist/types/patcher.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export interface PatcherOptions {
|
|
|
10
10
|
/** Snapshot store that minted and resolves hashline section tags. Required. */
|
|
11
11
|
snapshots: SnapshotStore;
|
|
12
12
|
/**
|
|
13
|
-
* Resolves `
|
|
14
|
-
* Optional: when omitted, any `
|
|
13
|
+
* Resolves `replace_block N:` anchors to concrete line spans via tree-sitter.
|
|
14
|
+
* Optional: when omitted, any `replace_block N:` edit throws on apply (the
|
|
15
15
|
* host did not wire a resolver). Plain line-range ops never need it.
|
|
16
16
|
*/
|
|
17
17
|
blockResolver?: BlockResolver;
|
|
@@ -41,7 +41,7 @@ export interface PatchSectionResult {
|
|
|
41
41
|
/** Warnings collected by the parser, applier, and (optionally) recovery. */
|
|
42
42
|
warnings: string[];
|
|
43
43
|
/**
|
|
44
|
-
* Resolved spans for any `
|
|
44
|
+
* Resolved spans for any `replace_block`/`delete_block` ops, present when the
|
|
45
45
|
* apply matched the tagged content. Undefined for patches with no block ops
|
|
46
46
|
* (and for resolutions routed through drift recovery, where numbers shift).
|
|
47
47
|
*/
|
|
@@ -11,6 +11,15 @@ export interface Snapshot {
|
|
|
11
11
|
readonly hash: string;
|
|
12
12
|
/** Timestamp (ms since epoch) the version was recorded. */
|
|
13
13
|
recordedAt: number;
|
|
14
|
+
/**
|
|
15
|
+
* 1-indexed file lines a producer (read/search) actually *displayed* under
|
|
16
|
+
* this tag. A partial read (range, or a structural summary that collapsed
|
|
17
|
+
* bodies) leaves this sparse; a whole-file read fills every line. Multiple
|
|
18
|
+
* reads of the same content union into one set. `undefined` means "no
|
|
19
|
+
* provenance recorded" — the patcher then skips the seen-line check and
|
|
20
|
+
* applies as before. Mutated in place as more of the same content is read.
|
|
21
|
+
*/
|
|
22
|
+
seenLines?: Set<number>;
|
|
14
23
|
}
|
|
15
24
|
/**
|
|
16
25
|
* Storage seam for full-file version snapshots. The patcher calls {@link head}
|
|
@@ -22,8 +31,19 @@ export declare abstract class SnapshotStore {
|
|
|
22
31
|
abstract head(path: string): Snapshot | null;
|
|
23
32
|
/** Recorded version for `path` whose tag equals `hash`, or `null`. */
|
|
24
33
|
abstract byHash(path: string, hash: string): Snapshot | null;
|
|
25
|
-
/**
|
|
26
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Record the full normalized text of `path` and return its content tag.
|
|
36
|
+
* `seenLines` (optional) are the 1-indexed lines the producer displayed;
|
|
37
|
+
* they merge into {@link Snapshot.seenLines} across reads of identical text.
|
|
38
|
+
*/
|
|
39
|
+
abstract record(path: string, fullText: string, seenLines?: Iterable<number>): string;
|
|
40
|
+
/**
|
|
41
|
+
* Merge `lines` into the {@link Snapshot.seenLines} of the version whose tag
|
|
42
|
+
* equals `hash`. No-op when no such version is retained (the content aged
|
|
43
|
+
* out or was overwritten). Lets producers attach displayed lines after the
|
|
44
|
+
* tag was already minted (the body is formatted after the hash is computed).
|
|
45
|
+
*/
|
|
46
|
+
abstract recordSeenLines(path: string, hash: string, lines: Iterable<number>): void;
|
|
27
47
|
/** Drop the version history for a single path. */
|
|
28
48
|
abstract invalidate(path: string): void;
|
|
29
49
|
/** Drop every version history. */
|
|
@@ -55,7 +75,8 @@ export declare class InMemorySnapshotStore extends SnapshotStore {
|
|
|
55
75
|
constructor(options?: InMemorySnapshotStoreOptions);
|
|
56
76
|
head(path: string): Snapshot | null;
|
|
57
77
|
byHash(path: string, hash: string): Snapshot | null;
|
|
58
|
-
record(path: string, fullText: string): string;
|
|
78
|
+
record(path: string, fullText: string, seenLines?: Iterable<number>): string;
|
|
79
|
+
recordSeenLines(path: string, hash: string, lines: Iterable<number>): void;
|
|
59
80
|
invalidate(path: string): void;
|
|
60
81
|
clear(): void;
|
|
61
82
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type Edit = {
|
|
|
34
34
|
index: number;
|
|
35
35
|
mode?: "replacement";
|
|
36
36
|
/**
|
|
37
|
-
* Present on inserts lowered from `
|
|
37
|
+
* Present on inserts lowered from `insert_after_block N:`: the
|
|
38
38
|
* resolved block's first line. Lets the applier slide a body that
|
|
39
39
|
* claims a depth inside the block back across the block's trailing
|
|
40
40
|
* closer lines (never above this line).
|
|
@@ -48,13 +48,13 @@ export type Edit = {
|
|
|
48
48
|
oldAssertion?: string;
|
|
49
49
|
} | {
|
|
50
50
|
/**
|
|
51
|
-
* Deferred block edit (`
|
|
52
|
-
* `
|
|
51
|
+
* Deferred block edit (`replace_block N:` / `delete_block N` /
|
|
52
|
+
* `insert_after_block N:`). The exact line span is unknown at parse
|
|
53
53
|
* time — it is computed by {@link resolveBlockEdits} once file text +
|
|
54
54
|
* path (→ language) are available, then expanded into concrete edits:
|
|
55
|
-
* a non-empty `payloads` without `mode` (from `
|
|
55
|
+
* a non-empty `payloads` without `mode` (from `replace_block`) becomes
|
|
56
56
|
* the same `replacement` inserts + deletes that `replace start..end:`
|
|
57
|
-
* produces; an empty `payloads` (from `
|
|
57
|
+
* produces; an empty `payloads` (from `delete_block`) becomes a pure
|
|
58
58
|
* range deletion; `mode: "insert_after"` becomes plain `after_anchor`
|
|
59
59
|
* inserts at the block's last line. `applyEdits` never sees this
|
|
60
60
|
* variant.
|
|
@@ -75,7 +75,7 @@ export interface ApplyResult {
|
|
|
75
75
|
/** Diagnostic warnings collected by the parser, patcher, or recovery. */
|
|
76
76
|
warnings?: string[];
|
|
77
77
|
/**
|
|
78
|
-
* Resolved spans for each `
|
|
78
|
+
* Resolved spans for each `replace_block`/`delete_block` op in this apply,
|
|
79
79
|
* in patch order. Present only when the apply matched the tagged content
|
|
80
80
|
* (the common no-drift path), so the line numbers line up with what the
|
|
81
81
|
* caller read. Absent when there were no block ops.
|
|
@@ -121,7 +121,7 @@ export interface CompactDiffOptions {
|
|
|
121
121
|
maxUnchangedRun?: number;
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* Resolved 1-indexed inclusive line span of a `
|
|
124
|
+
* Resolved 1-indexed inclusive line span of a `replace_block N:` target.
|
|
125
125
|
*/
|
|
126
126
|
export interface BlockSpan {
|
|
127
127
|
/** First line of the block (1-indexed, inclusive). */
|
|
@@ -130,7 +130,7 @@ export interface BlockSpan {
|
|
|
130
130
|
end: number;
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* One `
|
|
133
|
+
* One `replace_block N:` / `delete_block N` / `insert_after_block N:` anchor
|
|
134
134
|
* resolved to its concrete line span. Surfaced on {@link ApplyResult} so the
|
|
135
135
|
* host can echo "block N → lines start..end" and let the model catch a wrong
|
|
136
136
|
* opener — e.g. a decorator or doc-comment that sits in a separate node
|
|
@@ -146,7 +146,7 @@ export interface BlockResolution {
|
|
|
146
146
|
/** Which block op produced this resolution. */
|
|
147
147
|
op: "replace" | "delete" | "insert_after";
|
|
148
148
|
}
|
|
149
|
-
/** Request handed to a {@link BlockResolver} to resolve one `
|
|
149
|
+
/** Request handed to a {@link BlockResolver} to resolve one `replace_block N:` anchor. */
|
|
150
150
|
export interface BlockResolverRequest {
|
|
151
151
|
/** Target file path (used to infer language by extension). */
|
|
152
152
|
path: string;
|
|
@@ -156,7 +156,7 @@ export interface BlockResolverRequest {
|
|
|
156
156
|
line: number;
|
|
157
157
|
}
|
|
158
158
|
/**
|
|
159
|
-
* Resolves a `
|
|
159
|
+
* Resolves a `replace_block N:` anchor to the line span of the syntactic block
|
|
160
160
|
* that begins on line N. Returns `null` when no block can be resolved
|
|
161
161
|
* (unrecognized language, blank/out-of-range line, no node begins there, or the
|
|
162
162
|
* resolved subtree has a syntax error). Pure seam: the hashline core declares
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/hashline",
|
|
4
|
-
"version": "15.13.
|
|
4
|
+
"version": "15.13.2",
|
|
5
5
|
"description": "Hashline: a compact, line-anchored patch language and applier. Pluggable FS/IO so it works over disk, in-memory, or any custom backend.",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|