opencode-hashline 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-IVZSANZ4.js → chunk-3AJXTHJ3.js} +3 -2
- package/dist/{chunk-VW5NAHEY.js → chunk-DUTTCCXT.js} +35 -9
- package/dist/{hashline-W2FT5QN4.js → hashline-V3FR43UZ.js} +1 -1
- package/dist/index.cjs +74 -16
- package/dist/index.js +43 -12
- package/dist/utils.cjs +37 -10
- package/dist/utils.js +2 -2
- package/package.json +1 -1
|
@@ -111,13 +111,14 @@ function stripHashes(content, prefix) {
|
|
|
111
111
|
const escapedPrefix = effectivePrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
112
112
|
let hashLinePattern = stripRegexCache.get(escapedPrefix);
|
|
113
113
|
if (!hashLinePattern) {
|
|
114
|
-
hashLinePattern = new RegExp(
|
|
114
|
+
hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
|
|
115
115
|
stripRegexCache.set(escapedPrefix, hashLinePattern);
|
|
116
116
|
}
|
|
117
117
|
return content.split("\n").map((line) => {
|
|
118
118
|
const match = line.match(hashLinePattern);
|
|
119
119
|
if (match) {
|
|
120
|
-
|
|
120
|
+
const patchMarker = match[1] || "";
|
|
121
|
+
return patchMarker + line.slice(match[0].length);
|
|
121
122
|
}
|
|
122
123
|
return line;
|
|
123
124
|
}).join("\n");
|
|
@@ -4,13 +4,26 @@ import {
|
|
|
4
4
|
resolveConfig,
|
|
5
5
|
shouldExclude,
|
|
6
6
|
stripHashes
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3AJXTHJ3.js";
|
|
8
8
|
|
|
9
9
|
// src/hooks.ts
|
|
10
10
|
import { appendFileSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
import { homedir } from "os";
|
|
13
13
|
var DEBUG_LOG = join(homedir(), ".config", "opencode", "hashline-debug.log");
|
|
14
|
+
var MAX_PROCESSED_IDS = 1e4;
|
|
15
|
+
function createBoundedSet(maxSize) {
|
|
16
|
+
const set = /* @__PURE__ */ new Set();
|
|
17
|
+
const originalAdd = set.add.bind(set);
|
|
18
|
+
set.add = (value) => {
|
|
19
|
+
if (set.size >= maxSize) {
|
|
20
|
+
const first = set.values().next().value;
|
|
21
|
+
if (first !== void 0) set.delete(first);
|
|
22
|
+
}
|
|
23
|
+
return originalAdd(value);
|
|
24
|
+
};
|
|
25
|
+
return set;
|
|
26
|
+
}
|
|
14
27
|
function debug(...args) {
|
|
15
28
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
16
29
|
`;
|
|
@@ -40,7 +53,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
40
53
|
const resolved = config ?? resolveConfig();
|
|
41
54
|
const hashLen = resolved.hashLength || 0;
|
|
42
55
|
const prefix = resolved.prefix;
|
|
43
|
-
const processedCallIds =
|
|
56
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
44
57
|
return async (input, output) => {
|
|
45
58
|
debug("tool.execute.after:", input.tool, "args:", input.args);
|
|
46
59
|
if (input.callID) {
|
|
@@ -87,7 +100,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
87
100
|
function createFileEditBeforeHook(config) {
|
|
88
101
|
const resolved = config ?? resolveConfig();
|
|
89
102
|
const prefix = resolved.prefix;
|
|
90
|
-
const processedCallIds =
|
|
103
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
91
104
|
return async (input, output) => {
|
|
92
105
|
if (input.callID) {
|
|
93
106
|
if (processedCallIds.has(input.callID)) {
|
|
@@ -101,7 +114,7 @@ function createFileEditBeforeHook(config) {
|
|
|
101
114
|
);
|
|
102
115
|
if (!isFileEdit) return;
|
|
103
116
|
if (!output.args || typeof output.args !== "object") return;
|
|
104
|
-
const contentFields = [
|
|
117
|
+
const contentFields = /* @__PURE__ */ new Set([
|
|
105
118
|
"content",
|
|
106
119
|
"new_content",
|
|
107
120
|
"old_content",
|
|
@@ -111,13 +124,26 @@ function createFileEditBeforeHook(config) {
|
|
|
111
124
|
"text",
|
|
112
125
|
"diff",
|
|
113
126
|
"patch",
|
|
114
|
-
"patchText"
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
"patchText",
|
|
128
|
+
"body"
|
|
129
|
+
]);
|
|
130
|
+
function stripDeep(obj) {
|
|
131
|
+
for (const key of Object.keys(obj)) {
|
|
132
|
+
const val = obj[key];
|
|
133
|
+
if (typeof val === "string" && contentFields.has(key)) {
|
|
134
|
+
obj[key] = stripHashes(val, prefix);
|
|
135
|
+
} else if (Array.isArray(val)) {
|
|
136
|
+
for (const item of val) {
|
|
137
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
138
|
+
stripDeep(item);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
142
|
+
stripDeep(val);
|
|
143
|
+
}
|
|
119
144
|
}
|
|
120
145
|
}
|
|
146
|
+
stripDeep(output.args);
|
|
121
147
|
};
|
|
122
148
|
}
|
|
123
149
|
function createSystemPromptHook(config) {
|
package/dist/index.cjs
CHANGED
|
@@ -123,13 +123,14 @@ function stripHashes(content, prefix) {
|
|
|
123
123
|
const escapedPrefix = effectivePrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
124
124
|
let hashLinePattern = stripRegexCache.get(escapedPrefix);
|
|
125
125
|
if (!hashLinePattern) {
|
|
126
|
-
hashLinePattern = new RegExp(
|
|
126
|
+
hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
|
|
127
127
|
stripRegexCache.set(escapedPrefix, hashLinePattern);
|
|
128
128
|
}
|
|
129
129
|
return content.split("\n").map((line) => {
|
|
130
130
|
const match = line.match(hashLinePattern);
|
|
131
131
|
if (match) {
|
|
132
|
-
|
|
132
|
+
const patchMarker = match[1] || "";
|
|
133
|
+
return patchMarker + line.slice(match[0].length);
|
|
133
134
|
}
|
|
134
135
|
return line;
|
|
135
136
|
}).join("\n");
|
|
@@ -467,6 +468,19 @@ var import_path = require("path");
|
|
|
467
468
|
var import_os = require("os");
|
|
468
469
|
init_hashline();
|
|
469
470
|
var DEBUG_LOG = (0, import_path.join)((0, import_os.homedir)(), ".config", "opencode", "hashline-debug.log");
|
|
471
|
+
var MAX_PROCESSED_IDS = 1e4;
|
|
472
|
+
function createBoundedSet(maxSize) {
|
|
473
|
+
const set = /* @__PURE__ */ new Set();
|
|
474
|
+
const originalAdd = set.add.bind(set);
|
|
475
|
+
set.add = (value) => {
|
|
476
|
+
if (set.size >= maxSize) {
|
|
477
|
+
const first = set.values().next().value;
|
|
478
|
+
if (first !== void 0) set.delete(first);
|
|
479
|
+
}
|
|
480
|
+
return originalAdd(value);
|
|
481
|
+
};
|
|
482
|
+
return set;
|
|
483
|
+
}
|
|
470
484
|
function debug(...args) {
|
|
471
485
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
472
486
|
`;
|
|
@@ -496,7 +510,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
496
510
|
const resolved = config ?? resolveConfig();
|
|
497
511
|
const hashLen = resolved.hashLength || 0;
|
|
498
512
|
const prefix = resolved.prefix;
|
|
499
|
-
const processedCallIds =
|
|
513
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
500
514
|
return async (input, output) => {
|
|
501
515
|
debug("tool.execute.after:", input.tool, "args:", input.args);
|
|
502
516
|
if (input.callID) {
|
|
@@ -543,7 +557,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
543
557
|
function createFileEditBeforeHook(config) {
|
|
544
558
|
const resolved = config ?? resolveConfig();
|
|
545
559
|
const prefix = resolved.prefix;
|
|
546
|
-
const processedCallIds =
|
|
560
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
547
561
|
return async (input, output) => {
|
|
548
562
|
if (input.callID) {
|
|
549
563
|
if (processedCallIds.has(input.callID)) {
|
|
@@ -557,7 +571,7 @@ function createFileEditBeforeHook(config) {
|
|
|
557
571
|
);
|
|
558
572
|
if (!isFileEdit) return;
|
|
559
573
|
if (!output.args || typeof output.args !== "object") return;
|
|
560
|
-
const contentFields = [
|
|
574
|
+
const contentFields = /* @__PURE__ */ new Set([
|
|
561
575
|
"content",
|
|
562
576
|
"new_content",
|
|
563
577
|
"old_content",
|
|
@@ -567,13 +581,26 @@ function createFileEditBeforeHook(config) {
|
|
|
567
581
|
"text",
|
|
568
582
|
"diff",
|
|
569
583
|
"patch",
|
|
570
|
-
"patchText"
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
584
|
+
"patchText",
|
|
585
|
+
"body"
|
|
586
|
+
]);
|
|
587
|
+
function stripDeep(obj) {
|
|
588
|
+
for (const key of Object.keys(obj)) {
|
|
589
|
+
const val = obj[key];
|
|
590
|
+
if (typeof val === "string" && contentFields.has(key)) {
|
|
591
|
+
obj[key] = stripHashes(val, prefix);
|
|
592
|
+
} else if (Array.isArray(val)) {
|
|
593
|
+
for (const item of val) {
|
|
594
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
595
|
+
stripDeep(item);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
599
|
+
stripDeep(val);
|
|
600
|
+
}
|
|
575
601
|
}
|
|
576
602
|
}
|
|
603
|
+
stripDeep(output.args);
|
|
577
604
|
};
|
|
578
605
|
}
|
|
579
606
|
function createSystemPromptHook(config) {
|
|
@@ -661,15 +688,21 @@ function createHashlineEditTool(config, cache) {
|
|
|
661
688
|
async execute(args, context) {
|
|
662
689
|
const { path, operation, startRef, endRef, replacement } = args;
|
|
663
690
|
const absPath = (0, import_path2.isAbsolute)(path) ? path : (0, import_path2.resolve)(context.directory, path);
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
691
|
+
let realAbs;
|
|
692
|
+
try {
|
|
693
|
+
realAbs = (0, import_fs2.realpathSync)(absPath);
|
|
694
|
+
} catch {
|
|
695
|
+
realAbs = (0, import_path2.resolve)(absPath);
|
|
696
|
+
}
|
|
697
|
+
const realWorktree = (0, import_fs2.realpathSync)((0, import_path2.resolve)(context.worktree));
|
|
698
|
+
if (realAbs !== realWorktree && !realAbs.startsWith(realWorktree + import_path2.sep)) {
|
|
667
699
|
throw new Error(`Access denied: "${path}" resolves outside the project directory`);
|
|
668
700
|
}
|
|
701
|
+
const normalizedAbs = (0, import_path2.resolve)(absPath);
|
|
669
702
|
const displayPath = (0, import_path2.relative)(context.worktree, absPath) || path;
|
|
670
703
|
let current;
|
|
671
704
|
try {
|
|
672
|
-
current = (0, import_fs2.readFileSync)(
|
|
705
|
+
current = (0, import_fs2.readFileSync)(realAbs, "utf-8");
|
|
673
706
|
} catch (error) {
|
|
674
707
|
const reason = error instanceof Error ? error.message : String(error);
|
|
675
708
|
throw new Error(`Failed to read "${displayPath}": ${reason}`);
|
|
@@ -696,14 +729,15 @@ function createHashlineEditTool(config, cache) {
|
|
|
696
729
|
throw new Error(`Hashline edit failed for "${displayPath}": ${reason}`);
|
|
697
730
|
}
|
|
698
731
|
try {
|
|
699
|
-
(0, import_fs2.writeFileSync)(
|
|
732
|
+
(0, import_fs2.writeFileSync)(realAbs, nextContent, "utf-8");
|
|
700
733
|
} catch (error) {
|
|
701
734
|
const reason = error instanceof Error ? error.message : String(error);
|
|
702
735
|
throw new Error(`Failed to write "${displayPath}": ${reason}`);
|
|
703
736
|
}
|
|
704
737
|
if (cache) {
|
|
705
|
-
cache.invalidate(
|
|
738
|
+
cache.invalidate(realAbs);
|
|
706
739
|
cache.invalidate(normalizedAbs);
|
|
740
|
+
cache.invalidate(absPath);
|
|
707
741
|
if (path !== absPath) cache.invalidate(path);
|
|
708
742
|
if (displayPath !== absPath) cache.invalidate(displayPath);
|
|
709
743
|
}
|
|
@@ -751,6 +785,7 @@ function loadConfig(projectDir, userConfig) {
|
|
|
751
785
|
function createHashlinePlugin(userConfig) {
|
|
752
786
|
return async (input) => {
|
|
753
787
|
const projectDir = input.directory;
|
|
788
|
+
const worktree = input.worktree;
|
|
754
789
|
const fileConfig = loadConfig(projectDir, userConfig);
|
|
755
790
|
const config = resolveConfig(fileConfig);
|
|
756
791
|
const cache = new HashlineCache(config.cacheSize);
|
|
@@ -761,6 +796,17 @@ function createHashlinePlugin(userConfig) {
|
|
|
761
796
|
`);
|
|
762
797
|
} catch {
|
|
763
798
|
}
|
|
799
|
+
const tempFiles = /* @__PURE__ */ new Set();
|
|
800
|
+
const cleanupTempFiles = () => {
|
|
801
|
+
for (const f of tempFiles) {
|
|
802
|
+
try {
|
|
803
|
+
(0, import_fs3.unlinkSync)(f);
|
|
804
|
+
} catch {
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
tempFiles.clear();
|
|
808
|
+
};
|
|
809
|
+
process.on("exit", cleanupTempFiles);
|
|
764
810
|
return {
|
|
765
811
|
tool: {
|
|
766
812
|
hashline_edit: createHashlineEditTool(config, cache)
|
|
@@ -782,6 +828,17 @@ function createHashlinePlugin(userConfig) {
|
|
|
782
828
|
filePath = (0, import_url.fileURLToPath)(p.url);
|
|
783
829
|
}
|
|
784
830
|
if (!filePath) continue;
|
|
831
|
+
if (worktree) {
|
|
832
|
+
try {
|
|
833
|
+
const realFile = (0, import_fs3.realpathSync)(filePath);
|
|
834
|
+
const realWorktree = (0, import_fs3.realpathSync)((0, import_path3.resolve)(worktree));
|
|
835
|
+
if (realFile !== realWorktree && !realFile.startsWith(realWorktree + import_path3.sep)) {
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
} catch {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
785
842
|
if (shouldExclude2(filePath, config.exclude)) continue;
|
|
786
843
|
let content;
|
|
787
844
|
try {
|
|
@@ -794,6 +851,7 @@ function createHashlinePlugin(userConfig) {
|
|
|
794
851
|
if (cached) {
|
|
795
852
|
const tmpPath2 = (0, import_path3.join)((0, import_os2.tmpdir)(), `hashline-${p.id}.txt`);
|
|
796
853
|
(0, import_fs3.writeFileSync)(tmpPath2, cached, "utf-8");
|
|
854
|
+
tempFiles.add(tmpPath2);
|
|
797
855
|
p.url = `file://${tmpPath2}`;
|
|
798
856
|
writeLog(debugLog, `[${(/* @__PURE__ */ new Date()).toISOString()}] chat.message annotated (cached): ${filePath}
|
|
799
857
|
`);
|
package/dist/index.js
CHANGED
|
@@ -2,21 +2,21 @@ import {
|
|
|
2
2
|
createFileEditBeforeHook,
|
|
3
3
|
createFileReadAfterHook,
|
|
4
4
|
createSystemPromptHook
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DUTTCCXT.js";
|
|
6
6
|
import {
|
|
7
7
|
HashlineCache,
|
|
8
8
|
applyHashEdit,
|
|
9
9
|
resolveConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3AJXTHJ3.js";
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
14
|
-
import { join } from "path";
|
|
13
|
+
import { readFileSync as readFileSync2, realpathSync as realpathSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
14
|
+
import { join, resolve as resolve2, sep as sep2 } from "path";
|
|
15
15
|
import { homedir, tmpdir } from "os";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
17
|
|
|
18
18
|
// src/hashline-tool.ts
|
|
19
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
19
|
+
import { readFileSync, realpathSync, writeFileSync } from "fs";
|
|
20
20
|
import { isAbsolute, relative, resolve, sep } from "path";
|
|
21
21
|
import { z } from "zod";
|
|
22
22
|
function createHashlineEditTool(config, cache) {
|
|
@@ -32,15 +32,21 @@ function createHashlineEditTool(config, cache) {
|
|
|
32
32
|
async execute(args, context) {
|
|
33
33
|
const { path, operation, startRef, endRef, replacement } = args;
|
|
34
34
|
const absPath = isAbsolute(path) ? path : resolve(context.directory, path);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
let realAbs;
|
|
36
|
+
try {
|
|
37
|
+
realAbs = realpathSync(absPath);
|
|
38
|
+
} catch {
|
|
39
|
+
realAbs = resolve(absPath);
|
|
40
|
+
}
|
|
41
|
+
const realWorktree = realpathSync(resolve(context.worktree));
|
|
42
|
+
if (realAbs !== realWorktree && !realAbs.startsWith(realWorktree + sep)) {
|
|
38
43
|
throw new Error(`Access denied: "${path}" resolves outside the project directory`);
|
|
39
44
|
}
|
|
45
|
+
const normalizedAbs = resolve(absPath);
|
|
40
46
|
const displayPath = relative(context.worktree, absPath) || path;
|
|
41
47
|
let current;
|
|
42
48
|
try {
|
|
43
|
-
current = readFileSync(
|
|
49
|
+
current = readFileSync(realAbs, "utf-8");
|
|
44
50
|
} catch (error) {
|
|
45
51
|
const reason = error instanceof Error ? error.message : String(error);
|
|
46
52
|
throw new Error(`Failed to read "${displayPath}": ${reason}`);
|
|
@@ -67,14 +73,15 @@ function createHashlineEditTool(config, cache) {
|
|
|
67
73
|
throw new Error(`Hashline edit failed for "${displayPath}": ${reason}`);
|
|
68
74
|
}
|
|
69
75
|
try {
|
|
70
|
-
writeFileSync(
|
|
76
|
+
writeFileSync(realAbs, nextContent, "utf-8");
|
|
71
77
|
} catch (error) {
|
|
72
78
|
const reason = error instanceof Error ? error.message : String(error);
|
|
73
79
|
throw new Error(`Failed to write "${displayPath}": ${reason}`);
|
|
74
80
|
}
|
|
75
81
|
if (cache) {
|
|
76
|
-
cache.invalidate(
|
|
82
|
+
cache.invalidate(realAbs);
|
|
77
83
|
cache.invalidate(normalizedAbs);
|
|
84
|
+
cache.invalidate(absPath);
|
|
78
85
|
if (path !== absPath) cache.invalidate(path);
|
|
79
86
|
if (displayPath !== absPath) cache.invalidate(displayPath);
|
|
80
87
|
}
|
|
@@ -122,6 +129,7 @@ function loadConfig(projectDir, userConfig) {
|
|
|
122
129
|
function createHashlinePlugin(userConfig) {
|
|
123
130
|
return async (input) => {
|
|
124
131
|
const projectDir = input.directory;
|
|
132
|
+
const worktree = input.worktree;
|
|
125
133
|
const fileConfig = loadConfig(projectDir, userConfig);
|
|
126
134
|
const config = resolveConfig(fileConfig);
|
|
127
135
|
const cache = new HashlineCache(config.cacheSize);
|
|
@@ -132,6 +140,17 @@ function createHashlinePlugin(userConfig) {
|
|
|
132
140
|
`);
|
|
133
141
|
} catch {
|
|
134
142
|
}
|
|
143
|
+
const tempFiles = /* @__PURE__ */ new Set();
|
|
144
|
+
const cleanupTempFiles = () => {
|
|
145
|
+
for (const f of tempFiles) {
|
|
146
|
+
try {
|
|
147
|
+
unlinkSync(f);
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
tempFiles.clear();
|
|
152
|
+
};
|
|
153
|
+
process.on("exit", cleanupTempFiles);
|
|
135
154
|
return {
|
|
136
155
|
tool: {
|
|
137
156
|
hashline_edit: createHashlineEditTool(config, cache)
|
|
@@ -144,7 +163,7 @@ function createHashlinePlugin(userConfig) {
|
|
|
144
163
|
const out = output;
|
|
145
164
|
const hashLen = config.hashLength || 0;
|
|
146
165
|
const prefix = config.prefix;
|
|
147
|
-
const { formatFileWithHashes, shouldExclude, getByteLength } = await import("./hashline-
|
|
166
|
+
const { formatFileWithHashes, shouldExclude, getByteLength } = await import("./hashline-V3FR43UZ.js");
|
|
148
167
|
for (const p of out.parts ?? []) {
|
|
149
168
|
if (p.type !== "file") continue;
|
|
150
169
|
if (!p.url || !p.mime?.startsWith("text/")) continue;
|
|
@@ -153,6 +172,17 @@ function createHashlinePlugin(userConfig) {
|
|
|
153
172
|
filePath = fileURLToPath(p.url);
|
|
154
173
|
}
|
|
155
174
|
if (!filePath) continue;
|
|
175
|
+
if (worktree) {
|
|
176
|
+
try {
|
|
177
|
+
const realFile = realpathSync2(filePath);
|
|
178
|
+
const realWorktree = realpathSync2(resolve2(worktree));
|
|
179
|
+
if (realFile !== realWorktree && !realFile.startsWith(realWorktree + sep2)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
156
186
|
if (shouldExclude(filePath, config.exclude)) continue;
|
|
157
187
|
let content;
|
|
158
188
|
try {
|
|
@@ -165,6 +195,7 @@ function createHashlinePlugin(userConfig) {
|
|
|
165
195
|
if (cached) {
|
|
166
196
|
const tmpPath2 = join(tmpdir(), `hashline-${p.id}.txt`);
|
|
167
197
|
writeFileSync2(tmpPath2, cached, "utf-8");
|
|
198
|
+
tempFiles.add(tmpPath2);
|
|
168
199
|
p.url = `file://${tmpPath2}`;
|
|
169
200
|
writeLog(debugLog, `[${(/* @__PURE__ */ new Date()).toISOString()}] chat.message annotated (cached): ${filePath}
|
|
170
201
|
`);
|
package/dist/utils.cjs
CHANGED
|
@@ -170,13 +170,14 @@ function stripHashes(content, prefix) {
|
|
|
170
170
|
const escapedPrefix = effectivePrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
171
171
|
let hashLinePattern = stripRegexCache.get(escapedPrefix);
|
|
172
172
|
if (!hashLinePattern) {
|
|
173
|
-
hashLinePattern = new RegExp(
|
|
173
|
+
hashLinePattern = new RegExp(`^([+ \\-])?${escapedPrefix}\\d+:[0-9a-f]{2,8}\\|`);
|
|
174
174
|
stripRegexCache.set(escapedPrefix, hashLinePattern);
|
|
175
175
|
}
|
|
176
176
|
return content.split("\n").map((line) => {
|
|
177
177
|
const match = line.match(hashLinePattern);
|
|
178
178
|
if (match) {
|
|
179
|
-
|
|
179
|
+
const patchMarker = match[1] || "";
|
|
180
|
+
return patchMarker + line.slice(match[0].length);
|
|
180
181
|
}
|
|
181
182
|
return line;
|
|
182
183
|
}).join("\n");
|
|
@@ -451,6 +452,19 @@ var import_fs = require("fs");
|
|
|
451
452
|
var import_path = require("path");
|
|
452
453
|
var import_os = require("os");
|
|
453
454
|
var DEBUG_LOG = (0, import_path.join)((0, import_os.homedir)(), ".config", "opencode", "hashline-debug.log");
|
|
455
|
+
var MAX_PROCESSED_IDS = 1e4;
|
|
456
|
+
function createBoundedSet(maxSize) {
|
|
457
|
+
const set = /* @__PURE__ */ new Set();
|
|
458
|
+
const originalAdd = set.add.bind(set);
|
|
459
|
+
set.add = (value) => {
|
|
460
|
+
if (set.size >= maxSize) {
|
|
461
|
+
const first = set.values().next().value;
|
|
462
|
+
if (first !== void 0) set.delete(first);
|
|
463
|
+
}
|
|
464
|
+
return originalAdd(value);
|
|
465
|
+
};
|
|
466
|
+
return set;
|
|
467
|
+
}
|
|
454
468
|
function debug(...args) {
|
|
455
469
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
456
470
|
`;
|
|
@@ -480,7 +494,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
480
494
|
const resolved = config ?? resolveConfig();
|
|
481
495
|
const hashLen = resolved.hashLength || 0;
|
|
482
496
|
const prefix = resolved.prefix;
|
|
483
|
-
const processedCallIds =
|
|
497
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
484
498
|
return async (input, output) => {
|
|
485
499
|
debug("tool.execute.after:", input.tool, "args:", input.args);
|
|
486
500
|
if (input.callID) {
|
|
@@ -527,7 +541,7 @@ function createFileReadAfterHook(cache, config) {
|
|
|
527
541
|
function createFileEditBeforeHook(config) {
|
|
528
542
|
const resolved = config ?? resolveConfig();
|
|
529
543
|
const prefix = resolved.prefix;
|
|
530
|
-
const processedCallIds =
|
|
544
|
+
const processedCallIds = createBoundedSet(MAX_PROCESSED_IDS);
|
|
531
545
|
return async (input, output) => {
|
|
532
546
|
if (input.callID) {
|
|
533
547
|
if (processedCallIds.has(input.callID)) {
|
|
@@ -541,7 +555,7 @@ function createFileEditBeforeHook(config) {
|
|
|
541
555
|
);
|
|
542
556
|
if (!isFileEdit) return;
|
|
543
557
|
if (!output.args || typeof output.args !== "object") return;
|
|
544
|
-
const contentFields = [
|
|
558
|
+
const contentFields = /* @__PURE__ */ new Set([
|
|
545
559
|
"content",
|
|
546
560
|
"new_content",
|
|
547
561
|
"old_content",
|
|
@@ -551,13 +565,26 @@ function createFileEditBeforeHook(config) {
|
|
|
551
565
|
"text",
|
|
552
566
|
"diff",
|
|
553
567
|
"patch",
|
|
554
|
-
"patchText"
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
568
|
+
"patchText",
|
|
569
|
+
"body"
|
|
570
|
+
]);
|
|
571
|
+
function stripDeep(obj) {
|
|
572
|
+
for (const key of Object.keys(obj)) {
|
|
573
|
+
const val = obj[key];
|
|
574
|
+
if (typeof val === "string" && contentFields.has(key)) {
|
|
575
|
+
obj[key] = stripHashes(val, prefix);
|
|
576
|
+
} else if (Array.isArray(val)) {
|
|
577
|
+
for (const item of val) {
|
|
578
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
579
|
+
stripDeep(item);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
583
|
+
stripDeep(val);
|
|
584
|
+
}
|
|
559
585
|
}
|
|
560
586
|
}
|
|
587
|
+
stripDeep(output.args);
|
|
561
588
|
};
|
|
562
589
|
}
|
|
563
590
|
function createSystemPromptHook(config) {
|
package/dist/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createFileReadAfterHook,
|
|
4
4
|
createSystemPromptHook,
|
|
5
5
|
isFileReadTool
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DUTTCCXT.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-
|
|
28
|
+
} from "./chunk-3AJXTHJ3.js";
|
|
29
29
|
export {
|
|
30
30
|
DEFAULT_CONFIG,
|
|
31
31
|
DEFAULT_EXCLUDE_PATTERNS,
|
package/package.json
CHANGED