@oh-my-pi/hashline 15.12.4 → 15.13.0
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 +36 -74
- package/package.json +1 -1
- package/src/apply.ts +15 -3
- package/src/input.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,121 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [15.12.0] - 2026-06-12
|
|
6
|
-
|
|
7
|
-
### Changed
|
|
8
|
-
|
|
9
|
-
- Condensed all parser/applier/patcher error and warning messages: shorter wording, same diagnostic anchors (op names, line numbers, suggested fallback forms)
|
|
10
|
-
|
|
11
|
-
## [15.11.4] - 2026-06-12
|
|
12
|
-
|
|
13
|
-
### Added
|
|
14
|
-
|
|
15
|
-
- 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
|
|
16
|
-
- 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`
|
|
17
|
-
|
|
18
|
-
### Changed
|
|
19
|
-
|
|
20
|
-
- 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
|
|
21
|
-
|
|
22
|
-
## [15.11.1] - 2026-06-11
|
|
23
|
-
|
|
24
|
-
### Fixed
|
|
25
|
-
|
|
26
|
-
- 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))
|
|
27
|
-
|
|
28
|
-
## [15.11.0] - 2026-06-10
|
|
29
|
-
|
|
30
|
-
### Changed
|
|
31
|
-
|
|
32
|
-
- 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
|
|
33
|
-
|
|
34
|
-
## [15.10.11] - 2026-06-10
|
|
35
|
-
|
|
36
5
|
### Breaking Changes
|
|
37
6
|
|
|
38
7
|
- Changed `BlockResolution.isDelete` to `BlockResolution.op` (`"replace" | "delete" | "insert_after"`) so resolutions can describe every block-anchored op
|
|
8
|
+
- Changed hashline file section headers from `¶PATH#TAG` to `[PATH#TAG]` so model-authored edits use ASCII delimiters instead of a pilcrow sigil.
|
|
39
9
|
|
|
40
10
|
### Added
|
|
41
11
|
|
|
12
|
+
- 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
|
+
- 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`
|
|
42
14
|
- 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
|
|
43
15
|
- 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
|
|
44
16
|
- 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
|
|
45
22
|
|
|
46
23
|
### Changed
|
|
47
24
|
|
|
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
|
|
48
28
|
- 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
|
|
49
29
|
- 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.
|
|
50
33
|
|
|
51
34
|
### Fixed
|
|
52
35
|
|
|
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))
|
|
53
39
|
- 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
|
|
54
40
|
- 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
|
|
55
41
|
- 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
|
|
56
42
|
- 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)
|
|
57
|
-
|
|
58
|
-
## [15.10.5] - 2026-06-08
|
|
59
|
-
|
|
60
|
-
### Added
|
|
61
|
-
|
|
62
|
-
- 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
|
|
63
|
-
|
|
64
|
-
### Changed
|
|
65
|
-
|
|
66
|
-
- Changed `buildCompactDiffPreview` to omit removed lines from the preview while preserving removal counts for offset tracking
|
|
67
|
-
- 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)
|
|
68
|
-
|
|
69
|
-
### Fixed
|
|
70
|
-
|
|
71
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.
|
|
72
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)).
|
|
73
49
|
|
|
74
|
-
## [15.
|
|
75
|
-
|
|
76
|
-
### Added
|
|
77
|
-
|
|
78
|
-
- 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.
|
|
50
|
+
## [15.13.0] - 2026-06-14
|
|
79
51
|
|
|
80
|
-
|
|
52
|
+
## [15.12.5] - 2026-06-13
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
## [15.12.0] - 2026-06-12
|
|
83
55
|
|
|
84
|
-
## [15.
|
|
56
|
+
## [15.11.4] - 2026-06-12
|
|
85
57
|
|
|
86
|
-
|
|
58
|
+
## [15.11.1] - 2026-06-11
|
|
87
59
|
|
|
88
|
-
|
|
60
|
+
## [15.11.0] - 2026-06-10
|
|
89
61
|
|
|
90
|
-
## [15.
|
|
62
|
+
## [15.10.11] - 2026-06-10
|
|
91
63
|
|
|
92
|
-
|
|
64
|
+
## [15.10.5] - 2026-06-08
|
|
93
65
|
|
|
94
|
-
|
|
66
|
+
## [15.10.3] - 2026-06-08
|
|
95
67
|
|
|
96
|
-
|
|
68
|
+
## [15.10.2] - 2026-06-08
|
|
97
69
|
|
|
98
|
-
|
|
70
|
+
## [15.9.67] - 2026-06-06
|
|
99
71
|
|
|
100
72
|
## [15.8.2] - 2026-06-03
|
|
101
73
|
|
|
102
|
-
### Fixed
|
|
103
|
-
|
|
104
|
-
- 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.
|
|
105
|
-
|
|
106
74
|
## [15.8.0] - 2026-06-02
|
|
107
75
|
|
|
108
|
-
### Fixed
|
|
109
|
-
|
|
110
|
-
- 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)).
|
|
111
|
-
|
|
112
76
|
## [15.7.0] - 2026-05-31
|
|
113
|
-
### Added
|
|
114
|
-
|
|
115
|
-
- 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
|
|
116
|
-
- Added `BlockResolver` support in `Patcher` and `PatchSection.applyTo`/`applyPartialTo` to wire language-specific block-resolution at apply time
|
|
117
|
-
- Added `resolveBlockEdits` and block edit type definitions to the package API for resolving deferred `replace block` / `delete block` edits
|
|
118
77
|
|
|
119
78
|
## [15.5.13] - 2026-05-29
|
|
79
|
+
|
|
120
80
|
### Breaking Changes
|
|
121
81
|
|
|
122
82
|
- Changed hashline section tags from 3-hex to 4-hex content-hash tags, so legacy 3-digit tags are no longer valid
|
|
@@ -152,6 +112,7 @@
|
|
|
152
112
|
- `MismatchError` now distinguishes "hash recognized but file content drifted" from "hash never recorded for this path". The latter (likely fabricated or carried over from a prior session) emits a dedicated `hash #X is not from this session` rejection message with explicit "never invent the tag" guidance. The `MismatchDetails` interface gains an optional `hashRecognized?: boolean` (defaults to `true` for backward compatibility); `MismatchError` exposes it as a readonly field so callers can branch on the cause.
|
|
153
113
|
|
|
154
114
|
## [15.5.8] - 2026-05-28
|
|
115
|
+
|
|
155
116
|
### Breaking Changes
|
|
156
117
|
|
|
157
118
|
- Removed the single-number hunk header shorthand. A hunk header now REQUIRES two line numbers (`A A` for a single line, `A B` for a range); a bare `A` row throws `single-number hunk header "A" is no longer accepted`. The `&A` body-row shorthand for `&A..A` is unchanged.
|
|
@@ -210,6 +171,7 @@
|
|
|
210
171
|
All notable changes to this package will be documented in this file.
|
|
211
172
|
|
|
212
173
|
## [15.5.4] - 2026-05-27
|
|
174
|
+
|
|
213
175
|
### Added
|
|
214
176
|
|
|
215
177
|
- Added a high-level `Patcher` API with all-or-nothing `apply` and staged `prepare`/`commit` flows for multi-file patch updates
|
|
@@ -225,4 +187,4 @@ All notable changes to this package will be documented in this file.
|
|
|
225
187
|
|
|
226
188
|
- Fixed repeated patch application mutating cached `after_anchor` edits between target snapshots
|
|
227
189
|
- Fixed multi-section patching to preflight write policies and reject duplicate canonical targets before any section is committed
|
|
228
|
-
- Fixed mixed line-ending restoration to preserve the first newline style instead of rewriting ties to LF
|
|
190
|
+
- Fixed mixed line-ending restoration to preserve the first newline style instead of rewriting ties to LF
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/hashline",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.13.0",
|
|
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",
|
package/src/apply.ts
CHANGED
|
@@ -318,11 +318,22 @@ function findDuplicatePrefix(group: ReplacementGroup, fileLines: readonly string
|
|
|
318
318
|
return 0;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
function payloadEndsWithDeletedSuffix(group: ReplacementGroup, fileLines: readonly string[], count: number): boolean {
|
|
322
|
+
if (group.payload.length < count) return false;
|
|
323
|
+
const deletedStart = group.endLine - count;
|
|
324
|
+
const payloadStart = group.payload.length - count;
|
|
325
|
+
for (let offset = 0; offset < count; offset++) {
|
|
326
|
+
if (group.payload[payloadStart + offset] !== fileLines[deletedStart + offset]) return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
321
331
|
/**
|
|
322
332
|
* Smallest `m` such that the range's last `m` deleted lines are all pure
|
|
323
|
-
* structural closers
|
|
324
|
-
*
|
|
325
|
-
* payload never
|
|
333
|
+
* structural closers, the payload does not already restate those same suffix
|
|
334
|
+
* lines, and sparing them (keeping instead of deleting) zeroes `delta`. The
|
|
335
|
+
* mirror mistake: a range that swallows a closing delimiter the payload never
|
|
336
|
+
* restates.
|
|
326
337
|
*/
|
|
327
338
|
function findDroppedSuffixClosers(
|
|
328
339
|
group: ReplacementGroup,
|
|
@@ -333,6 +344,7 @@ function findDroppedSuffixClosers(
|
|
|
333
344
|
const maxM = group.deleteIndices.length;
|
|
334
345
|
for (let m = 1; m <= maxM; m++) {
|
|
335
346
|
if (!STRUCTURAL_CLOSER_RE.test(fileLines[group.endLine - m] ?? "")) break;
|
|
347
|
+
if (payloadEndsWithDeletedSuffix(group, fileLines, m)) continue;
|
|
336
348
|
if (balanceEqual(computeDelimiterBalance(fileLines.slice(group.endLine - m, group.endLine)), wanted)) return m;
|
|
337
349
|
}
|
|
338
350
|
return 0;
|
package/src/input.ts
CHANGED
|
@@ -88,8 +88,9 @@ function normalizeHashlinePath(rawPath: string, cwd?: string): string {
|
|
|
88
88
|
const unquoted = stripApplyPatchPathNoise(unquoteHashlinePath(rawPath.trim()));
|
|
89
89
|
if (!cwd || !path.isAbsolute(unquoted)) return unquoted;
|
|
90
90
|
const relative = path.relative(path.resolve(cwd), path.resolve(unquoted));
|
|
91
|
+
const normalizedRelative = relative.split(path.sep).join("/");
|
|
91
92
|
const isWithinCwd = relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
92
|
-
return isWithinCwd ?
|
|
93
|
+
return isWithinCwd ? normalizedRelative || "." : unquoted;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
interface RawSection {
|