jeopi-hashline 16.2.13 → 16.2.14
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 +2 -0
- package/package.json +4 -4
- package/dist/types/apply.d.ts +0 -10
- package/dist/types/block.d.ts +0 -39
- package/dist/types/diff-preview.d.ts +0 -14
- package/dist/types/format.d.ts +0 -83
- package/dist/types/fs.d.ts +0 -109
- package/dist/types/index.d.ts +0 -17
- package/dist/types/input.d.ts +0 -110
- package/dist/types/messages.d.ts +0 -127
- package/dist/types/mismatch.d.ts +0 -44
- package/dist/types/normalize.d.ts +0 -20
- package/dist/types/parser.d.ts +0 -27
- package/dist/types/patcher.d.ts +0 -118
- package/dist/types/prefixes.d.ts +0 -42
- package/dist/types/recovery.d.ts +0 -44
- package/dist/types/snapshots.d.ts +0 -127
- package/dist/types/stream.d.ts +0 -2
- package/dist/types/tokenizer.d.ts +0 -73
- package/dist/types/types.d.ts +0 -172
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-hashline",
|
|
4
|
-
"version": "16.2.
|
|
4
|
+
"version": "16.2.14",
|
|
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://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"fmt": "biome format --write ."
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"diff": "
|
|
37
|
-
"lru-cache": "
|
|
36
|
+
"diff": "catalog:",
|
|
37
|
+
"lru-cache": "catalog:"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/bun": "
|
|
40
|
+
"@types/bun": "catalog:"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"bun": ">=1.3.14"
|
package/dist/types/apply.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ApplyResult, Edit } from "./types";
|
|
2
|
-
/** A line that is nothing but closing delimiters: `}`, `)`, `];`, `})`, `},`. */
|
|
3
|
-
export declare const STRUCTURAL_CLOSER_RE: RegExp;
|
|
4
|
-
/**
|
|
5
|
-
* Apply a parsed list of edits to a text body. Pure function — no I/O.
|
|
6
|
-
*
|
|
7
|
-
* Returns the post-edit text and the first changed line number (1-indexed).
|
|
8
|
-
* Throws if an anchor is out of bounds.
|
|
9
|
-
*/
|
|
10
|
-
export declare function applyEdits(text: string, edits: readonly Edit[]): ApplyResult;
|
package/dist/types/block.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { BlockResolution, BlockResolver, Edit } from "./types";
|
|
2
|
-
export interface ResolveBlockEditsOptions {
|
|
3
|
-
/**
|
|
4
|
-
* How to handle a replace/delete block edit that cannot be resolved
|
|
5
|
-
* (missing resolver or a `null` span). `"throw"` (default) raises a
|
|
6
|
-
* `blockUnresolvedMessage` error — used by the authoritative apply + final
|
|
7
|
-
* preview paths. `"drop"` silently skips the edit — used by the streaming
|
|
8
|
-
* preview, where a half-written file or transient parse error must not
|
|
9
|
-
* throw. Unresolvable `insert_after_block N:` edits never reach this: they
|
|
10
|
-
* are lowered to plain `insert after N:` with a warning.
|
|
11
|
-
*/
|
|
12
|
-
onUnresolved?: "throw" | "drop";
|
|
13
|
-
/**
|
|
14
|
-
* Invoked once per successfully resolved block edit, in patch order, with
|
|
15
|
-
* the anchor line and the concrete span it resolved to. Lets the host echo
|
|
16
|
-
* the resolution back to the caller. Never fired for dropped/unresolvable
|
|
17
|
-
* edits.
|
|
18
|
-
*/
|
|
19
|
-
onResolved?: (resolution: BlockResolution) => void;
|
|
20
|
-
/**
|
|
21
|
-
* Invoked once per diagnostic produced while resolving — currently the
|
|
22
|
-
* `insert_after_block N:` lowerings (closer anchor or unresolvable block).
|
|
23
|
-
* Hosts should surface these on the apply result's `warnings`.
|
|
24
|
-
*/
|
|
25
|
-
onWarning?: (message: string) => void;
|
|
26
|
-
}
|
|
27
|
-
/** True when at least one edit is an unresolved deferred block edit. */
|
|
28
|
-
export declare function hasBlockEdit(edits: readonly Edit[]): boolean;
|
|
29
|
-
/**
|
|
30
|
-
* Resolve every deferred block edit in `edits` against `text` (parsed as the
|
|
31
|
-
* language inferred from `path`). Non-block edits pass through untouched.
|
|
32
|
-
* Returns a fresh edit list with no `block` variants. The fast path returns the
|
|
33
|
-
* input unchanged when there is nothing to resolve.
|
|
34
|
-
*
|
|
35
|
-
* Synthesized inserts/deletes carry sequential `index` values for readability
|
|
36
|
-
* only — {@link applyEdits} re-derives every edit's index from array order, so
|
|
37
|
-
* the passthrough edits keeping their original indices is harmless.
|
|
38
|
-
*/
|
|
39
|
-
export declare function resolveBlockEdits(edits: readonly Edit[], text: string, path: string, resolver: BlockResolver | undefined, options?: ResolveBlockEditsOptions): readonly Edit[];
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-number a unified diff that uses the `+<lineNum>|content` /
|
|
3
|
-
* `-<lineNum>|content` / ` <lineNum>|content` line format into a compact
|
|
4
|
-
* current-file preview. Removed lines are counted for stats and post-edit
|
|
5
|
-
* offset tracking, but omitted from the preview. Added and context lines are
|
|
6
|
-
* anchored to their post-edit positions so a follow-up edit can reuse visible
|
|
7
|
-
* concrete lines directly. Long contiguous added runs are summarized with a
|
|
8
|
-
* `…` marker instead of echoing every inserted line.
|
|
9
|
-
*
|
|
10
|
-
* This is intentionally decoupled from the diff producer: anything that
|
|
11
|
-
* emits the `<sign><lineNum>|<content>` shape works.
|
|
12
|
-
*/
|
|
13
|
-
import type { CompactDiffOptions, CompactDiffPreview } from "./types";
|
|
14
|
-
export declare function buildCompactDiffPreview(diff: string, options?: CompactDiffOptions): CompactDiffPreview;
|
package/dist/types/format.d.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hashline format primitives: sigils, separators, regex fragments, and
|
|
3
|
-
* display helpers. These are the single source of truth for the parser, the
|
|
4
|
-
* tokenizer, the prompt, and the formal grammar.
|
|
5
|
-
*/
|
|
6
|
-
import type { Cursor } from "./types";
|
|
7
|
-
/** File-section header delimiters: `[path#hash]`. */
|
|
8
|
-
export declare const HL_FILE_PREFIX = "[";
|
|
9
|
-
export declare const HL_FILE_SUFFIX = "]";
|
|
10
|
-
/** Payload sigil for literal body rows. */
|
|
11
|
-
export declare const HL_PAYLOAD_REPLACE = "+";
|
|
12
|
-
/** Hunk-header keyword for concrete line replacement. */
|
|
13
|
-
export declare const HL_REPLACE_KEYWORD = "SWAP";
|
|
14
|
-
/** Hunk-header keyword for concrete line deletion. */
|
|
15
|
-
export declare const HL_DELETE_KEYWORD = "DEL";
|
|
16
|
-
/** Hunk-header keyword for insertion operations. */
|
|
17
|
-
export declare const HL_INSERT_KEYWORD = "INS";
|
|
18
|
-
/** Insert position keyword for inserting before a concrete line. */
|
|
19
|
-
export declare const HL_INSERT_BEFORE = "PRE";
|
|
20
|
-
/** Insert position keyword for inserting after a concrete line. */
|
|
21
|
-
export declare const HL_INSERT_AFTER = "POST";
|
|
22
|
-
/** Insert position keyword for inserting at the start of the file. */
|
|
23
|
-
export declare const HL_INSERT_HEAD = "HEAD";
|
|
24
|
-
/** Insert position keyword for inserting at the end of the file. */
|
|
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";
|
|
32
|
-
/** File-level keyword: `REM` deletes the whole file named by the section header. */
|
|
33
|
-
export declare const HL_REM_KEYWORD = "REM";
|
|
34
|
-
/** File-level keyword: `MV DEST` renames/moves the section file to `DEST`. */
|
|
35
|
-
export declare const HL_MOVE_KEYWORD = "MV";
|
|
36
|
-
export declare const HL_HEADER_COLON = ":";
|
|
37
|
-
/** Separator between a hashline file path and its opaque snapshot tag. */
|
|
38
|
-
export declare const HL_FILE_HASH_SEP = "#";
|
|
39
|
-
/** Separator between two line numbers in a range, e.g. `5.=10`. */
|
|
40
|
-
export declare const HL_RANGE_SEP = ".=";
|
|
41
|
-
/** Separator between a line number and displayed line content in hashline mode. */
|
|
42
|
-
export declare const HL_LINE_BODY_SEP = ":";
|
|
43
|
-
/** Bare positive line-number Lid (no decorations, no captures, no anchors). */
|
|
44
|
-
export declare const HL_LINE_RE_RAW = "[1-9]\\d*";
|
|
45
|
-
/** Capture-group form of {@link HL_LINE_RE_RAW}. */
|
|
46
|
-
export declare const HL_LINE_CAPTURE_RE_RAW = "([1-9]\\d*)";
|
|
47
|
-
/** Format a concrete replacement hunk header. */
|
|
48
|
-
export declare function formatReplaceHeader(start: number, end: number): string;
|
|
49
|
-
/** Format a concrete deletion hunk header. */
|
|
50
|
-
export declare function formatDeleteHeader(start: number, end?: number): string;
|
|
51
|
-
/** Format an insertion hunk header for a cursor position. */
|
|
52
|
-
export declare function formatInsertHeader(cursor: Cursor): string;
|
|
53
|
-
/** Number of hex characters in a content-derived file-hash tag. */
|
|
54
|
-
export declare const HL_FILE_HASH_LENGTH = 4;
|
|
55
|
-
/** Canonical uppercase hexadecimal content-hash tag carried by a hashline section header. */
|
|
56
|
-
export declare const HL_FILE_HASH_RE_RAW = "[0-9A-F]{4}";
|
|
57
|
-
/** Capture-group form of {@link HL_FILE_HASH_RE_RAW}. */
|
|
58
|
-
export declare const HL_FILE_HASH_CAPTURE_RE_RAW = "([0-9A-F]{4})";
|
|
59
|
-
/** Regex-escaped form of {@link HL_LINE_BODY_SEP}, safe for embedding inside a regex. */
|
|
60
|
-
export declare const HL_LINE_BODY_SEP_RE_RAW: string;
|
|
61
|
-
/**
|
|
62
|
-
* Representative file-hash tags for use in user-facing error messages and
|
|
63
|
-
* prompt examples.
|
|
64
|
-
*/
|
|
65
|
-
export declare const HL_FILE_HASH_EXAMPLES: readonly ["1A2B", "3C4D", "9F3E"];
|
|
66
|
-
/**
|
|
67
|
-
* Compute the content-derived hash tag carried by a hashline section header.
|
|
68
|
-
* The tag is a 4-hex fingerprint of the whole file's normalized text: any read
|
|
69
|
-
* of byte-identical content mints the same tag, and a follow-up edit anchored
|
|
70
|
-
* at any line validates whenever the live file still hashes to it.
|
|
71
|
-
*/
|
|
72
|
-
export declare function computeFileHash(text: string): string;
|
|
73
|
-
/**
|
|
74
|
-
* Format a comma-separated list of example anchors with an optional line-number
|
|
75
|
-
* prefix, quoted for inclusion in error messages: `"160", "42", "7"`.
|
|
76
|
-
*/
|
|
77
|
-
export declare function describeAnchorExamples(linePrefix?: string): string;
|
|
78
|
-
/** Format a hashline section header for a file path and snapshot tag. */
|
|
79
|
-
export declare function formatHashlineHeader(filePath: string, fileHash: string): string;
|
|
80
|
-
/** Formats a single numbered line as `LINE:TEXT`. */
|
|
81
|
-
export declare function formatNumberedLine(lineNumber: number, line: string): string;
|
|
82
|
-
/** Format file text with hashline-mode line-number prefixes for display. */
|
|
83
|
-
export declare function formatNumberedLines(text: string, startLine?: number): string;
|
package/dist/types/fs.d.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Result returned by {@link Filesystem.writeText}. The patcher echoes back
|
|
3
|
-
* `text` so adapters that transform on serialization (e.g. notebooks) can
|
|
4
|
-
* report what actually landed on disk.
|
|
5
|
-
*/
|
|
6
|
-
export interface WriteResult {
|
|
7
|
-
/** Final text that was persisted. May differ from the input if the FS transformed it. */
|
|
8
|
-
text: string;
|
|
9
|
-
}
|
|
10
|
-
import type { FileOp } from "./types";
|
|
11
|
-
/** Optional hints for {@link Filesystem.preflightWrite}. */
|
|
12
|
-
export interface PreflightWriteOptions {
|
|
13
|
-
fileOp?: FileOp;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* ENOENT-like error thrown by {@link Filesystem.readText} when a path is
|
|
17
|
-
* missing. Carrying a `code` property keeps the contract compatible with
|
|
18
|
-
* `node:fs` callers that already check `err.code === "ENOENT"`.
|
|
19
|
-
*/
|
|
20
|
-
export declare class NotFoundError extends Error {
|
|
21
|
-
readonly code = "ENOENT";
|
|
22
|
-
constructor(path: string, cause?: unknown);
|
|
23
|
-
}
|
|
24
|
-
/** Type guard for {@link NotFoundError} and structurally-compatible errors. */
|
|
25
|
-
export declare function isNotFound(error: unknown): boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Abstract storage backend the {@link Patcher} reads from and writes to.
|
|
28
|
-
* Subclass for new backends; the package ships {@link InMemoryFilesystem} and
|
|
29
|
-
* {@link NodeFilesystem} for the most common cases.
|
|
30
|
-
*
|
|
31
|
-
* Implementations work with raw text — the patcher handles BOM stripping and
|
|
32
|
-
* line-ending normalization itself. `readText` MUST throw {@link
|
|
33
|
-
* NotFoundError} (or any error for which {@link isNotFound} returns true)
|
|
34
|
-
* when the path doesn't exist; that's how the patcher detects a create-vs-
|
|
35
|
-
* update.
|
|
36
|
-
*/
|
|
37
|
-
export declare abstract class Filesystem {
|
|
38
|
-
/** Read the file's full text content. Throw on missing file. */
|
|
39
|
-
abstract readText(path: string): Promise<string>;
|
|
40
|
-
/** Read raw bytes for backends whose text is a direct decode of persisted bytes. */
|
|
41
|
-
readBinary?(path: string): Promise<Uint8Array | undefined>;
|
|
42
|
-
/** Validate that `path` is writable before a prepared batch starts committing. */
|
|
43
|
-
preflightWrite(_path: string, _options?: PreflightWriteOptions): Promise<void>;
|
|
44
|
-
/** Persist `content` at `path`. Returns the actual final text that was written. */
|
|
45
|
-
abstract writeText(path: string, content: string): Promise<WriteResult>;
|
|
46
|
-
/** Delete the file at `path`. Default: not supported. */
|
|
47
|
-
delete(path: string): Promise<void>;
|
|
48
|
-
/**
|
|
49
|
-
* Move/rename `from` to `to`. When `content` is provided the destination
|
|
50
|
-
* receives that text; otherwise implementations may preserve the source bytes.
|
|
51
|
-
*/
|
|
52
|
-
move(from: string, to: string, content?: string): Promise<void>;
|
|
53
|
-
/** Return true when the path exists and can be read. Default: probe via {@link readText}. */
|
|
54
|
-
exists(path: string): Promise<boolean>;
|
|
55
|
-
/**
|
|
56
|
-
* Canonical path used as a key by external caches (e.g. snapshot
|
|
57
|
-
* stores). The default is identity; override to return an absolute or
|
|
58
|
-
* otherwise canonicalised path so producers and consumers of cached
|
|
59
|
-
* snapshots agree on the key without each having to redo the resolution.
|
|
60
|
-
*/
|
|
61
|
-
canonicalPath(path: string): string;
|
|
62
|
-
/**
|
|
63
|
-
* Whether a section whose authored path is missing may be redirected to
|
|
64
|
-
* the file its snapshot tag names (tag-based path recovery in
|
|
65
|
-
* {@link Patcher.prepare}). `resolvedPath` is the canonical path the
|
|
66
|
-
* redirect would read and write. Default: allow.
|
|
67
|
-
*
|
|
68
|
-
* Hosts that grant write privileges by path shape override this to refuse
|
|
69
|
-
* redirects that could escalate beyond what the caller approved — e.g. an
|
|
70
|
-
* internal-URL authored target (approved read-only), or a `resolvedPath`
|
|
71
|
-
* outside the working tree (a sandbox/vault/out-of-tree write).
|
|
72
|
-
*/
|
|
73
|
-
allowTagPathRecovery(_authoredPath: string, _resolvedPath: string): boolean;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* In-memory {@link Filesystem}. Useful for tests, sandboxes, dry-runs, and as
|
|
77
|
-
* a building block for stacked adapters (e.g. an LRU layer on top).
|
|
78
|
-
*/
|
|
79
|
-
export declare class InMemoryFilesystem extends Filesystem {
|
|
80
|
-
#private;
|
|
81
|
-
constructor(initial?: Iterable<readonly [string, string]>);
|
|
82
|
-
readText(path: string): Promise<string>;
|
|
83
|
-
writeText(path: string, content: string): Promise<WriteResult>;
|
|
84
|
-
delete(path: string): Promise<void>;
|
|
85
|
-
move(from: string, to: string, content?: string): Promise<void>;
|
|
86
|
-
exists(path: string): Promise<boolean>;
|
|
87
|
-
/** Synchronous helper for setting up fixtures without awaiting. */
|
|
88
|
-
set(path: string, content: string): void;
|
|
89
|
-
/** Synchronous helper for inspecting state without awaiting. */
|
|
90
|
-
get(path: string): string | undefined;
|
|
91
|
-
/** Wipe all entries. */
|
|
92
|
-
clear(): void;
|
|
93
|
-
/** Iterate `[path, content]` pairs. */
|
|
94
|
-
entries(): IterableIterator<[string, string]>;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Disk-backed {@link Filesystem} using Bun's file APIs. The default for CLI
|
|
98
|
-
* use. Paths are accepted as-is; callers responsible for any cwd or
|
|
99
|
-
* jail/sandbox resolution should wrap this with their own subclass.
|
|
100
|
-
*/
|
|
101
|
-
export declare class NodeFilesystem extends Filesystem {
|
|
102
|
-
readText(path: string): Promise<string>;
|
|
103
|
-
readBinary(path: string): Promise<Uint8Array>;
|
|
104
|
-
writeText(path: string, content: string): Promise<WriteResult>;
|
|
105
|
-
delete(path: string): Promise<void>;
|
|
106
|
-
move(from: string, to: string, content?: string): Promise<void>;
|
|
107
|
-
canonicalPath(path: string): string;
|
|
108
|
-
exists(path: string): Promise<boolean>;
|
|
109
|
-
}
|
package/dist/types/index.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export * from "./apply";
|
|
2
|
-
export * from "./block";
|
|
3
|
-
export * from "./diff-preview";
|
|
4
|
-
export * from "./format";
|
|
5
|
-
export * from "./fs";
|
|
6
|
-
export * from "./input";
|
|
7
|
-
export * from "./messages";
|
|
8
|
-
export * from "./mismatch";
|
|
9
|
-
export * from "./normalize";
|
|
10
|
-
export * from "./parser";
|
|
11
|
-
export * from "./patcher";
|
|
12
|
-
export * from "./prefixes";
|
|
13
|
-
export * from "./recovery";
|
|
14
|
-
export * from "./snapshots";
|
|
15
|
-
export * from "./stream";
|
|
16
|
-
export * from "./tokenizer";
|
|
17
|
-
export * from "./types";
|
package/dist/types/input.d.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import type { ApplyResult, BlockResolver, Edit, FileOp, SplitOptions } from "./types";
|
|
2
|
-
interface RawSection {
|
|
3
|
-
path: string;
|
|
4
|
-
fileHash?: string;
|
|
5
|
-
diff: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Returns true when the input contains at least one line that the tokenizer
|
|
9
|
-
* recognizes as a hashline op. Used by streaming previews to decide whether
|
|
10
|
-
* the partial input is worth treating as a hashline patch yet.
|
|
11
|
-
*/
|
|
12
|
-
export declare function containsRecognizableHashlineOperations(input: string): boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Snapshot of one section in a parsed {@link Patch}: a target file plus the
|
|
15
|
-
* lazily-parsed list of edits that should land on it. Constructed by
|
|
16
|
-
* {@link Patch.parse}; consumers usually iterate `patch.sections` rather
|
|
17
|
-
* than build these directly.
|
|
18
|
-
*/
|
|
19
|
-
export declare class PatchSection {
|
|
20
|
-
#private;
|
|
21
|
-
readonly path: string;
|
|
22
|
-
readonly fileHash: string | undefined;
|
|
23
|
-
readonly diff: string;
|
|
24
|
-
constructor(raw: RawSection);
|
|
25
|
-
/**
|
|
26
|
-
* Parse this section's diff body. Cached: subsequent calls return the
|
|
27
|
-
* same `{ edits, fileOp?, warnings }` object so callers can safely call this from
|
|
28
|
-
* multiple paths (preflight, apply, diff-preview).
|
|
29
|
-
*/
|
|
30
|
-
parse(): {
|
|
31
|
-
edits: Edit[];
|
|
32
|
-
fileOp?: FileOp;
|
|
33
|
-
warnings: readonly string[];
|
|
34
|
-
};
|
|
35
|
-
/** Parsed edits for this section. */
|
|
36
|
-
get edits(): readonly Edit[];
|
|
37
|
-
/** Optional whole-file operation (`REM` / `MV`). */
|
|
38
|
-
get fileOp(): FileOp | undefined;
|
|
39
|
-
/** Warnings emitted during parsing of this section. */
|
|
40
|
-
get warnings(): readonly string[];
|
|
41
|
-
/**
|
|
42
|
-
* True when at least one edit anchors to concrete file content. Pure
|
|
43
|
-
* `insert head:` / `insert tail:` literal inserts do not count: those are
|
|
44
|
-
* safe to apply to files that don't yet exist.
|
|
45
|
-
*/
|
|
46
|
-
get hasAnchorScopedEdit(): boolean;
|
|
47
|
-
/** Anchor lines touched by this section, sorted ascending and deduplicated. */
|
|
48
|
-
collectAnchorLines(): readonly number[];
|
|
49
|
-
/**
|
|
50
|
-
* Apply this section's edits to `text` and return the post-edit result.
|
|
51
|
-
* Pure: does no I/O, does not validate the section snapshot tag. The
|
|
52
|
-
* {@link Patcher} owns tag validation and recovery; reach for this
|
|
53
|
-
* method directly when you've already validated the file content and
|
|
54
|
-
* just want the result.
|
|
55
|
-
*
|
|
56
|
-
* `blockResolver` resolves any `replace_block N:` edits against `text`; an
|
|
57
|
-
* unresolvable block throws (this is the final, authoritative preview path).
|
|
58
|
-
*/
|
|
59
|
-
applyTo(text: string, blockResolver?: BlockResolver): ApplyResult;
|
|
60
|
-
/**
|
|
61
|
-
* Streaming-tolerant counterpart to {@link applyTo}. Uses
|
|
62
|
-
* {@link parsePatchStreaming} so a trailing in-flight op (no payload yet,
|
|
63
|
-
* or a per-token parse error mid-stream) does not throw or emit a phantom
|
|
64
|
-
* empty-payload edit. Intended for incremental diff previews; the writer
|
|
65
|
-
* path should always use {@link applyTo}.
|
|
66
|
-
*
|
|
67
|
-
* `blockResolver` resolves any `replace_block N:` edits against `text`; an
|
|
68
|
-
* unresolvable block is silently dropped so a half-written file does not
|
|
69
|
-
* throw mid-stream.
|
|
70
|
-
*/
|
|
71
|
-
applyPartialTo(text: string, blockResolver?: BlockResolver): ApplyResult;
|
|
72
|
-
/**
|
|
73
|
-
* A copy of this section rebound to a different target `path`, preserving
|
|
74
|
-
* the snapshot tag, diff body, and any cached parse result. Used by the
|
|
75
|
-
* patcher's tag-based path recovery to redirect an edit whose authored
|
|
76
|
-
* path does not exist onto the file its snapshot tag actually names.
|
|
77
|
-
*/
|
|
78
|
-
withPath(path: string): PatchSection;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* A parsed hashline patch — zero or more {@link PatchSection}s, each rooted
|
|
82
|
-
* at a `[PATH#HASH]` header. Construct via {@link Patch.parse}.
|
|
83
|
-
*
|
|
84
|
-
* `Patch` is pure data: parsing is line-anchored and does not look at the
|
|
85
|
-
* filesystem. To apply a patch, hand it to {@link Patcher.apply}.
|
|
86
|
-
*/
|
|
87
|
-
export declare class Patch {
|
|
88
|
-
readonly sections: readonly PatchSection[];
|
|
89
|
-
private constructor();
|
|
90
|
-
/**
|
|
91
|
-
* Parse `input` into a {@link Patch}. `options.cwd` resolves absolute
|
|
92
|
-
* paths inside headers to cwd-relative form; `options.path` provides a
|
|
93
|
-
* fallback when the input lacks a header but contains hashline ops
|
|
94
|
-
* (useful for streaming previews).
|
|
95
|
-
*
|
|
96
|
-
* Consecutive sections targeting the same path are merged into a single
|
|
97
|
-
* section with concatenated diff bodies. Anchors authored against the
|
|
98
|
-
* same file snapshot must be applied as one batch; otherwise the first
|
|
99
|
-
* sub-edit shifts line numbers out from under the second's anchors and
|
|
100
|
-
* validation fails.
|
|
101
|
-
*/
|
|
102
|
-
static parse(input: string, options?: SplitOptions): Patch;
|
|
103
|
-
/**
|
|
104
|
-
* Parse `input` and return only the first section. Throws if the input
|
|
105
|
-
* has zero sections. Convenience for the single-section case where the
|
|
106
|
-
* caller already knows the patch is one hunk.
|
|
107
|
-
*/
|
|
108
|
-
static parseSingle(input: string, options?: SplitOptions): PatchSection;
|
|
109
|
-
}
|
|
110
|
-
export {};
|
package/dist/types/messages.d.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/** Centralized error/warning text for the hashline parser, applier, and patcher. */
|
|
2
|
-
/** Lines of context shown either side of a hash mismatch. */
|
|
3
|
-
export declare const MISMATCH_CONTEXT = 2;
|
|
4
|
-
/**
|
|
5
|
-
* Numbered `LINE:TEXT` rows around `anchorLines` (±{@link MISMATCH_CONTEXT}),
|
|
6
|
-
* `*`-marking anchors, `...` between non-adjacent runs. Out-of-range anchors
|
|
7
|
-
* contribute no rows.
|
|
8
|
-
*/
|
|
9
|
-
export declare function formatAnchoredContext(anchorLines: readonly number[], fileLines: readonly string[]): string[];
|
|
10
|
-
/** Optional patch envelope start marker; silently consumed. */
|
|
11
|
-
export declare const BEGIN_PATCH_MARKER = "*** Begin Patch";
|
|
12
|
-
/** Optional patch envelope end marker; terminates parsing. */
|
|
13
|
-
export declare const END_PATCH_MARKER = "*** End Patch";
|
|
14
|
-
/**
|
|
15
|
-
* Truncation sentinel emitted by an agent loop mid-call. Ends parsing like
|
|
16
|
-
* {@link END_PATCH_MARKER}, without a warning.
|
|
17
|
-
*/
|
|
18
|
-
export declare const ABORT_MARKER = "*** Abort";
|
|
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 `SWAP N.=M:` hunk per range \u2014 the body is the final content, never old+new.";
|
|
21
|
-
/** Bare body rows auto-converted to literal `+` rows. */
|
|
22
|
-
export declare const BARE_BODY_AUTO_PIPED_WARNING = "Auto-prefixed bare body row(s) with `+`. Body rows must be `+TEXT` literal lines.";
|
|
23
|
-
/** Unified-diff-style `-` row in a hunk body. */
|
|
24
|
-
export declare const MINUS_ROW_REJECTED = "`-` rows are not valid; the range already names the lines being changed. For Markdown bullets or other literal `-` lines, prefix the literal row with `+`: `+- item`.";
|
|
25
|
-
/** Replace hunk with no body. */
|
|
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`.";
|
|
29
|
-
/**
|
|
30
|
-
* Block-anchored replace/delete could not resolve to a syntactic block
|
|
31
|
-
* (unsupported language, blank/out-of-range line, no node beginning on N, or
|
|
32
|
-
* parse error). Appends a {@link formatAnchoredContext} preview when
|
|
33
|
-
* `fileLines` is given. `insert_after_block N:` never reaches this — it is
|
|
34
|
-
* lowered to plain `insert after N:` instead (see
|
|
35
|
-
* {@link insertAfterBlockUnresolvedLoweredWarning}).
|
|
36
|
-
*/
|
|
37
|
-
export declare function blockUnresolvedMessage(line: number, op?: "replace" | "delete", fileLines?: readonly string[]): string;
|
|
38
|
-
/** Block-anchored edit reached a path with no {@link BlockResolver} wired in — a host-configuration bug. */
|
|
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.";
|
|
40
|
-
/**
|
|
41
|
-
* `insert_after_block N:` anchored on a closing-delimiter line, lowered to
|
|
42
|
-
* plain `insert after N:` — the closer ends a block, and inserting after it
|
|
43
|
-
* is exactly what the plain form does.
|
|
44
|
-
*/
|
|
45
|
-
export declare function insertAfterBlockCloserLoweredWarning(line: number): string;
|
|
46
|
-
/**
|
|
47
|
-
* `insert_after_block N:` anchor unresolvable (unsupported language, blank
|
|
48
|
-
* line, parse error, or no resolver), lowered to plain `insert after N:` —
|
|
49
|
-
* applying with a warning beats failing the patch.
|
|
50
|
-
*/
|
|
51
|
-
export declare function insertAfterBlockUnresolvedLoweredWarning(line: number): string;
|
|
52
|
-
/**
|
|
53
|
-
* Internal invariant: `applyEdits` received an unresolved `replace_block N:`
|
|
54
|
-
* edit; `resolveBlockEdits` must run first. Wiring bug, not authored input.
|
|
55
|
-
*/
|
|
56
|
-
export declare const UNRESOLVED_BLOCK_INTERNAL = "internal error: unresolved `SWAP.BLK` edit reached the applier (resolveBlockEdits was not run).";
|
|
57
|
-
/** Delete hunk received a body row. */
|
|
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
|
-
/** `REM` received a body row or coexists with line edits. */
|
|
60
|
-
export declare const REM_TAKES_NO_BODY = "`REM` deletes the whole file and takes no body rows or line ops. Issue it alone under the header.";
|
|
61
|
-
/** `MV` received a body row. */
|
|
62
|
-
export declare const MOVE_TAKES_NO_BODY = "`MV DEST` does not take body rows. Put line edits above the `MV` row; the destination path follows `MV` on the same line.";
|
|
63
|
-
/** `delete_block N` hunk received a body row. */
|
|
64
|
-
export declare const DELETE_BLOCK_TAKES_NO_BODY = "`DEL.BLK N` does not take body rows. Remove the body, or use `SWAP.BLK N:`.";
|
|
65
|
-
/** Insert hunk with no body. */
|
|
66
|
-
export declare const EMPTY_INSERT = "`INS` needs at least one `+TEXT` body row.";
|
|
67
|
-
/**
|
|
68
|
-
* `insert after` body indented shallower than the anchor: the landing slid
|
|
69
|
-
* forward past trailing closer lines — the common "anchored on the last line
|
|
70
|
-
* I read instead of after the block" mistake.
|
|
71
|
-
*/
|
|
72
|
-
export declare function afterInsertLandingShiftWarning(anchorLine: number, landingLine: number, crossed: number): string;
|
|
73
|
-
/**
|
|
74
|
-
* `insert_after_block N:` body indented deeper than the block's closer: the
|
|
75
|
-
* landing was pulled inside the block — a deeper body almost always means
|
|
76
|
-
* "append inside the block's body".
|
|
77
|
-
*/
|
|
78
|
-
export declare function blockInsertLandingShiftWarning(blockStart: number, closerLine: number, landingLine: number): string;
|
|
79
|
-
/** `Recovery`: an external write matched a cached snapshot. */
|
|
80
|
-
export declare const RECOVERY_EXTERNAL_WARNING = "Recovered from a stale file hash using a previous read snapshot (file changed externally between read and edit).";
|
|
81
|
-
/** `Recovery`: a prior in-session edit advanced the hash. */
|
|
82
|
-
export declare const RECOVERY_SESSION_CHAIN_WARNING = "Recovered from a stale file hash using an earlier in-session snapshot (a prior edit in this session advanced the hash).";
|
|
83
|
-
/**
|
|
84
|
-
* `Recovery`: session-chain replay fast-path. Less certain than
|
|
85
|
-
* {@link RECOVERY_SESSION_CHAIN_WARNING} — the 3-way merge refused, the
|
|
86
|
-
* anchor-content gate passed, but a coincidental insert+delete earlier in
|
|
87
|
-
* the chain could still misplace an anchor — hence the verify hedge.
|
|
88
|
-
*/
|
|
89
|
-
export declare const RECOVERY_SESSION_REPLAY_WARNING = "Recovered by replaying your edits onto the current file content (a prior in-session edit changed the lines you re-targeted with a stale hash). Verify the diff matches your intent.";
|
|
90
|
-
/** `Recovery`: stale anchors were relocated to unchanged live lines after drift. */
|
|
91
|
-
export declare const RECOVERY_LINE_REMAP_WARNING = "Recovered by remapping stale line anchors to unchanged current lines (file changed since the tagged read). Verify the diff matches your intent.";
|
|
92
|
-
/**
|
|
93
|
-
* `insert head:`/`insert tail:` applied despite a stale snapshot tag.
|
|
94
|
-
* Head/tail position is content-independent, so drift is non-fatal: apply
|
|
95
|
-
* onto live content and warn instead of hard-failing.
|
|
96
|
-
*/
|
|
97
|
-
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.";
|
|
98
|
-
/**
|
|
99
|
-
* Section omitted the mandatory snapshot tag. Shared by the apply
|
|
100
|
-
* ({@link Patcher.prepare}) and preview/diff paths so both stay in lockstep.
|
|
101
|
-
*/
|
|
102
|
-
export declare function missingSnapshotTagMessage(sectionPath: string): string;
|
|
103
|
-
/**
|
|
104
|
-
* A section named a path that does not exist, but its filename and snapshot
|
|
105
|
-
* tag together match exactly one file read earlier this session — the model
|
|
106
|
-
* gave the bare filename (or wrong directory) for a file it just read. The
|
|
107
|
-
* edit was rebound to that file's full path. Surfaced as a warning so the
|
|
108
|
-
* model (and user) learn the corrected path and stop reusing the wrong one.
|
|
109
|
-
*/
|
|
110
|
-
export declare function pathRecoveredFromTagMessage(authoredPath: string, resolvedPath: string, tag: string): string;
|
|
111
|
-
/**
|
|
112
|
-
* An anchored edit referenced lines the read that minted the cited tag never
|
|
113
|
-
* displayed (a partial range, or a structural summary that collapsed bodies).
|
|
114
|
-
* Editing lines you have not read is the off-by-memory failure that mangles
|
|
115
|
-
* files; reject and make the model re-read those exact lines first.
|
|
116
|
-
*/
|
|
117
|
-
export declare function unseenLinesMessage(sectionPath: string, unseenLines: readonly number[], tag: string): string;
|
|
118
|
-
/** Op kind of a deferred block edit, for {@link blockSingleLineMessage}. */
|
|
119
|
-
export type BlockOp = "replace" | "delete" | "insert_after";
|
|
120
|
-
/**
|
|
121
|
-
* A `replace_block`/`delete_block`/`insert_after_block` anchor resolved to a
|
|
122
|
-
* single line — almost always a bare statement the model mis-anchored, not a
|
|
123
|
-
* multi-line construct. The plain op is unambiguous for one line; the block
|
|
124
|
-
* form only earns its keep when it spares counting a closing line you cannot
|
|
125
|
-
* see. Reject and point at both fixes.
|
|
126
|
-
*/
|
|
127
|
-
export declare function blockSingleLineMessage(line: number, op: BlockOp): string;
|
package/dist/types/mismatch.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/** Format the required-shape diagnostic shown when a line reference is malformed. */
|
|
2
|
-
export declare function formatFullAnchorRequirement(raw?: string): string;
|
|
3
|
-
/** Parse a decorated bare line-number anchor like `42`, `*42:foo`, ` > 7`. */
|
|
4
|
-
export declare function parseTag(ref: string): {
|
|
5
|
-
line: number;
|
|
6
|
-
};
|
|
7
|
-
export interface MismatchDetails {
|
|
8
|
-
path?: string;
|
|
9
|
-
expectedFileHash: string;
|
|
10
|
-
actualFileHash: string;
|
|
11
|
-
fileLines: string[];
|
|
12
|
-
anchorLines?: readonly number[];
|
|
13
|
-
/**
|
|
14
|
-
* `true` when the section's expected hash resolved to a recorded snapshot
|
|
15
|
-
* (file content drifted since that snapshot), `false` when no snapshot
|
|
16
|
-
* was ever recorded for the hash (likely fabricated or carried over from
|
|
17
|
-
* a prior session). Drives a more actionable rejection message; defaults
|
|
18
|
-
* to `true` for backward compatibility with direct callers.
|
|
19
|
-
*/
|
|
20
|
-
hashRecognized?: boolean;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Raised when a hashline section's snapshot tag doesn't match the live file's
|
|
24
|
-
* content (and recovery, if configured, declined the merge). Carries the
|
|
25
|
-
* file lines plus anchored lines so renderers can produce a richer
|
|
26
|
-
* diagnostic via {@link MismatchError.displayMessage}.
|
|
27
|
-
*/
|
|
28
|
-
export declare class MismatchError extends Error {
|
|
29
|
-
readonly path: string | undefined;
|
|
30
|
-
readonly expectedFileHash: string;
|
|
31
|
-
readonly actualFileHash: string;
|
|
32
|
-
readonly fileLines: string[];
|
|
33
|
-
readonly anchorLines: readonly number[];
|
|
34
|
-
readonly hashRecognized: boolean;
|
|
35
|
-
constructor(details: MismatchDetails);
|
|
36
|
-
get displayMessage(): string;
|
|
37
|
-
static rejectionHeader(details: MismatchDetails): string[];
|
|
38
|
-
static formatDisplayMessage(details: MismatchDetails): string;
|
|
39
|
-
static formatMessage(details: MismatchDetails): string;
|
|
40
|
-
}
|
|
41
|
-
/** Throws when the line reference is out of bounds for the given file. */
|
|
42
|
-
export declare function validateLineRef(ref: {
|
|
43
|
-
line: number;
|
|
44
|
-
}, fileLines: string[]): void;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal text-shape normalization: line-ending detection / round-trip and
|
|
3
|
-
* BOM stripping. The patcher uses these to canonicalize text to LF before
|
|
4
|
-
* applying edits and to restore the original shape on write-back.
|
|
5
|
-
*/
|
|
6
|
-
export type LineEnding = "\r\n" | "\n";
|
|
7
|
-
/** Detect the first line ending style in `content`. Defaults to LF when neither is present. */
|
|
8
|
-
export declare function detectLineEnding(content: string): LineEnding;
|
|
9
|
-
/** Normalize every line ending to LF. */
|
|
10
|
-
export declare function normalizeToLF(text: string): string;
|
|
11
|
-
/** Re-encode LF text with the requested line ending. */
|
|
12
|
-
export declare function restoreLineEndings(text: string, ending: LineEnding): string;
|
|
13
|
-
export interface BomResult {
|
|
14
|
-
/** Either the empty string or the BOM sequence (currently UTF-8 BOM). */
|
|
15
|
-
bom: string;
|
|
16
|
-
/** Text with any leading BOM removed. */
|
|
17
|
-
text: string;
|
|
18
|
-
}
|
|
19
|
-
/** Strip a UTF-8 BOM if present and return both the BOM and the trailing text. */
|
|
20
|
-
export declare function stripBom(content: string): BomResult;
|
package/dist/types/parser.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { type Token } from "./tokenizer";
|
|
2
|
-
import type { Edit, FileOp } from "./types";
|
|
3
|
-
export declare class Executor {
|
|
4
|
-
#private;
|
|
5
|
-
feed(token: Token): void;
|
|
6
|
-
end(): {
|
|
7
|
-
edits: Edit[];
|
|
8
|
-
fileOp?: FileOp;
|
|
9
|
-
warnings: string[];
|
|
10
|
-
};
|
|
11
|
-
endStreaming(): {
|
|
12
|
-
edits: Edit[];
|
|
13
|
-
fileOp?: FileOp;
|
|
14
|
-
warnings: string[];
|
|
15
|
-
};
|
|
16
|
-
reset(): void;
|
|
17
|
-
}
|
|
18
|
-
export declare function parsePatch(diff: string): {
|
|
19
|
-
edits: Edit[];
|
|
20
|
-
fileOp?: FileOp;
|
|
21
|
-
warnings: string[];
|
|
22
|
-
};
|
|
23
|
-
export declare function parsePatchStreaming(diff: string): {
|
|
24
|
-
edits: Edit[];
|
|
25
|
-
fileOp?: FileOp;
|
|
26
|
-
warnings: string[];
|
|
27
|
-
};
|
package/dist/types/patcher.d.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import type { Filesystem } from "./fs";
|
|
2
|
-
import type { Patch, PatchSection } from "./input";
|
|
3
|
-
import { type LineEnding } from "./normalize";
|
|
4
|
-
import { Recovery } from "./recovery";
|
|
5
|
-
import type { SnapshotStore } from "./snapshots";
|
|
6
|
-
import type { ApplyResult, BlockResolution, BlockResolver, FileOp } from "./types";
|
|
7
|
-
export interface PatcherOptions {
|
|
8
|
-
/** Storage backend used for all reads and writes. */
|
|
9
|
-
fs: Filesystem;
|
|
10
|
-
/** Snapshot store that minted and resolves hashline section tags. Required. */
|
|
11
|
-
snapshots: SnapshotStore;
|
|
12
|
-
/**
|
|
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
|
-
* host did not wire a resolver). Plain line-range ops never need it.
|
|
16
|
-
*/
|
|
17
|
-
blockResolver?: BlockResolver;
|
|
18
|
-
}
|
|
19
|
-
/** Per-section result returned by {@link Patcher.apply} / {@link Patcher.commit}. */
|
|
20
|
-
export interface PatchSectionResult {
|
|
21
|
-
/** Section path (as authored, after cwd-resolution at parse time). */
|
|
22
|
-
path: string;
|
|
23
|
-
/** Filesystem-canonical key for this section (e.g. absolute path). */
|
|
24
|
-
canonicalPath: string;
|
|
25
|
-
/** `"noop"` when the apply produced no change; `"delete"` removes the file; otherwise `"create"` / `"update"`. */
|
|
26
|
-
op: "create" | "update" | "delete" | "noop";
|
|
27
|
-
/** Pre-edit text (LF-normalized, BOM-stripped). */
|
|
28
|
-
before: string;
|
|
29
|
-
/** Post-edit text (LF-normalized, BOM-stripped). For `"noop"` equals `before`. */
|
|
30
|
-
after: string;
|
|
31
|
-
/** Same text as `after` but with the original BOM and line ending restored. */
|
|
32
|
-
persisted: string;
|
|
33
|
-
/** Final text that the {@link Filesystem} actually wrote (may differ if the FS transformed it). */
|
|
34
|
-
written: string;
|
|
35
|
-
/** 4-hex content-hash tag for `after`. Use to anchor follow-up edits. */
|
|
36
|
-
fileHash: string;
|
|
37
|
-
/** Hashline section header (`[path#tag]`) of the post-edit content. */
|
|
38
|
-
header: string;
|
|
39
|
-
/** 1-indexed first changed line in `after`, or `undefined` for noops. */
|
|
40
|
-
firstChangedLine?: number;
|
|
41
|
-
/** Warnings collected by the parser, applier, and (optionally) recovery. */
|
|
42
|
-
warnings: string[];
|
|
43
|
-
/** Destination path when this section includes `MV DEST`. */
|
|
44
|
-
moveDest?: string;
|
|
45
|
-
/**
|
|
46
|
-
* Resolved spans for any `replace_block`/`delete_block` ops, present when the
|
|
47
|
-
* apply matched the tagged content. Undefined for patches with no block ops
|
|
48
|
-
* (and for resolutions routed through drift recovery, where numbers shift).
|
|
49
|
-
*/
|
|
50
|
-
blockResolutions?: BlockResolution[];
|
|
51
|
-
}
|
|
52
|
-
export interface PatcherApplyResult {
|
|
53
|
-
sections: PatchSectionResult[];
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Opaque token returned by {@link Patcher.prepare}. Carries the section, the
|
|
57
|
-
* raw file content read off disk, and the in-memory apply result.
|
|
58
|
-
* {@link Patcher.commit} just writes the {@link PreparedSection.applyResult}.
|
|
59
|
-
*/
|
|
60
|
-
export declare class PreparedSection {
|
|
61
|
-
readonly section: PatchSection;
|
|
62
|
-
readonly canonicalPath: string;
|
|
63
|
-
readonly exists: boolean;
|
|
64
|
-
readonly rawContent: string;
|
|
65
|
-
readonly bom: string;
|
|
66
|
-
readonly lineEnding: LineEnding;
|
|
67
|
-
readonly normalized: string;
|
|
68
|
-
readonly applyResult: ApplyResult;
|
|
69
|
-
readonly parseWarnings: readonly string[];
|
|
70
|
-
readonly fileOp: FileOp | undefined;
|
|
71
|
-
/** @internal */
|
|
72
|
-
constructor(section: PatchSection, canonicalPath: string, exists: boolean, rawContent: string, bom: string, lineEnding: LineEnding, normalized: string, applyResult: ApplyResult, parseWarnings: readonly string[], fileOp: FileOp | undefined);
|
|
73
|
-
/** Convenience: returns true when the apply produced no change and no file op. */
|
|
74
|
-
get isNoop(): boolean;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* High-level patcher. Wires a {@link Filesystem} and a required
|
|
78
|
-
* {@link SnapshotStore} together with the parsing + applying core.
|
|
79
|
-
*
|
|
80
|
-
* Construct once per FS configuration; reuse across patches.
|
|
81
|
-
*/
|
|
82
|
-
export declare class Patcher {
|
|
83
|
-
#private;
|
|
84
|
-
readonly fs: Filesystem;
|
|
85
|
-
readonly snapshots: SnapshotStore;
|
|
86
|
-
readonly recovery: Recovery;
|
|
87
|
-
readonly blockResolver: BlockResolver | undefined;
|
|
88
|
-
constructor(options: PatcherOptions);
|
|
89
|
-
/**
|
|
90
|
-
* Apply every section in `patch`. `prepare` runs the full apply for each
|
|
91
|
-
* section in memory before any write hits the filesystem, so a
|
|
92
|
-
* multi-section batch is naturally all-or-nothing. Returns one
|
|
93
|
-
* {@link PatchSectionResult} per section in the original patch order.
|
|
94
|
-
*/
|
|
95
|
-
apply(patch: Patch): Promise<PatcherApplyResult>;
|
|
96
|
-
/**
|
|
97
|
-
* Run the preflight pass only: read, parse, validate, apply-in-memory.
|
|
98
|
-
* No writes hit the filesystem. Use for CI checks and dry runs.
|
|
99
|
-
*/
|
|
100
|
-
preflight(patch: Patch): Promise<void>;
|
|
101
|
-
/**
|
|
102
|
-
* Read a section's target file, parse the section, validate the snapshot
|
|
103
|
-
* tag (with recovery), and apply the edits in memory. Returns a
|
|
104
|
-
* {@link PreparedSection} which can be fed to {@link commit} to land
|
|
105
|
-
* the result on the filesystem.
|
|
106
|
-
*
|
|
107
|
-
* Throws on parse error, missing-file-for-anchored-edit, or unrecovered
|
|
108
|
-
* tag mismatch ({@link MismatchError}).
|
|
109
|
-
*/
|
|
110
|
-
prepare(section: PatchSection): Promise<PreparedSection>;
|
|
111
|
-
/**
|
|
112
|
-
* Commit a previously {@link prepare}d section to the filesystem.
|
|
113
|
-
* Restores line endings and BOM, writes via the {@link Filesystem}, and
|
|
114
|
-
* records a fresh snapshot in the {@link SnapshotStore} keyed by the
|
|
115
|
-
* filesystem-canonical path.
|
|
116
|
-
*/
|
|
117
|
-
commit(prepared: PreparedSection): Promise<PatchSectionResult>;
|
|
118
|
-
}
|
package/dist/types/prefixes.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* When a hashline payload is authored against `read`/`search` output, each
|
|
3
|
-
* line is prefixed with either a hashline-mode line number (`123:`) or, for
|
|
4
|
-
* diff-style echoes, a leading `+`. These helpers detect that and recover
|
|
5
|
-
* the raw text. Two strip modes are exposed:
|
|
6
|
-
*
|
|
7
|
-
* - {@link stripNewLinePrefixes} — opportunistic: strips when the input
|
|
8
|
-
* clearly carries hashline or diff prefixes, leaves it alone otherwise.
|
|
9
|
-
* - {@link stripHashlinePrefixes} — strict: only strips when every non-empty
|
|
10
|
-
* content line is hashline-prefixed.
|
|
11
|
-
*
|
|
12
|
-
* These run *before* the tokenizer; they exist because hashline mode is the
|
|
13
|
-
* common case for echoed file content, and erroneously echoed prefixes will
|
|
14
|
-
* otherwise turn every content line into a (malformed) op.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Single-pass variant of {@link stripLeadingHashlinePrefixes} that strips at
|
|
18
|
-
* most one leading hashline prefix (`N:`, `>>>N:`, `+N:` etc.) and does NOT
|
|
19
|
-
* loop. Use this when the input carries at most one snapshot prefix (e.g. a
|
|
20
|
-
* bare body row paste from `read` output) — recursive stripping would corrupt
|
|
21
|
-
* content whose own text starts with `digits:`.
|
|
22
|
-
*/
|
|
23
|
-
export declare function stripOneLeadingHashlinePrefix(line: string): string;
|
|
24
|
-
/**
|
|
25
|
-
* Strip whichever prefix scheme the lines appear to be carrying:
|
|
26
|
-
* - hashline line-number prefixes (`123:`) when every content line has one
|
|
27
|
-
* - leading `+` (diff style) when at least half the lines have one
|
|
28
|
-
* - mixed `+<n>:` form when present
|
|
29
|
-
*
|
|
30
|
-
* Returns the lines untouched if no scheme is recognized.
|
|
31
|
-
*/
|
|
32
|
-
export declare function stripNewLinePrefixes(lines: string[]): string[];
|
|
33
|
-
/**
|
|
34
|
-
* Strict variant: strip hashline prefixes only when every content line is
|
|
35
|
-
* hashline-prefixed. Returns the lines unchanged otherwise.
|
|
36
|
-
*/
|
|
37
|
-
export declare function stripHashlinePrefixes(lines: string[]): string[];
|
|
38
|
-
/**
|
|
39
|
-
* Normalize line payloads by stripping read/search line prefixes. `null` /
|
|
40
|
-
* `undefined` yield `[]`; a single multiline string is split on `\n`.
|
|
41
|
-
*/
|
|
42
|
-
export declare function hashlineParseText(edit: string[] | string | null | undefined): string[];
|
package/dist/types/recovery.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { SnapshotStore } from "./snapshots";
|
|
2
|
-
import type { Edit } from "./types";
|
|
3
|
-
export interface RecoveryArgs {
|
|
4
|
-
path: string;
|
|
5
|
-
currentText: string;
|
|
6
|
-
fileHash: string;
|
|
7
|
-
edits: readonly Edit[];
|
|
8
|
-
}
|
|
9
|
-
export interface RecoveryResult {
|
|
10
|
-
/** Post-recovery text. */
|
|
11
|
-
text: string;
|
|
12
|
-
/** First changed line (1-indexed) relative to the live `currentText`, or `undefined`. */
|
|
13
|
-
firstChangedLine: number | undefined;
|
|
14
|
-
/** Warnings collected during recovery, including the user-facing recovery banner. */
|
|
15
|
-
warnings: string[];
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Stateless recovery driver over a {@link SnapshotStore}. Construct once and
|
|
19
|
-
* call {@link Recovery.tryRecover} per stale-tag incident. The default
|
|
20
|
-
* implementation tries three strategies in order:
|
|
21
|
-
*
|
|
22
|
-
* 1. Apply the edits on the full-file version the tag names, then 3-way-merge
|
|
23
|
-
* the resulting patch onto the live content (handles external writes).
|
|
24
|
-
* 2. Remap every stale anchor through the unchanged-line diff from the tagged
|
|
25
|
-
* snapshot to the live text, then replay on live content. This handles a
|
|
26
|
-
* prior insertion/deletion before the target while refusing changed anchors
|
|
27
|
-
* and mixed offsets across the same edit range.
|
|
28
|
-
* 3. (Session chain) If that version wasn't the head, replay the edits onto
|
|
29
|
-
* the live content directly when line counts match AND every edit's anchor
|
|
30
|
-
* line content is unchanged between version and current — a prior in-session
|
|
31
|
-
* edit advanced the tag and the model's anchors still name the same logical
|
|
32
|
-
* rows. Emits a dedicated {@link RECOVERY_SESSION_REPLAY_WARNING} because
|
|
33
|
-
* even with both guards a coincidental insert+delete pair on duplicate rows
|
|
34
|
-
* can still land the edit on the wrong row; see {@link replaySessionChainOnCurrent}.
|
|
35
|
-
*/
|
|
36
|
-
export declare class Recovery {
|
|
37
|
-
readonly store: SnapshotStore;
|
|
38
|
-
constructor(store: SnapshotStore);
|
|
39
|
-
/**
|
|
40
|
-
* Attempt recovery. Returns `null` when no path forward is found — the
|
|
41
|
-
* caller should then surface a {@link MismatchError}.
|
|
42
|
-
*/
|
|
43
|
-
tryRecover(args: RecoveryArgs): RecoveryResult | null;
|
|
44
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One full-file version observed at a point in time. The tag the model sees is
|
|
3
|
-
* {@link Snapshot.hash}; recovery replays edits against {@link Snapshot.text}.
|
|
4
|
-
*/
|
|
5
|
-
export interface Snapshot {
|
|
6
|
-
/** Canonical path this version belongs to. */
|
|
7
|
-
readonly path: string;
|
|
8
|
-
/** Full normalized (LF, no BOM) file text as observed. */
|
|
9
|
-
readonly text: string;
|
|
10
|
-
/** Content-derived tag for {@link Snapshot.text} (see {@link computeFileHash}). */
|
|
11
|
-
readonly hash: string;
|
|
12
|
-
/** Timestamp (ms since epoch) the version was recorded. */
|
|
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>;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Storage seam for full-file version snapshots. The patcher calls {@link head}
|
|
26
|
-
* for the latest version of a path and {@link byHashExact} when it needs the
|
|
27
|
-
* specific historical version a section's stale tag names.
|
|
28
|
-
*/
|
|
29
|
-
export declare abstract class SnapshotStore {
|
|
30
|
-
/** Most-recently recorded version for `path`, or `null` if none. */
|
|
31
|
-
abstract head(path: string): Snapshot | null;
|
|
32
|
-
/**
|
|
33
|
-
* Recorded version for `path` whose tag equals `hash`, or `null`. When two
|
|
34
|
-
* distinct texts collide on the 16-bit tag, returns the most-recently
|
|
35
|
-
* recorded one; callers that treat the tag as content identity must use
|
|
36
|
-
* {@link byHashExact} (or verify {@link Snapshot.text} via {@link byContent}).
|
|
37
|
-
*/
|
|
38
|
-
abstract byHash(path: string, hash: string): Snapshot | null;
|
|
39
|
-
/**
|
|
40
|
-
* Collision-safe {@link byHash}: the single retained version for `path`
|
|
41
|
-
* whose tag equals `hash`, or `null` when none is retained OR when two or
|
|
42
|
-
* more distinct texts collide on the tag. In the collision case there is
|
|
43
|
-
* no way to know which retained text the model's line anchors were minted
|
|
44
|
-
* against, so consumers that replay anchors (recovery, previews) must
|
|
45
|
-
* refuse rather than pick one.
|
|
46
|
-
*/
|
|
47
|
-
abstract byHashExact(path: string, hash: string): Snapshot | null;
|
|
48
|
-
/**
|
|
49
|
-
* Recorded version for `path` whose {@link Snapshot.text} equals `fullText`,
|
|
50
|
-
* or `null`. Disambiguates hash collisions where two distinct file states
|
|
51
|
-
* share the same 4-hex tag: the patcher consults this before taking the
|
|
52
|
-
* no-drift path so a colliding live text is never accepted as the exact
|
|
53
|
-
* snapshot the model's line anchors were minted against.
|
|
54
|
-
*/
|
|
55
|
-
abstract byContent(path: string, fullText: string): Snapshot | null;
|
|
56
|
-
/**
|
|
57
|
-
* Every retained version whose tag equals `hash`, across all tracked
|
|
58
|
-
* paths. The patcher uses this to recover the intended file when a section
|
|
59
|
-
* names a path that does not exist on disk but carries a tag the store
|
|
60
|
-
* minted — the model mistyped the path of a file it read this session.
|
|
61
|
-
*
|
|
62
|
-
* The base returns no matches (recovery disabled); stores that can
|
|
63
|
-
* enumerate their contents override it to enable tag-based path recovery.
|
|
64
|
-
*/
|
|
65
|
-
findByHash(_hash: string): Snapshot[];
|
|
66
|
-
/**
|
|
67
|
-
* Record the full normalized text of `path` and return its content tag.
|
|
68
|
-
* `seenLines` (optional) are the 1-indexed lines the producer displayed;
|
|
69
|
-
* they merge into {@link Snapshot.seenLines} across reads of identical text.
|
|
70
|
-
*/
|
|
71
|
-
abstract record(path: string, fullText: string, seenLines?: Iterable<number>): string;
|
|
72
|
-
/**
|
|
73
|
-
* Merge `lines` into the {@link Snapshot.seenLines} of the version whose tag
|
|
74
|
-
* equals `hash`. No-op when no such version is retained (the content aged
|
|
75
|
-
* out or was overwritten). Lets producers attach displayed lines after the
|
|
76
|
-
* tag was already minted (the body is formatted after the hash is computed).
|
|
77
|
-
*/
|
|
78
|
-
abstract recordSeenLines(path: string, hash: string, lines: Iterable<number>): void;
|
|
79
|
-
/** Drop the version history for a single path. */
|
|
80
|
-
abstract invalidate(path: string): void;
|
|
81
|
-
/**
|
|
82
|
-
* Move retained version history (and read provenance) from `from` to `to`.
|
|
83
|
-
* No-op when `from` has no history. Used by file moves so tags minted from
|
|
84
|
-
* reads of the source path stay valid at the destination.
|
|
85
|
-
*/
|
|
86
|
-
abstract relocate(from: string, to: string): void;
|
|
87
|
-
/** Drop every version history. */
|
|
88
|
-
abstract clear(): void;
|
|
89
|
-
}
|
|
90
|
-
export interface InMemorySnapshotStoreOptions {
|
|
91
|
-
/** Maximum number of distinct paths tracked at once (default 30). LRU eviction. */
|
|
92
|
-
maxPaths?: number;
|
|
93
|
-
/** Maximum full-file versions retained per path (default 4). Oldest dropped first. */
|
|
94
|
-
maxVersionsPerPath?: number;
|
|
95
|
-
/**
|
|
96
|
-
* Global ceiling on retained snapshot text summed across every path's
|
|
97
|
-
* version history, measured in UTF-16 code units (default 64 MiB).
|
|
98
|
-
* Least-recently-used path histories are evicted to stay under it.
|
|
99
|
-
*/
|
|
100
|
-
maxTotalBytes?: number;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* In-memory {@link SnapshotStore} backed by `lru-cache`. Per-path history is a
|
|
104
|
-
* short ring of full-file versions (oldest dropped first); per-session path
|
|
105
|
-
* tracking is LRU-bounded so cold paths age out automatically.
|
|
106
|
-
*
|
|
107
|
-
* Recording byte-identical content again refreshes recency and reuses the
|
|
108
|
-
* existing tag (read fusion); recording new content unshifts a fresh version
|
|
109
|
-
* onto the front of the path history. Two distinct texts that collide on the
|
|
110
|
-
* short 4-hex tag are retained as separate versions so callers can still tell
|
|
111
|
-
* them apart via {@link Snapshot.text} — the tag is only a fast index, never
|
|
112
|
-
* the identity.
|
|
113
|
-
*/
|
|
114
|
-
export declare class InMemorySnapshotStore extends SnapshotStore {
|
|
115
|
-
#private;
|
|
116
|
-
constructor(options?: InMemorySnapshotStoreOptions);
|
|
117
|
-
head(path: string): Snapshot | null;
|
|
118
|
-
byHash(path: string, hash: string): Snapshot | null;
|
|
119
|
-
byHashExact(path: string, hash: string): Snapshot | null;
|
|
120
|
-
byContent(path: string, fullText: string): Snapshot | null;
|
|
121
|
-
findByHash(hash: string): Snapshot[];
|
|
122
|
-
record(path: string, fullText: string, seenLines?: Iterable<number>): string;
|
|
123
|
-
recordSeenLines(path: string, hash: string, lines: Iterable<number>): void;
|
|
124
|
-
invalidate(path: string): void;
|
|
125
|
-
relocate(from: string, to: string): void;
|
|
126
|
-
clear(): void;
|
|
127
|
-
}
|
package/dist/types/stream.d.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { Anchor, Cursor, ParsedRange } from "./types";
|
|
2
|
-
export declare function splitHashlineLines(text: string): string[];
|
|
3
|
-
export declare function cloneCursor(cursor: Cursor): Cursor;
|
|
4
|
-
/** Parse a bare line-number anchor. Throws on malformed input. */
|
|
5
|
-
export declare function parseLid(raw: string, lineNum: number): Anchor;
|
|
6
|
-
export type BlockTarget = {
|
|
7
|
-
kind: "replace";
|
|
8
|
-
range: ParsedRange;
|
|
9
|
-
} | {
|
|
10
|
-
kind: "block";
|
|
11
|
-
anchor: Anchor;
|
|
12
|
-
} | {
|
|
13
|
-
kind: "delete";
|
|
14
|
-
range: ParsedRange;
|
|
15
|
-
} | {
|
|
16
|
-
kind: "delete_block";
|
|
17
|
-
anchor: Anchor;
|
|
18
|
-
} | {
|
|
19
|
-
kind: "insert_before";
|
|
20
|
-
anchor: Anchor;
|
|
21
|
-
} | {
|
|
22
|
-
kind: "insert_after";
|
|
23
|
-
anchor: Anchor;
|
|
24
|
-
} | {
|
|
25
|
-
kind: "insert_after_block";
|
|
26
|
-
anchor: Anchor;
|
|
27
|
-
} | {
|
|
28
|
-
kind: "rem";
|
|
29
|
-
} | {
|
|
30
|
-
kind: "move";
|
|
31
|
-
dest: string;
|
|
32
|
-
} | {
|
|
33
|
-
kind: "bof";
|
|
34
|
-
} | {
|
|
35
|
-
kind: "eof";
|
|
36
|
-
};
|
|
37
|
-
interface TokenBase {
|
|
38
|
-
lineNum: number;
|
|
39
|
-
}
|
|
40
|
-
export type Token = (TokenBase & {
|
|
41
|
-
kind: "blank";
|
|
42
|
-
}) | (TokenBase & {
|
|
43
|
-
kind: "envelope-begin";
|
|
44
|
-
}) | (TokenBase & {
|
|
45
|
-
kind: "envelope-end";
|
|
46
|
-
}) | (TokenBase & {
|
|
47
|
-
kind: "abort";
|
|
48
|
-
}) | (TokenBase & {
|
|
49
|
-
kind: "header";
|
|
50
|
-
path: string;
|
|
51
|
-
fileHash?: string;
|
|
52
|
-
}) | (TokenBase & {
|
|
53
|
-
kind: "op-block";
|
|
54
|
-
target: BlockTarget;
|
|
55
|
-
}) | (TokenBase & {
|
|
56
|
-
kind: "payload-literal";
|
|
57
|
-
text: string;
|
|
58
|
-
}) | (TokenBase & {
|
|
59
|
-
kind: "raw";
|
|
60
|
-
text: string;
|
|
61
|
-
});
|
|
62
|
-
export declare class Tokenizer {
|
|
63
|
-
#private;
|
|
64
|
-
feed(chunk: string): Token[];
|
|
65
|
-
end(): Token[];
|
|
66
|
-
reset(): void;
|
|
67
|
-
tokenizeAll(text: string): Token[];
|
|
68
|
-
tokenize(line: string, lineNum?: number): Token;
|
|
69
|
-
isOp(line: string): boolean;
|
|
70
|
-
isHeader(line: string): boolean;
|
|
71
|
-
isEnvelopeMarker(line: string): boolean;
|
|
72
|
-
}
|
|
73
|
-
export type { ParsedRange } from "./types";
|
package/dist/types/types.d.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure data types shared across the hashline parser, applier, and patcher.
|
|
3
|
-
* Nothing in this file references a filesystem, agent runtime, or schema
|
|
4
|
-
* library — keep it that way.
|
|
5
|
-
*/
|
|
6
|
-
/** A line-number anchor (1-indexed). */
|
|
7
|
-
export interface Anchor {
|
|
8
|
-
line: number;
|
|
9
|
-
}
|
|
10
|
-
/** Where an `insert` edit should land relative to existing content. */
|
|
11
|
-
export type Cursor = {
|
|
12
|
-
kind: "bof";
|
|
13
|
-
} | {
|
|
14
|
-
kind: "eof";
|
|
15
|
-
} | {
|
|
16
|
-
kind: "before_anchor";
|
|
17
|
-
anchor: Anchor;
|
|
18
|
-
} | {
|
|
19
|
-
kind: "after_anchor";
|
|
20
|
-
anchor: Anchor;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* A single low-level edit produced by the parser and consumed by the applier.
|
|
24
|
-
* Multi-line replacements decompose to one `insert` per replacement line plus
|
|
25
|
-
* one `delete` per consumed line. Replacement payloads are tagged so the
|
|
26
|
-
* applier can distinguish literal insertion from new content for a deleted
|
|
27
|
-
* line.
|
|
28
|
-
*/
|
|
29
|
-
export type Edit = {
|
|
30
|
-
kind: "insert";
|
|
31
|
-
cursor: Cursor;
|
|
32
|
-
text: string;
|
|
33
|
-
lineNum: number;
|
|
34
|
-
index: number;
|
|
35
|
-
mode?: "replacement";
|
|
36
|
-
/**
|
|
37
|
-
* Present on inserts lowered from `insert_after_block N:`: the
|
|
38
|
-
* resolved block's first line. Lets the applier slide a body that
|
|
39
|
-
* claims a depth inside the block back across the block's trailing
|
|
40
|
-
* closer lines (never above this line).
|
|
41
|
-
*/
|
|
42
|
-
blockStart?: number;
|
|
43
|
-
} | {
|
|
44
|
-
kind: "delete";
|
|
45
|
-
anchor: Anchor;
|
|
46
|
-
lineNum: number;
|
|
47
|
-
index: number;
|
|
48
|
-
oldAssertion?: string;
|
|
49
|
-
} | {
|
|
50
|
-
/**
|
|
51
|
-
* Deferred block edit (`replace_block N:` / `delete_block N` /
|
|
52
|
-
* `insert_after_block N:`). The exact line span is unknown at parse
|
|
53
|
-
* time — it is computed by {@link resolveBlockEdits} once file text +
|
|
54
|
-
* path (→ language) are available, then expanded into concrete edits:
|
|
55
|
-
* a non-empty `payloads` without `mode` (from `replace_block`) becomes
|
|
56
|
-
* the same `replacement` inserts + deletes that `replace start.=end:`
|
|
57
|
-
* produces; an empty `payloads` (from `delete_block`) becomes a pure
|
|
58
|
-
* range deletion; `mode: "insert_after"` becomes plain `after_anchor`
|
|
59
|
-
* inserts at the block's last line. `applyEdits` never sees this
|
|
60
|
-
* variant.
|
|
61
|
-
*/
|
|
62
|
-
kind: "block";
|
|
63
|
-
anchor: Anchor;
|
|
64
|
-
payloads: string[];
|
|
65
|
-
mode?: "insert_after";
|
|
66
|
-
lineNum: number;
|
|
67
|
-
index: number;
|
|
68
|
-
};
|
|
69
|
-
/** File-level operation parsed from a section body (`REM` / `MV`). */
|
|
70
|
-
export type FileOp = {
|
|
71
|
-
kind: "rem";
|
|
72
|
-
} | {
|
|
73
|
-
kind: "move";
|
|
74
|
-
dest: string;
|
|
75
|
-
};
|
|
76
|
-
/** Result of applying a parsed set of edits to a text body. */
|
|
77
|
-
export interface ApplyResult {
|
|
78
|
-
/** Post-edit text body. */
|
|
79
|
-
text: string;
|
|
80
|
-
/** First line number (1-indexed) that changed, or `undefined` for a no-op apply. */
|
|
81
|
-
firstChangedLine?: number;
|
|
82
|
-
/** Diagnostic warnings collected by the parser, patcher, or recovery. */
|
|
83
|
-
warnings?: string[];
|
|
84
|
-
/**
|
|
85
|
-
* Resolved spans for each `replace_block`/`delete_block` op in this apply,
|
|
86
|
-
* in patch order. Present only when the apply matched the tagged content
|
|
87
|
-
* (the common no-drift path), so the line numbers line up with what the
|
|
88
|
-
* caller read. Absent when there were no block ops.
|
|
89
|
-
*/
|
|
90
|
-
blockResolutions?: BlockResolution[];
|
|
91
|
-
}
|
|
92
|
-
/** A parsed `[A.=B]` line range. */
|
|
93
|
-
export interface ParsedRange {
|
|
94
|
-
start: Anchor;
|
|
95
|
-
end: Anchor;
|
|
96
|
-
}
|
|
97
|
-
/** Optional hints for {@link splitPatchInput}. */
|
|
98
|
-
export interface SplitOptions {
|
|
99
|
-
/** Resolves absolute paths inside hashline headers to cwd-relative form. */
|
|
100
|
-
cwd?: string;
|
|
101
|
-
/**
|
|
102
|
-
* Fallback path used when the input lacks a `[PATH]` header but contains
|
|
103
|
-
* recognizable hashline operations. Lets streaming previews work before
|
|
104
|
-
* the model has written the header.
|
|
105
|
-
*/
|
|
106
|
-
path?: string;
|
|
107
|
-
}
|
|
108
|
-
/** Streaming-formatter knobs for {@link streamHashLines}. */
|
|
109
|
-
export interface StreamOptions {
|
|
110
|
-
/** First line number to use when formatting (1-indexed, default 1). */
|
|
111
|
-
startLine?: number;
|
|
112
|
-
/** Maximum formatted lines per yielded chunk (default 200). */
|
|
113
|
-
maxChunkLines?: number;
|
|
114
|
-
/** Maximum UTF-8 bytes per yielded chunk (default 64 KiB). */
|
|
115
|
-
maxChunkBytes?: number;
|
|
116
|
-
}
|
|
117
|
-
/** Result of {@link buildCompactDiffPreview}. */
|
|
118
|
-
export interface CompactDiffPreview {
|
|
119
|
-
preview: string;
|
|
120
|
-
addedLines: number;
|
|
121
|
-
removedLines: number;
|
|
122
|
-
}
|
|
123
|
-
/** Optional knobs for {@link buildCompactDiffPreview}. */
|
|
124
|
-
export interface CompactDiffOptions {
|
|
125
|
-
/** Added lines kept on each side of a long added-run elision (default 2). */
|
|
126
|
-
maxAddedRunContext?: number;
|
|
127
|
-
/** Back-compat alias for {@link maxAddedRunContext}. */
|
|
128
|
-
maxUnchangedRun?: number;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Resolved 1-indexed inclusive line span of a `replace_block N:` target.
|
|
132
|
-
*/
|
|
133
|
-
export interface BlockSpan {
|
|
134
|
-
/** First line of the block (1-indexed, inclusive). */
|
|
135
|
-
start: number;
|
|
136
|
-
/** Last line of the block (1-indexed, inclusive). */
|
|
137
|
-
end: number;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* One `replace_block N:` / `delete_block N` / `insert_after_block N:` anchor
|
|
141
|
-
* resolved to its concrete line span. Surfaced on {@link ApplyResult} so the
|
|
142
|
-
* host can echo "block N → lines start.=end" and let the model catch a wrong
|
|
143
|
-
* opener — e.g. a decorator or doc-comment that sits in a separate node
|
|
144
|
-
* outside the resolved block.
|
|
145
|
-
*/
|
|
146
|
-
export interface BlockResolution {
|
|
147
|
-
/** The 1-indexed line the block op was anchored on (the `N`). */
|
|
148
|
-
anchorLine: number;
|
|
149
|
-
/** First line of the resolved span (1-indexed, inclusive). */
|
|
150
|
-
start: number;
|
|
151
|
-
/** Last line of the resolved span (1-indexed, inclusive). */
|
|
152
|
-
end: number;
|
|
153
|
-
/** Which block op produced this resolution. */
|
|
154
|
-
op: "replace" | "delete" | "insert_after";
|
|
155
|
-
}
|
|
156
|
-
/** Request handed to a {@link BlockResolver} to resolve one `replace_block N:` anchor. */
|
|
157
|
-
export interface BlockResolverRequest {
|
|
158
|
-
/** Target file path (used to infer language by extension). */
|
|
159
|
-
path: string;
|
|
160
|
-
/** Full text the block must be resolved against (the snapshot the tag names). */
|
|
161
|
-
text: string;
|
|
162
|
-
/** 1-indexed line the block must begin on. */
|
|
163
|
-
line: number;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Resolves a `replace_block N:` anchor to the line span of the syntactic block
|
|
167
|
-
* that begins on line N. Returns `null` when no block can be resolved
|
|
168
|
-
* (unrecognized language, blank/out-of-range line, no node begins there, or the
|
|
169
|
-
* resolved subtree has a syntax error). Pure seam: the hashline core declares
|
|
170
|
-
* the contract; the host injects a tree-sitter-backed implementation.
|
|
171
|
-
*/
|
|
172
|
-
export type BlockResolver = (request: BlockResolverRequest) => BlockSpan | null;
|