opencode-hashline 1.1.1 → 1.1.2

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.
@@ -4,7 +4,7 @@ import {
4
4
  resolveConfig,
5
5
  shouldExclude,
6
6
  stripHashes
7
- } from "./chunk-DDXOFWTU.js";
7
+ } from "./chunk-X4NVISKE.js";
8
8
 
9
9
  // src/hooks.ts
10
10
  import { appendFileSync } from "fs";
@@ -88,7 +88,8 @@ function computeLineHash(idx, line, hashLen = 3) {
88
88
  return hash.toString(16).padStart(hashLen, "0");
89
89
  }
90
90
  function formatFileWithHashes(content, hashLen, prefix) {
91
- const lines = content.split("\n");
91
+ const normalized = content.includes("\r\n") ? content.replace(/\r\n/g, "\n") : content;
92
+ const lines = normalized.split("\n");
92
93
  const effectiveLen = hashLen && hashLen >= 3 ? hashLen : getAdaptiveHashLength(lines.length);
93
94
  const effectivePrefix = prefix === void 0 ? DEFAULT_PREFIX : prefix === false ? "" : prefix;
94
95
  const hashes = new Array(lines.length);
@@ -116,7 +117,9 @@ function stripHashes(content, prefix) {
116
117
  hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
117
118
  stripRegexCache.set(escapedPrefix, hashLinePattern);
118
119
  }
119
- return content.split("\n").map((line) => {
120
+ const lineEnding = detectLineEnding(content);
121
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
122
+ const result = normalized.split("\n").map((line) => {
120
123
  const match = line.match(hashLinePattern);
121
124
  if (match) {
122
125
  const patchMarker = match[1] || "";
@@ -124,6 +127,7 @@ function stripHashes(content, prefix) {
124
127
  }
125
128
  return line;
126
129
  }).join("\n");
130
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
127
131
  }
128
132
  function parseHashRef(ref) {
129
133
  const match = ref.match(/^(\d+):([0-9a-f]{2,8})$/);
@@ -189,12 +193,14 @@ function resolveRange(startRef, endRef, content, hashLen) {
189
193
  `Invalid range: start line ${start.line} is after end line ${end.line}`
190
194
  );
191
195
  }
192
- const lines = content.split("\n");
193
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
196
+ const lineEnding = detectLineEnding(content);
197
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
198
+ const lines = normalized.split("\n");
199
+ const startVerify = verifyHash(start.line, start.hash, normalized, hashLen, lines);
194
200
  if (!startVerify.valid) {
195
201
  throw new Error(`Start reference invalid: ${startVerify.message}`);
196
202
  }
197
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
203
+ const endVerify = verifyHash(end.line, end.hash, normalized, hashLen, lines);
198
204
  if (!endVerify.valid) {
199
205
  throw new Error(`End reference invalid: ${endVerify.message}`);
200
206
  }
@@ -203,22 +209,27 @@ function resolveRange(startRef, endRef, content, hashLen) {
203
209
  startLine: start.line,
204
210
  endLine: end.line,
205
211
  lines: rangeLines,
206
- content: rangeLines.join("\n")
212
+ content: rangeLines.join(lineEnding)
207
213
  };
208
214
  }
209
215
  function replaceRange(startRef, endRef, content, replacement, hashLen) {
210
- const range = resolveRange(startRef, endRef, content, hashLen);
211
- const lines = content.split("\n");
216
+ const lineEnding = detectLineEnding(content);
217
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
218
+ const range = resolveRange(startRef, endRef, normalized, hashLen);
219
+ const lines = normalized.split("\n");
212
220
  const before = lines.slice(0, range.startLine - 1);
213
221
  const after = lines.slice(range.endLine);
214
222
  const replacementLines = replacement.split("\n");
215
- return [...before, ...replacementLines, ...after].join("\n");
223
+ const result = [...before, ...replacementLines, ...after].join("\n");
224
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
216
225
  }
217
226
  function applyHashEdit(input, content, hashLen) {
227
+ const lineEnding = detectLineEnding(content);
228
+ const workContent = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
218
229
  const normalizedStart = normalizeHashRef(input.startRef);
219
230
  const start = parseHashRef(normalizedStart);
220
- const lines = content.split("\n");
221
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
231
+ const lines = workContent.split("\n");
232
+ const startVerify = verifyHash(start.line, start.hash, workContent, hashLen, lines);
222
233
  if (!startVerify.valid) {
223
234
  throw new Error(`Start reference invalid: ${startVerify.message}`);
224
235
  }
@@ -237,7 +248,7 @@ function applyHashEdit(input, content, hashLen) {
237
248
  operation: input.operation,
238
249
  startLine: start.line,
239
250
  endLine: start.line,
240
- content: next2
251
+ content: lineEnding === "\r\n" ? next2.replace(/\n/g, "\r\n") : next2
241
252
  };
242
253
  }
243
254
  const normalizedEnd = normalizeHashRef(input.endRef ?? input.startRef);
@@ -247,7 +258,7 @@ function applyHashEdit(input, content, hashLen) {
247
258
  `Invalid range: start line ${start.line} is after end line ${end.line}`
248
259
  );
249
260
  }
250
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
261
+ const endVerify = verifyHash(end.line, end.hash, workContent, hashLen, lines);
251
262
  if (!endVerify.valid) {
252
263
  throw new Error(`End reference invalid: ${endVerify.message}`);
253
264
  }
@@ -263,7 +274,7 @@ function applyHashEdit(input, content, hashLen) {
263
274
  operation: input.operation,
264
275
  startLine: start.line,
265
276
  endLine: end.line,
266
- content: next
277
+ content: lineEnding === "\r\n" ? next.replace(/\n/g, "\r\n") : next
267
278
  };
268
279
  }
269
280
  var HashlineCache = class {
@@ -338,6 +349,9 @@ var textEncoder = new TextEncoder();
338
349
  function getByteLength(content) {
339
350
  return textEncoder.encode(content).length;
340
351
  }
352
+ function detectLineEnding(content) {
353
+ return content.includes("\r\n") ? "\r\n" : "\n";
354
+ }
341
355
  function createHashline(config) {
342
356
  const resolved = resolveConfig(config);
343
357
  const cache = new HashlineCache(resolved.cacheSize);
@@ -410,5 +424,6 @@ export {
410
424
  matchesGlob,
411
425
  shouldExclude,
412
426
  getByteLength,
427
+ detectLineEnding,
413
428
  createHashline
414
429
  };
@@ -7,6 +7,7 @@ import {
7
7
  buildHashMap,
8
8
  computeLineHash,
9
9
  createHashline,
10
+ detectLineEnding,
10
11
  formatFileWithHashes,
11
12
  getAdaptiveHashLength,
12
13
  getByteLength,
@@ -19,7 +20,7 @@ import {
19
20
  shouldExclude,
20
21
  stripHashes,
21
22
  verifyHash
22
- } from "./chunk-DDXOFWTU.js";
23
+ } from "./chunk-X4NVISKE.js";
23
24
  export {
24
25
  DEFAULT_CONFIG,
25
26
  DEFAULT_EXCLUDE_PATTERNS,
@@ -29,6 +30,7 @@ export {
29
30
  buildHashMap,
30
31
  computeLineHash,
31
32
  createHashline,
33
+ detectLineEnding,
32
34
  formatFileWithHashes,
33
35
  getAdaptiveHashLength,
34
36
  getByteLength,
@@ -41,6 +41,7 @@ __export(hashline_exports, {
41
41
  buildHashMap: () => buildHashMap,
42
42
  computeLineHash: () => computeLineHash,
43
43
  createHashline: () => createHashline,
44
+ detectLineEnding: () => detectLineEnding,
44
45
  formatFileWithHashes: () => formatFileWithHashes,
45
46
  getAdaptiveHashLength: () => getAdaptiveHashLength,
46
47
  getByteLength: () => getByteLength,
@@ -100,7 +101,8 @@ function computeLineHash(idx, line, hashLen = 3) {
100
101
  return hash.toString(16).padStart(hashLen, "0");
101
102
  }
102
103
  function formatFileWithHashes(content, hashLen, prefix) {
103
- const lines = content.split("\n");
104
+ const normalized = content.includes("\r\n") ? content.replace(/\r\n/g, "\n") : content;
105
+ const lines = normalized.split("\n");
104
106
  const effectiveLen = hashLen && hashLen >= 3 ? hashLen : getAdaptiveHashLength(lines.length);
105
107
  const effectivePrefix = prefix === void 0 ? DEFAULT_PREFIX : prefix === false ? "" : prefix;
106
108
  const hashes = new Array(lines.length);
@@ -127,7 +129,9 @@ function stripHashes(content, prefix) {
127
129
  hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
128
130
  stripRegexCache.set(escapedPrefix, hashLinePattern);
129
131
  }
130
- return content.split("\n").map((line) => {
132
+ const lineEnding = detectLineEnding(content);
133
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
134
+ const result = normalized.split("\n").map((line) => {
131
135
  const match = line.match(hashLinePattern);
132
136
  if (match) {
133
137
  const patchMarker = match[1] || "";
@@ -135,6 +139,7 @@ function stripHashes(content, prefix) {
135
139
  }
136
140
  return line;
137
141
  }).join("\n");
142
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
138
143
  }
139
144
  function parseHashRef(ref) {
140
145
  const match = ref.match(/^(\d+):([0-9a-f]{2,8})$/);
@@ -200,12 +205,14 @@ function resolveRange(startRef, endRef, content, hashLen) {
200
205
  `Invalid range: start line ${start.line} is after end line ${end.line}`
201
206
  );
202
207
  }
203
- const lines = content.split("\n");
204
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
208
+ const lineEnding = detectLineEnding(content);
209
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
210
+ const lines = normalized.split("\n");
211
+ const startVerify = verifyHash(start.line, start.hash, normalized, hashLen, lines);
205
212
  if (!startVerify.valid) {
206
213
  throw new Error(`Start reference invalid: ${startVerify.message}`);
207
214
  }
208
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
215
+ const endVerify = verifyHash(end.line, end.hash, normalized, hashLen, lines);
209
216
  if (!endVerify.valid) {
210
217
  throw new Error(`End reference invalid: ${endVerify.message}`);
211
218
  }
@@ -214,22 +221,27 @@ function resolveRange(startRef, endRef, content, hashLen) {
214
221
  startLine: start.line,
215
222
  endLine: end.line,
216
223
  lines: rangeLines,
217
- content: rangeLines.join("\n")
224
+ content: rangeLines.join(lineEnding)
218
225
  };
219
226
  }
220
227
  function replaceRange(startRef, endRef, content, replacement, hashLen) {
221
- const range = resolveRange(startRef, endRef, content, hashLen);
222
- const lines = content.split("\n");
228
+ const lineEnding = detectLineEnding(content);
229
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
230
+ const range = resolveRange(startRef, endRef, normalized, hashLen);
231
+ const lines = normalized.split("\n");
223
232
  const before = lines.slice(0, range.startLine - 1);
224
233
  const after = lines.slice(range.endLine);
225
234
  const replacementLines = replacement.split("\n");
226
- return [...before, ...replacementLines, ...after].join("\n");
235
+ const result = [...before, ...replacementLines, ...after].join("\n");
236
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
227
237
  }
228
238
  function applyHashEdit(input, content, hashLen) {
239
+ const lineEnding = detectLineEnding(content);
240
+ const workContent = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
229
241
  const normalizedStart = normalizeHashRef(input.startRef);
230
242
  const start = parseHashRef(normalizedStart);
231
- const lines = content.split("\n");
232
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
243
+ const lines = workContent.split("\n");
244
+ const startVerify = verifyHash(start.line, start.hash, workContent, hashLen, lines);
233
245
  if (!startVerify.valid) {
234
246
  throw new Error(`Start reference invalid: ${startVerify.message}`);
235
247
  }
@@ -248,7 +260,7 @@ function applyHashEdit(input, content, hashLen) {
248
260
  operation: input.operation,
249
261
  startLine: start.line,
250
262
  endLine: start.line,
251
- content: next2
263
+ content: lineEnding === "\r\n" ? next2.replace(/\n/g, "\r\n") : next2
252
264
  };
253
265
  }
254
266
  const normalizedEnd = normalizeHashRef(input.endRef ?? input.startRef);
@@ -258,7 +270,7 @@ function applyHashEdit(input, content, hashLen) {
258
270
  `Invalid range: start line ${start.line} is after end line ${end.line}`
259
271
  );
260
272
  }
261
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
273
+ const endVerify = verifyHash(end.line, end.hash, workContent, hashLen, lines);
262
274
  if (!endVerify.valid) {
263
275
  throw new Error(`End reference invalid: ${endVerify.message}`);
264
276
  }
@@ -274,7 +286,7 @@ function applyHashEdit(input, content, hashLen) {
274
286
  operation: input.operation,
275
287
  startLine: start.line,
276
288
  endLine: end.line,
277
- content: next
289
+ content: lineEnding === "\r\n" ? next.replace(/\n/g, "\r\n") : next
278
290
  };
279
291
  }
280
292
  function matchesGlob(filePath, pattern) {
@@ -289,6 +301,9 @@ function shouldExclude(filePath, patterns) {
289
301
  function getByteLength(content) {
290
302
  return textEncoder.encode(content).length;
291
303
  }
304
+ function detectLineEnding(content) {
305
+ return content.includes("\r\n") ? "\r\n" : "\n";
306
+ }
292
307
  function createHashline(config) {
293
308
  const resolved = resolveConfig(config);
294
309
  const cache = new HashlineCache(resolved.cacheSize);
@@ -3,12 +3,12 @@ import {
3
3
  createFileReadAfterHook,
4
4
  createSystemPromptHook,
5
5
  setDebug
6
- } from "./chunk-JOA7B5LK.js";
6
+ } from "./chunk-MKNRMMMR.js";
7
7
  import {
8
8
  HashlineCache,
9
9
  applyHashEdit,
10
10
  resolveConfig
11
- } from "./chunk-DDXOFWTU.js";
11
+ } from "./chunk-X4NVISKE.js";
12
12
 
13
13
  // src/index.ts
14
14
  import { readFileSync as readFileSync2, realpathSync as realpathSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
@@ -172,7 +172,7 @@ function createHashlinePlugin(userConfig) {
172
172
  const out = output;
173
173
  const hashLen = config.hashLength || 0;
174
174
  const prefix = config.prefix;
175
- const { formatFileWithHashes, shouldExclude, getByteLength } = await import("./hashline-GY4XM34F.js");
175
+ const { formatFileWithHashes, shouldExclude, getByteLength } = await import("./hashline-6YDKBNND.js");
176
176
  for (const p of out.parts ?? []) {
177
177
  if (p.type !== "file") continue;
178
178
  if (!p.url || !p.mime?.startsWith("text/")) continue;
package/dist/utils.cjs CHANGED
@@ -147,7 +147,8 @@ function computeLineHash(idx, line, hashLen = 3) {
147
147
  return hash.toString(16).padStart(hashLen, "0");
148
148
  }
149
149
  function formatFileWithHashes(content, hashLen, prefix) {
150
- const lines = content.split("\n");
150
+ const normalized = content.includes("\r\n") ? content.replace(/\r\n/g, "\n") : content;
151
+ const lines = normalized.split("\n");
151
152
  const effectiveLen = hashLen && hashLen >= 3 ? hashLen : getAdaptiveHashLength(lines.length);
152
153
  const effectivePrefix = prefix === void 0 ? DEFAULT_PREFIX : prefix === false ? "" : prefix;
153
154
  const hashes = new Array(lines.length);
@@ -175,7 +176,9 @@ function stripHashes(content, prefix) {
175
176
  hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
176
177
  stripRegexCache.set(escapedPrefix, hashLinePattern);
177
178
  }
178
- return content.split("\n").map((line) => {
179
+ const lineEnding = detectLineEnding(content);
180
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
181
+ const result = normalized.split("\n").map((line) => {
179
182
  const match = line.match(hashLinePattern);
180
183
  if (match) {
181
184
  const patchMarker = match[1] || "";
@@ -183,6 +186,7 @@ function stripHashes(content, prefix) {
183
186
  }
184
187
  return line;
185
188
  }).join("\n");
189
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
186
190
  }
187
191
  function parseHashRef(ref) {
188
192
  const match = ref.match(/^(\d+):([0-9a-f]{2,8})$/);
@@ -248,12 +252,14 @@ function resolveRange(startRef, endRef, content, hashLen) {
248
252
  `Invalid range: start line ${start.line} is after end line ${end.line}`
249
253
  );
250
254
  }
251
- const lines = content.split("\n");
252
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
255
+ const lineEnding = detectLineEnding(content);
256
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
257
+ const lines = normalized.split("\n");
258
+ const startVerify = verifyHash(start.line, start.hash, normalized, hashLen, lines);
253
259
  if (!startVerify.valid) {
254
260
  throw new Error(`Start reference invalid: ${startVerify.message}`);
255
261
  }
256
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
262
+ const endVerify = verifyHash(end.line, end.hash, normalized, hashLen, lines);
257
263
  if (!endVerify.valid) {
258
264
  throw new Error(`End reference invalid: ${endVerify.message}`);
259
265
  }
@@ -262,22 +268,27 @@ function resolveRange(startRef, endRef, content, hashLen) {
262
268
  startLine: start.line,
263
269
  endLine: end.line,
264
270
  lines: rangeLines,
265
- content: rangeLines.join("\n")
271
+ content: rangeLines.join(lineEnding)
266
272
  };
267
273
  }
268
274
  function replaceRange(startRef, endRef, content, replacement, hashLen) {
269
- const range = resolveRange(startRef, endRef, content, hashLen);
270
- const lines = content.split("\n");
275
+ const lineEnding = detectLineEnding(content);
276
+ const normalized = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
277
+ const range = resolveRange(startRef, endRef, normalized, hashLen);
278
+ const lines = normalized.split("\n");
271
279
  const before = lines.slice(0, range.startLine - 1);
272
280
  const after = lines.slice(range.endLine);
273
281
  const replacementLines = replacement.split("\n");
274
- return [...before, ...replacementLines, ...after].join("\n");
282
+ const result = [...before, ...replacementLines, ...after].join("\n");
283
+ return lineEnding === "\r\n" ? result.replace(/\n/g, "\r\n") : result;
275
284
  }
276
285
  function applyHashEdit(input, content, hashLen) {
286
+ const lineEnding = detectLineEnding(content);
287
+ const workContent = lineEnding === "\r\n" ? content.replace(/\r\n/g, "\n") : content;
277
288
  const normalizedStart = normalizeHashRef(input.startRef);
278
289
  const start = parseHashRef(normalizedStart);
279
- const lines = content.split("\n");
280
- const startVerify = verifyHash(start.line, start.hash, content, hashLen, lines);
290
+ const lines = workContent.split("\n");
291
+ const startVerify = verifyHash(start.line, start.hash, workContent, hashLen, lines);
281
292
  if (!startVerify.valid) {
282
293
  throw new Error(`Start reference invalid: ${startVerify.message}`);
283
294
  }
@@ -296,7 +307,7 @@ function applyHashEdit(input, content, hashLen) {
296
307
  operation: input.operation,
297
308
  startLine: start.line,
298
309
  endLine: start.line,
299
- content: next2
310
+ content: lineEnding === "\r\n" ? next2.replace(/\n/g, "\r\n") : next2
300
311
  };
301
312
  }
302
313
  const normalizedEnd = normalizeHashRef(input.endRef ?? input.startRef);
@@ -306,7 +317,7 @@ function applyHashEdit(input, content, hashLen) {
306
317
  `Invalid range: start line ${start.line} is after end line ${end.line}`
307
318
  );
308
319
  }
309
- const endVerify = verifyHash(end.line, end.hash, content, hashLen, lines);
320
+ const endVerify = verifyHash(end.line, end.hash, workContent, hashLen, lines);
310
321
  if (!endVerify.valid) {
311
322
  throw new Error(`End reference invalid: ${endVerify.message}`);
312
323
  }
@@ -322,7 +333,7 @@ function applyHashEdit(input, content, hashLen) {
322
333
  operation: input.operation,
323
334
  startLine: start.line,
324
335
  endLine: end.line,
325
- content: next
336
+ content: lineEnding === "\r\n" ? next.replace(/\n/g, "\r\n") : next
326
337
  };
327
338
  }
328
339
  var HashlineCache = class {
@@ -397,6 +408,9 @@ var textEncoder = new TextEncoder();
397
408
  function getByteLength(content) {
398
409
  return textEncoder.encode(content).length;
399
410
  }
411
+ function detectLineEnding(content) {
412
+ return content.includes("\r\n") ? "\r\n" : "\n";
413
+ }
400
414
  function createHashline(config) {
401
415
  const resolved = resolveConfig(config);
402
416
  const cache = new HashlineCache(resolved.cacheSize);
package/dist/utils.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createFileReadAfterHook,
4
4
  createSystemPromptHook,
5
5
  isFileReadTool
6
- } from "./chunk-JOA7B5LK.js";
6
+ } from "./chunk-MKNRMMMR.js";
7
7
  import {
8
8
  DEFAULT_CONFIG,
9
9
  DEFAULT_EXCLUDE_PATTERNS,
@@ -25,7 +25,7 @@ import {
25
25
  shouldExclude,
26
26
  stripHashes,
27
27
  verifyHash
28
- } from "./chunk-DDXOFWTU.js";
28
+ } from "./chunk-X4NVISKE.js";
29
29
  export {
30
30
  DEFAULT_CONFIG,
31
31
  DEFAULT_EXCLUDE_PATTERNS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-hashline",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Hashline plugin for OpenCode — content-addressable line hashing for precise AI code editing",
5
5
  "main": "dist/opencode-hashline.cjs",
6
6
  "module": "dist/opencode-hashline.js",