@oh-my-pi/hashline 15.5.12 → 15.5.13
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/dist/types/format.d.ts +37 -23
- package/dist/types/input.d.ts +3 -3
- package/dist/types/messages.d.ts +14 -34
- package/dist/types/parser.d.ts +0 -53
- package/dist/types/recovery.d.ts +11 -13
- package/dist/types/snapshots.d.ts +36 -114
- package/dist/types/tokenizer.d.ts +10 -53
- package/dist/types/types.d.ts +7 -11
- package/package.json +3 -2
- package/src/apply.ts +334 -53
- package/src/format.ts +64 -28
- package/src/grammar.lark +10 -10
- package/src/input.ts +10 -13
- package/src/messages.ts +17 -36
- package/src/mismatch.ts +3 -4
- package/src/parser.ts +71 -329
- package/src/patcher.ts +21 -43
- package/src/prompt.md +43 -44
- package/src/recovery.ts +22 -72
- package/src/snapshots.ts +84 -390
- package/src/tokenizer.ts +102 -155
- package/src/types.ts +9 -13
package/src/input.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { applyEdits } from "./apply";
|
|
12
|
-
import { HL_FILE_HASH_SEP, HL_FILE_PREFIX } from "./format";
|
|
12
|
+
import { HL_FILE_HASH_LENGTH, HL_FILE_HASH_SEP, HL_FILE_PREFIX } from "./format";
|
|
13
13
|
import { parsePatch, parsePatchStreaming } from "./parser";
|
|
14
14
|
import { Tokenizer } from "./tokenizer";
|
|
15
15
|
import type { ApplyResult, Edit, SplitOptions } from "./types";
|
|
@@ -56,7 +56,7 @@ function tryParseRecoveryHeader(line: string, cwd?: string): RawSection | null {
|
|
|
56
56
|
if (!line.startsWith(HL_FILE_PREFIX)) return null;
|
|
57
57
|
const body = stripApplyPatchPathNoise(line.slice(HL_FILE_PREFIX.length).trim());
|
|
58
58
|
if (body.length === 0) return null;
|
|
59
|
-
const match =
|
|
59
|
+
const match = new RegExp(`^(\\S+?)(?:#([0-9A-Fa-f]{${HL_FILE_HASH_LENGTH}}))?\\s*$`).exec(body);
|
|
60
60
|
if (match === null) return null;
|
|
61
61
|
const path = normalizeHashlinePath(match[1], cwd);
|
|
62
62
|
if (path.length === 0) return null;
|
|
@@ -95,7 +95,7 @@ function parseHashlineHeaderLine(line: string, cwd?: string): RawSection | null
|
|
|
95
95
|
const recovered = tryParseRecoveryHeader(trimmed, cwd);
|
|
96
96
|
if (recovered !== null) return recovered;
|
|
97
97
|
throw new Error(
|
|
98
|
-
`Input header must be ${HL_FILE_PREFIX}PATH or ${HL_FILE_PREFIX}PATH${HL_FILE_HASH_SEP}TAG with a
|
|
98
|
+
`Input header must be ${HL_FILE_PREFIX}PATH or ${HL_FILE_PREFIX}PATH${HL_FILE_HASH_SEP}TAG with a ${HL_FILE_HASH_LENGTH}-hex content-hash tag; got ${JSON.stringify(trimmed)}.`,
|
|
99
99
|
);
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -159,7 +159,7 @@ function splitRawSections(input: string, options: SplitOptions = {}): RawSection
|
|
|
159
159
|
if (/^@@\s+[-+]?\d+,\d+\s+[-+]?\d+,\d+\s+@@/.test(firstTrimmed)) {
|
|
160
160
|
throw new Error(
|
|
161
161
|
"unified-diff hunk header (`@@ -N,M +N,M @@`) is not valid in hashline. " +
|
|
162
|
-
"File sections start with `¶path#HASH`;
|
|
162
|
+
"File sections start with `¶path#HASH`; use `replace`, `delete`, or `insert` ops.",
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
const preview = JSON.stringify(firstLine.slice(0, 120));
|
|
@@ -244,14 +244,14 @@ export class PatchSection {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
|
-
* True when at least one edit anchors to concrete file content. Pure
|
|
248
|
-
* literal inserts do not count: those are
|
|
249
|
-
* yet exist.
|
|
247
|
+
* True when at least one edit anchors to concrete file content. Pure
|
|
248
|
+
* `insert head:` / `insert tail:` literal inserts do not count: those are
|
|
249
|
+
* safe to apply to files that don't yet exist.
|
|
250
250
|
*/
|
|
251
251
|
get hasAnchorScopedEdit(): boolean {
|
|
252
252
|
return this.edits.some(edit => {
|
|
253
|
-
if (edit.kind === "delete"
|
|
254
|
-
return edit.cursor.kind === "before_anchor";
|
|
253
|
+
if (edit.kind === "delete") return true;
|
|
254
|
+
return edit.cursor.kind === "before_anchor" || edit.cursor.kind === "after_anchor";
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
|
|
@@ -263,10 +263,7 @@ export class PatchSection {
|
|
|
263
263
|
lines.add(edit.anchor.line);
|
|
264
264
|
continue;
|
|
265
265
|
}
|
|
266
|
-
if (edit.kind === "
|
|
267
|
-
for (let line = edit.range.start.line; line <= edit.range.end.line; line++) lines.add(line);
|
|
268
|
-
}
|
|
269
|
-
if (edit.cursor.kind === "before_anchor") {
|
|
266
|
+
if (edit.cursor.kind === "before_anchor" || edit.cursor.kind === "after_anchor") {
|
|
270
267
|
lines.add(edit.cursor.anchor.line);
|
|
271
268
|
}
|
|
272
269
|
}
|
package/src/messages.ts
CHANGED
|
@@ -21,49 +21,30 @@ export const END_PATCH_MARKER = "*** End Patch";
|
|
|
21
21
|
*/
|
|
22
22
|
export const ABORT_MARKER = "*** Abort";
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Warning text appended when two consecutive hunks target the exact same
|
|
26
|
-
* concrete range. The second hunk wins; the first is discarded.
|
|
27
|
-
*/
|
|
24
|
+
/** Warning text appended when two consecutive hunks target the exact same concrete range. */
|
|
28
25
|
export const REPLACE_PAIR_COALESCED_WARNING =
|
|
29
|
-
"Detected two identical-range hashline hunks; kept only the second hunk. Issue ONE hunk per range — payload is the final desired content, never both old and new.";
|
|
26
|
+
"Detected two identical-range hashline hunks; kept only the second hunk. Issue ONE `replace N..M:` hunk per range — payload is the final desired content, never both old and new.";
|
|
30
27
|
|
|
31
|
-
/**
|
|
32
|
-
* Warning text appended when a bare hunk header (`A B` with no body)
|
|
33
|
-
* is followed by an overlapping concrete hunk. The earlier bare hunk is
|
|
34
|
-
* dropped on the assumption that the model expressed an old/new pair across
|
|
35
|
-
* two hunks; only the second hunk's payload is applied.
|
|
36
|
-
*/
|
|
28
|
+
/** Warning text appended when an empty bodyless hunk is followed by an overlapping concrete hunk. */
|
|
37
29
|
export const REPLACE_PAIR_COALESCED_OVERLAP_WARNING =
|
|
38
|
-
"Detected an overlapping bare hashline hunk immediately followed by a concrete hunk; dropped the earlier bare hunk. Issue ONE hunk per range — payload is the final desired content, never both old and new.";
|
|
30
|
+
"Detected an overlapping bare hashline hunk immediately followed by a concrete hunk; dropped the earlier bare hunk. Issue ONE `replace N..M:` hunk per range — payload is the final desired content, never both old and new.";
|
|
39
31
|
|
|
40
|
-
/**
|
|
41
|
-
* Warning text appended when bare body rows (no `+` / `&` prefix) follow a
|
|
42
|
-
* hunk header and the parser auto-converts them to `+literal` rows because
|
|
43
|
-
* no `+`/`&` row was present in the hunk. Helps the model learn the
|
|
44
|
-
* canonical body-row syntax while keeping the patch applying.
|
|
45
|
-
*/
|
|
32
|
+
/** Warning text appended when bare body rows are auto-converted to literal rows. */
|
|
46
33
|
export const BARE_BODY_AUTO_PIPED_WARNING =
|
|
47
|
-
"Auto-prefixed bare body row(s) with `+`.
|
|
34
|
+
"Auto-prefixed bare body row(s) with `+`. Body rows must be `+TEXT` literal lines; pasting raw code as payload is not a portable shape.";
|
|
48
35
|
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
* the row as a `&A..B` repeat so the patch still applies, then surface this
|
|
53
|
-
* warning so the model sees the mistake on the next turn.
|
|
54
|
-
*/
|
|
55
|
-
export const PLUS_PREFIXED_REPEAT_WARNING =
|
|
56
|
-
"A body row started with `+&A..B`. `+` (literal text) and `&A..B` (repeat) are sibling row kinds — a row uses exactly one of them. Treated as `&A..B`; remove the leading `+` next time.";
|
|
36
|
+
/** Error text emitted when a hunk body contains a unified-diff-style `-` row. */
|
|
37
|
+
export const MINUS_ROW_REJECTED =
|
|
38
|
+
"`-` rows are not valid; hashline ranges already name the lines being changed. To insert a literal line starting with `-`, write `+-…`.";
|
|
57
39
|
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*/
|
|
65
|
-
export const
|
|
66
|
-
"Hunk body contained unified-diff-style rows (`-old`, ` context`). The `-` rows were dropped (the hunk header's range already deletes those lines); context rows were treated as `+TEXT` literals. Use `+TEXT` (literal) or `&A..B` (repeat) directly next time.";
|
|
40
|
+
/** Error text emitted when a replace hunk has no body. */
|
|
41
|
+
export const EMPTY_REPLACE = "`replace N..M:` needs at least one `+TEXT` body row. To delete lines, use `delete N..M`.";
|
|
42
|
+
|
|
43
|
+
/** Error text emitted when a delete hunk receives a body row. */
|
|
44
|
+
export const DELETE_TAKES_NO_BODY = "`delete N..M` does not take body rows. Remove the body, or use `replace N..M:`.";
|
|
45
|
+
|
|
46
|
+
/** Error text emitted when an insert hunk has no body. */
|
|
47
|
+
export const EMPTY_INSERT = "`insert` needs at least one `+TEXT` body row.";
|
|
67
48
|
|
|
68
49
|
/** Warning text emitted by `Recovery` when an external write fits a cached snapshot. */
|
|
69
50
|
export const RECOVERY_EXTERNAL_WARNING =
|
package/src/mismatch.ts
CHANGED
|
@@ -6,17 +6,16 @@
|
|
|
6
6
|
* plus a couple of lines of surrounding context. The {@link MismatchError}
|
|
7
7
|
* formats this into a message at construction time.
|
|
8
8
|
*/
|
|
9
|
-
import { formatNumberedLine, HL_FILE_HASH_SEP, HL_FILE_PREFIX } from "./format";
|
|
9
|
+
import { formatNumberedLine, HL_FILE_HASH_EXAMPLES, HL_FILE_HASH_SEP, HL_FILE_PREFIX } from "./format";
|
|
10
10
|
import { MISMATCH_CONTEXT } from "./messages";
|
|
11
11
|
|
|
12
12
|
const LINE_REF_RE = /^\s*[>+\-*]*\s*(\d+)(?::.*)?\s*$/;
|
|
13
|
-
|
|
14
13
|
/** Format the required-shape diagnostic shown when a line reference is malformed. */
|
|
15
14
|
export function formatFullAnchorRequirement(raw?: string): string {
|
|
16
15
|
const received = raw === undefined ? "" : ` Received ${JSON.stringify(raw)}.`;
|
|
17
16
|
return (
|
|
18
|
-
`a bare line number from read/search output plus the section header
|
|
19
|
-
`(for example ${HL_FILE_PREFIX}src/foo.ts${HL_FILE_HASH_SEP}
|
|
17
|
+
`a bare line number from read/search output plus the section header content-hash tag ` +
|
|
18
|
+
`(for example ${HL_FILE_PREFIX}src/foo.ts${HL_FILE_HASH_SEP}${HL_FILE_HASH_EXAMPLES[0]} and line "160")${received}`
|
|
20
19
|
);
|
|
21
20
|
}
|
|
22
21
|
|