dsh-code-index 0.1.0 → 0.2.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.
- package/LICENSE +20 -20
- package/README.md +129 -125
- package/README.zh.md +125 -124
- package/cordis.patch.yml +4 -4
- package/dist/index.js +271 -36
- package/dist/index.js.map +1 -1
- package/package.json +13 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,14 @@ import path from "path";
|
|
|
9
9
|
import Parser from "web-tree-sitter";
|
|
10
10
|
var require2 = createRequire(import.meta.url);
|
|
11
11
|
var WASM_DIR = path.dirname(require2.resolve("tree-sitter-wasms/out/tree-sitter-typescript.wasm"));
|
|
12
|
+
var GRAMMAR_NAMES = {
|
|
13
|
+
typescript: "tree-sitter-typescript",
|
|
14
|
+
javascript: "tree-sitter-javascript",
|
|
15
|
+
python: "tree-sitter-python",
|
|
16
|
+
go: "tree-sitter-go",
|
|
17
|
+
rust: "tree-sitter-rust",
|
|
18
|
+
java: "tree-sitter-java"
|
|
19
|
+
};
|
|
12
20
|
var EXT_TO_LANG = {
|
|
13
21
|
".ts": "typescript",
|
|
14
22
|
".tsx": "typescript",
|
|
@@ -17,7 +25,12 @@ var EXT_TO_LANG = {
|
|
|
17
25
|
".js": "javascript",
|
|
18
26
|
".jsx": "javascript",
|
|
19
27
|
".mjs": "javascript",
|
|
20
|
-
".cjs": "javascript"
|
|
28
|
+
".cjs": "javascript",
|
|
29
|
+
".py": "python",
|
|
30
|
+
".pyi": "python",
|
|
31
|
+
".go": "go",
|
|
32
|
+
".rs": "rust",
|
|
33
|
+
".java": "java"
|
|
21
34
|
};
|
|
22
35
|
function languageForFile(filePath) {
|
|
23
36
|
const ext = path.extname(filePath).toLowerCase();
|
|
@@ -43,6 +56,29 @@ var QUERIES = {
|
|
|
43
56
|
(method_definition) @method
|
|
44
57
|
(class_declaration) @class
|
|
45
58
|
(variable_declarator) @variable
|
|
59
|
+
`,
|
|
60
|
+
python: `
|
|
61
|
+
(function_definition) @function
|
|
62
|
+
(class_definition) @class
|
|
63
|
+
`,
|
|
64
|
+
go: `
|
|
65
|
+
(function_declaration) @function
|
|
66
|
+
(method_declaration) @method
|
|
67
|
+
(type_spec) @type
|
|
68
|
+
`,
|
|
69
|
+
rust: `
|
|
70
|
+
(function_item) @function
|
|
71
|
+
(function_signature_item) @method
|
|
72
|
+
(struct_item) @class
|
|
73
|
+
(enum_item) @enum
|
|
74
|
+
(trait_item) @interface
|
|
75
|
+
`,
|
|
76
|
+
java: `
|
|
77
|
+
(class_declaration) @class
|
|
78
|
+
(interface_declaration) @interface
|
|
79
|
+
(enum_declaration) @enum
|
|
80
|
+
(method_declaration) @method
|
|
81
|
+
(constructor_declaration) @method
|
|
46
82
|
`
|
|
47
83
|
};
|
|
48
84
|
var CAPTURE_KINDS = {
|
|
@@ -55,6 +91,29 @@ var CAPTURE_KINDS = {
|
|
|
55
91
|
variable: { kind: "variable" },
|
|
56
92
|
field: { kind: "field" }
|
|
57
93
|
};
|
|
94
|
+
var IMPORT_QUERIES = {
|
|
95
|
+
typescript: `
|
|
96
|
+
(import_statement) @import
|
|
97
|
+
(export_statement) @reexport
|
|
98
|
+
`,
|
|
99
|
+
javascript: `
|
|
100
|
+
(import_statement) @import
|
|
101
|
+
(export_statement) @reexport
|
|
102
|
+
`,
|
|
103
|
+
python: `
|
|
104
|
+
(import_from_statement) @import
|
|
105
|
+
(import_statement) @import
|
|
106
|
+
`,
|
|
107
|
+
go: `
|
|
108
|
+
(import_spec) @import
|
|
109
|
+
`,
|
|
110
|
+
rust: `
|
|
111
|
+
(use_declaration) @use
|
|
112
|
+
`,
|
|
113
|
+
java: `
|
|
114
|
+
(import_declaration) @import
|
|
115
|
+
`
|
|
116
|
+
};
|
|
58
117
|
var parserPromise = null;
|
|
59
118
|
async function getParser() {
|
|
60
119
|
if (!parserPromise) {
|
|
@@ -71,82 +130,184 @@ var languageCache = /* @__PURE__ */ new Map();
|
|
|
71
130
|
function getLanguage(id) {
|
|
72
131
|
let entry = languageCache.get(id);
|
|
73
132
|
if (!entry) {
|
|
74
|
-
entry = getParser().then(async (
|
|
75
|
-
const
|
|
76
|
-
const grammarPath = path.join(WASM_DIR, `${grammarName}.wasm`);
|
|
133
|
+
entry = getParser().then(async () => {
|
|
134
|
+
const grammarPath = path.join(WASM_DIR, `${GRAMMAR_NAMES[id]}.wasm`);
|
|
77
135
|
const bytes = await readFile(grammarPath);
|
|
78
136
|
const lang = await Parser.Language.load(bytes);
|
|
79
137
|
const q = lang.query(QUERIES[id]);
|
|
80
138
|
q.delete();
|
|
139
|
+
const iq = lang.query(IMPORT_QUERIES[id]);
|
|
140
|
+
iq.delete();
|
|
81
141
|
return lang;
|
|
82
142
|
});
|
|
83
143
|
languageCache.set(id, entry);
|
|
84
144
|
}
|
|
85
145
|
return entry;
|
|
86
146
|
}
|
|
87
|
-
async function
|
|
147
|
+
async function extractAll(code, id) {
|
|
88
148
|
const lang = await getLanguage(id);
|
|
89
149
|
const parser = await getParser();
|
|
90
150
|
parser.setLanguage(lang);
|
|
91
151
|
const tree = parser.parse(code);
|
|
92
152
|
try {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
153
|
+
const symbols = [];
|
|
154
|
+
const symbolQuery = lang.query(QUERIES[id]);
|
|
95
155
|
try {
|
|
96
|
-
const captures =
|
|
156
|
+
const captures = symbolQuery.captures(tree.rootNode);
|
|
97
157
|
for (const cap of captures) {
|
|
98
158
|
const def = CAPTURE_KINDS[cap.name];
|
|
99
159
|
if (!def) continue;
|
|
100
160
|
const node = cap.node;
|
|
161
|
+
if (def.kind === "variable" && !isModuleLevelVariable(node)) continue;
|
|
101
162
|
const name2 = nameOf(node);
|
|
102
163
|
if (!name2) continue;
|
|
103
|
-
|
|
164
|
+
symbols.push({
|
|
104
165
|
name: name2,
|
|
105
|
-
kind: def.kind,
|
|
166
|
+
kind: kindFor(id, node, def.kind),
|
|
106
167
|
file: "",
|
|
107
168
|
// set by the caller (extractor is file-agnostic)
|
|
108
169
|
line: node.startPosition.row + 1,
|
|
109
170
|
endLine: node.endPosition.row + 1,
|
|
110
|
-
exported: isExported(node),
|
|
171
|
+
exported: isExported(id, node),
|
|
111
172
|
signature: signatureFor(node)
|
|
112
173
|
});
|
|
113
174
|
}
|
|
114
175
|
} finally {
|
|
115
|
-
|
|
176
|
+
symbolQuery.delete();
|
|
116
177
|
}
|
|
117
|
-
|
|
118
|
-
|
|
178
|
+
const imports = [];
|
|
179
|
+
const importQuery = lang.query(IMPORT_QUERIES[id]);
|
|
180
|
+
try {
|
|
181
|
+
for (const cap of importQuery.captures(tree.rootNode)) {
|
|
182
|
+
const spec = specifierOf(id, cap.node);
|
|
183
|
+
if (spec) imports.push(spec);
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
importQuery.delete();
|
|
187
|
+
}
|
|
188
|
+
symbols.sort((a, b) => a.line - b.line);
|
|
189
|
+
return { symbols, imports };
|
|
119
190
|
} finally {
|
|
120
191
|
tree.delete();
|
|
121
192
|
}
|
|
122
193
|
}
|
|
194
|
+
async function extractSymbols(code, id) {
|
|
195
|
+
return (await extractAll(code, id)).symbols;
|
|
196
|
+
}
|
|
123
197
|
function nameOf(node) {
|
|
124
198
|
const field = node.childForFieldName?.("name");
|
|
125
199
|
if (field) return field.text.trim();
|
|
126
200
|
return "";
|
|
127
201
|
}
|
|
202
|
+
function specifierOf(id, node) {
|
|
203
|
+
switch (node.type) {
|
|
204
|
+
case "import_statement":
|
|
205
|
+
if (id === "python") return dottedToPath(node.childForFieldName("name")?.text);
|
|
206
|
+
return stripQuotes(node.childForFieldName("source")?.text);
|
|
207
|
+
case "export_statement":
|
|
208
|
+
return stripQuotes(node.childForFieldName("source")?.text);
|
|
209
|
+
case "import_from_statement": {
|
|
210
|
+
const raw = node.childForFieldName("module_name")?.text;
|
|
211
|
+
if (raw == null) return null;
|
|
212
|
+
return raw.startsWith(".") ? pythonRelative(raw) : dottedToPath(raw);
|
|
213
|
+
}
|
|
214
|
+
case "import_spec":
|
|
215
|
+
return stripQuotes(node.childForFieldName("path")?.text);
|
|
216
|
+
case "use_declaration":
|
|
217
|
+
return rustUsePath(node.childForFieldName("argument")?.text);
|
|
218
|
+
case "import_declaration":
|
|
219
|
+
return dottedToPath(node.namedChildren[0]?.text);
|
|
220
|
+
default:
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function rustUsePath(text) {
|
|
225
|
+
if (!text) return null;
|
|
226
|
+
const base = text.split("{")[0].trim();
|
|
227
|
+
if (!base) return null;
|
|
228
|
+
const parts = base.split("::").filter(Boolean);
|
|
229
|
+
if (parts[0] === "crate") parts.shift();
|
|
230
|
+
return parts.map((p) => p === "super" ? ".." : p === "self" ? "." : p).join("/");
|
|
231
|
+
}
|
|
232
|
+
function stripQuotes(text) {
|
|
233
|
+
if (text == null || text.length < 2) return null;
|
|
234
|
+
const first = text[0];
|
|
235
|
+
const last = text[text.length - 1];
|
|
236
|
+
if (first === "'" && last === "'" || first === '"' && last === '"') {
|
|
237
|
+
return text.slice(1, -1);
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function dottedToPath(text) {
|
|
242
|
+
if (!text) return null;
|
|
243
|
+
return text.trim().replace(/\./g, "/");
|
|
244
|
+
}
|
|
245
|
+
function pythonRelative(raw) {
|
|
246
|
+
const dots = raw.match(/^\.+/)?.[0].length ?? 0;
|
|
247
|
+
const rest = raw.slice(dots).replace(/\./g, "/");
|
|
248
|
+
const prefix = dots === 1 ? "./" : "../".repeat(dots - 1);
|
|
249
|
+
return prefix + rest;
|
|
250
|
+
}
|
|
128
251
|
function signatureFor(node) {
|
|
129
252
|
const name2 = nameOf(node);
|
|
130
|
-
const params = node.namedChildren.find(
|
|
131
|
-
(c) => c.type === "formal_parameters" || c.type === "method_parameters"
|
|
253
|
+
const params = node.childForFieldName?.("parameters") ?? node.namedChildren.find(
|
|
254
|
+
(c) => c.type === "formal_parameters" || c.type === "method_parameters" || c.type === "parameters" || c.type === "parameter_list"
|
|
132
255
|
);
|
|
133
256
|
if (params) {
|
|
134
|
-
return `${name2}${params.text}`;
|
|
257
|
+
return `${name2}${collapseSpace(params.text)}`;
|
|
135
258
|
}
|
|
136
259
|
const first = node.namedChildren[0];
|
|
137
|
-
return first ? first.text
|
|
260
|
+
return first ? collapseSpace(first.text) : name2;
|
|
138
261
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
262
|
+
function collapseSpace(text) {
|
|
263
|
+
return text.replace(/\s+/g, " ").replace(/\( /g, "(").replace(/ \)/g, ")").replace(/ ,/g, ",").replace(/,\)/g, ")").trim();
|
|
264
|
+
}
|
|
265
|
+
function isModuleLevelVariable(node) {
|
|
266
|
+
const declaration = node.parent;
|
|
267
|
+
const container = declaration?.parent;
|
|
268
|
+
return container?.type === "program" || container?.type === "export_statement";
|
|
269
|
+
}
|
|
270
|
+
function isExported(id, node) {
|
|
271
|
+
if (id === "python") {
|
|
272
|
+
return node.parent?.type === "module";
|
|
273
|
+
}
|
|
274
|
+
if (id === "go") {
|
|
275
|
+
const name2 = nameOf(node);
|
|
276
|
+
return !!name2 && /^[A-Z]/.test(name2);
|
|
277
|
+
}
|
|
278
|
+
if (id === "rust") {
|
|
279
|
+
return node.namedChildren.some(
|
|
280
|
+
(c) => c.type === "visibility_modifier" && c.text === "pub"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (id === "java") {
|
|
284
|
+
if (node.parent?.type === "interface_body") return true;
|
|
285
|
+
return node.namedChildren.some((c) => c.type === "modifiers" && /\bpublic\b/.test(c.text));
|
|
286
|
+
}
|
|
287
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
143
288
|
if (parent.type === "export_statement") return true;
|
|
144
|
-
if (parent.type === "statement_block") return false;
|
|
145
|
-
parent
|
|
146
|
-
depth++;
|
|
289
|
+
if (parent.type === "statement_block" || parent.type === "class_body") return false;
|
|
290
|
+
if (parent.type === "program") return false;
|
|
147
291
|
}
|
|
148
292
|
return false;
|
|
149
293
|
}
|
|
294
|
+
function kindFor(id, node, kind) {
|
|
295
|
+
if (id === "python" && kind === "function") {
|
|
296
|
+
const inClassBody = node.parent?.type === "block" && node.parent?.parent?.type === "class_definition";
|
|
297
|
+
if (inClassBody) return "method";
|
|
298
|
+
}
|
|
299
|
+
if (id === "go" && node.type === "type_spec") {
|
|
300
|
+
const type = node.childForFieldName("type")?.type;
|
|
301
|
+
if (type === "struct_type") return "class";
|
|
302
|
+
if (type === "interface_type") return "interface";
|
|
303
|
+
return "type";
|
|
304
|
+
}
|
|
305
|
+
if (id === "rust" && node.type === "function_item") {
|
|
306
|
+
const inImpl = node.parent?.type === "declaration_list" && node.parent?.parent?.type === "impl_item";
|
|
307
|
+
if (inImpl) return "method";
|
|
308
|
+
}
|
|
309
|
+
return kind;
|
|
310
|
+
}
|
|
150
311
|
async function parseFileToSymbols(filePath, repoRoot, code) {
|
|
151
312
|
const lang = languageForFile(filePath);
|
|
152
313
|
if (!lang) return [];
|
|
@@ -183,7 +344,12 @@ var SUPPORTED_EXTS = /* @__PURE__ */ new Set([
|
|
|
183
344
|
".js",
|
|
184
345
|
".jsx",
|
|
185
346
|
".mjs",
|
|
186
|
-
".cjs"
|
|
347
|
+
".cjs",
|
|
348
|
+
".py",
|
|
349
|
+
".pyi",
|
|
350
|
+
".go",
|
|
351
|
+
".rs",
|
|
352
|
+
".java"
|
|
187
353
|
]);
|
|
188
354
|
async function scanRepo(root, options = {}) {
|
|
189
355
|
const excluded = /* @__PURE__ */ new Set([...DEFAULT_EXCLUDED_DIRS, ...options.excludeDirs ?? []]);
|
|
@@ -240,11 +406,19 @@ async function loadIndex(cachePath) {
|
|
|
240
406
|
const raw = await readFile2(cachePath, "utf8");
|
|
241
407
|
const parsed = JSON.parse(raw);
|
|
242
408
|
if (!parsed || typeof parsed.root !== "string" || !Array.isArray(parsed.files)) return null;
|
|
243
|
-
return parsed;
|
|
409
|
+
return healSymbolFiles(parsed);
|
|
244
410
|
} catch {
|
|
245
411
|
return null;
|
|
246
412
|
}
|
|
247
413
|
}
|
|
414
|
+
function healSymbolFiles(index) {
|
|
415
|
+
for (const file of index.files) {
|
|
416
|
+
for (const symbol of file.symbols) {
|
|
417
|
+
if (!symbol.file) symbol.file = file.path;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return index;
|
|
421
|
+
}
|
|
248
422
|
async function saveIndex(cachePath, index) {
|
|
249
423
|
await mkdir(path3.dirname(cachePath), { recursive: true });
|
|
250
424
|
const temporaryPath = `${cachePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
@@ -283,12 +457,13 @@ async function buildIndex(root, options = {}, previous = null) {
|
|
|
283
457
|
} catch {
|
|
284
458
|
return null;
|
|
285
459
|
}
|
|
286
|
-
const symbols = await
|
|
460
|
+
const { symbols, imports } = await extractAll(code, lang);
|
|
287
461
|
return {
|
|
288
462
|
path: f.rel,
|
|
289
463
|
lang,
|
|
290
464
|
mtimeMs: f.mtimeMs,
|
|
291
|
-
symbols: symbols.map((s) => ({ ...s, file: f.rel }))
|
|
465
|
+
symbols: symbols.map((s) => ({ ...s, file: f.rel })),
|
|
466
|
+
imports
|
|
292
467
|
};
|
|
293
468
|
})
|
|
294
469
|
);
|
|
@@ -367,8 +542,17 @@ function matchScore(name2, query) {
|
|
|
367
542
|
if (n === q) return 1;
|
|
368
543
|
if (n.startsWith(q)) return 0.8;
|
|
369
544
|
if (n.includes(q)) return 0.5;
|
|
545
|
+
if (q.length >= 3 && isSubsequence(q, n)) return 0.3;
|
|
370
546
|
return 0;
|
|
371
547
|
}
|
|
548
|
+
function isSubsequence(query, name2) {
|
|
549
|
+
let i = 0;
|
|
550
|
+
for (const ch of name2) {
|
|
551
|
+
if (ch === query[i]) i++;
|
|
552
|
+
if (i === query.length) return true;
|
|
553
|
+
}
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
372
556
|
function searchSymbols(index, filter, limit = 50) {
|
|
373
557
|
const q = (filter.query ?? "").trim();
|
|
374
558
|
const filePat = filter.file?.trim().toLowerCase();
|
|
@@ -397,6 +581,7 @@ function renderHit(hit) {
|
|
|
397
581
|
}
|
|
398
582
|
|
|
399
583
|
// src/repomap.ts
|
|
584
|
+
import path5 from "path";
|
|
400
585
|
var KIND_WEIGHT = {
|
|
401
586
|
class: 1,
|
|
402
587
|
interface: 1,
|
|
@@ -409,18 +594,25 @@ var KIND_WEIGHT = {
|
|
|
409
594
|
import: 0.05,
|
|
410
595
|
module: 0.3
|
|
411
596
|
};
|
|
597
|
+
var TEST_PATH_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx?$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
412
598
|
function scoreFile(file) {
|
|
413
599
|
let score = 0;
|
|
414
600
|
for (const sym of file.symbols) {
|
|
415
601
|
score += KIND_WEIGHT[sym.kind] ?? 0.3;
|
|
416
602
|
if (sym.exported) score += 0.3;
|
|
417
603
|
}
|
|
418
|
-
|
|
604
|
+
score /= 1 + file.symbols.length * 0.04;
|
|
605
|
+
if (TEST_PATH_RE.test(file.path)) score *= 0.2;
|
|
606
|
+
return score;
|
|
419
607
|
}
|
|
420
608
|
function rankRepoMap(index, options = {}) {
|
|
421
609
|
const topFiles = options.topFiles ?? 24;
|
|
422
610
|
const perFile = options.symbolsPerFile ?? 18;
|
|
423
|
-
const
|
|
611
|
+
const refs = countReferences(index.files);
|
|
612
|
+
const ranked = index.files.filter((f) => f.symbols.length > 0).map((f) => ({
|
|
613
|
+
file: f,
|
|
614
|
+
score: scoreFile(f) + REF_WEIGHT * (refs.get(f.path) ?? 0)
|
|
615
|
+
})).sort((a, b) => b.score - a.score || a.file.path.localeCompare(b.file.path)).slice(0, topFiles);
|
|
424
616
|
return ranked.map(({ file, score }) => ({
|
|
425
617
|
path: file.path,
|
|
426
618
|
score,
|
|
@@ -432,6 +624,45 @@ function rankRepoMap(index, options = {}) {
|
|
|
432
624
|
}))
|
|
433
625
|
}));
|
|
434
626
|
}
|
|
627
|
+
var REF_WEIGHT = 0.5;
|
|
628
|
+
function countReferences(files) {
|
|
629
|
+
const fileSet = new Set(files.map((f) => f.path));
|
|
630
|
+
const counts = /* @__PURE__ */ new Map();
|
|
631
|
+
for (const file of files) {
|
|
632
|
+
const targets = /* @__PURE__ */ new Set();
|
|
633
|
+
for (const spec of file.imports ?? []) {
|
|
634
|
+
const target = resolveImport(spec, file.path, fileSet);
|
|
635
|
+
if (target && target !== file.path) targets.add(target);
|
|
636
|
+
}
|
|
637
|
+
for (const target of targets) {
|
|
638
|
+
counts.set(target, (counts.get(target) ?? 0) + 1);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return counts;
|
|
642
|
+
}
|
|
643
|
+
function resolveImport(spec, fromPath, fileSet) {
|
|
644
|
+
if (!spec) return null;
|
|
645
|
+
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
646
|
+
const base = path5.posix.normalize(path5.posix.join(path5.posix.dirname(fromPath), spec));
|
|
647
|
+
return candidateFor(base, fileSet);
|
|
648
|
+
}
|
|
649
|
+
const segments = spec.split("/").filter(Boolean);
|
|
650
|
+
for (let skip = 0; skip < segments.length; skip++) {
|
|
651
|
+
const hit = candidateFor(segments.slice(skip).join("/"), fileSet);
|
|
652
|
+
if (hit) return hit;
|
|
653
|
+
}
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
function candidateFor(base, fileSet) {
|
|
657
|
+
for (const ext of SUPPORTED_EXTS) {
|
|
658
|
+
if (fileSet.has(`${base}${ext}`)) return `${base}${ext}`;
|
|
659
|
+
}
|
|
660
|
+
for (const ext of SUPPORTED_EXTS) {
|
|
661
|
+
if (fileSet.has(`${base}/index${ext}`)) return `${base}/index${ext}`;
|
|
662
|
+
if (fileSet.has(`${base}/__init__${ext}`)) return `${base}/__init__${ext}`;
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
435
666
|
function renderRepoMap(entries, options = {}) {
|
|
436
667
|
const maxChars = options.maxChars ?? 3200;
|
|
437
668
|
const lines = ["# repo map"];
|
|
@@ -454,13 +685,14 @@ function renderRepoMap(entries, options = {}) {
|
|
|
454
685
|
|
|
455
686
|
// src/tools.ts
|
|
456
687
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
457
|
-
import
|
|
688
|
+
import path6 from "path";
|
|
458
689
|
|
|
459
690
|
// src/config.ts
|
|
460
691
|
var DEFAULTS = {
|
|
461
692
|
excludeDirs: [],
|
|
462
693
|
mapTopFiles: 24,
|
|
463
694
|
mapMaxChars: 3200,
|
|
695
|
+
mapTtlMs: 6e4,
|
|
464
696
|
autoInject: true
|
|
465
697
|
};
|
|
466
698
|
var state = { current: { ...DEFAULTS } };
|
|
@@ -476,6 +708,9 @@ function applyConfig(partial) {
|
|
|
476
708
|
if (!Number.isFinite(state.current.mapMaxChars) || state.current.mapMaxChars < 200) {
|
|
477
709
|
state.current.mapMaxChars = DEFAULTS.mapMaxChars;
|
|
478
710
|
}
|
|
711
|
+
if (!Number.isFinite(state.current.mapTtlMs) || state.current.mapTtlMs < 1e3) {
|
|
712
|
+
state.current.mapTtlMs = DEFAULTS.mapTtlMs;
|
|
713
|
+
}
|
|
479
714
|
}
|
|
480
715
|
function getConfig() {
|
|
481
716
|
return state.current;
|
|
@@ -507,7 +742,7 @@ function createIndexCache(load, ttlMs = 6e4, now = Date.now) {
|
|
|
507
742
|
}
|
|
508
743
|
return {
|
|
509
744
|
get(root, force = false) {
|
|
510
|
-
const resolvedRoot =
|
|
745
|
+
const resolvedRoot = path6.resolve(root);
|
|
511
746
|
const key = cacheKeyForRoot(resolvedRoot);
|
|
512
747
|
const running = inFlight.get(key);
|
|
513
748
|
if (running) {
|
|
@@ -544,7 +779,7 @@ async function resolveRoot(arg, exec) {
|
|
|
544
779
|
const cwd = exec.agent?.session?.header?.cwd;
|
|
545
780
|
const base = arg ?? cwd ?? process.cwd();
|
|
546
781
|
const root = await findRepoRoot(base);
|
|
547
|
-
if (!root) throw new Error(`no git repository found from ${
|
|
782
|
+
if (!root) throw new Error(`no git repository found from ${path6.resolve(base)}`);
|
|
548
783
|
return root;
|
|
549
784
|
}
|
|
550
785
|
var tools = [
|
|
@@ -698,7 +933,6 @@ var tools = [
|
|
|
698
933
|
// src/index.ts
|
|
699
934
|
var name = "dsh-code-index";
|
|
700
935
|
var inject = ["tools", "systemPrompt"];
|
|
701
|
-
var MAP_TTL_MS = 6e4;
|
|
702
936
|
function apply(ctx, pluginConfig) {
|
|
703
937
|
applyConfig(pluginConfig);
|
|
704
938
|
invalidateIndexCache();
|
|
@@ -744,7 +978,7 @@ function apply(ctx, pluginConfig) {
|
|
|
744
978
|
// before tool guidance (100–199), after persona (0)
|
|
745
979
|
text: () => {
|
|
746
980
|
const now = Date.now();
|
|
747
|
-
if (cached && now - cached.at <
|
|
981
|
+
if (cached && now - cached.at < getConfig().mapTtlMs) return cached.text;
|
|
748
982
|
void warmMap();
|
|
749
983
|
return cached?.text ?? "";
|
|
750
984
|
}
|
|
@@ -772,6 +1006,7 @@ export {
|
|
|
772
1006
|
buildIndex,
|
|
773
1007
|
buildIndexWithCache,
|
|
774
1008
|
defaultCachePath,
|
|
1009
|
+
extractAll,
|
|
775
1010
|
extractSymbols,
|
|
776
1011
|
findRepoRoot,
|
|
777
1012
|
inject,
|