@chances-ai/engine 28.0.0 → 30.0.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.
Files changed (151) hide show
  1. package/dist/agents/index.js +1 -1
  2. package/dist/ai/adapters/ai-sdk-stream.js +15 -0
  3. package/dist/ai/adapters/ai-sdk-stream.js.map +1 -1
  4. package/dist/ai/adapters/ai-sdk.js +1 -1
  5. package/dist/ai/adapters/ai-sdk.js.map +1 -1
  6. package/dist/ai/setup.d.ts +1 -1
  7. package/dist/ai/setup.js +1 -1
  8. package/dist/ai/types.d.ts +13 -1
  9. package/dist/ai/types.d.ts.map +1 -1
  10. package/dist/core/engine.d.ts +17 -3
  11. package/dist/core/engine.d.ts.map +1 -1
  12. package/dist/core/engine.js +49 -3
  13. package/dist/core/engine.js.map +1 -1
  14. package/dist/core/task-tool.d.ts +1 -1
  15. package/dist/core/task-tool.js +1 -1
  16. package/dist/core/workspace-query.d.ts +1 -1
  17. package/dist/core/workspace-query.js +1 -1
  18. package/dist/hashline/apply.d.ts +9 -0
  19. package/dist/hashline/apply.d.ts.map +1 -0
  20. package/dist/hashline/apply.js +523 -0
  21. package/dist/hashline/apply.js.map +1 -0
  22. package/dist/hashline/block.d.ts +25 -0
  23. package/dist/hashline/block.d.ts.map +1 -0
  24. package/dist/hashline/block.js +71 -0
  25. package/dist/hashline/block.js.map +1 -0
  26. package/dist/hashline/format.d.ts +106 -0
  27. package/dist/hashline/format.d.ts.map +1 -0
  28. package/dist/hashline/format.js +191 -0
  29. package/dist/hashline/format.js.map +1 -0
  30. package/dist/hashline/fs.d.ts +87 -0
  31. package/dist/hashline/fs.d.ts.map +1 -0
  32. package/dist/hashline/fs.js +123 -0
  33. package/dist/hashline/fs.js.map +1 -0
  34. package/dist/hashline/index.d.ts +27 -0
  35. package/dist/hashline/index.d.ts.map +1 -0
  36. package/dist/hashline/index.js +27 -0
  37. package/dist/hashline/index.js.map +1 -0
  38. package/dist/hashline/input.d.ts +101 -0
  39. package/dist/hashline/input.d.ts.map +1 -0
  40. package/dist/hashline/input.js +378 -0
  41. package/dist/hashline/input.js.map +1 -0
  42. package/dist/hashline/messages.d.ts +87 -0
  43. package/dist/hashline/messages.d.ts.map +1 -0
  44. package/dist/hashline/messages.js +94 -0
  45. package/dist/hashline/messages.js.map +1 -0
  46. package/dist/hashline/mismatch.d.ts +45 -0
  47. package/dist/hashline/mismatch.d.ts.map +1 -0
  48. package/dist/hashline/mismatch.js +118 -0
  49. package/dist/hashline/mismatch.js.map +1 -0
  50. package/dist/hashline/normalize.d.ts +22 -0
  51. package/dist/hashline/normalize.d.ts.map +1 -0
  52. package/dist/hashline/normalize.js +31 -0
  53. package/dist/hashline/normalize.js.map +1 -0
  54. package/dist/hashline/parser.d.ts +24 -0
  55. package/dist/hashline/parser.d.ts.map +1 -0
  56. package/dist/hashline/parser.js +295 -0
  57. package/dist/hashline/parser.js.map +1 -0
  58. package/dist/hashline/patcher.d.ts +111 -0
  59. package/dist/hashline/patcher.d.ts.map +1 -0
  60. package/dist/hashline/patcher.js +332 -0
  61. package/dist/hashline/patcher.js.map +1 -0
  62. package/dist/hashline/recovery.d.ts +41 -0
  63. package/dist/hashline/recovery.d.ts.map +1 -0
  64. package/dist/hashline/recovery.js +175 -0
  65. package/dist/hashline/recovery.js.map +1 -0
  66. package/dist/hashline/snapshots.d.ts +62 -0
  67. package/dist/hashline/snapshots.d.ts.map +1 -0
  68. package/dist/hashline/snapshots.js +127 -0
  69. package/dist/hashline/snapshots.js.map +1 -0
  70. package/dist/hashline/tokenizer.d.ts +66 -0
  71. package/dist/hashline/tokenizer.d.ts.map +1 -0
  72. package/dist/hashline/tokenizer.js +408 -0
  73. package/dist/hashline/tokenizer.js.map +1 -0
  74. package/dist/hashline/types.d.ts +117 -0
  75. package/dist/hashline/types.d.ts.map +1 -0
  76. package/dist/hashline/types.js +13 -0
  77. package/dist/hashline/types.js.map +1 -0
  78. package/dist/local-vault/index.d.ts +1 -1
  79. package/dist/local-vault/index.js +1 -1
  80. package/dist/lsp/index.d.ts +1 -1
  81. package/dist/lsp/index.js +1 -1
  82. package/dist/lsp/types.d.ts +2 -2
  83. package/dist/mcp/host.d.ts +3 -3
  84. package/dist/mcp/load-mcp-host.d.ts +4 -4
  85. package/dist/mcp/load-mcp-host.js +4 -4
  86. package/dist/mcp/oauth/provider.d.ts.map +1 -1
  87. package/dist/mcp/oauth/provider.js +10 -1
  88. package/dist/mcp/oauth/provider.js.map +1 -1
  89. package/dist/plugin-api/index.d.ts +10 -2
  90. package/dist/plugin-api/index.d.ts.map +1 -1
  91. package/dist/plugin-api/index.js +15 -0
  92. package/dist/plugin-api/index.js.map +1 -1
  93. package/dist/tools/approval.d.ts +1 -1
  94. package/dist/tools/approval.js +1 -1
  95. package/dist/tools/builtins/_hashline-fs.d.ts +16 -0
  96. package/dist/tools/builtins/_hashline-fs.d.ts.map +1 -0
  97. package/dist/tools/builtins/_hashline-fs.js +62 -0
  98. package/dist/tools/builtins/_hashline-fs.js.map +1 -0
  99. package/dist/tools/builtins/_image.d.ts +26 -0
  100. package/dist/tools/builtins/_image.d.ts.map +1 -0
  101. package/dist/tools/builtins/_image.js +76 -0
  102. package/dist/tools/builtins/_image.js.map +1 -0
  103. package/dist/tools/builtins/_login-shell.d.ts +17 -0
  104. package/dist/tools/builtins/_login-shell.d.ts.map +1 -0
  105. package/dist/tools/builtins/_login-shell.js +66 -0
  106. package/dist/tools/builtins/_login-shell.js.map +1 -0
  107. package/dist/tools/builtins/_notebook.d.ts +15 -0
  108. package/dist/tools/builtins/_notebook.d.ts.map +1 -0
  109. package/dist/tools/builtins/_notebook.js +81 -0
  110. package/dist/tools/builtins/_notebook.js.map +1 -0
  111. package/dist/tools/builtins/_pdf.d.ts +19 -0
  112. package/dist/tools/builtins/_pdf.d.ts.map +1 -0
  113. package/dist/tools/builtins/_pdf.js +42 -0
  114. package/dist/tools/builtins/_pdf.js.map +1 -0
  115. package/dist/tools/builtins/_shared.d.ts +11 -0
  116. package/dist/tools/builtins/_shared.d.ts.map +1 -1
  117. package/dist/tools/builtins/_shared.js +40 -1
  118. package/dist/tools/builtins/_shared.js.map +1 -1
  119. package/dist/tools/builtins/ast-edit.d.ts +18 -0
  120. package/dist/tools/builtins/ast-edit.d.ts.map +1 -0
  121. package/dist/tools/builtins/ast-edit.js +109 -0
  122. package/dist/tools/builtins/ast-edit.js.map +1 -0
  123. package/dist/tools/builtins/ast-grep.d.ts +6 -0
  124. package/dist/tools/builtins/ast-grep.d.ts.map +1 -0
  125. package/dist/tools/builtins/ast-grep.js +67 -0
  126. package/dist/tools/builtins/ast-grep.js.map +1 -0
  127. package/dist/tools/builtins/bash.d.ts.map +1 -1
  128. package/dist/tools/builtins/bash.js +13 -2
  129. package/dist/tools/builtins/bash.js.map +1 -1
  130. package/dist/tools/builtins/edit.d.ts.map +1 -1
  131. package/dist/tools/builtins/edit.js +112 -31
  132. package/dist/tools/builtins/edit.js.map +1 -1
  133. package/dist/tools/builtins/lsp.js +1 -1
  134. package/dist/tools/builtins/pty.d.ts +1 -1
  135. package/dist/tools/builtins/read.d.ts.map +1 -1
  136. package/dist/tools/builtins/read.js +187 -11
  137. package/dist/tools/builtins/read.js.map +1 -1
  138. package/dist/tools/builtins.d.ts.map +1 -1
  139. package/dist/tools/builtins.js +4 -0
  140. package/dist/tools/builtins.js.map +1 -1
  141. package/dist/tools/file-lock.d.ts +8 -0
  142. package/dist/tools/file-lock.d.ts.map +1 -1
  143. package/dist/tools/file-lock.js +22 -0
  144. package/dist/tools/file-lock.js.map +1 -1
  145. package/dist/tools/index.d.ts +1 -1
  146. package/dist/tools/index.d.ts.map +1 -1
  147. package/dist/tools/index.js.map +1 -1
  148. package/dist/tools/types.d.ts +27 -1
  149. package/dist/tools/types.d.ts.map +1 -1
  150. package/dist/tools/types.js.map +1 -1
  151. package/package.json +8 -3
@@ -0,0 +1,106 @@
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
+ * Ported from oh-my-pi `packages/hashline/src/format.ts`. **chances-cli
7
+ * changes (7.9 R1-M3/S7):**
8
+ * - `computeFileHash` uses `node:crypto` (xxHash32 was `Bun.hash`, which is
9
+ * unavailable under Node — serve-on-Node must run the same code).
10
+ * - `computeFileHash` is path-aware (`trimTrailing`): Markdown trailing
11
+ * whitespace is semantically meaningful (hard line break), so the hash —
12
+ * the edit safety boundary — must NOT trim it for `.md`/`.markdown`.
13
+ * - The 16-bit tag is only a fast INDEX into the snapshot store; the
14
+ * authoritative integrity check is exact snapshot-text equality in
15
+ * `patcher.ts` (so a 16-bit collision can never silently corrupt content).
16
+ */
17
+ import type { Cursor } from "./types.js";
18
+ /** File-section header prefix: `¶path#hash`. */
19
+ export declare const HL_FILE_PREFIX = "\u00B6";
20
+ /** Payload sigil for literal body rows. */
21
+ export declare const HL_PAYLOAD_REPLACE = "+";
22
+ /** Hunk-header keyword for concrete line replacement. */
23
+ export declare const HL_REPLACE_KEYWORD = "replace";
24
+ /** Hunk-header sub-keyword: `replace block N:` resolves N to a tree-sitter block range. */
25
+ export declare const HL_BLOCK_KEYWORD = "block";
26
+ /** Hunk-header keyword for concrete line deletion. */
27
+ export declare const HL_DELETE_KEYWORD = "delete";
28
+ /** Hunk-header keyword for insertion operations. */
29
+ export declare const HL_INSERT_KEYWORD = "insert";
30
+ /** Insert position keyword for inserting before a concrete line. */
31
+ export declare const HL_INSERT_BEFORE = "before";
32
+ /** Insert position keyword for inserting after a concrete line. */
33
+ export declare const HL_INSERT_AFTER = "after";
34
+ /** Insert position keyword for inserting at the start of the file. */
35
+ export declare const HL_INSERT_HEAD = "head";
36
+ /** Insert position keyword for inserting at the end of the file. */
37
+ export declare const HL_INSERT_TAIL = "tail";
38
+ /** Hunk-header terminator for body-bearing operations. */
39
+ export declare const HL_HEADER_COLON = ":";
40
+ /** Separator between a hashline file path and its opaque snapshot tag. */
41
+ export declare const HL_FILE_HASH_SEP = "#";
42
+ /** Separator between two line numbers in a range, e.g. `5..10`. */
43
+ export declare const HL_RANGE_SEP = "..";
44
+ /** Separator between a line number and displayed line content in hashline mode. */
45
+ export declare const HL_LINE_BODY_SEP = ":";
46
+ /** Bare positive line-number Lid (no decorations, no captures, no anchors). */
47
+ export declare const HL_LINE_RE_RAW = "[1-9]\\d*";
48
+ /** Capture-group form of {@link HL_LINE_RE_RAW}. */
49
+ export declare const HL_LINE_CAPTURE_RE_RAW = "([1-9]\\d*)";
50
+ /** Format a concrete replacement hunk header. */
51
+ export declare function formatReplaceHeader(start: number, end: number): string;
52
+ /** Format a concrete deletion hunk header. */
53
+ export declare function formatDeleteHeader(start: number, end?: number): string;
54
+ /** Format an insertion hunk header for a cursor position. */
55
+ export declare function formatInsertHeader(cursor: Cursor): string;
56
+ /** Number of hex characters in a content-derived file-hash tag. */
57
+ export declare const HL_FILE_HASH_LENGTH = 4;
58
+ /** Canonical uppercase hexadecimal content-hash tag carried by a hashline section header. */
59
+ export declare const HL_FILE_HASH_RE_RAW = "[0-9A-F]{4}";
60
+ /** Capture-group form of {@link HL_FILE_HASH_RE_RAW}. */
61
+ export declare const HL_FILE_HASH_CAPTURE_RE_RAW = "([0-9A-F]{4})";
62
+ /** Regex-escaped form of {@link HL_LINE_BODY_SEP}, safe for embedding inside a regex. */
63
+ export declare const HL_LINE_BODY_SEP_RE_RAW: string;
64
+ /**
65
+ * Representative file-hash tags for use in user-facing error messages and
66
+ * prompt examples.
67
+ */
68
+ export declare const HL_FILE_HASH_EXAMPLES: readonly ["1A2B", "3C4D", "9F3E"];
69
+ /**
70
+ * True when the hash for `path` should trim trailing whitespace. Markdown is
71
+ * the exception: trailing double-space encodes a hard line break (R1-S7).
72
+ */
73
+ export declare function hashTrimsTrailing(path: string): boolean;
74
+ /**
75
+ * Compute the content-derived hash tag carried by a hashline section header.
76
+ * The tag is a 4-hex fingerprint of the whole file's normalized text. It is a
77
+ * fast INDEX into the snapshot store, NOT the integrity check: `patcher.ts`
78
+ * confirms an exact snapshot-text match before applying, so a 16-bit collision
79
+ * can never silently corrupt content (it degrades to a forced re-read).
80
+ *
81
+ * `trimTrailing` defaults to true; pass `hashTrimsTrailing(path)` so Markdown
82
+ * keeps its meaningful trailing whitespace.
83
+ */
84
+ export declare function computeFileHash(text: string, trimTrailing?: boolean): string;
85
+ /**
86
+ * Format a comma-separated list of example anchors with an optional line-number
87
+ * prefix, quoted for inclusion in error messages: `"160", "42", "7"`.
88
+ */
89
+ export declare function describeAnchorExamples(linePrefix?: string): string;
90
+ /** Format a hashline section header for a file path and snapshot tag. */
91
+ export declare function formatHashlineHeader(filePath: string, fileHash: string): string;
92
+ /** Formats a single numbered line as `LINE:TEXT`. */
93
+ export declare function formatNumberedLine(lineNumber: number, line: string): string;
94
+ /** Format file text with hashline-mode line-number prefixes for display. */
95
+ export declare function formatNumberedLines(text: string, startLine?: number): string;
96
+ /**
97
+ * Build a "did you mean" hint listing the nearest real, non-blank lines to a
98
+ * target line number, formatted as `LINE:TEXT`. This is the cheapest,
99
+ * algorithm-independent success-rate lever (7.9 [5.2] / §3.8): when an edit
100
+ * anchor is out of range or a `replace block N:` cannot be resolved, show the
101
+ * model valid anchors it can re-aim at instead of a bare "line N does not
102
+ * exist". Returns `""` (no hint) for an empty file. The leading `\n` lets
103
+ * callers append it directly onto an error message.
104
+ */
105
+ export declare function formatNearestAnchors(fileLines: string[], target: number, max?: number): string;
106
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/hashline/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,gDAAgD;AAChD,eAAO,MAAM,cAAc,WAAM,CAAC;AAElC,2CAA2C;AAC3C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,UAAU,CAAC;AACxC,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,WAAW,CAAC;AACzC,mEAAmE;AACnE,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,sEAAsE;AACtE,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,oEAAoE;AACpE,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,0DAA0D;AAC1D,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,mEAAmE;AACnE,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAMpC,+EAA+E;AAC/E,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,gBAAwB,CAAC;AAE5D,iDAAiD;AACjD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAQ,GAAG,MAAM,CAIrE;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWzD;AAED,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,gBAAqC,CAAC;AACtE,yDAAyD;AACzD,eAAO,MAAM,2BAA2B,kBAA6B,CAAC;AACtE,yFAAyF;AACzF,eAAO,MAAM,uBAAuB,QAAgC,CAAC;AACrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,mCAAoC,CAAC;AAcvE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,UAAO,GAAG,MAAM,CASzE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,SAAK,GAAG,MAAM,CAK9D;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,MAAM,CAGvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,CAyBzF"}
@@ -0,0 +1,191 @@
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
+ * Ported from oh-my-pi `packages/hashline/src/format.ts`. **chances-cli
7
+ * changes (7.9 R1-M3/S7):**
8
+ * - `computeFileHash` uses `node:crypto` (xxHash32 was `Bun.hash`, which is
9
+ * unavailable under Node — serve-on-Node must run the same code).
10
+ * - `computeFileHash` is path-aware (`trimTrailing`): Markdown trailing
11
+ * whitespace is semantically meaningful (hard line break), so the hash —
12
+ * the edit safety boundary — must NOT trim it for `.md`/`.markdown`.
13
+ * - The 16-bit tag is only a fast INDEX into the snapshot store; the
14
+ * authoritative integrity check is exact snapshot-text equality in
15
+ * `patcher.ts` (so a 16-bit collision can never silently corrupt content).
16
+ */
17
+ import { createHash } from "node:crypto";
18
+ /** File-section header prefix: `¶path#hash`. */
19
+ export const HL_FILE_PREFIX = "¶";
20
+ /** Payload sigil for literal body rows. */
21
+ export const HL_PAYLOAD_REPLACE = "+";
22
+ /** Hunk-header keyword for concrete line replacement. */
23
+ export const HL_REPLACE_KEYWORD = "replace";
24
+ /** Hunk-header sub-keyword: `replace block N:` resolves N to a tree-sitter block range. */
25
+ export const HL_BLOCK_KEYWORD = "block";
26
+ /** Hunk-header keyword for concrete line deletion. */
27
+ export const HL_DELETE_KEYWORD = "delete";
28
+ /** Hunk-header keyword for insertion operations. */
29
+ export const HL_INSERT_KEYWORD = "insert";
30
+ /** Insert position keyword for inserting before a concrete line. */
31
+ export const HL_INSERT_BEFORE = "before";
32
+ /** Insert position keyword for inserting after a concrete line. */
33
+ export const HL_INSERT_AFTER = "after";
34
+ /** Insert position keyword for inserting at the start of the file. */
35
+ export const HL_INSERT_HEAD = "head";
36
+ /** Insert position keyword for inserting at the end of the file. */
37
+ export const HL_INSERT_TAIL = "tail";
38
+ /** Hunk-header terminator for body-bearing operations. */
39
+ export const HL_HEADER_COLON = ":";
40
+ /** Separator between a hashline file path and its opaque snapshot tag. */
41
+ export const HL_FILE_HASH_SEP = "#";
42
+ /** Separator between two line numbers in a range, e.g. `5..10`. */
43
+ export const HL_RANGE_SEP = "..";
44
+ /** Separator between a line number and displayed line content in hashline mode. */
45
+ export const HL_LINE_BODY_SEP = ":";
46
+ function regexEscape(str) {
47
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48
+ }
49
+ /** Bare positive line-number Lid (no decorations, no captures, no anchors). */
50
+ export const HL_LINE_RE_RAW = `[1-9]\\d*`;
51
+ /** Capture-group form of {@link HL_LINE_RE_RAW}. */
52
+ export const HL_LINE_CAPTURE_RE_RAW = `(${HL_LINE_RE_RAW})`;
53
+ /** Format a concrete replacement hunk header. */
54
+ export function formatReplaceHeader(start, end) {
55
+ return `${HL_REPLACE_KEYWORD} ${start}${HL_RANGE_SEP}${end}${HL_HEADER_COLON}`;
56
+ }
57
+ /** Format a concrete deletion hunk header. */
58
+ export function formatDeleteHeader(start, end = start) {
59
+ return start === end
60
+ ? `${HL_DELETE_KEYWORD} ${start}`
61
+ : `${HL_DELETE_KEYWORD} ${start}${HL_RANGE_SEP}${end}`;
62
+ }
63
+ /** Format an insertion hunk header for a cursor position. */
64
+ export function formatInsertHeader(cursor) {
65
+ switch (cursor.kind) {
66
+ case "before_anchor":
67
+ return `${HL_INSERT_KEYWORD} ${HL_INSERT_BEFORE} ${cursor.anchor.line}${HL_HEADER_COLON}`;
68
+ case "after_anchor":
69
+ return `${HL_INSERT_KEYWORD} ${HL_INSERT_AFTER} ${cursor.anchor.line}${HL_HEADER_COLON}`;
70
+ case "bof":
71
+ return `${HL_INSERT_KEYWORD} ${HL_INSERT_HEAD}${HL_HEADER_COLON}`;
72
+ case "eof":
73
+ return `${HL_INSERT_KEYWORD} ${HL_INSERT_TAIL}${HL_HEADER_COLON}`;
74
+ }
75
+ }
76
+ /** Number of hex characters in a content-derived file-hash tag. */
77
+ export const HL_FILE_HASH_LENGTH = 4;
78
+ /** Canonical uppercase hexadecimal content-hash tag carried by a hashline section header. */
79
+ export const HL_FILE_HASH_RE_RAW = `[0-9A-F]{${HL_FILE_HASH_LENGTH}}`;
80
+ /** Capture-group form of {@link HL_FILE_HASH_RE_RAW}. */
81
+ export const HL_FILE_HASH_CAPTURE_RE_RAW = `(${HL_FILE_HASH_RE_RAW})`;
82
+ /** Regex-escaped form of {@link HL_LINE_BODY_SEP}, safe for embedding inside a regex. */
83
+ export const HL_LINE_BODY_SEP_RE_RAW = regexEscape(HL_LINE_BODY_SEP);
84
+ /**
85
+ * Representative file-hash tags for use in user-facing error messages and
86
+ * prompt examples.
87
+ */
88
+ export const HL_FILE_HASH_EXAMPLES = ["1A2B", "3C4D", "9F3E"];
89
+ /**
90
+ * Normalize text before hashing: trim trailing `[ \t\r]` from every line (and
91
+ * the final line) in a single pass so CRLF endings and display-trimmed lines
92
+ * do not invalidate a tag. When `trimTrailing` is false (Markdown), leave
93
+ * trailing whitespace intact — `.md` trailing double-space is a hard line
94
+ * break, a meaningful edit the hash must NOT mask.
95
+ */
96
+ function normalizeFileHashText(text, trimTrailing) {
97
+ if (!trimTrailing)
98
+ return text.replace(/\r\n?/g, "\n");
99
+ return text.replace(/[ \t\r]+(?=\n|$)/g, "");
100
+ }
101
+ /**
102
+ * True when the hash for `path` should trim trailing whitespace. Markdown is
103
+ * the exception: trailing double-space encodes a hard line break (R1-S7).
104
+ */
105
+ export function hashTrimsTrailing(path) {
106
+ return !/\.(md|markdown)$/i.test(path);
107
+ }
108
+ /**
109
+ * Compute the content-derived hash tag carried by a hashline section header.
110
+ * The tag is a 4-hex fingerprint of the whole file's normalized text. It is a
111
+ * fast INDEX into the snapshot store, NOT the integrity check: `patcher.ts`
112
+ * confirms an exact snapshot-text match before applying, so a 16-bit collision
113
+ * can never silently corrupt content (it degrades to a forced re-read).
114
+ *
115
+ * `trimTrailing` defaults to true; pass `hashTrimsTrailing(path)` so Markdown
116
+ * keeps its meaningful trailing whitespace.
117
+ */
118
+ export function computeFileHash(text, trimTrailing = true) {
119
+ const normalized = normalizeFileHashText(text, trimTrailing);
120
+ // node:crypto for cross-runtime (Bun + Node). Take the low 16 bits of a SHA-1
121
+ // digest → 4-hex, matching the model-facing tag width oh-my-pi tuned.
122
+ const digest = createHash("sha1").update(normalized, "utf8").digest();
123
+ // First two bytes → 16-bit tag. `readUInt16BE` avoids index access (a SHA-1
124
+ // digest is always ≥ 2 bytes, but noUncheckedIndexedAccess can't prove it).
125
+ const low16 = digest.readUInt16BE(0);
126
+ return low16.toString(16).padStart(HL_FILE_HASH_LENGTH, "0").toUpperCase();
127
+ }
128
+ /**
129
+ * Format a comma-separated list of example anchors with an optional line-number
130
+ * prefix, quoted for inclusion in error messages: `"160", "42", "7"`.
131
+ */
132
+ export function describeAnchorExamples(linePrefix = "") {
133
+ const examples = linePrefix
134
+ ? [linePrefix, `${linePrefix.slice(0, -1) || "4"}2`, "7"]
135
+ : ["160", "42", "7"];
136
+ return examples.map((e) => `"${e}"`).join(", ");
137
+ }
138
+ /** Format a hashline section header for a file path and snapshot tag. */
139
+ export function formatHashlineHeader(filePath, fileHash) {
140
+ return `${HL_FILE_PREFIX}${filePath}${HL_FILE_HASH_SEP}${fileHash}`;
141
+ }
142
+ /** Formats a single numbered line as `LINE:TEXT`. */
143
+ export function formatNumberedLine(lineNumber, line) {
144
+ return `${lineNumber}${HL_LINE_BODY_SEP}${line}`;
145
+ }
146
+ /** Format file text with hashline-mode line-number prefixes for display. */
147
+ export function formatNumberedLines(text, startLine = 1) {
148
+ const lines = text.split("\n");
149
+ return lines.map((line, i) => formatNumberedLine(startLine + i, line)).join("\n");
150
+ }
151
+ /**
152
+ * Build a "did you mean" hint listing the nearest real, non-blank lines to a
153
+ * target line number, formatted as `LINE:TEXT`. This is the cheapest,
154
+ * algorithm-independent success-rate lever (7.9 [5.2] / §3.8): when an edit
155
+ * anchor is out of range or a `replace block N:` cannot be resolved, show the
156
+ * model valid anchors it can re-aim at instead of a bare "line N does not
157
+ * exist". Returns `""` (no hint) for an empty file. The leading `\n` lets
158
+ * callers append it directly onto an error message.
159
+ */
160
+ export function formatNearestAnchors(fileLines, target, max = 3) {
161
+ const total = fileLines.length;
162
+ if (total === 0)
163
+ return "";
164
+ const clamped = Math.min(Math.max(Math.trunc(target) || 1, 1), total);
165
+ const isBlank = (ln) => (fileLines[ln - 1] ?? "").trim().length === 0;
166
+ // Walk outward from the clamped target, nearest-first. First pass keeps only
167
+ // non-blank lines (real anchors); if the whole neighborhood is blank, fall
168
+ // back to including blanks so the hint is never empty for a non-empty file.
169
+ const collect = (skipBlank) => {
170
+ const out = [];
171
+ for (let d = 0; out.length < max && d <= total; d++) {
172
+ for (const ln of d === 0 ? [clamped] : [clamped - d, clamped + d]) {
173
+ if (ln < 1 || ln > total || out.includes(ln))
174
+ continue;
175
+ if (skipBlank && isBlank(ln))
176
+ continue;
177
+ out.push(ln);
178
+ if (out.length >= max)
179
+ break;
180
+ }
181
+ }
182
+ return out;
183
+ };
184
+ const picked = collect(true);
185
+ const lines = (picked.length > 0 ? picked : collect(false)).sort((a, b) => a - b);
186
+ if (lines.length === 0)
187
+ return "";
188
+ const numbered = lines.map((ln) => formatNumberedLine(ln, fileLines[ln - 1] ?? "")).join("\n");
189
+ return `\nNearest readable lines (re-read for a fresh ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}tag, then anchor on these):\n${numbered}`;
190
+ }
191
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/hashline/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Storage seam for the hashline patcher. {@link Filesystem} is intentionally
3
+ * minimal — `readText`, `writeText`, `exists` — so any backing store can be
4
+ * adapted: disk, memory, S3, an LSP text-document protocol, a Git tree, a
5
+ * VFS, etc. Ported from oh-my-pi `packages/hashline/src/fs.ts`.
6
+ *
7
+ * **chances-cli change (7.9):** the Bun-based `NodeFilesystem` is dropped (it
8
+ * used `Bun.file`/`Bun.write`, unavailable under Node — serve-on-Node). The
9
+ * disk-backed implementation lives in the engine `edit` tool as `EngineToolFs`,
10
+ * which routes every write through `guardWrite` (realpath jail + protected-path
11
+ * floor) + `writeFileAtomic` + fs-scan cache invalidation. {@link
12
+ * InMemoryFilesystem} stays here for tests/sandboxes.
13
+ *
14
+ * The patcher does its own BOM stripping and LF normalization between
15
+ * {@link Filesystem.readText} and {@link Filesystem.writeText}; the FS deals
16
+ * only in raw text strings.
17
+ */
18
+ /**
19
+ * Result returned by {@link Filesystem.writeText}. The patcher echoes back
20
+ * `text` so adapters that transform on serialization (e.g. notebooks) can
21
+ * report what actually landed on disk.
22
+ */
23
+ export interface WriteResult {
24
+ /** Final text that was persisted. May differ from the input if the FS transformed it. */
25
+ text: string;
26
+ }
27
+ /**
28
+ * ENOENT-like error thrown by {@link Filesystem.readText} when a path is
29
+ * missing. Carrying a `code` property keeps the contract compatible with
30
+ * `node:fs` callers that already check `err.code === "ENOENT"`.
31
+ */
32
+ export declare class NotFoundError extends Error {
33
+ readonly code = "ENOENT";
34
+ constructor(path: string, cause?: unknown);
35
+ }
36
+ /** Type guard for {@link NotFoundError} and structurally-compatible errors. */
37
+ export declare function isNotFound(error: unknown): boolean;
38
+ /**
39
+ * Abstract storage backend the {@link Patcher} reads from and writes to.
40
+ * Subclass for new backends; the package ships {@link InMemoryFilesystem} for
41
+ * tests and the engine wires `EngineToolFs` for disk.
42
+ *
43
+ * Implementations work with raw text — the patcher handles BOM stripping and
44
+ * line-ending normalization itself. `readText` MUST throw {@link
45
+ * NotFoundError} (or any error for which {@link isNotFound} returns true)
46
+ * when the path doesn't exist; that's how the patcher detects a create-vs-
47
+ * update.
48
+ */
49
+ export declare abstract class Filesystem {
50
+ /** Read the file's full text content. Throw on missing file. */
51
+ abstract readText(path: string): Promise<string>;
52
+ /** Validate that `path` is writable before a prepared batch starts committing. */
53
+ preflightWrite(_path: string): Promise<void>;
54
+ /** Persist `content` at `path`. Returns the actual final text that was written. */
55
+ abstract writeText(path: string, content: string): Promise<WriteResult>;
56
+ /** Return true when the path exists and can be read. Default: probe via {@link readText}. */
57
+ exists(path: string): Promise<boolean>;
58
+ /**
59
+ * Canonical path used as a key by external caches (e.g. snapshot
60
+ * stores). The default is identity; override to return an absolute or
61
+ * otherwise canonicalised path so producers and consumers of cached
62
+ * snapshots agree on the key without each having to redo the resolution.
63
+ */
64
+ canonicalPath(path: string): string;
65
+ }
66
+ /**
67
+ * In-memory {@link Filesystem}. Useful for tests, sandboxes, dry-runs, and as
68
+ * a building block for stacked adapters (e.g. an LRU layer on top).
69
+ */
70
+ export declare class InMemoryFilesystem extends Filesystem {
71
+ #private;
72
+ constructor(initial?: Iterable<readonly [string, string]>);
73
+ readText(path: string): Promise<string>;
74
+ writeText(path: string, content: string): Promise<WriteResult>;
75
+ exists(path: string): Promise<boolean>;
76
+ /** Synchronous helper for setting up fixtures without awaiting. */
77
+ set(path: string, content: string): void;
78
+ /** Synchronous helper for inspecting state without awaiting. */
79
+ get(path: string): string | undefined;
80
+ /** Remove a single entry. Returns true when something was removed. */
81
+ delete(path: string): boolean;
82
+ /** Wipe all entries. */
83
+ clear(): void;
84
+ /** Iterate `[path, content]` pairs. */
85
+ entries(): IterableIterator<[string, string]>;
86
+ }
87
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/hashline/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,YAAY;gBAEb,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAK1C;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIlD;AAED;;;;;;;;;;GAUG;AACH,8BAAsB,UAAU;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEhD,kFAAkF;IAC5E,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAElD,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAEvE,6FAA6F;IACvF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU5C;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAGpC;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;;gBAGpC,OAAO,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAOnD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAKrD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD,mEAAmE;IACnE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIxC,gEAAgE;IAChE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrC,sEAAsE;IACtE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B,wBAAwB;IACxB,KAAK,IAAI,IAAI;IAIb,uCAAuC;IACvC,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAG9C"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Storage seam for the hashline patcher. {@link Filesystem} is intentionally
3
+ * minimal — `readText`, `writeText`, `exists` — so any backing store can be
4
+ * adapted: disk, memory, S3, an LSP text-document protocol, a Git tree, a
5
+ * VFS, etc. Ported from oh-my-pi `packages/hashline/src/fs.ts`.
6
+ *
7
+ * **chances-cli change (7.9):** the Bun-based `NodeFilesystem` is dropped (it
8
+ * used `Bun.file`/`Bun.write`, unavailable under Node — serve-on-Node). The
9
+ * disk-backed implementation lives in the engine `edit` tool as `EngineToolFs`,
10
+ * which routes every write through `guardWrite` (realpath jail + protected-path
11
+ * floor) + `writeFileAtomic` + fs-scan cache invalidation. {@link
12
+ * InMemoryFilesystem} stays here for tests/sandboxes.
13
+ *
14
+ * The patcher does its own BOM stripping and LF normalization between
15
+ * {@link Filesystem.readText} and {@link Filesystem.writeText}; the FS deals
16
+ * only in raw text strings.
17
+ */
18
+ /**
19
+ * ENOENT-like error thrown by {@link Filesystem.readText} when a path is
20
+ * missing. Carrying a `code` property keeps the contract compatible with
21
+ * `node:fs` callers that already check `err.code === "ENOENT"`.
22
+ */
23
+ export class NotFoundError extends Error {
24
+ code = "ENOENT";
25
+ constructor(path, cause) {
26
+ super(`File not found: ${path}`);
27
+ this.name = "NotFoundError";
28
+ if (cause !== undefined)
29
+ this.cause = cause;
30
+ }
31
+ }
32
+ /** Type guard for {@link NotFoundError} and structurally-compatible errors. */
33
+ export function isNotFound(error) {
34
+ if (error instanceof NotFoundError)
35
+ return true;
36
+ if (error instanceof Error && error.code === "ENOENT")
37
+ return true;
38
+ return false;
39
+ }
40
+ /**
41
+ * Abstract storage backend the {@link Patcher} reads from and writes to.
42
+ * Subclass for new backends; the package ships {@link InMemoryFilesystem} for
43
+ * tests and the engine wires `EngineToolFs` for disk.
44
+ *
45
+ * Implementations work with raw text — the patcher handles BOM stripping and
46
+ * line-ending normalization itself. `readText` MUST throw {@link
47
+ * NotFoundError} (or any error for which {@link isNotFound} returns true)
48
+ * when the path doesn't exist; that's how the patcher detects a create-vs-
49
+ * update.
50
+ */
51
+ export class Filesystem {
52
+ /** Validate that `path` is writable before a prepared batch starts committing. */
53
+ async preflightWrite(_path) { }
54
+ /** Return true when the path exists and can be read. Default: probe via {@link readText}. */
55
+ async exists(path) {
56
+ try {
57
+ await this.readText(path);
58
+ return true;
59
+ }
60
+ catch (error) {
61
+ if (isNotFound(error))
62
+ return false;
63
+ throw error;
64
+ }
65
+ }
66
+ /**
67
+ * Canonical path used as a key by external caches (e.g. snapshot
68
+ * stores). The default is identity; override to return an absolute or
69
+ * otherwise canonicalised path so producers and consumers of cached
70
+ * snapshots agree on the key without each having to redo the resolution.
71
+ */
72
+ canonicalPath(path) {
73
+ return path;
74
+ }
75
+ }
76
+ /**
77
+ * In-memory {@link Filesystem}. Useful for tests, sandboxes, dry-runs, and as
78
+ * a building block for stacked adapters (e.g. an LRU layer on top).
79
+ */
80
+ export class InMemoryFilesystem extends Filesystem {
81
+ #files = new Map();
82
+ constructor(initial) {
83
+ super();
84
+ if (initial) {
85
+ for (const [path, content] of initial)
86
+ this.#files.set(path, content);
87
+ }
88
+ }
89
+ async readText(path) {
90
+ const text = this.#files.get(path);
91
+ if (text === undefined)
92
+ throw new NotFoundError(path);
93
+ return text;
94
+ }
95
+ async writeText(path, content) {
96
+ this.#files.set(path, content);
97
+ return { text: content };
98
+ }
99
+ async exists(path) {
100
+ return this.#files.has(path);
101
+ }
102
+ /** Synchronous helper for setting up fixtures without awaiting. */
103
+ set(path, content) {
104
+ this.#files.set(path, content);
105
+ }
106
+ /** Synchronous helper for inspecting state without awaiting. */
107
+ get(path) {
108
+ return this.#files.get(path);
109
+ }
110
+ /** Remove a single entry. Returns true when something was removed. */
111
+ delete(path) {
112
+ return this.#files.delete(path);
113
+ }
114
+ /** Wipe all entries. */
115
+ clear() {
116
+ this.#files.clear();
117
+ }
118
+ /** Iterate `[path, content]` pairs. */
119
+ entries() {
120
+ return this.#files.entries();
121
+ }
122
+ }
123
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/hashline/fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @chances-ai/engine hashline core — a compact, line-anchored patch language
3
+ * and applier for LLM-driven file edits. Ported from oh-my-pi
4
+ * `packages/hashline` (7.9 task 09); see the per-file headers for the
5
+ * chances-cli deltas (cross-runtime hash, exact-text integrity, FS seam).
6
+ *
7
+ * Every section binds to a whole-file content hash so stale anchors are
8
+ * rejected before they corrupt code; the patcher abstracts over the
9
+ * filesystem so the same core works on disk, in memory, or against any custom
10
+ * backend. The engine `edit` tool wires `EngineToolFs` (guardWrite + atomic) +
11
+ * a `native.blockRangeAt` block resolver + a per-session `SnapshotStore`.
12
+ */
13
+ export * from "./apply.js";
14
+ export * from "./block.js";
15
+ export * from "./format.js";
16
+ export * from "./fs.js";
17
+ export * from "./input.js";
18
+ export * from "./messages.js";
19
+ export * from "./mismatch.js";
20
+ export * from "./normalize.js";
21
+ export * from "./parser.js";
22
+ export * from "./patcher.js";
23
+ export * from "./recovery.js";
24
+ export * from "./snapshots.js";
25
+ export * from "./tokenizer.js";
26
+ export * from "./types.js";
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hashline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @chances-ai/engine hashline core — a compact, line-anchored patch language
3
+ * and applier for LLM-driven file edits. Ported from oh-my-pi
4
+ * `packages/hashline` (7.9 task 09); see the per-file headers for the
5
+ * chances-cli deltas (cross-runtime hash, exact-text integrity, FS seam).
6
+ *
7
+ * Every section binds to a whole-file content hash so stale anchors are
8
+ * rejected before they corrupt code; the patcher abstracts over the
9
+ * filesystem so the same core works on disk, in memory, or against any custom
10
+ * backend. The engine `edit` tool wires `EngineToolFs` (guardWrite + atomic) +
11
+ * a `native.blockRangeAt` block resolver + a per-session `SnapshotStore`.
12
+ */
13
+ export * from "./apply.js";
14
+ export * from "./block.js";
15
+ export * from "./format.js";
16
+ export * from "./fs.js";
17
+ export * from "./input.js";
18
+ export * from "./messages.js";
19
+ export * from "./mismatch.js";
20
+ export * from "./normalize.js";
21
+ export * from "./parser.js";
22
+ export * from "./patcher.js";
23
+ export * from "./recovery.js";
24
+ export * from "./snapshots.js";
25
+ export * from "./tokenizer.js";
26
+ export * from "./types.js";
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hashline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}