@oh-my-pi/hashline 16.1.22 → 16.1.23
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 +10 -0
- package/package.json +1 -1
- package/src/patcher.ts +10 -3
- package/src/prompt.md +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.1.23] - 2026-06-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Updated prompt documentation to include support for Markdown section operations
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Improved file path recovery to correctly handle read-only or incorrectly typed paths
|
|
14
|
+
|
|
5
15
|
## [16.1.14] - 2026-06-22
|
|
6
16
|
|
|
7
17
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/hashline",
|
|
4
|
-
"version": "16.1.
|
|
4
|
+
"version": "16.1.23",
|
|
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/patcher.ts
CHANGED
|
@@ -256,13 +256,15 @@ export class Patcher {
|
|
|
256
256
|
|
|
257
257
|
let target = section;
|
|
258
258
|
let canonicalPath = this.fs.canonicalPath(target.path);
|
|
259
|
-
await this.fs.preflightWrite(target.path);
|
|
260
259
|
let read = await this.#tryRead(target.path);
|
|
261
260
|
|
|
262
261
|
// Path recovery: the authored path doesn't exist on disk, but its
|
|
263
262
|
// filename + snapshot tag may name a file the model read this session
|
|
264
263
|
// (it supplied a bare filename, or the wrong directory). Rebind to that
|
|
265
|
-
// file so the edit lands where the tag points, and warn.
|
|
264
|
+
// file so the edit lands where the tag points, and warn. This runs
|
|
265
|
+
// before the write gate so a recoverable bare/mis-typed path is rebound
|
|
266
|
+
// to its real (writable) location instead of being rejected against the
|
|
267
|
+
// literal — possibly read-only — path it was authored as.
|
|
266
268
|
if (!read.exists) {
|
|
267
269
|
const recovered = this.#recoverSectionPathFromTag(target, canonicalPath);
|
|
268
270
|
if (recovered && this.fs.allowTagPathRecovery(target.path, recovered.section.path)) {
|
|
@@ -271,10 +273,15 @@ export class Patcher {
|
|
|
271
273
|
);
|
|
272
274
|
target = recovered.section;
|
|
273
275
|
canonicalPath = recovered.canonicalPath;
|
|
274
|
-
await this.fs.preflightWrite(target.path);
|
|
275
276
|
read = await this.#tryRead(target.path);
|
|
276
277
|
}
|
|
277
278
|
}
|
|
279
|
+
|
|
280
|
+
// Gate the final (possibly recovered) target before any write work, so
|
|
281
|
+
// an unrecoverable read-only target (e.g. a plan-mode working-tree path)
|
|
282
|
+
// fails with the write guard rather than a misleading "file not found".
|
|
283
|
+
await this.fs.preflightWrite(target.path);
|
|
284
|
+
|
|
278
285
|
if (!read.exists) {
|
|
279
286
|
throw new Error(`File not found: ${target.path}. Use the write tool to create new files.`);
|
|
280
287
|
}
|
package/src/prompt.md
CHANGED
|
@@ -34,6 +34,7 @@ Body rows appear only under a `:` header. Every body row is `+TEXT` — add a li
|
|
|
34
34
|
- Whole construct → `SWAP.BLK N` (tree-sitter resolves the end); lines inside it → `SWAP N.=M`.
|
|
35
35
|
- `SWAP.BLK N` resolves EXACTLY the node at N. Leading decorators/attributes/doc-comments are separate nodes: point N at the FIRST decorator to sweep both; standalone line-comments are never swept — use `SWAP N.=M`.
|
|
36
36
|
- Block ops (`SWAP.BLK`/`DEL.BLK`/`INS.BLK.POST`) anchor the OPENING line of a MULTI-LINE construct — never its closer, last line, or a bare inner statement. Anchoring one statement resolves to ONE line and is REJECTED: use the plain op (`SWAP N.=N` / `DEL N` / `INS.POST N`), or point N at the real opener. Saw the closer? Use plain `INS.POST M:`.
|
|
37
|
+
- Markdown: a heading line IS a block opener — `SWAP.BLK`/`DEL.BLK`/`INS.BLK.POST` on a `##`/`###` heading resolves its WHOLE section (heading through every nested deeper heading, up to the next same-or-higher heading). So `DEL.BLK` drops the section, `SWAP.BLK` rewrites it, `INS.BLK.POST` lands after it (end the inserted body with a blank line to keep the next heading separated).
|
|
37
38
|
- Non-adjacent changes = separate hunks; untouched lines stay out of every range.
|
|
38
39
|
- Pure additions use `INS.PRE` / `INS.POST` / `INS.HEAD` / `INS.TAIL`, never a widened `SWAP` — retyped keepers are exactly what gets dropped. (A multi-line `SWAP` whose body restates the line just past the range is auto-dropped as an off-by-one keeper with a warning — issue the payload for the range only; never lean on the repair.)
|
|
39
40
|
- NEVER format/restyle code with this tool; run the project formatter instead.
|