@wrongstack/tools 0.7.5 → 0.7.7
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/audit.js +3 -3
- package/dist/audit.js.map +1 -1
- package/dist/bash.js +7 -6
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +2070 -133
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.d.ts +120 -0
- package/dist/codebase-index/index.js +1974 -0
- package/dist/codebase-index/index.js.map +1 -0
- package/dist/codebase-stats-tool-BLhQmPNc.d.ts +158 -0
- package/dist/diff.js +4 -4
- package/dist/document.js +2 -2
- package/dist/edit.js +2 -2
- package/dist/exec.js +5 -5
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js +4 -3
- package/dist/fetch.js.map +1 -1
- package/dist/format.js +3 -3
- package/dist/format.js.map +1 -1
- package/dist/git.js +3 -3
- package/dist/glob.js +2 -2
- package/dist/grep.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2039 -102
- package/dist/index.js.map +1 -1
- package/dist/install.js +3 -3
- package/dist/install.js.map +1 -1
- package/dist/json.js +1 -1
- package/dist/lint.js +3 -3
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +5 -5
- package/dist/logs.js.map +1 -1
- package/dist/outdated.js +3 -3
- package/dist/pack.js +2070 -133
- package/dist/pack.js.map +1 -1
- package/dist/patch.js +4 -4
- package/dist/process-registry.js +1 -1
- package/dist/read.js +2 -2
- package/dist/replace.js +4 -4
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js +2 -2
- package/dist/test.js +3 -3
- package/dist/test.js.map +1 -1
- package/dist/tree.js +2 -2
- package/dist/typecheck.js +3 -3
- package/dist/typecheck.js.map +1 -1
- package/dist/write.js +2 -2
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import * as fs4 from 'fs/promises';
|
|
2
|
-
import { stat } from 'fs/promises';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { dirname } from 'path';
|
|
1
|
+
import * as fs4 from 'node:fs/promises';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
5
|
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, stripAnsi, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan } from '@wrongstack/core';
|
|
6
|
-
import { spawn } from 'child_process';
|
|
7
|
-
import * as os from 'os';
|
|
8
|
-
import * as dns from 'dns/promises';
|
|
9
|
-
import * as net from 'net';
|
|
10
|
-
import
|
|
6
|
+
import { spawn, execSync, spawnSync } from 'node:child_process';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
import * as dns from 'node:dns/promises';
|
|
9
|
+
import * as net from 'node:net';
|
|
10
|
+
import * as fs13 from 'node:fs';
|
|
11
|
+
import { statSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
12
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
13
|
+
import * as ts from 'typescript';
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
|
+
}) : x)(function(x) {
|
|
18
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
|
+
});
|
|
13
21
|
function resolvePath(input, ctx) {
|
|
14
22
|
return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
|
|
15
23
|
}
|
|
@@ -63,17 +71,17 @@ var readTool = {
|
|
|
63
71
|
async execute(input, ctx) {
|
|
64
72
|
if (!input?.path) throw new Error("read: path is required");
|
|
65
73
|
const absPath = safeResolve(input.path, ctx);
|
|
66
|
-
let
|
|
74
|
+
let stat11;
|
|
67
75
|
try {
|
|
68
|
-
|
|
76
|
+
stat11 = await fs4.stat(absPath);
|
|
69
77
|
} catch (err) {
|
|
70
78
|
const code = err.code;
|
|
71
79
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
72
80
|
throw new Error(`read: failed to stat "${input.path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
73
81
|
}
|
|
74
|
-
if (!
|
|
75
|
-
if (
|
|
76
|
-
throw new Error(`read: file too large (${
|
|
82
|
+
if (!stat11.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
83
|
+
if (stat11.size > MAX_BYTES) {
|
|
84
|
+
throw new Error(`read: file too large (${stat11.size} bytes, limit ${MAX_BYTES})`);
|
|
77
85
|
}
|
|
78
86
|
const buf = await fs4.readFile(absPath);
|
|
79
87
|
if (isBinaryBuffer(buf)) {
|
|
@@ -85,14 +93,14 @@ var readTool = {
|
|
|
85
93
|
const offset = Math.max(1, input.offset ?? 1);
|
|
86
94
|
const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
|
|
87
95
|
if (limit === 0) {
|
|
88
|
-
ctx.recordRead(absPath,
|
|
96
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
89
97
|
return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
|
|
90
98
|
}
|
|
91
99
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
92
100
|
const truncated = offset - 1 + slice.length < total;
|
|
93
101
|
const width = String(offset + slice.length - 1).length;
|
|
94
102
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
95
|
-
ctx.recordRead(absPath,
|
|
103
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
96
104
|
return {
|
|
97
105
|
text: numbered,
|
|
98
106
|
total_lines: total,
|
|
@@ -124,12 +132,12 @@ var writeTool = {
|
|
|
124
132
|
let existed = false;
|
|
125
133
|
let prev = "";
|
|
126
134
|
try {
|
|
127
|
-
const
|
|
128
|
-
existed =
|
|
135
|
+
const stat12 = await fs4.stat(absPath);
|
|
136
|
+
existed = stat12.isFile();
|
|
129
137
|
if (existed) {
|
|
130
138
|
if (!ctx.hasRead(absPath)) {
|
|
131
139
|
prev = await fs4.readFile(absPath, "utf8");
|
|
132
|
-
ctx.recordRead(absPath,
|
|
140
|
+
ctx.recordRead(absPath, stat12.mtimeMs);
|
|
133
141
|
} else {
|
|
134
142
|
prev = await fs4.readFile(absPath, "utf8");
|
|
135
143
|
}
|
|
@@ -142,8 +150,8 @@ var writeTool = {
|
|
|
142
150
|
await atomicWrite(absPath, input.content);
|
|
143
151
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
144
152
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
145
|
-
const
|
|
146
|
-
ctx.recordRead(absPath,
|
|
153
|
+
const stat11 = await fs4.stat(absPath);
|
|
154
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
147
155
|
ctx.session.recordFileChange({
|
|
148
156
|
path: absPath,
|
|
149
157
|
action: existed ? "modified" : "created",
|
|
@@ -182,13 +190,13 @@ var editTool = {
|
|
|
182
190
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
183
191
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
184
192
|
const absPath = safeResolve(input.path, ctx);
|
|
185
|
-
const
|
|
193
|
+
const stat11 = await fs4.stat(absPath).catch((err) => {
|
|
186
194
|
if (err.code === "ENOENT") {
|
|
187
195
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
188
196
|
}
|
|
189
197
|
throw err;
|
|
190
198
|
});
|
|
191
|
-
if (!
|
|
199
|
+
if (!stat11.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
|
|
192
200
|
if (!ctx.hasRead(absPath)) {
|
|
193
201
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
194
202
|
}
|
|
@@ -380,8 +388,8 @@ var replaceTool = {
|
|
|
380
388
|
}
|
|
381
389
|
const rel = path.relative(ctx.projectRoot, realPath);
|
|
382
390
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
383
|
-
const
|
|
384
|
-
if (!
|
|
391
|
+
const stat11 = await fs4.stat(realPath).catch(() => null);
|
|
392
|
+
if (!stat11 || !stat11.isFile()) continue;
|
|
385
393
|
let content;
|
|
386
394
|
try {
|
|
387
395
|
const buf = await fs4.readFile(realPath);
|
|
@@ -406,7 +414,7 @@ var replaceTool = {
|
|
|
406
414
|
totalReplacements += count;
|
|
407
415
|
if (!dryRun) {
|
|
408
416
|
const newContent = toStyle(newContentLf, style);
|
|
409
|
-
await atomicWrite(realPath, newContent, { mode:
|
|
417
|
+
await atomicWrite(realPath, newContent, { mode: stat11.mode & 511 });
|
|
410
418
|
}
|
|
411
419
|
const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
412
420
|
fromFile: absPath,
|
|
@@ -436,15 +444,15 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
|
|
|
436
444
|
const resolved = [];
|
|
437
445
|
for (const p of parts) {
|
|
438
446
|
const absPath = safeResolve(p, ctx);
|
|
439
|
-
const
|
|
440
|
-
if (
|
|
447
|
+
const stat11 = await fs4.stat(absPath).catch(() => null);
|
|
448
|
+
if (stat11?.isFile()) {
|
|
441
449
|
resolved.push(absPath);
|
|
442
450
|
}
|
|
443
451
|
}
|
|
444
452
|
return resolved;
|
|
445
453
|
}
|
|
446
454
|
async function globFiles(pattern, base, extraGlob) {
|
|
447
|
-
const { spawn: spawn11 } = await import('child_process');
|
|
455
|
+
const { spawn: spawn11 } = await import('node:child_process');
|
|
448
456
|
const rgAvailable = await checkRg();
|
|
449
457
|
if (rgAvailable) {
|
|
450
458
|
try {
|
|
@@ -456,13 +464,13 @@ async function globFiles(pattern, base, extraGlob) {
|
|
|
456
464
|
return await globNative(pattern, base, extraGlob);
|
|
457
465
|
}
|
|
458
466
|
function checkRg() {
|
|
459
|
-
return new Promise((
|
|
467
|
+
return new Promise((resolve6) => {
|
|
460
468
|
try {
|
|
461
469
|
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore" });
|
|
462
|
-
p.on("error", () =>
|
|
463
|
-
p.on("close", (code) =>
|
|
470
|
+
p.on("error", () => resolve6(false));
|
|
471
|
+
p.on("close", (code) => resolve6(code === 0));
|
|
464
472
|
} catch {
|
|
465
|
-
|
|
473
|
+
resolve6(false);
|
|
466
474
|
}
|
|
467
475
|
});
|
|
468
476
|
}
|
|
@@ -474,10 +482,10 @@ function spawnRgFind(pattern, base) {
|
|
|
474
482
|
buf += chunk.toString();
|
|
475
483
|
});
|
|
476
484
|
return {
|
|
477
|
-
promise: new Promise((
|
|
485
|
+
promise: new Promise((resolve6, reject) => {
|
|
478
486
|
child.on("error", reject);
|
|
479
487
|
child.on("close", () => {
|
|
480
|
-
|
|
488
|
+
resolve6(buf.split("\n").filter(Boolean));
|
|
481
489
|
});
|
|
482
490
|
})
|
|
483
491
|
};
|
|
@@ -496,8 +504,8 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
496
504
|
if (DEFAULT_IGNORE.includes(e.name)) continue;
|
|
497
505
|
const full = path.join(dir, e.name);
|
|
498
506
|
try {
|
|
499
|
-
const
|
|
500
|
-
if (
|
|
507
|
+
const stat11 = await fs4.lstat(full);
|
|
508
|
+
if (stat11.isSymbolicLink()) continue;
|
|
501
509
|
} catch {
|
|
502
510
|
continue;
|
|
503
511
|
}
|
|
@@ -646,13 +654,13 @@ var grepTool = {
|
|
|
646
654
|
}
|
|
647
655
|
};
|
|
648
656
|
async function detectRg(signal) {
|
|
649
|
-
return new Promise((
|
|
657
|
+
return new Promise((resolve6) => {
|
|
650
658
|
try {
|
|
651
659
|
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal });
|
|
652
|
-
p.on("error", () =>
|
|
653
|
-
p.on("close", (code) =>
|
|
660
|
+
p.on("error", () => resolve6(false));
|
|
661
|
+
p.on("close", (code) => resolve6(code === 0));
|
|
654
662
|
} catch {
|
|
655
|
-
|
|
663
|
+
resolve6(false);
|
|
656
664
|
}
|
|
657
665
|
});
|
|
658
666
|
}
|
|
@@ -813,8 +821,8 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
813
821
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
814
822
|
if (globRe) globRe.lastIndex = 0;
|
|
815
823
|
try {
|
|
816
|
-
const
|
|
817
|
-
if (
|
|
824
|
+
const stat11 = await fs4.stat(full);
|
|
825
|
+
if (stat11.size > 1e6) continue;
|
|
818
826
|
const head = await fs4.readFile(full);
|
|
819
827
|
if (isBinaryBuffer(head)) continue;
|
|
820
828
|
const text = head.toString("utf8");
|
|
@@ -1363,10 +1371,10 @@ var bashTool = {
|
|
|
1363
1371
|
queue.push(c);
|
|
1364
1372
|
}
|
|
1365
1373
|
};
|
|
1366
|
-
const next = () => new Promise((
|
|
1374
|
+
const next = () => new Promise((resolve6) => {
|
|
1367
1375
|
const c = queue.shift();
|
|
1368
|
-
if (c)
|
|
1369
|
-
else resolveNext =
|
|
1376
|
+
if (c) resolve6(c);
|
|
1377
|
+
else resolveNext = resolve6;
|
|
1370
1378
|
});
|
|
1371
1379
|
let lastFlush = Date.now();
|
|
1372
1380
|
const flush = () => {
|
|
@@ -1493,8 +1501,8 @@ var BLOCKED_ARG_PATTERNS = {
|
|
|
1493
1501
|
docker: [/^build$/, /^run$/, /^exec$/, /^push$/, /^pull$/],
|
|
1494
1502
|
// find -exec/-ok/-execdir execute arbitrary commands
|
|
1495
1503
|
find: [/^-exec$/, /^-exec;$/, /^-ok$/, /^-ok;$/, /^-execdir$/, /^-execdir;$/, /^-exec=/, /^-ok=/, /^-execdir=/],
|
|
1496
|
-
// rm -rf / is catastrophic — block
|
|
1497
|
-
rm: [
|
|
1504
|
+
// rm -rf / is catastrophic — block absolute paths, home, and dot-dir targets
|
|
1505
|
+
rm: [/^\//, /^~/, /^\.{1,2}$/],
|
|
1498
1506
|
// npm run/exec/create/pack/publish can execute arbitrary scripts or publish malware
|
|
1499
1507
|
npm: [/^run$/, /^exec$/, /^create$/, /^init$/, /^pack$/, /^publish$/, /^deploy$/],
|
|
1500
1508
|
// pnpm run/dlx/exec/create can execute arbitrary scripts
|
|
@@ -1601,7 +1609,7 @@ var execTool = {
|
|
|
1601
1609
|
}
|
|
1602
1610
|
};
|
|
1603
1611
|
function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
1604
|
-
return new Promise((
|
|
1612
|
+
return new Promise((resolve6) => {
|
|
1605
1613
|
let stdout = "";
|
|
1606
1614
|
let stderr = "";
|
|
1607
1615
|
let killed = false;
|
|
@@ -1635,7 +1643,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1635
1643
|
const durationMs = Date.now() - startedAt;
|
|
1636
1644
|
const exitCode = killed ? 124 : code ?? 1;
|
|
1637
1645
|
registry.afterCall(durationMs, exitCode !== 0);
|
|
1638
|
-
|
|
1646
|
+
resolve6({
|
|
1639
1647
|
command: cmd,
|
|
1640
1648
|
args,
|
|
1641
1649
|
stdout: stdout.slice(0, MAX_OUTPUT2),
|
|
@@ -1649,7 +1657,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1649
1657
|
clearTimeout(timer);
|
|
1650
1658
|
if (typeof pid === "number") registry.unregister(pid);
|
|
1651
1659
|
registry.afterCall(Date.now() - startedAt, true);
|
|
1652
|
-
|
|
1660
|
+
resolve6({
|
|
1653
1661
|
command: cmd,
|
|
1654
1662
|
args,
|
|
1655
1663
|
stdout: stdout.slice(0, MAX_OUTPUT2),
|
|
@@ -2448,8 +2456,8 @@ function findGitDir(cwd, projectRoot) {
|
|
|
2448
2456
|
let dir = cwd;
|
|
2449
2457
|
for (let i = 0; i < 20; i++) {
|
|
2450
2458
|
try {
|
|
2451
|
-
const
|
|
2452
|
-
if (
|
|
2459
|
+
const stat11 = statSync(`${dir}/.git`);
|
|
2460
|
+
if (stat11.isDirectory()) return dir;
|
|
2453
2461
|
} catch {
|
|
2454
2462
|
}
|
|
2455
2463
|
if (dir === root) break;
|
|
@@ -2530,7 +2538,7 @@ function buildArgs(input) {
|
|
|
2530
2538
|
}
|
|
2531
2539
|
}
|
|
2532
2540
|
function runGit(args, cwd, signal) {
|
|
2533
|
-
return new Promise((
|
|
2541
|
+
return new Promise((resolve6) => {
|
|
2534
2542
|
let stdout = "";
|
|
2535
2543
|
let stderr = "";
|
|
2536
2544
|
const child = spawn("git", args, {
|
|
@@ -2550,7 +2558,7 @@ function runGit(args, cwd, signal) {
|
|
|
2550
2558
|
}
|
|
2551
2559
|
});
|
|
2552
2560
|
child.on("error", (err) => {
|
|
2553
|
-
|
|
2561
|
+
resolve6({
|
|
2554
2562
|
command: args[0],
|
|
2555
2563
|
stdout,
|
|
2556
2564
|
stderr: err.message,
|
|
@@ -2559,7 +2567,7 @@ function runGit(args, cwd, signal) {
|
|
|
2559
2567
|
});
|
|
2560
2568
|
});
|
|
2561
2569
|
child.on("close", (code) => {
|
|
2562
|
-
|
|
2570
|
+
resolve6({
|
|
2563
2571
|
command: args[0],
|
|
2564
2572
|
stdout: stdout.slice(0, MAX_OUTPUT3),
|
|
2565
2573
|
stderr: stderr.slice(0, MAX_OUTPUT3),
|
|
@@ -2655,7 +2663,7 @@ function stripPathComponents(p, strip) {
|
|
|
2655
2663
|
return parts.slice(strip).join("/");
|
|
2656
2664
|
}
|
|
2657
2665
|
function runPatch(args, cwd, signal) {
|
|
2658
|
-
return new Promise((
|
|
2666
|
+
return new Promise((resolve6) => {
|
|
2659
2667
|
let stdout = "";
|
|
2660
2668
|
let stderr = "";
|
|
2661
2669
|
const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
|
|
@@ -2666,8 +2674,8 @@ function runPatch(args, cwd, signal) {
|
|
|
2666
2674
|
child.stderr?.on("data", (c) => {
|
|
2667
2675
|
stderr += c.toString();
|
|
2668
2676
|
});
|
|
2669
|
-
child.on("close", (code) =>
|
|
2670
|
-
child.on("error", (e) =>
|
|
2677
|
+
child.on("close", (code) => resolve6({ exitCode: code ?? 1, stdout, stderr }));
|
|
2678
|
+
child.on("error", (e) => resolve6({ exitCode: 1, stdout: "", stderr: e.message }));
|
|
2671
2679
|
});
|
|
2672
2680
|
}
|
|
2673
2681
|
function extractPatchedFiles(output) {
|
|
@@ -2750,8 +2758,8 @@ var jsonTool = {
|
|
|
2750
2758
|
};
|
|
2751
2759
|
}
|
|
2752
2760
|
};
|
|
2753
|
-
function query(data,
|
|
2754
|
-
const parts =
|
|
2761
|
+
function query(data, path17) {
|
|
2762
|
+
const parts = path17.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
2755
2763
|
let current = data;
|
|
2756
2764
|
for (const part of parts) {
|
|
2757
2765
|
if (current === null || current === void 0) return void 0;
|
|
@@ -2858,8 +2866,8 @@ function findGitDir2(cwd) {
|
|
|
2858
2866
|
let dir = cwd;
|
|
2859
2867
|
for (let i = 0; i < 20; i++) {
|
|
2860
2868
|
try {
|
|
2861
|
-
const
|
|
2862
|
-
if (
|
|
2869
|
+
const stat11 = statSync(path.join(dir, ".git"));
|
|
2870
|
+
if (stat11.isDirectory()) return dir;
|
|
2863
2871
|
} catch {
|
|
2864
2872
|
}
|
|
2865
2873
|
const parent = path.dirname(dir);
|
|
@@ -2869,7 +2877,7 @@ function findGitDir2(cwd) {
|
|
|
2869
2877
|
return null;
|
|
2870
2878
|
}
|
|
2871
2879
|
function runGit2(args, cwd, signal) {
|
|
2872
|
-
return new Promise((
|
|
2880
|
+
return new Promise((resolve6) => {
|
|
2873
2881
|
let stdout = "";
|
|
2874
2882
|
let stderr = "";
|
|
2875
2883
|
const child = spawn("git", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -2879,8 +2887,8 @@ function runGit2(args, cwd, signal) {
|
|
|
2879
2887
|
child.stderr?.on("data", (c) => {
|
|
2880
2888
|
stderr += c.toString();
|
|
2881
2889
|
});
|
|
2882
|
-
child.on("close", (code) =>
|
|
2883
|
-
child.on("error", (e) =>
|
|
2890
|
+
child.on("close", (code) => resolve6({ stdout, stderr, exitCode: code ?? 0 }));
|
|
2891
|
+
child.on("error", (e) => resolve6({ stdout: "", stderr: e.message, exitCode: 1 }));
|
|
2884
2892
|
});
|
|
2885
2893
|
}
|
|
2886
2894
|
async function fileDiff(input, ctx, signal) {
|
|
@@ -2898,8 +2906,8 @@ async function fileDiff(input, ctx, signal) {
|
|
|
2898
2906
|
const results = [];
|
|
2899
2907
|
for (const file of files) {
|
|
2900
2908
|
const absPath = safeResolve(file, ctx);
|
|
2901
|
-
const
|
|
2902
|
-
if (!
|
|
2909
|
+
const stat11 = await fs4.stat(absPath).catch(() => null);
|
|
2910
|
+
if (!stat11?.isFile()) continue;
|
|
2903
2911
|
const content = await fs4.readFile(absPath, "utf8");
|
|
2904
2912
|
const lines = content.split(/\r?\n/);
|
|
2905
2913
|
results.push(`--- ${file}
|
|
@@ -3131,8 +3139,8 @@ async function* spawnStream(opts) {
|
|
|
3131
3139
|
let spawnFailed = false;
|
|
3132
3140
|
for (; ; ) {
|
|
3133
3141
|
while (queue.length === 0) {
|
|
3134
|
-
await new Promise((
|
|
3135
|
-
waiter =
|
|
3142
|
+
await new Promise((resolve6) => {
|
|
3143
|
+
waiter = resolve6;
|
|
3136
3144
|
});
|
|
3137
3145
|
}
|
|
3138
3146
|
const chunk = queue.shift();
|
|
@@ -3241,11 +3249,11 @@ var lintTool = {
|
|
|
3241
3249
|
}
|
|
3242
3250
|
};
|
|
3243
3251
|
async function detectLinter(cwd) {
|
|
3244
|
-
const { stat:
|
|
3252
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3245
3253
|
const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
|
|
3246
3254
|
for (const f of checks) {
|
|
3247
3255
|
try {
|
|
3248
|
-
await
|
|
3256
|
+
await stat11(`${cwd}/${f}`);
|
|
3249
3257
|
if (f.includes("biome")) return "biome";
|
|
3250
3258
|
if (f.includes("eslint")) return "eslint";
|
|
3251
3259
|
if (f.includes("tslint")) return "tslint";
|
|
@@ -3340,13 +3348,13 @@ var formatTool = {
|
|
|
3340
3348
|
}
|
|
3341
3349
|
};
|
|
3342
3350
|
async function detectFixer(cwd) {
|
|
3343
|
-
const { stat:
|
|
3351
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3344
3352
|
try {
|
|
3345
|
-
await
|
|
3353
|
+
await stat11(`${cwd}/biome.json`);
|
|
3346
3354
|
return "biome";
|
|
3347
3355
|
} catch {
|
|
3348
3356
|
try {
|
|
3349
|
-
await
|
|
3357
|
+
await stat11(`${cwd}/.prettierrc`);
|
|
3350
3358
|
return "prettier";
|
|
3351
3359
|
} catch {
|
|
3352
3360
|
return "biome";
|
|
@@ -3422,11 +3430,11 @@ var typecheckTool = {
|
|
|
3422
3430
|
}
|
|
3423
3431
|
};
|
|
3424
3432
|
async function findTsConfig(cwd) {
|
|
3425
|
-
const { stat:
|
|
3433
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3426
3434
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
3427
3435
|
for (const f of candidates) {
|
|
3428
3436
|
try {
|
|
3429
|
-
const s = await
|
|
3437
|
+
const s = await stat11(path.join(cwd, f));
|
|
3430
3438
|
if (s.isFile()) return path.join(cwd, f);
|
|
3431
3439
|
} catch {
|
|
3432
3440
|
}
|
|
@@ -3503,11 +3511,11 @@ var testTool = {
|
|
|
3503
3511
|
}
|
|
3504
3512
|
};
|
|
3505
3513
|
async function detectRunner(cwd) {
|
|
3506
|
-
const { stat:
|
|
3514
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3507
3515
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
3508
3516
|
for (const f of candidates) {
|
|
3509
3517
|
try {
|
|
3510
|
-
await
|
|
3518
|
+
await stat11(path.join(cwd, f));
|
|
3511
3519
|
if (f.includes("vitest")) return "vitest";
|
|
3512
3520
|
if (f.includes("jest")) return "jest";
|
|
3513
3521
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -3678,13 +3686,13 @@ var installTool = {
|
|
|
3678
3686
|
}
|
|
3679
3687
|
};
|
|
3680
3688
|
async function detectPackageManager(cwd) {
|
|
3681
|
-
const { stat:
|
|
3689
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3682
3690
|
try {
|
|
3683
|
-
await
|
|
3691
|
+
await stat11(`${cwd}/pnpm-lock.yaml`);
|
|
3684
3692
|
return "pnpm";
|
|
3685
3693
|
} catch {
|
|
3686
3694
|
try {
|
|
3687
|
-
await
|
|
3695
|
+
await stat11(`${cwd}/yarn.lock`);
|
|
3688
3696
|
return "yarn";
|
|
3689
3697
|
} catch {
|
|
3690
3698
|
return "npm";
|
|
@@ -3743,14 +3751,14 @@ var auditTool = {
|
|
|
3743
3751
|
}
|
|
3744
3752
|
};
|
|
3745
3753
|
async function detectManager(cwd) {
|
|
3746
|
-
const { stat:
|
|
3754
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3747
3755
|
try {
|
|
3748
|
-
await
|
|
3756
|
+
await stat11(`${cwd}/pnpm-lock.yaml`);
|
|
3749
3757
|
return "pnpm";
|
|
3750
3758
|
} catch {
|
|
3751
3759
|
}
|
|
3752
3760
|
try {
|
|
3753
|
-
await
|
|
3761
|
+
await stat11(`${cwd}/yarn.lock`);
|
|
3754
3762
|
return "yarn";
|
|
3755
3763
|
} catch {
|
|
3756
3764
|
}
|
|
@@ -3851,7 +3859,7 @@ async function detectManager2(cwd) {
|
|
|
3851
3859
|
return "npm";
|
|
3852
3860
|
}
|
|
3853
3861
|
function runOutdated(manager, args, cwd, signal) {
|
|
3854
|
-
return new Promise((
|
|
3862
|
+
return new Promise((resolve6) => {
|
|
3855
3863
|
let stdout = "";
|
|
3856
3864
|
let stderr = "";
|
|
3857
3865
|
const MAX = 1e5;
|
|
@@ -3864,10 +3872,10 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
3864
3872
|
});
|
|
3865
3873
|
child.on("close", (code) => {
|
|
3866
3874
|
const result = parseOutdatedOutput(stdout, code ?? 0);
|
|
3867
|
-
|
|
3875
|
+
resolve6(result);
|
|
3868
3876
|
});
|
|
3869
3877
|
child.on("error", (e) => {
|
|
3870
|
-
|
|
3878
|
+
resolve6({
|
|
3871
3879
|
exit_code: 1,
|
|
3872
3880
|
packages: [],
|
|
3873
3881
|
total: 0,
|
|
@@ -3991,7 +3999,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
3991
3999
|
};
|
|
3992
4000
|
}
|
|
3993
4001
|
args.push("--timestamps", service);
|
|
3994
|
-
return new Promise((
|
|
4002
|
+
return new Promise((resolve6) => {
|
|
3995
4003
|
let stdout = "";
|
|
3996
4004
|
let stderr = "";
|
|
3997
4005
|
const MAX = 2e5;
|
|
@@ -4005,7 +4013,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
4005
4013
|
child.on("close", (code) => {
|
|
4006
4014
|
const output = stdout + stderr;
|
|
4007
4015
|
const entries = parseLogLines(output, filterRe);
|
|
4008
|
-
|
|
4016
|
+
resolve6({
|
|
4009
4017
|
source: `docker:${service}`,
|
|
4010
4018
|
entries,
|
|
4011
4019
|
total: entries.length,
|
|
@@ -4014,7 +4022,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
4014
4022
|
});
|
|
4015
4023
|
});
|
|
4016
4024
|
child.on("error", (e) => {
|
|
4017
|
-
|
|
4025
|
+
resolve6({
|
|
4018
4026
|
source: `docker:${service}`,
|
|
4019
4027
|
entries: [],
|
|
4020
4028
|
total: 0,
|
|
@@ -4025,16 +4033,16 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
4025
4033
|
});
|
|
4026
4034
|
}
|
|
4027
4035
|
var MAX_TAIL_LINES = 1e5;
|
|
4028
|
-
async function fileLogs(
|
|
4029
|
-
const { createInterface } = await import('readline');
|
|
4030
|
-
const { createReadStream } = await import('fs');
|
|
4036
|
+
async function fileLogs(path17, lines, filterRe, stream) {
|
|
4037
|
+
const { createInterface } = await import('node:readline');
|
|
4038
|
+
const { createReadStream } = await import('node:fs');
|
|
4031
4039
|
const entries = [];
|
|
4032
4040
|
const effLines = lines > 0 ? Math.min(lines, MAX_TAIL_LINES) : MAX_TAIL_LINES;
|
|
4033
4041
|
const window = new Array(effLines);
|
|
4034
4042
|
let writeIdx = 0;
|
|
4035
4043
|
let totalLines = 0;
|
|
4036
4044
|
const rl = createInterface({
|
|
4037
|
-
input: createReadStream(
|
|
4045
|
+
input: createReadStream(path17),
|
|
4038
4046
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
4039
4047
|
});
|
|
4040
4048
|
for await (const line of rl) {
|
|
@@ -4055,7 +4063,7 @@ async function fileLogs(path12, lines, filterRe, stream) {
|
|
|
4055
4063
|
if (parsed) entries.push(parsed);
|
|
4056
4064
|
}
|
|
4057
4065
|
return {
|
|
4058
|
-
source:
|
|
4066
|
+
source: path17,
|
|
4059
4067
|
entries,
|
|
4060
4068
|
total: entries.length,
|
|
4061
4069
|
truncated: totalLines > effLines,
|
|
@@ -4082,7 +4090,7 @@ function parseLine(line) {
|
|
|
4082
4090
|
message: match[3] ?? ""
|
|
4083
4091
|
};
|
|
4084
4092
|
}
|
|
4085
|
-
const levelRe = /(
|
|
4093
|
+
const levelRe = /(ERROR|WARN|INFO|DEBUG|TRACE)\s+(.*)/i;
|
|
4086
4094
|
const levelMatch = levelRe.exec(line);
|
|
4087
4095
|
if (levelMatch) {
|
|
4088
4096
|
return {
|
|
@@ -4178,8 +4186,8 @@ async function resolveFiles2(filesInput, cwd) {
|
|
|
4178
4186
|
for (const f of files) {
|
|
4179
4187
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
4180
4188
|
try {
|
|
4181
|
-
const
|
|
4182
|
-
if (
|
|
4189
|
+
const stat11 = await fs4.stat(absPath);
|
|
4190
|
+
if (stat11.isFile()) resolved.push(absPath);
|
|
4183
4191
|
} catch {
|
|
4184
4192
|
}
|
|
4185
4193
|
}
|
|
@@ -4933,6 +4941,1932 @@ ${mode.description}`
|
|
|
4933
4941
|
};
|
|
4934
4942
|
}
|
|
4935
4943
|
|
|
4944
|
+
// src/codebase-index/schema.ts
|
|
4945
|
+
var SCHEMA_VERSION = 1;
|
|
4946
|
+
|
|
4947
|
+
// src/codebase-index/lsp-kind.ts
|
|
4948
|
+
function lspKindToInternalKind(k) {
|
|
4949
|
+
switch (k) {
|
|
4950
|
+
case 5 /* Class */:
|
|
4951
|
+
return "class";
|
|
4952
|
+
case 6 /* Method */:
|
|
4953
|
+
return "method";
|
|
4954
|
+
case 7 /* Property */:
|
|
4955
|
+
case 8 /* Field */:
|
|
4956
|
+
return "property";
|
|
4957
|
+
case 9 /* Constructor */:
|
|
4958
|
+
return "class";
|
|
4959
|
+
case 10 /* Enum */:
|
|
4960
|
+
return "enum";
|
|
4961
|
+
case 11 /* Interface */:
|
|
4962
|
+
return "interface";
|
|
4963
|
+
case 12 /* Function */:
|
|
4964
|
+
return "function";
|
|
4965
|
+
case 13 /* Variable */:
|
|
4966
|
+
return "var";
|
|
4967
|
+
case 14 /* Constant */:
|
|
4968
|
+
return "const";
|
|
4969
|
+
case 22 /* EnumMember */:
|
|
4970
|
+
return "enum";
|
|
4971
|
+
case 26 /* TypeParameter */:
|
|
4972
|
+
return "type";
|
|
4973
|
+
case 3 /* Namespace */:
|
|
4974
|
+
return "namespace";
|
|
4975
|
+
default:
|
|
4976
|
+
return null;
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
|
|
4980
|
+
// src/codebase-index/writer.ts
|
|
4981
|
+
var INDEX_DIR = ".codebase-index";
|
|
4982
|
+
var DB_FILE = "index.db";
|
|
4983
|
+
var IndexStore = class {
|
|
4984
|
+
constructor(projectRoot) {
|
|
4985
|
+
this.projectRoot = projectRoot;
|
|
4986
|
+
const dir = path.join(projectRoot, INDEX_DIR);
|
|
4987
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
4988
|
+
this.db = new DatabaseSync(path.join(dir, DB_FILE));
|
|
4989
|
+
this.initSchema();
|
|
4990
|
+
}
|
|
4991
|
+
projectRoot;
|
|
4992
|
+
db;
|
|
4993
|
+
initSchema() {
|
|
4994
|
+
this.db.exec(`
|
|
4995
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
4996
|
+
key TEXT PRIMARY KEY,
|
|
4997
|
+
value TEXT NOT NULL
|
|
4998
|
+
);
|
|
4999
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
5000
|
+
file TEXT PRIMARY KEY,
|
|
5001
|
+
lang TEXT NOT NULL,
|
|
5002
|
+
mtime_ms INTEGER NOT NULL,
|
|
5003
|
+
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
5004
|
+
last_indexed INTEGER NOT NULL
|
|
5005
|
+
);
|
|
5006
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
5007
|
+
id INTEGER PRIMARY KEY,
|
|
5008
|
+
lang TEXT NOT NULL,
|
|
5009
|
+
kind TEXT NOT NULL,
|
|
5010
|
+
name TEXT NOT NULL,
|
|
5011
|
+
file TEXT NOT NULL,
|
|
5012
|
+
line INTEGER NOT NULL,
|
|
5013
|
+
col INTEGER NOT NULL,
|
|
5014
|
+
signature TEXT NOT NULL DEFAULT '',
|
|
5015
|
+
doc_comment TEXT NOT NULL DEFAULT '',
|
|
5016
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
5017
|
+
text TEXT NOT NULL DEFAULT '',
|
|
5018
|
+
file_fk TEXT NOT NULL
|
|
5019
|
+
);
|
|
5020
|
+
`);
|
|
5021
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)");
|
|
5022
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)");
|
|
5023
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)");
|
|
5024
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)");
|
|
5025
|
+
this.db.exec(`
|
|
5026
|
+
CREATE TABLE IF NOT EXISTS refs (
|
|
5027
|
+
id INTEGER PRIMARY KEY,
|
|
5028
|
+
from_id INTEGER NOT NULL,
|
|
5029
|
+
to_name TEXT NOT NULL,
|
|
5030
|
+
to_id INTEGER,
|
|
5031
|
+
call_type TEXT NOT NULL,
|
|
5032
|
+
line INTEGER NOT NULL
|
|
5033
|
+
);
|
|
5034
|
+
`);
|
|
5035
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)");
|
|
5036
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)");
|
|
5037
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)");
|
|
5038
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)");
|
|
5039
|
+
const versionRows = this.db.prepare("SELECT value FROM metadata WHERE key = ?").all("version");
|
|
5040
|
+
if (!versionRows.length) {
|
|
5041
|
+
this.db.prepare("INSERT INTO metadata(key, value) VALUES (?, ?)").run("version", String(SCHEMA_VERSION));
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
// ─── Symbol CRUD ─────────────────────────────────────────────────────────────
|
|
5045
|
+
insertSymbols(symbols, nextId) {
|
|
5046
|
+
const stmt = this.db.prepare(
|
|
5047
|
+
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
5048
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
5049
|
+
);
|
|
5050
|
+
let id = nextId;
|
|
5051
|
+
for (const s of symbols) {
|
|
5052
|
+
stmt.run(
|
|
5053
|
+
id++,
|
|
5054
|
+
s.lang,
|
|
5055
|
+
s.kind,
|
|
5056
|
+
s.name,
|
|
5057
|
+
s.file,
|
|
5058
|
+
s.line,
|
|
5059
|
+
s.col,
|
|
5060
|
+
s.signature,
|
|
5061
|
+
s.docComment,
|
|
5062
|
+
s.scope,
|
|
5063
|
+
s.text,
|
|
5064
|
+
s.file
|
|
5065
|
+
);
|
|
5066
|
+
}
|
|
5067
|
+
return id;
|
|
5068
|
+
}
|
|
5069
|
+
deleteSymbolsForFile(file) {
|
|
5070
|
+
this.db.prepare("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
5071
|
+
}
|
|
5072
|
+
deleteFile(file) {
|
|
5073
|
+
this.db.prepare("DELETE FROM files WHERE file = ?").run(file);
|
|
5074
|
+
}
|
|
5075
|
+
// ─── File metadata ──────────────────────────────────────────────────────────
|
|
5076
|
+
upsertFile(meta) {
|
|
5077
|
+
this.db.prepare(
|
|
5078
|
+
`INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)
|
|
5079
|
+
VALUES (?, ?, ?, ?, ?)
|
|
5080
|
+
ON CONFLICT(file) DO UPDATE SET
|
|
5081
|
+
lang = excluded.lang,
|
|
5082
|
+
mtime_ms = excluded.mtime_ms,
|
|
5083
|
+
symbol_count = excluded.symbol_count,
|
|
5084
|
+
last_indexed = excluded.last_indexed`
|
|
5085
|
+
).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);
|
|
5086
|
+
}
|
|
5087
|
+
getFileMeta(file) {
|
|
5088
|
+
const rows = this.db.prepare(
|
|
5089
|
+
"SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
|
|
5090
|
+
).all(file);
|
|
5091
|
+
if (!rows.length) return null;
|
|
5092
|
+
const r = rows[0];
|
|
5093
|
+
return { file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed };
|
|
5094
|
+
}
|
|
5095
|
+
getAllFileMetas() {
|
|
5096
|
+
return this.db.prepare(
|
|
5097
|
+
"SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files"
|
|
5098
|
+
).all().map(
|
|
5099
|
+
(r) => ({ file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed })
|
|
5100
|
+
);
|
|
5101
|
+
}
|
|
5102
|
+
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
5103
|
+
search(query2, filter) {
|
|
5104
|
+
const conditions = [];
|
|
5105
|
+
const values = [];
|
|
5106
|
+
let effectiveKind = filter?.kind;
|
|
5107
|
+
if (filter?.lspKind !== void 0) {
|
|
5108
|
+
const mapped = lspKindToInternalKind(filter.lspKind);
|
|
5109
|
+
if (mapped !== null) {
|
|
5110
|
+
effectiveKind = mapped;
|
|
5111
|
+
} else {
|
|
5112
|
+
return [];
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5115
|
+
if (effectiveKind) {
|
|
5116
|
+
conditions.push("kind = ?");
|
|
5117
|
+
values.push(effectiveKind);
|
|
5118
|
+
}
|
|
5119
|
+
if (filter?.lang) {
|
|
5120
|
+
conditions.push("lang = ?");
|
|
5121
|
+
values.push(filter.lang);
|
|
5122
|
+
}
|
|
5123
|
+
if (filter?.file) {
|
|
5124
|
+
conditions.push("file LIKE ?");
|
|
5125
|
+
values.push(`%${filter.file}%`);
|
|
5126
|
+
}
|
|
5127
|
+
if (query2.trim()) {
|
|
5128
|
+
const tokens = query2.toLowerCase().split(/\s+/).filter(Boolean);
|
|
5129
|
+
const tokenConds = tokens.map(() => "text LIKE ?");
|
|
5130
|
+
conditions.push(`(${tokenConds.join(" OR ")})`);
|
|
5131
|
+
for (const t of tokens) values.push(`%${t}%`);
|
|
5132
|
+
}
|
|
5133
|
+
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
5134
|
+
const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}`;
|
|
5135
|
+
const stmt = this.db.prepare(sql);
|
|
5136
|
+
const rows = stmt.all(...values);
|
|
5137
|
+
return rows.map((r) => ({
|
|
5138
|
+
id: r.id,
|
|
5139
|
+
lang: r.lang,
|
|
5140
|
+
kind: r.kind,
|
|
5141
|
+
name: r.name,
|
|
5142
|
+
file: r.file,
|
|
5143
|
+
line: r.line,
|
|
5144
|
+
col: r.col,
|
|
5145
|
+
signature: r.signature,
|
|
5146
|
+
docComment: r.doc_comment,
|
|
5147
|
+
score: 0,
|
|
5148
|
+
snippet: "",
|
|
5149
|
+
lspKind: filter?.lspKind
|
|
5150
|
+
}));
|
|
5151
|
+
}
|
|
5152
|
+
getAllIndexable() {
|
|
5153
|
+
return this.db.prepare("SELECT id, text FROM symbols").all().map(
|
|
5154
|
+
({ id, text }) => ({ id, text })
|
|
5155
|
+
);
|
|
5156
|
+
}
|
|
5157
|
+
// ─── Stats ───────────────────────────────────────────────────────────────────
|
|
5158
|
+
getStats() {
|
|
5159
|
+
const sizeBytes = this.sizeBytes();
|
|
5160
|
+
const lastRows = this.db.prepare(
|
|
5161
|
+
"SELECT value FROM metadata WHERE key = 'last_indexed'"
|
|
5162
|
+
).all();
|
|
5163
|
+
const lastIndexed = lastRows.length ? Number(lastRows[0].value) : null;
|
|
5164
|
+
const totalRows = this.db.prepare("SELECT COUNT(*) FROM symbols").all();
|
|
5165
|
+
const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
|
|
5166
|
+
const fileRows = this.db.prepare("SELECT COUNT(*) FROM files").all();
|
|
5167
|
+
const totalFiles = fileRows[0] ? Number(fileRows[0]["COUNT(*)"]) : 0;
|
|
5168
|
+
const langRows = this.db.prepare(
|
|
5169
|
+
"SELECT lang, COUNT(*) FROM symbols GROUP BY lang"
|
|
5170
|
+
).all();
|
|
5171
|
+
const byLang = {};
|
|
5172
|
+
for (const row of langRows) byLang[row.lang] = Number(row["COUNT(*)"]);
|
|
5173
|
+
const kindRows = this.db.prepare(
|
|
5174
|
+
"SELECT kind, COUNT(*) FROM symbols GROUP BY kind"
|
|
5175
|
+
).all();
|
|
5176
|
+
const byKind = {};
|
|
5177
|
+
for (const row of kindRows) byKind[row.kind] = Number(row["COUNT(*)"]);
|
|
5178
|
+
return {
|
|
5179
|
+
totalSymbols,
|
|
5180
|
+
totalFiles,
|
|
5181
|
+
byLang,
|
|
5182
|
+
byKind,
|
|
5183
|
+
indexPath: path.join(this.projectRoot, INDEX_DIR),
|
|
5184
|
+
lastIndexed,
|
|
5185
|
+
sizeBytes,
|
|
5186
|
+
version: SCHEMA_VERSION
|
|
5187
|
+
};
|
|
5188
|
+
}
|
|
5189
|
+
setLastIndexed(ts2) {
|
|
5190
|
+
this.db.prepare(
|
|
5191
|
+
"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)"
|
|
5192
|
+
).run(String(ts2));
|
|
5193
|
+
}
|
|
5194
|
+
clearAll() {
|
|
5195
|
+
this.db.exec("DELETE FROM symbols");
|
|
5196
|
+
this.db.exec("DELETE FROM files");
|
|
5197
|
+
this.db.exec("DELETE FROM refs");
|
|
5198
|
+
}
|
|
5199
|
+
// ─── Ref CRUD ────────────────────────────────────────────────────────────────
|
|
5200
|
+
/**
|
|
5201
|
+
* Insert cross-references for a given source symbol id.
|
|
5202
|
+
* Replaces any existing refs from the same source (idempotent on re-index).
|
|
5203
|
+
*/
|
|
5204
|
+
insertRefs(fromId, refs) {
|
|
5205
|
+
this.db.prepare("DELETE FROM refs WHERE from_id = ?").run(fromId);
|
|
5206
|
+
if (refs.length === 0) return;
|
|
5207
|
+
const stmt = this.db.prepare(
|
|
5208
|
+
`INSERT INTO refs(from_id, to_name, to_id, call_type, line)
|
|
5209
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
5210
|
+
);
|
|
5211
|
+
for (const ref of refs) {
|
|
5212
|
+
stmt.run(fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
/**
|
|
5216
|
+
* Delete all refs whose source symbols are in a given file.
|
|
5217
|
+
* Used when re-indexing a file to clear stale refs.
|
|
5218
|
+
*/
|
|
5219
|
+
deleteRefsForFile(file) {
|
|
5220
|
+
const ids = this.db.prepare(
|
|
5221
|
+
"SELECT id FROM symbols WHERE file = ?"
|
|
5222
|
+
).all(file);
|
|
5223
|
+
if (!ids.length) return;
|
|
5224
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
5225
|
+
this.db.prepare(`DELETE FROM refs WHERE from_id IN (${placeholders})`).run(...ids.map((r) => r.id));
|
|
5226
|
+
}
|
|
5227
|
+
/**
|
|
5228
|
+
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
5229
|
+
* Call this after all symbols have been inserted to fill in cross-references.
|
|
5230
|
+
*/
|
|
5231
|
+
resolveRefs() {
|
|
5232
|
+
const unresolved = this.db.prepare(
|
|
5233
|
+
"SELECT id, to_name FROM refs WHERE to_id IS NULL AND to_name IS NOT NULL"
|
|
5234
|
+
).all();
|
|
5235
|
+
let resolved = 0;
|
|
5236
|
+
for (const row of unresolved) {
|
|
5237
|
+
const target = this.db.prepare("SELECT id FROM symbols WHERE name = ? LIMIT 1").all(row.to_name);
|
|
5238
|
+
if (target.length) {
|
|
5239
|
+
this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(target[0].id, row.id);
|
|
5240
|
+
resolved++;
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
return resolved;
|
|
5244
|
+
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
5247
|
+
*/
|
|
5248
|
+
findRefsTo(symbolId) {
|
|
5249
|
+
return this.db.prepare(
|
|
5250
|
+
"SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE to_id = ? OR to_name = (SELECT name FROM symbols WHERE id = ?)"
|
|
5251
|
+
).all(symbolId, symbolId).map((r) => ({
|
|
5252
|
+
id: r.id,
|
|
5253
|
+
fromId: r.from_id,
|
|
5254
|
+
toName: r.to_name,
|
|
5255
|
+
toId: r.to_id ?? void 0,
|
|
5256
|
+
callType: r.call_type,
|
|
5257
|
+
line: r.line
|
|
5258
|
+
}));
|
|
5259
|
+
}
|
|
5260
|
+
/**
|
|
5261
|
+
* Find all references FROM a given symbol (what does this symbol call/use?).
|
|
5262
|
+
*/
|
|
5263
|
+
findRefsFrom(symbolId) {
|
|
5264
|
+
return this.db.prepare(
|
|
5265
|
+
"SELECT id, from_id, to_name, to_id, call_type, line FROM refs WHERE from_id = ?"
|
|
5266
|
+
).all(symbolId).map((r) => ({
|
|
5267
|
+
id: r.id,
|
|
5268
|
+
fromId: r.from_id,
|
|
5269
|
+
toName: r.to_name,
|
|
5270
|
+
toId: r.to_id ?? void 0,
|
|
5271
|
+
callType: r.call_type,
|
|
5272
|
+
line: r.line
|
|
5273
|
+
}));
|
|
5274
|
+
}
|
|
5275
|
+
sizeBytes() {
|
|
5276
|
+
const dbPath = path.join(this.projectRoot, INDEX_DIR, DB_FILE);
|
|
5277
|
+
try {
|
|
5278
|
+
return fs13.statSync(dbPath).size;
|
|
5279
|
+
} catch {
|
|
5280
|
+
return 0;
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
close() {
|
|
5284
|
+
try {
|
|
5285
|
+
this.db.close();
|
|
5286
|
+
} catch {
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
};
|
|
5290
|
+
var KIND_MAP = {
|
|
5291
|
+
[ts.SyntaxKind.ClassDeclaration]: "class",
|
|
5292
|
+
[ts.SyntaxKind.InterfaceDeclaration]: "interface",
|
|
5293
|
+
[ts.SyntaxKind.EnumDeclaration]: "enum",
|
|
5294
|
+
[ts.SyntaxKind.TypeAliasDeclaration]: "type",
|
|
5295
|
+
[ts.SyntaxKind.FunctionDeclaration]: "function",
|
|
5296
|
+
[ts.SyntaxKind.MethodDeclaration]: "method",
|
|
5297
|
+
[ts.SyntaxKind.GetAccessor]: "property",
|
|
5298
|
+
[ts.SyntaxKind.SetAccessor]: "property",
|
|
5299
|
+
[ts.SyntaxKind.PropertyDeclaration]: "property",
|
|
5300
|
+
[ts.SyntaxKind.Parameter]: "parameter",
|
|
5301
|
+
[ts.SyntaxKind.NamespaceExportDeclaration]: "namespace"
|
|
5302
|
+
};
|
|
5303
|
+
function kindOf(node) {
|
|
5304
|
+
if (ts.isVariableDeclaration(node)) {
|
|
5305
|
+
const parent = node.parent;
|
|
5306
|
+
if (ts.isVariableDeclarationList(parent)) {
|
|
5307
|
+
const flags = parent.flags;
|
|
5308
|
+
if (flags & ts.NodeFlags.Let) return "let";
|
|
5309
|
+
if (flags & ts.NodeFlags.Const) return "const";
|
|
5310
|
+
return "var";
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
if (ts.isModuleDeclaration(node)) return "namespace";
|
|
5314
|
+
return KIND_MAP[node.kind] ?? null;
|
|
5315
|
+
}
|
|
5316
|
+
function extToLang(ext) {
|
|
5317
|
+
switch (ext) {
|
|
5318
|
+
case ".ts":
|
|
5319
|
+
return "ts";
|
|
5320
|
+
case ".tsx":
|
|
5321
|
+
return "tsx";
|
|
5322
|
+
case ".js":
|
|
5323
|
+
return "js";
|
|
5324
|
+
case ".jsx":
|
|
5325
|
+
return "jsx";
|
|
5326
|
+
case ".go":
|
|
5327
|
+
return "go";
|
|
5328
|
+
case ".py":
|
|
5329
|
+
return "py";
|
|
5330
|
+
case ".rs":
|
|
5331
|
+
return "rs";
|
|
5332
|
+
case ".json":
|
|
5333
|
+
return "json";
|
|
5334
|
+
case ".yaml":
|
|
5335
|
+
return "yaml";
|
|
5336
|
+
case ".yml":
|
|
5337
|
+
return "yaml";
|
|
5338
|
+
default:
|
|
5339
|
+
return null;
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
function getSignature(node, sourceFile) {
|
|
5343
|
+
const printer = ts.createPrinter({});
|
|
5344
|
+
const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
5345
|
+
return raw.replace(/\s+/g, " ").slice(0, 500);
|
|
5346
|
+
}
|
|
5347
|
+
function getJsDoc(node, sourceFile) {
|
|
5348
|
+
const fullText = sourceFile.getFullText();
|
|
5349
|
+
const nodePos = node.getFullWidth();
|
|
5350
|
+
const comments = ts.getLeadingCommentRanges(fullText, nodePos);
|
|
5351
|
+
if (!comments) return "";
|
|
5352
|
+
for (const range of comments) {
|
|
5353
|
+
const commentText = fullText.slice(range.pos, range.end);
|
|
5354
|
+
const trimmed = commentText.trim();
|
|
5355
|
+
if (trimmed.startsWith("/**") && trimmed.endsWith("*/")) {
|
|
5356
|
+
const inner = trimmed.slice(3, -2).replace(/^[ \t]*\*[ ]?/gm, "").trim();
|
|
5357
|
+
return inner.split("\n")[0]?.trim().slice(0, 200) ?? "";
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
return "";
|
|
5361
|
+
}
|
|
5362
|
+
function buildScope(node) {
|
|
5363
|
+
const parts = [];
|
|
5364
|
+
let current = node.parent;
|
|
5365
|
+
while (current) {
|
|
5366
|
+
if (ts.isClassDeclaration(current) || ts.isInterfaceDeclaration(current) || ts.isEnumDeclaration(current) || ts.isTypeAliasDeclaration(current)) {
|
|
5367
|
+
parts.unshift(current.name?.text ?? "Anon");
|
|
5368
|
+
} else if (ts.isMethodDeclaration(current) || ts.isGetAccessor(current) || ts.isSetAccessor(current) || ts.isPropertyDeclaration(current) || ts.isFunctionDeclaration(current)) {
|
|
5369
|
+
if (current.name && ts.isIdentifier(current.name)) {
|
|
5370
|
+
parts.unshift(current.name.text);
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
current = current.parent;
|
|
5374
|
+
}
|
|
5375
|
+
return parts.join(".");
|
|
5376
|
+
}
|
|
5377
|
+
function parseSymbols(opts) {
|
|
5378
|
+
const { file, content, lang } = opts;
|
|
5379
|
+
let sourceFile;
|
|
5380
|
+
try {
|
|
5381
|
+
sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
|
|
5382
|
+
} catch {
|
|
5383
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
5384
|
+
}
|
|
5385
|
+
const symbols = [];
|
|
5386
|
+
function visit(node) {
|
|
5387
|
+
const kind = kindOf(node);
|
|
5388
|
+
if (kind) {
|
|
5389
|
+
const nameNode = node.name;
|
|
5390
|
+
if (!nameNode || !ts.isIdentifier(nameNode)) return;
|
|
5391
|
+
const name = nameNode.text;
|
|
5392
|
+
const pos = nameNode.getStart(sourceFile);
|
|
5393
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
5394
|
+
const scope = buildScope(node);
|
|
5395
|
+
const signature = getSignature(node, sourceFile);
|
|
5396
|
+
const docComment = getJsDoc(node, sourceFile);
|
|
5397
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
5398
|
+
symbols.push({
|
|
5399
|
+
id: 0,
|
|
5400
|
+
lang,
|
|
5401
|
+
kind,
|
|
5402
|
+
name,
|
|
5403
|
+
file,
|
|
5404
|
+
line: line + 1,
|
|
5405
|
+
col: character,
|
|
5406
|
+
signature,
|
|
5407
|
+
docComment,
|
|
5408
|
+
scope,
|
|
5409
|
+
text
|
|
5410
|
+
});
|
|
5411
|
+
}
|
|
5412
|
+
ts.forEachChild(node, visit);
|
|
5413
|
+
}
|
|
5414
|
+
visit(sourceFile);
|
|
5415
|
+
const refs = extractRefs(sourceFile);
|
|
5416
|
+
return { file, lang, symbols, refs, mtimeMs: Date.now() };
|
|
5417
|
+
}
|
|
5418
|
+
function extractRefs(sourceFile) {
|
|
5419
|
+
const refs = [];
|
|
5420
|
+
function visit(node) {
|
|
5421
|
+
const pos = node.getStart(sourceFile);
|
|
5422
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
5423
|
+
const lineNum = line + 1;
|
|
5424
|
+
if (ts.isCallExpression(node)) {
|
|
5425
|
+
const expr = node.expression;
|
|
5426
|
+
if (ts.isIdentifier(expr)) {
|
|
5427
|
+
refs.push({ fromId: 0, toName: expr.text, callType: "call", line: lineNum });
|
|
5428
|
+
}
|
|
5429
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
5430
|
+
if (ts.isIdentifier(node.expression)) {
|
|
5431
|
+
refs.push({ fromId: 0, toName: node.expression.text, callType: "call", line: lineNum });
|
|
5432
|
+
}
|
|
5433
|
+
} else if (ts.isTypeReferenceNode(node)) {
|
|
5434
|
+
const name = getTypeName(node.typeName);
|
|
5435
|
+
if (name) refs.push({ fromId: 0, toName: name, callType: "type_ref", line: lineNum });
|
|
5436
|
+
} else if (ts.isHeritageClause(node)) {
|
|
5437
|
+
for (const t of node.types) {
|
|
5438
|
+
const name = getTypeName(t.expression);
|
|
5439
|
+
if (name) refs.push({ fromId: 0, toName: name, callType: node.token === ts.SyntaxKind.ExtendsKeyword ? "inherit" : "implement", line: lineNum });
|
|
5440
|
+
}
|
|
5441
|
+
} else if (ts.isImportDeclaration(node)) {
|
|
5442
|
+
const moduleName = getModuleName(node);
|
|
5443
|
+
if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: "import", line: lineNum });
|
|
5444
|
+
}
|
|
5445
|
+
ts.forEachChild(node, visit);
|
|
5446
|
+
}
|
|
5447
|
+
visit(sourceFile);
|
|
5448
|
+
return deduplicateRefs(refs);
|
|
5449
|
+
}
|
|
5450
|
+
function getTypeName(name) {
|
|
5451
|
+
if (ts.isIdentifier(name)) return name.text;
|
|
5452
|
+
if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;
|
|
5453
|
+
return "";
|
|
5454
|
+
}
|
|
5455
|
+
function getModuleName(node) {
|
|
5456
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
5457
|
+
if (ts.isStringLiteral(moduleSpecifier)) return moduleSpecifier.text;
|
|
5458
|
+
return "";
|
|
5459
|
+
}
|
|
5460
|
+
function deduplicateRefs(refs) {
|
|
5461
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5462
|
+
return refs.filter((r) => {
|
|
5463
|
+
const key = `${r.toName}:${r.callType}:${r.line}`;
|
|
5464
|
+
if (seen.has(key)) return false;
|
|
5465
|
+
seen.add(key);
|
|
5466
|
+
return true;
|
|
5467
|
+
});
|
|
5468
|
+
}
|
|
5469
|
+
function detectLang(file) {
|
|
5470
|
+
const idx = file.lastIndexOf(".");
|
|
5471
|
+
if (idx < 0) return null;
|
|
5472
|
+
return extToLang(file.slice(idx));
|
|
5473
|
+
}
|
|
5474
|
+
function parseSymbols2(opts) {
|
|
5475
|
+
const { file, content, lang } = opts;
|
|
5476
|
+
try {
|
|
5477
|
+
return syncGoParse(file, content, lang);
|
|
5478
|
+
} catch {
|
|
5479
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
var GO_PARSE_SCRIPT = `
|
|
5483
|
+
package main
|
|
5484
|
+
|
|
5485
|
+
import (
|
|
5486
|
+
"encoding/json"
|
|
5487
|
+
"fmt"
|
|
5488
|
+
"go/ast"
|
|
5489
|
+
"go/parser"
|
|
5490
|
+
"go/token"
|
|
5491
|
+
"os"
|
|
5492
|
+
"strings"
|
|
5493
|
+
)
|
|
5494
|
+
|
|
5495
|
+
type Sym struct {
|
|
5496
|
+
Name string \`json:"name"\`
|
|
5497
|
+
Kind string \`json:"kind"\`
|
|
5498
|
+
Line int \`json:"line"\`
|
|
5499
|
+
Col int \`json:"col"\`
|
|
5500
|
+
Signature string \`json:"signature"\`
|
|
5501
|
+
Scope string \`json:"scope"\`
|
|
5502
|
+
}
|
|
5503
|
+
|
|
5504
|
+
func main() {
|
|
5505
|
+
if len(os.Args) < 2 {
|
|
5506
|
+
fmt.Print("[]")
|
|
5507
|
+
return
|
|
5508
|
+
}
|
|
5509
|
+
src, err := os.ReadFile(os.Args[1])
|
|
5510
|
+
if err != nil {
|
|
5511
|
+
fmt.Print("[]")
|
|
5512
|
+
return
|
|
5513
|
+
}
|
|
5514
|
+
fset := token.NewFileSet()
|
|
5515
|
+
node, err := parser.ParseFile(fset, os.Args[1], src, 0)
|
|
5516
|
+
if err != nil {
|
|
5517
|
+
fmt.Print("[]")
|
|
5518
|
+
return
|
|
5519
|
+
}
|
|
5520
|
+
|
|
5521
|
+
var syms []Sym
|
|
5522
|
+
|
|
5523
|
+
// Package-level scope
|
|
5524
|
+
pkgScope := node.Name.Name
|
|
5525
|
+
|
|
5526
|
+
// Collect all top-level declarations
|
|
5527
|
+
for _, decl := range node.Decls {
|
|
5528
|
+
switch d := decl.(type) {
|
|
5529
|
+
case *ast.FuncDecl:
|
|
5530
|
+
name := d.Name.Name
|
|
5531
|
+
kind := "function"
|
|
5532
|
+
scope := pkgScope
|
|
5533
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
5534
|
+
scope = pkgScope + "." + recvTypeName(d.Recv.List[0].Type) + "." + name
|
|
5535
|
+
kind = "method"
|
|
5536
|
+
} else {
|
|
5537
|
+
scope = pkgScope + "." + name
|
|
5538
|
+
}
|
|
5539
|
+
pos := fset.Position(d.Pos())
|
|
5540
|
+
sig := formatFuncSig(d)
|
|
5541
|
+
syms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: scope})
|
|
5542
|
+
|
|
5543
|
+
case *ast.GenDecl:
|
|
5544
|
+
for _, spec := range d.Specs {
|
|
5545
|
+
switch s := spec.(type) {
|
|
5546
|
+
case *ast.TypeSpec:
|
|
5547
|
+
name := s.Name.Name
|
|
5548
|
+
pos := fset.Position(s.Pos())
|
|
5549
|
+
sig := "type " + name
|
|
5550
|
+
if s.TypeParams != nil {
|
|
5551
|
+
sig += formatTypeParams(s.TypeParams)
|
|
5552
|
+
}
|
|
5553
|
+
if st, ok := s.Type.(*ast.StructType); ok {
|
|
5554
|
+
sig += " = struct { " + formatFields(st.Fields.List) + " }"
|
|
5555
|
+
} else if it, ok := s.Type.(*ast.InterfaceType); ok {
|
|
5556
|
+
sig += " = interface { " + formatMethods(it.Methods.List) + " }"
|
|
5557
|
+
} else {
|
|
5558
|
+
sig += " = " + formatType(s.Type)
|
|
5559
|
+
}
|
|
5560
|
+
syms = append(syms, Sym{Name: name, Kind: "type", Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
5561
|
+
|
|
5562
|
+
case *ast.ValueSpec:
|
|
5563
|
+
for _, n := range s.Names {
|
|
5564
|
+
name := n.Name
|
|
5565
|
+
pos := fset.Position(n.Pos())
|
|
5566
|
+
kind := "var"
|
|
5567
|
+
if d.Tok == token.CONST {
|
|
5568
|
+
kind = "const"
|
|
5569
|
+
}
|
|
5570
|
+
sig := kind + " " + name
|
|
5571
|
+
if s.Type != nil {
|
|
5572
|
+
sig += " " + formatType(s.Type)
|
|
5573
|
+
}
|
|
5574
|
+
syms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
}
|
|
5580
|
+
|
|
5581
|
+
data, err := json.Marshal(syms)
|
|
5582
|
+
if err != nil {
|
|
5583
|
+
fmt.Print("[]")
|
|
5584
|
+
return
|
|
5585
|
+
}
|
|
5586
|
+
fmt.Print(string(data))
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5589
|
+
func recvTypeName(t ast.Expr) string {
|
|
5590
|
+
switch v := t.(type) {
|
|
5591
|
+
case *ast.Ident:
|
|
5592
|
+
return v.Name
|
|
5593
|
+
case *ast.StarExpr:
|
|
5594
|
+
return recvTypeName(v.X)
|
|
5595
|
+
default:
|
|
5596
|
+
return "?"
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
|
|
5600
|
+
func formatFuncSig(d *ast.FuncDecl) string {
|
|
5601
|
+
scope := ""
|
|
5602
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
5603
|
+
scope = "(" + formatFieldList(d.Recv.List) + ") "
|
|
5604
|
+
}
|
|
5605
|
+
scope += formatFuncType(d.Type)
|
|
5606
|
+
return "func " + scope
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
func formatFuncType(f *ast.FuncType) string {
|
|
5610
|
+
params := formatFieldList(f.Params.List)
|
|
5611
|
+
results := ""
|
|
5612
|
+
if f.Results != nil {
|
|
5613
|
+
results = " -> " + formatFieldList(f.Results.List)
|
|
5614
|
+
}
|
|
5615
|
+
return params + results
|
|
5616
|
+
}
|
|
5617
|
+
|
|
5618
|
+
func formatFieldList(fields []*ast.Field) string {
|
|
5619
|
+
if len(fields) == 0 {
|
|
5620
|
+
return "()"
|
|
5621
|
+
}
|
|
5622
|
+
names := make([]string, 0, len(fields))
|
|
5623
|
+
for _, f := range fields {
|
|
5624
|
+
name := ""
|
|
5625
|
+
if len(f.Names) > 0 {
|
|
5626
|
+
name = f.Names[0].Name
|
|
5627
|
+
}
|
|
5628
|
+
t := formatType(f.Type)
|
|
5629
|
+
if name != "" {
|
|
5630
|
+
names = append(names, name+" "+t)
|
|
5631
|
+
} else {
|
|
5632
|
+
names = append(names, t)
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
return "(" + strings.Join(names, ", ") + ")"
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5638
|
+
func formatFields(fields []*ast.Field) string {
|
|
5639
|
+
lines := make([]string, 0)
|
|
5640
|
+
for _, f := range fields {
|
|
5641
|
+
name := ""
|
|
5642
|
+
if len(f.Names) > 0 {
|
|
5643
|
+
name = f.Names[0].Name
|
|
5644
|
+
}
|
|
5645
|
+
t := formatType(f.Type)
|
|
5646
|
+
if name != "" {
|
|
5647
|
+
lines = append(lines, name+" "+t)
|
|
5648
|
+
} else {
|
|
5649
|
+
lines = append(lines, t)
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
return strings.Join(lines, "; ")
|
|
5653
|
+
}
|
|
5654
|
+
|
|
5655
|
+
func formatMethods(fields []*ast.Field) string {
|
|
5656
|
+
return formatFields(fields)
|
|
5657
|
+
}
|
|
5658
|
+
|
|
5659
|
+
func formatTypeParams(tp *ast.TypeParams) string {
|
|
5660
|
+
if tp == nil || len(tp.List) == 0 {
|
|
5661
|
+
return ""
|
|
5662
|
+
}
|
|
5663
|
+
params := make([]string, len(tp.List))
|
|
5664
|
+
for i, p := range tp.List {
|
|
5665
|
+
if len(p.Names) > 0 {
|
|
5666
|
+
params[i] = p.Names[0].Name
|
|
5667
|
+
} else {
|
|
5668
|
+
params[i] = "T"
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
return "[" + strings.Join(params, ", ") + "]"
|
|
5672
|
+
}
|
|
5673
|
+
|
|
5674
|
+
func formatType(t ast.Expr) string {
|
|
5675
|
+
if t == nil {
|
|
5676
|
+
return "?"
|
|
5677
|
+
}
|
|
5678
|
+
switch v := t.(type) {
|
|
5679
|
+
case *ast.Ident:
|
|
5680
|
+
return v.Name
|
|
5681
|
+
case *ast.SelectorExpr:
|
|
5682
|
+
return formatType(v.X) + "." + v.Sel.Name
|
|
5683
|
+
case *ast.StarExpr:
|
|
5684
|
+
return "*" + formatType(v.X)
|
|
5685
|
+
case *ast.ArrayType:
|
|
5686
|
+
if v.Len == nil {
|
|
5687
|
+
return "[]" + formatType(v.Elt)
|
|
5688
|
+
}
|
|
5689
|
+
return "[...]" + formatType(v.Elt)
|
|
5690
|
+
case *ast.MapType:
|
|
5691
|
+
return "map[" + formatType(v.Key) + "]" + formatType(v.Value)
|
|
5692
|
+
case *ast.InterfaceType:
|
|
5693
|
+
return "interface{}"
|
|
5694
|
+
case *ast.StructType:
|
|
5695
|
+
return "struct{}"
|
|
5696
|
+
case *ast.FuncType:
|
|
5697
|
+
return formatFuncType(v)
|
|
5698
|
+
case *ast.ChanType:
|
|
5699
|
+
return "chan " + formatType(v.Value)
|
|
5700
|
+
case *ast.BasicLit:
|
|
5701
|
+
return v.Value
|
|
5702
|
+
default:
|
|
5703
|
+
return "?"
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
`;
|
|
5707
|
+
function syncGoParse(filePath, _content, lang) {
|
|
5708
|
+
const tmpDir = path.join(process.env.TEMP ?? "/tmp", "ws-go-parse");
|
|
5709
|
+
try {
|
|
5710
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
5711
|
+
const scriptPath = path.join(tmpDir, "parse.go");
|
|
5712
|
+
writeFileSync(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5713
|
+
let stdout;
|
|
5714
|
+
try {
|
|
5715
|
+
stdout = execSync(`go run "${scriptPath}" "${filePath}"`, {
|
|
5716
|
+
timeout: 15e3,
|
|
5717
|
+
encoding: "utf8",
|
|
5718
|
+
windowsHide: true
|
|
5719
|
+
});
|
|
5720
|
+
} finally {
|
|
5721
|
+
try {
|
|
5722
|
+
unlinkSync(scriptPath);
|
|
5723
|
+
} catch {
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
if (!stdout.trim()) {
|
|
5727
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5728
|
+
}
|
|
5729
|
+
const raw = JSON.parse(stdout.trim());
|
|
5730
|
+
const symbols = raw.map((s) => ({
|
|
5731
|
+
id: 0,
|
|
5732
|
+
lang,
|
|
5733
|
+
kind: s.kind,
|
|
5734
|
+
name: s.name,
|
|
5735
|
+
file: filePath,
|
|
5736
|
+
line: s.line,
|
|
5737
|
+
col: s.col,
|
|
5738
|
+
signature: s.signature ?? "",
|
|
5739
|
+
docComment: "",
|
|
5740
|
+
scope: s.scope ?? "",
|
|
5741
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
5742
|
+
}));
|
|
5743
|
+
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
5744
|
+
} catch {
|
|
5745
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5746
|
+
}
|
|
5747
|
+
}
|
|
5748
|
+
function parseSymbols3(opts) {
|
|
5749
|
+
const { file, lang } = opts;
|
|
5750
|
+
try {
|
|
5751
|
+
return syncPyParse(file, lang);
|
|
5752
|
+
} catch {
|
|
5753
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
var PY_PARSE_SCRIPT = `import ast, json, sys, os
|
|
5757
|
+
|
|
5758
|
+
def get_name(node):
|
|
5759
|
+
if isinstance(node, ast.Name):
|
|
5760
|
+
return node.id
|
|
5761
|
+
elif isinstance(node, ast.Attribute):
|
|
5762
|
+
return get_name(node.value) + "." + node.attr
|
|
5763
|
+
elif isinstance(node, ast.Subscript):
|
|
5764
|
+
return get_name(node.value)
|
|
5765
|
+
elif isinstance(node, ast.Call):
|
|
5766
|
+
return get_name(node.func)
|
|
5767
|
+
elif isinstance(node, ast.Constant):
|
|
5768
|
+
return str(node.value)
|
|
5769
|
+
return ""
|
|
5770
|
+
|
|
5771
|
+
def get_decorators(node):
|
|
5772
|
+
decs = []
|
|
5773
|
+
for dec in node.decorator_list:
|
|
5774
|
+
decs.append(get_name(dec))
|
|
5775
|
+
return decs
|
|
5776
|
+
|
|
5777
|
+
def get_bases(node):
|
|
5778
|
+
bases = []
|
|
5779
|
+
for base in node.bases:
|
|
5780
|
+
bases.append(get_name(base))
|
|
5781
|
+
return bases
|
|
5782
|
+
|
|
5783
|
+
def get_args(args):
|
|
5784
|
+
parts = []
|
|
5785
|
+
for arg in args.args:
|
|
5786
|
+
parts.append(arg.arg)
|
|
5787
|
+
return ", ".join(parts)
|
|
5788
|
+
|
|
5789
|
+
def get_returns(node):
|
|
5790
|
+
if node.returns is None:
|
|
5791
|
+
return ""
|
|
5792
|
+
return get_name(node.returns)
|
|
5793
|
+
|
|
5794
|
+
class Sym:
|
|
5795
|
+
def __init__(self, name, kind, line, col, signature, scope):
|
|
5796
|
+
self.name = name
|
|
5797
|
+
self.kind = kind
|
|
5798
|
+
self.line = line
|
|
5799
|
+
self.col = col
|
|
5800
|
+
self.signature = signature
|
|
5801
|
+
self.scope = scope
|
|
5802
|
+
def to_dict(self):
|
|
5803
|
+
return {
|
|
5804
|
+
"name": self.name,
|
|
5805
|
+
"kind": self.kind,
|
|
5806
|
+
"line": self.line,
|
|
5807
|
+
"col": self.col,
|
|
5808
|
+
"signature": self.signature,
|
|
5809
|
+
"scope": self.scope,
|
|
5810
|
+
}
|
|
5811
|
+
|
|
5812
|
+
def is_private(name):
|
|
5813
|
+
return name.startswith("__") and not name.endswith("__")
|
|
5814
|
+
|
|
5815
|
+
syms = []
|
|
5816
|
+
errors = []
|
|
5817
|
+
|
|
5818
|
+
try:
|
|
5819
|
+
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
|
5820
|
+
source = f.read()
|
|
5821
|
+
tree = ast.parse(source, filename=sys.argv[1])
|
|
5822
|
+
except Exception as e:
|
|
5823
|
+
errors.append(str(e))
|
|
5824
|
+
print("[]")
|
|
5825
|
+
sys.exit(0)
|
|
5826
|
+
|
|
5827
|
+
# Module-level scope
|
|
5828
|
+
module_scope = os.path.basename(sys.argv[1])[:-3] # strip .py
|
|
5829
|
+
|
|
5830
|
+
class ModuleVisitor(ast.NodeVisitor):
|
|
5831
|
+
def __init__(self):
|
|
5832
|
+
self.scope_stack = [module_scope]
|
|
5833
|
+
|
|
5834
|
+
def visit_ClassDef(self, node):
|
|
5835
|
+
bases = get_bases(node)
|
|
5836
|
+
decs = get_decorators(node)
|
|
5837
|
+
sig = "class " + node.name
|
|
5838
|
+
if bases:
|
|
5839
|
+
sig += "(" + ", ".join(bases) + ")"
|
|
5840
|
+
sig += ": ..."
|
|
5841
|
+
syms.append(Sym(
|
|
5842
|
+
name=node.name,
|
|
5843
|
+
kind="class",
|
|
5844
|
+
line=node.lineno,
|
|
5845
|
+
col=node.col_offset,
|
|
5846
|
+
signature=sig,
|
|
5847
|
+
scope=".".join(self.scope_stack) + "." + node.name,
|
|
5848
|
+
))
|
|
5849
|
+
self.scope_stack.append(node.name)
|
|
5850
|
+
self.generic_visit(node)
|
|
5851
|
+
self.scope_stack.pop()
|
|
5852
|
+
|
|
5853
|
+
def visit_FunctionDef(self, node):
|
|
5854
|
+
decs = get_decorators(node)
|
|
5855
|
+
args = get_args(node.args)
|
|
5856
|
+
returns = get_returns(node)
|
|
5857
|
+
is_async = isinstance(node, ast.AsyncFunctionDef)
|
|
5858
|
+
|
|
5859
|
+
kind = "function"
|
|
5860
|
+
prefix = "def "
|
|
5861
|
+
if decs:
|
|
5862
|
+
for d in decs:
|
|
5863
|
+
if d.endswith(".staticmethod"):
|
|
5864
|
+
kind = "staticmethod"
|
|
5865
|
+
elif d.endswith(".classmethod"):
|
|
5866
|
+
kind = "classmethod"
|
|
5867
|
+
elif d == "property":
|
|
5868
|
+
kind = "property"
|
|
5869
|
+
|
|
5870
|
+
if is_async:
|
|
5871
|
+
kind = "async_" + kind
|
|
5872
|
+
|
|
5873
|
+
sig = f"{prefix}{node.name}({args})"
|
|
5874
|
+
if returns:
|
|
5875
|
+
sig += f" -> {returns}"
|
|
5876
|
+
scope = ".".join(self.scope_stack) + "." + node.name
|
|
5877
|
+
|
|
5878
|
+
syms.append(Sym(
|
|
5879
|
+
name=node.name,
|
|
5880
|
+
kind=kind,
|
|
5881
|
+
line=node.lineno,
|
|
5882
|
+
col=node.col_offset,
|
|
5883
|
+
signature=sig,
|
|
5884
|
+
scope=scope,
|
|
5885
|
+
))
|
|
5886
|
+
# Don't descend into function bodies to avoid local symbols
|
|
5887
|
+
# self.generic_visit(node)
|
|
5888
|
+
|
|
5889
|
+
def visit_AsyncFunctionDef(self, node):
|
|
5890
|
+
# Treat as function
|
|
5891
|
+
self.visit_FunctionDef(node)
|
|
5892
|
+
|
|
5893
|
+
def visit_Assign(self, node):
|
|
5894
|
+
for target in node.targets:
|
|
5895
|
+
if isinstance(target, ast.Name):
|
|
5896
|
+
name = target.id
|
|
5897
|
+
if is_private(name):
|
|
5898
|
+
continue
|
|
5899
|
+
# Infer constness from UPPER_CASE naming
|
|
5900
|
+
kind = "const" if name.isupper() else "var"
|
|
5901
|
+
col = target.col_offset if hasattr(target, 'col_offset') else 0
|
|
5902
|
+
syms.append(Sym(
|
|
5903
|
+
name=name,
|
|
5904
|
+
kind=kind,
|
|
5905
|
+
line=node.lineno,
|
|
5906
|
+
col=col,
|
|
5907
|
+
signature=f"{name} = ...",
|
|
5908
|
+
scope=".".join(self.scope_stack),
|
|
5909
|
+
))
|
|
5910
|
+
|
|
5911
|
+
def visit_AnnAssign(self, node):
|
|
5912
|
+
if isinstance(node.target, ast.Name):
|
|
5913
|
+
name = node.target.id
|
|
5914
|
+
if is_private(name):
|
|
5915
|
+
return
|
|
5916
|
+
kind = "const" if name.isupper() else "var"
|
|
5917
|
+
col = node.target.col_offset if hasattr(node.target, 'col_offset') else 0
|
|
5918
|
+
sig = f"{name}: {get_name(node.annotation)}"
|
|
5919
|
+
if node.value:
|
|
5920
|
+
sig += " = ..."
|
|
5921
|
+
syms.append(Sym(
|
|
5922
|
+
name=name,
|
|
5923
|
+
kind=kind,
|
|
5924
|
+
line=node.lineno,
|
|
5925
|
+
col=col,
|
|
5926
|
+
signature=sig,
|
|
5927
|
+
scope=".".join(self.scope_stack),
|
|
5928
|
+
))
|
|
5929
|
+
|
|
5930
|
+
def visit_Import(self, node):
|
|
5931
|
+
for alias in node.names:
|
|
5932
|
+
name = alias.asname or alias.name
|
|
5933
|
+
syms.append(Sym(
|
|
5934
|
+
name=name,
|
|
5935
|
+
kind="import",
|
|
5936
|
+
line=node.lineno,
|
|
5937
|
+
col=node.col_offset,
|
|
5938
|
+
signature=f"import {alias.name}",
|
|
5939
|
+
scope=".".join(self.scope_stack),
|
|
5940
|
+
))
|
|
5941
|
+
|
|
5942
|
+
def visit_ImportFrom(self, node):
|
|
5943
|
+
module = node.module or ""
|
|
5944
|
+
for alias in node.names:
|
|
5945
|
+
name = alias.asname or alias.name
|
|
5946
|
+
syms.append(Sym(
|
|
5947
|
+
name=name,
|
|
5948
|
+
kind="import",
|
|
5949
|
+
line=node.lineno,
|
|
5950
|
+
col=node.col_offset,
|
|
5951
|
+
signature=f"from {module} import {alias.name}",
|
|
5952
|
+
scope=".".join(self.scope_stack),
|
|
5953
|
+
))
|
|
5954
|
+
|
|
5955
|
+
visitor = ModuleVisitor()
|
|
5956
|
+
visitor.visit(tree)
|
|
5957
|
+
|
|
5958
|
+
print(json.dumps([s.to_dict() for s in syms]))
|
|
5959
|
+
`;
|
|
5960
|
+
function syncPyParse(filePath, lang) {
|
|
5961
|
+
try {
|
|
5962
|
+
const stdout = execSync(`python -c "${PY_PARSE_SCRIPT.replace(/"/g, '\\"')}" "${filePath}"`, {
|
|
5963
|
+
timeout: 15e3,
|
|
5964
|
+
encoding: "utf8",
|
|
5965
|
+
windowsHide: true
|
|
5966
|
+
});
|
|
5967
|
+
if (!stdout.trim()) {
|
|
5968
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5969
|
+
}
|
|
5970
|
+
const raw = JSON.parse(stdout.trim());
|
|
5971
|
+
const symbols = raw.map((s) => ({
|
|
5972
|
+
id: 0,
|
|
5973
|
+
lang,
|
|
5974
|
+
kind: s.kind,
|
|
5975
|
+
name: s.name,
|
|
5976
|
+
file: filePath,
|
|
5977
|
+
line: s.line,
|
|
5978
|
+
col: s.col,
|
|
5979
|
+
signature: s.signature ?? "",
|
|
5980
|
+
docComment: "",
|
|
5981
|
+
scope: s.scope ?? "",
|
|
5982
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
5983
|
+
}));
|
|
5984
|
+
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
5985
|
+
} catch {
|
|
5986
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
function parseSymbols4(opts) {
|
|
5990
|
+
const { file, content, lang } = opts;
|
|
5991
|
+
const nativeAvailable = checkNativeParser();
|
|
5992
|
+
if (nativeAvailable) {
|
|
5993
|
+
const result = tryNativeParse(file, content);
|
|
5994
|
+
if (result) return result;
|
|
5995
|
+
}
|
|
5996
|
+
return regexParse({ file, content, lang });
|
|
5997
|
+
}
|
|
5998
|
+
function checkNativeParser() {
|
|
5999
|
+
try {
|
|
6000
|
+
execSync("rustc --version", { stdio: "pipe" });
|
|
6001
|
+
const toolsDir = path.join(process.cwd(), "tools");
|
|
6002
|
+
try {
|
|
6003
|
+
execSync("cargo metadata --no-deps --format-version 1 --manifest-path " + path.join(toolsDir, "Cargo.toml"), { stdio: "pipe" });
|
|
6004
|
+
return true;
|
|
6005
|
+
} catch {
|
|
6006
|
+
return false;
|
|
6007
|
+
}
|
|
6008
|
+
} catch {
|
|
6009
|
+
return false;
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
function tryNativeParse(file, content) {
|
|
6013
|
+
try {
|
|
6014
|
+
const toolsDir = path.join(process.cwd(), "tools");
|
|
6015
|
+
const crateDir = path.join(toolsDir, "syn-parser");
|
|
6016
|
+
const tmpFile = path.join(crateDir, "src", "input.rs");
|
|
6017
|
+
const { writeFileSync: writeFileSync2 } = __require("node:fs");
|
|
6018
|
+
writeFileSync2(tmpFile, content, "utf8");
|
|
6019
|
+
const result = spawnSync("cargo", ["run", "--manifest-path", path.join(toolsDir, "Cargo.toml")], {
|
|
6020
|
+
cwd: process.cwd(),
|
|
6021
|
+
encoding: "utf8",
|
|
6022
|
+
timeout: 15e3,
|
|
6023
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6024
|
+
});
|
|
6025
|
+
if (result.status === 0 && result.stdout) {
|
|
6026
|
+
const symbols = JSON.parse(result.stdout);
|
|
6027
|
+
return {
|
|
6028
|
+
file,
|
|
6029
|
+
lang: "rs",
|
|
6030
|
+
symbols: symbols.map((s) => ({ ...s, id: 0, lang: "rs" })),
|
|
6031
|
+
mtimeMs: Date.now()
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6034
|
+
} catch {
|
|
6035
|
+
}
|
|
6036
|
+
return null;
|
|
6037
|
+
}
|
|
6038
|
+
var RS_PATTERNS = [
|
|
6039
|
+
{ regex: /fn\s+(\w+)\s*\([^)]*\)/g, kind: "function" },
|
|
6040
|
+
{ regex: /struct\s+(\w+)/g, kind: "struct" },
|
|
6041
|
+
{ regex: /enum\s+(\w+)/g, kind: "enum" },
|
|
6042
|
+
{ regex: /trait\s+(\w+)/g, kind: "trait" },
|
|
6043
|
+
{ regex: /impl\s+(?:<[^>]+>)?(\w+)/g, kind: "impl" },
|
|
6044
|
+
{ regex: /type\s+(\w+)\s*=/g, kind: "type" },
|
|
6045
|
+
{ regex: /const\s+(\w+)/g, kind: "const" },
|
|
6046
|
+
{ regex: /static\s+(\w+)/g, kind: "static" },
|
|
6047
|
+
{ regex: /mod\s+(\w+)/g, kind: "mod" }
|
|
6048
|
+
];
|
|
6049
|
+
function regexParse(opts) {
|
|
6050
|
+
const { file, content, lang } = opts;
|
|
6051
|
+
const symbols = [];
|
|
6052
|
+
const lines = content.split("\n");
|
|
6053
|
+
const lineOffsets = [0];
|
|
6054
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6055
|
+
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
6056
|
+
}
|
|
6057
|
+
function lineFromOffset(offset) {
|
|
6058
|
+
let lo = 0, hi = lineOffsets.length - 1;
|
|
6059
|
+
while (lo < hi) {
|
|
6060
|
+
const mid = lo + hi + 1 >>> 1;
|
|
6061
|
+
if (lineOffsets[mid] <= offset) lo = mid;
|
|
6062
|
+
else hi = mid - 1;
|
|
6063
|
+
}
|
|
6064
|
+
return lo + 1;
|
|
6065
|
+
}
|
|
6066
|
+
function extractDeclaration(lineIdx, match) {
|
|
6067
|
+
const line = lines[lineIdx] ?? "";
|
|
6068
|
+
return line.trim().slice(0, 500);
|
|
6069
|
+
}
|
|
6070
|
+
for (const pattern of RS_PATTERNS) {
|
|
6071
|
+
pattern.regex.lastIndex = 0;
|
|
6072
|
+
let match;
|
|
6073
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
6074
|
+
const name = match[1];
|
|
6075
|
+
const offset = match.index;
|
|
6076
|
+
const line = lineFromOffset(offset);
|
|
6077
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6078
|
+
const lineIdx = line - 1;
|
|
6079
|
+
const signature = extractDeclaration(lineIdx);
|
|
6080
|
+
symbols.push({
|
|
6081
|
+
id: 0,
|
|
6082
|
+
lang,
|
|
6083
|
+
kind: pattern.kind,
|
|
6084
|
+
name,
|
|
6085
|
+
file,
|
|
6086
|
+
line,
|
|
6087
|
+
col,
|
|
6088
|
+
signature,
|
|
6089
|
+
docComment: "",
|
|
6090
|
+
scope: "",
|
|
6091
|
+
text: `${name} ${signature}`.trim()
|
|
6092
|
+
});
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
6095
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6096
|
+
const deduped = symbols.filter((s) => {
|
|
6097
|
+
const key = `${s.name}:${s.line}`;
|
|
6098
|
+
if (seen.has(key)) return false;
|
|
6099
|
+
seen.add(key);
|
|
6100
|
+
return true;
|
|
6101
|
+
});
|
|
6102
|
+
return { file, lang, symbols: deduped, mtimeMs: Date.now() };
|
|
6103
|
+
}
|
|
6104
|
+
function parseSymbols5(opts) {
|
|
6105
|
+
const { file, content, lang } = opts;
|
|
6106
|
+
try {
|
|
6107
|
+
return regexParse2({ file, content, lang });
|
|
6108
|
+
} catch {
|
|
6109
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
function regexParse2(opts) {
|
|
6113
|
+
const { file, content, lang } = opts;
|
|
6114
|
+
const symbols = [];
|
|
6115
|
+
const basename2 = path.basename(file).toLowerCase();
|
|
6116
|
+
const isPackageJson = basename2 === "package.json";
|
|
6117
|
+
const isTsconfig = basename2 === "tsconfig.json" || basename2 === "tsconfig.build.json";
|
|
6118
|
+
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
6119
|
+
const isOpenApi = content.includes("openapi") || content.includes("swagger");
|
|
6120
|
+
const lines = content.split("\n");
|
|
6121
|
+
const lineOffsets = [0];
|
|
6122
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6123
|
+
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
6124
|
+
}
|
|
6125
|
+
function lineFromOffset(offset) {
|
|
6126
|
+
let lo = 0, hi = lineOffsets.length - 1;
|
|
6127
|
+
while (lo < hi) {
|
|
6128
|
+
const mid = lo + hi + 1 >>> 1;
|
|
6129
|
+
if (lineOffsets[mid] <= offset) lo = mid;
|
|
6130
|
+
else hi = mid - 1;
|
|
6131
|
+
}
|
|
6132
|
+
return lo + 1;
|
|
6133
|
+
}
|
|
6134
|
+
const rootMatch = content.match(/^\s*\{/m);
|
|
6135
|
+
if (rootMatch) {
|
|
6136
|
+
const offset = rootMatch.index;
|
|
6137
|
+
const line = lineFromOffset(offset);
|
|
6138
|
+
symbols.push(makeSymbol({
|
|
6139
|
+
name: path.basename(file),
|
|
6140
|
+
kind: "object",
|
|
6141
|
+
line,
|
|
6142
|
+
col: 0,
|
|
6143
|
+
signature: `"${path.basename(file)}" = { ... }`,
|
|
6144
|
+
file,
|
|
6145
|
+
lang
|
|
6146
|
+
}));
|
|
6147
|
+
}
|
|
6148
|
+
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
6149
|
+
let match;
|
|
6150
|
+
while ((match = topLevelKeyRegex.exec(content)) !== null) {
|
|
6151
|
+
const key = match[1];
|
|
6152
|
+
const offset = match.index;
|
|
6153
|
+
const line = lineFromOffset(offset);
|
|
6154
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6155
|
+
let kind = "property";
|
|
6156
|
+
let signature = `"${key}": ..."`;
|
|
6157
|
+
if (isPackageJson) {
|
|
6158
|
+
if (key === "scripts" || key === "dependencies" || key === "devDependencies" || key === "peerDependencies" || key === "optionalDependencies") {
|
|
6159
|
+
kind = "const";
|
|
6160
|
+
signature = `"${key}": { ... }`;
|
|
6161
|
+
}
|
|
6162
|
+
} else if (isTsconfig) {
|
|
6163
|
+
if (key === "compilerOptions") {
|
|
6164
|
+
kind = "property";
|
|
6165
|
+
signature = `"compilerOptions": { ... }`;
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
if (isJsonSchema || isOpenApi) {
|
|
6169
|
+
if (key === "$schema" || key === "$id") {
|
|
6170
|
+
kind = "schema";
|
|
6171
|
+
signature = `"${key}": "..."`;
|
|
6172
|
+
} else if (key === "$ref") {
|
|
6173
|
+
kind = "schema";
|
|
6174
|
+
signature = `"$ref": "..."`;
|
|
6175
|
+
}
|
|
6176
|
+
}
|
|
6177
|
+
symbols.push(makeSymbol({
|
|
6178
|
+
name: key,
|
|
6179
|
+
kind,
|
|
6180
|
+
line,
|
|
6181
|
+
col,
|
|
6182
|
+
signature,
|
|
6183
|
+
file,
|
|
6184
|
+
lang
|
|
6185
|
+
}));
|
|
6186
|
+
if (isPackageJson && key === "scripts") {
|
|
6187
|
+
extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset);
|
|
6188
|
+
}
|
|
6189
|
+
if (isTsconfig && key === "compilerOptions") {
|
|
6190
|
+
extractCompilerOptions(content, symbols, file, lang, lineOffsets, line, lineFromOffset);
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
6194
|
+
let defsMatch;
|
|
6195
|
+
while ((defsMatch = defsRegex.exec(content)) !== null) {
|
|
6196
|
+
const offset = defsMatch.index;
|
|
6197
|
+
const line = lineFromOffset(offset);
|
|
6198
|
+
symbols.push(makeSymbol({
|
|
6199
|
+
name: "$defs",
|
|
6200
|
+
kind: "property",
|
|
6201
|
+
line,
|
|
6202
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
6203
|
+
signature: '"$defs": { ... }',
|
|
6204
|
+
file,
|
|
6205
|
+
lang
|
|
6206
|
+
}));
|
|
6207
|
+
break;
|
|
6208
|
+
}
|
|
6209
|
+
const defsPatterns = [
|
|
6210
|
+
/"\$defs"\s*:/g,
|
|
6211
|
+
/"definitions"\s*:/g,
|
|
6212
|
+
/"components"\s*:/g,
|
|
6213
|
+
/"schemas"\s*:/g
|
|
6214
|
+
];
|
|
6215
|
+
for (const pat of defsPatterns) {
|
|
6216
|
+
pat.lastIndex = 0;
|
|
6217
|
+
while ((match = pat.exec(content)) !== null) {
|
|
6218
|
+
const offset = match.index;
|
|
6219
|
+
const line = lineFromOffset(offset);
|
|
6220
|
+
const key = match[0].match(/"([^"]+)"/)?.[1] ?? match[0];
|
|
6221
|
+
symbols.push(makeSymbol({
|
|
6222
|
+
name: key,
|
|
6223
|
+
kind: "property",
|
|
6224
|
+
line,
|
|
6225
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
6226
|
+
signature: `"${key}": { ... }`,
|
|
6227
|
+
file,
|
|
6228
|
+
lang
|
|
6229
|
+
}));
|
|
6230
|
+
}
|
|
6231
|
+
}
|
|
6232
|
+
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
6233
|
+
}
|
|
6234
|
+
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
6235
|
+
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
6236
|
+
let match;
|
|
6237
|
+
while ((match = scriptsBlockRegex.exec(content)) !== null) {
|
|
6238
|
+
const blockContent = match[0];
|
|
6239
|
+
const blockOffset = match.index;
|
|
6240
|
+
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
6241
|
+
let scriptMatch;
|
|
6242
|
+
while ((scriptMatch = scriptKeyRegex.exec(blockContent)) !== null) {
|
|
6243
|
+
const key = scriptMatch[1];
|
|
6244
|
+
const keyOffset = blockOffset + scriptMatch.index;
|
|
6245
|
+
const line = lineFromOffset(keyOffset);
|
|
6246
|
+
symbols.push(makeSymbol({
|
|
6247
|
+
name: key,
|
|
6248
|
+
kind: "function",
|
|
6249
|
+
line,
|
|
6250
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
6251
|
+
signature: `"${key}": "..."`,
|
|
6252
|
+
file,
|
|
6253
|
+
lang
|
|
6254
|
+
}));
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
}
|
|
6258
|
+
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
6259
|
+
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
6260
|
+
let match;
|
|
6261
|
+
while ((match = optsBlockRegex.exec(content)) !== null) {
|
|
6262
|
+
const blockContent = match[0];
|
|
6263
|
+
const blockOffset = match.index;
|
|
6264
|
+
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
6265
|
+
let optMatch;
|
|
6266
|
+
while ((optMatch = optKeyRegex.exec(blockContent)) !== null) {
|
|
6267
|
+
const key = optMatch[1];
|
|
6268
|
+
const keyOffset = blockOffset + optMatch.index;
|
|
6269
|
+
const line = lineFromOffset(keyOffset);
|
|
6270
|
+
if (line <= parentLine) continue;
|
|
6271
|
+
symbols.push(makeSymbol({
|
|
6272
|
+
name: key,
|
|
6273
|
+
kind: "property",
|
|
6274
|
+
line,
|
|
6275
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
6276
|
+
signature: `"${key}": ...`,
|
|
6277
|
+
file,
|
|
6278
|
+
lang
|
|
6279
|
+
}));
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
function makeSymbol(opts) {
|
|
6284
|
+
return {
|
|
6285
|
+
id: 0,
|
|
6286
|
+
lang: opts.lang,
|
|
6287
|
+
kind: opts.kind,
|
|
6288
|
+
name: opts.name,
|
|
6289
|
+
file: opts.file,
|
|
6290
|
+
line: opts.line,
|
|
6291
|
+
col: opts.col,
|
|
6292
|
+
signature: opts.signature,
|
|
6293
|
+
docComment: "",
|
|
6294
|
+
scope: "",
|
|
6295
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
6296
|
+
};
|
|
6297
|
+
}
|
|
6298
|
+
|
|
6299
|
+
// src/codebase-index/yaml-parser.ts
|
|
6300
|
+
function parseSymbols6(opts) {
|
|
6301
|
+
const { file, content, lang } = opts;
|
|
6302
|
+
try {
|
|
6303
|
+
return regexParse3({ file, content, lang });
|
|
6304
|
+
} catch {
|
|
6305
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
6306
|
+
}
|
|
6307
|
+
}
|
|
6308
|
+
function regexParse3(opts) {
|
|
6309
|
+
const { file, content, lang } = opts;
|
|
6310
|
+
const symbols = [];
|
|
6311
|
+
const lines = content.split("\n");
|
|
6312
|
+
const lineOffsets = [0];
|
|
6313
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6314
|
+
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
6315
|
+
}
|
|
6316
|
+
function lineFromOffset(offset) {
|
|
6317
|
+
let lo = 0, hi = lineOffsets.length - 1;
|
|
6318
|
+
while (lo < hi) {
|
|
6319
|
+
const mid = lo + hi + 1 >>> 1;
|
|
6320
|
+
if (lineOffsets[mid] <= offset) lo = mid;
|
|
6321
|
+
else hi = mid - 1;
|
|
6322
|
+
}
|
|
6323
|
+
return lo + 1;
|
|
6324
|
+
}
|
|
6325
|
+
const anchorRegex = /&(\w[\w-]*)/g;
|
|
6326
|
+
let match;
|
|
6327
|
+
while ((match = anchorRegex.exec(content)) !== null) {
|
|
6328
|
+
const name = match[1];
|
|
6329
|
+
const offset = match.index;
|
|
6330
|
+
const line = lineFromOffset(offset);
|
|
6331
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6332
|
+
symbols.push(makeSymbol2({
|
|
6333
|
+
name,
|
|
6334
|
+
kind: "const",
|
|
6335
|
+
line,
|
|
6336
|
+
col,
|
|
6337
|
+
signature: `&${name}`,
|
|
6338
|
+
file,
|
|
6339
|
+
lang
|
|
6340
|
+
}));
|
|
6341
|
+
}
|
|
6342
|
+
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
6343
|
+
while ((match = aliasRegex.exec(content)) !== null) {
|
|
6344
|
+
const name = match[1];
|
|
6345
|
+
const offset = match.index;
|
|
6346
|
+
const line = lineFromOffset(offset);
|
|
6347
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6348
|
+
symbols.push(makeSymbol2({
|
|
6349
|
+
name,
|
|
6350
|
+
kind: "const",
|
|
6351
|
+
line,
|
|
6352
|
+
col,
|
|
6353
|
+
signature: `*${name}`,
|
|
6354
|
+
file,
|
|
6355
|
+
lang
|
|
6356
|
+
}));
|
|
6357
|
+
}
|
|
6358
|
+
const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6359
|
+
while ((match = kvRegex.exec(content)) !== null) {
|
|
6360
|
+
const indent = match[1].length;
|
|
6361
|
+
const key = match[2];
|
|
6362
|
+
const offset = match.index;
|
|
6363
|
+
const line = lineFromOffset(offset);
|
|
6364
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6365
|
+
const lineContent = lines[line - 1] ?? "";
|
|
6366
|
+
if (/^[|&>]/.test(lineContent.trim())) continue;
|
|
6367
|
+
if (key === "---" || key === "...") continue;
|
|
6368
|
+
if (indent > 12) continue;
|
|
6369
|
+
const value = extractValue(content, match.index);
|
|
6370
|
+
const kind = isScalar(value) ? "literal" : "property";
|
|
6371
|
+
const signature = `${key}: ${truncate(value, 60)}`;
|
|
6372
|
+
symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
|
|
6373
|
+
}
|
|
6374
|
+
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6375
|
+
while ((match = listItemRegex.exec(content)) !== null) {
|
|
6376
|
+
const key = match[2];
|
|
6377
|
+
const offset = match.index;
|
|
6378
|
+
const line = lineFromOffset(offset);
|
|
6379
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6380
|
+
const value = extractValue(content, offset + match[0].length);
|
|
6381
|
+
const kind = isScalar(value) ? "literal" : "property";
|
|
6382
|
+
symbols.push(makeSymbol2({
|
|
6383
|
+
name: key,
|
|
6384
|
+
kind,
|
|
6385
|
+
line,
|
|
6386
|
+
col,
|
|
6387
|
+
signature: `- ${key}: ${truncate(value, 60)}`,
|
|
6388
|
+
file,
|
|
6389
|
+
lang
|
|
6390
|
+
}));
|
|
6391
|
+
}
|
|
6392
|
+
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
6393
|
+
while ((match = blockScalarRegex.exec(content)) !== null) {
|
|
6394
|
+
const key = match[2];
|
|
6395
|
+
const offset = match.index;
|
|
6396
|
+
const line = lineFromOffset(offset);
|
|
6397
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
6398
|
+
symbols.push(makeSymbol2({
|
|
6399
|
+
name: key,
|
|
6400
|
+
kind: "property",
|
|
6401
|
+
line,
|
|
6402
|
+
col,
|
|
6403
|
+
signature: `${key}: | ...`,
|
|
6404
|
+
file,
|
|
6405
|
+
lang
|
|
6406
|
+
}));
|
|
6407
|
+
}
|
|
6408
|
+
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
6409
|
+
}
|
|
6410
|
+
function extractValue(content, afterColonOffset) {
|
|
6411
|
+
const lineEnd = content.indexOf("\n", afterColonOffset);
|
|
6412
|
+
const rest = content.slice(afterColonOffset, lineEnd < 0 ? void 0 : lineEnd);
|
|
6413
|
+
return rest.trim();
|
|
6414
|
+
}
|
|
6415
|
+
function isScalar(value) {
|
|
6416
|
+
if (!value) return false;
|
|
6417
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(value)) return true;
|
|
6418
|
+
if (/^(true|false|null|undefined)$/i.test(value)) return true;
|
|
6419
|
+
if (/^'[^']*'$/.test(value) || /^"[^"]*"$/.test(value)) return true;
|
|
6420
|
+
return false;
|
|
6421
|
+
}
|
|
6422
|
+
function truncate(s, max) {
|
|
6423
|
+
if (s.length <= max) return s;
|
|
6424
|
+
return s.slice(0, max) + "...";
|
|
6425
|
+
}
|
|
6426
|
+
function makeSymbol2(opts) {
|
|
6427
|
+
return {
|
|
6428
|
+
id: 0,
|
|
6429
|
+
lang: opts.lang,
|
|
6430
|
+
kind: opts.kind,
|
|
6431
|
+
name: opts.name,
|
|
6432
|
+
file: opts.file,
|
|
6433
|
+
line: opts.line,
|
|
6434
|
+
col: opts.col,
|
|
6435
|
+
signature: opts.signature,
|
|
6436
|
+
docComment: "",
|
|
6437
|
+
scope: "",
|
|
6438
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
|
|
6442
|
+
// src/codebase-index/indexer.ts
|
|
6443
|
+
var DEFAULT_IGNORE5 = [
|
|
6444
|
+
"node_modules",
|
|
6445
|
+
".git",
|
|
6446
|
+
"dist",
|
|
6447
|
+
"build",
|
|
6448
|
+
".next",
|
|
6449
|
+
"coverage",
|
|
6450
|
+
".turbo",
|
|
6451
|
+
"__snapshots__",
|
|
6452
|
+
".nyc_output"
|
|
6453
|
+
];
|
|
6454
|
+
async function findSourceFiles(projectRoot, ignore) {
|
|
6455
|
+
const results = [];
|
|
6456
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE5, ...ignore]);
|
|
6457
|
+
const globs = [
|
|
6458
|
+
{ ext: ".ts", pat: compileGlob("**/*.ts") },
|
|
6459
|
+
{ ext: ".tsx", pat: compileGlob("**/*.tsx") },
|
|
6460
|
+
{ ext: ".js", pat: compileGlob("**/*.js") },
|
|
6461
|
+
{ ext: ".jsx", pat: compileGlob("**/*.jsx") },
|
|
6462
|
+
{ ext: ".go", pat: compileGlob("**/*.go") },
|
|
6463
|
+
{ ext: ".py", pat: compileGlob("**/*.py") },
|
|
6464
|
+
{ ext: ".rs", pat: compileGlob("**/*.rs") },
|
|
6465
|
+
{ ext: ".json", pat: compileGlob("**/*.json") },
|
|
6466
|
+
{ ext: ".yaml", pat: compileGlob("**/*.yaml") },
|
|
6467
|
+
{ ext: ".yml", pat: compileGlob("**/*.yml") }
|
|
6468
|
+
];
|
|
6469
|
+
const walk = async (dir) => {
|
|
6470
|
+
let entries;
|
|
6471
|
+
try {
|
|
6472
|
+
entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
6473
|
+
} catch {
|
|
6474
|
+
return;
|
|
6475
|
+
}
|
|
6476
|
+
for (const e of entries) {
|
|
6477
|
+
if (ignoreSet.has(e.name)) continue;
|
|
6478
|
+
const full = path.join(dir, e.name);
|
|
6479
|
+
if (e.isDirectory()) {
|
|
6480
|
+
await walk(full);
|
|
6481
|
+
} else if (e.isFile()) {
|
|
6482
|
+
const rel = path.relative(projectRoot, full).replace(/\\/g, "/");
|
|
6483
|
+
const ext = path.extname(e.name);
|
|
6484
|
+
for (const { ext: extName, pat } of globs) {
|
|
6485
|
+
if (ext === extName && (pat.test(rel) || pat.test(e.name))) {
|
|
6486
|
+
results.push(full);
|
|
6487
|
+
break;
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
};
|
|
6493
|
+
await walk(projectRoot);
|
|
6494
|
+
return results;
|
|
6495
|
+
}
|
|
6496
|
+
async function parseFile(file, content, lang) {
|
|
6497
|
+
switch (lang) {
|
|
6498
|
+
case "ts":
|
|
6499
|
+
case "tsx":
|
|
6500
|
+
case "js":
|
|
6501
|
+
case "jsx":
|
|
6502
|
+
return parseSymbols({ file, content, lang });
|
|
6503
|
+
case "go":
|
|
6504
|
+
return parseSymbols2({ file, content, lang: "go" });
|
|
6505
|
+
case "py":
|
|
6506
|
+
return parseSymbols3({ file, lang: "py" });
|
|
6507
|
+
case "rs":
|
|
6508
|
+
return parseSymbols4({ file, content, lang: "rs" });
|
|
6509
|
+
case "json":
|
|
6510
|
+
return parseSymbols5({ file, content, lang: "json" });
|
|
6511
|
+
case "yaml":
|
|
6512
|
+
return parseSymbols6({ file, content, lang: "yaml" });
|
|
6513
|
+
default:
|
|
6514
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
async function runIndexer(ctx, opts) {
|
|
6518
|
+
const { projectRoot, force = false, langs, ignore = [] } = opts;
|
|
6519
|
+
const store = new IndexStore(projectRoot);
|
|
6520
|
+
const startMs = Date.now();
|
|
6521
|
+
const errors = [];
|
|
6522
|
+
const langStats = {};
|
|
6523
|
+
let filesIndexed = 0;
|
|
6524
|
+
let symbolsIndexed = 0;
|
|
6525
|
+
let files;
|
|
6526
|
+
if (opts.files && opts.files.length > 0) {
|
|
6527
|
+
files = opts.files.map((f) => path.resolve(projectRoot, f));
|
|
6528
|
+
} else {
|
|
6529
|
+
files = await findSourceFiles(projectRoot, ignore);
|
|
6530
|
+
}
|
|
6531
|
+
if (langs && langs.length > 0) {
|
|
6532
|
+
const langSet = new Set(langs);
|
|
6533
|
+
files = files.filter((f) => {
|
|
6534
|
+
const lang = detectLang(f);
|
|
6535
|
+
return lang ? langSet.has(lang) : false;
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
if (force) store.clearAll();
|
|
6539
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
6540
|
+
if (!force) {
|
|
6541
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
6542
|
+
}
|
|
6543
|
+
for (const file of files) {
|
|
6544
|
+
let stat11;
|
|
6545
|
+
try {
|
|
6546
|
+
stat11 = await fs4.stat(file);
|
|
6547
|
+
} catch {
|
|
6548
|
+
store.deleteFile(file);
|
|
6549
|
+
continue;
|
|
6550
|
+
}
|
|
6551
|
+
if (!stat11.isFile()) continue;
|
|
6552
|
+
const lang = detectLang(file);
|
|
6553
|
+
if (!lang) continue;
|
|
6554
|
+
const meta = existingMeta.get(file);
|
|
6555
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat11.mtimeMs)) {
|
|
6556
|
+
langStats[lang] = (langStats[lang] ?? 0) + meta.symbolCount;
|
|
6557
|
+
symbolsIndexed += meta.symbolCount;
|
|
6558
|
+
filesIndexed++;
|
|
6559
|
+
continue;
|
|
6560
|
+
}
|
|
6561
|
+
store.deleteSymbolsForFile(file);
|
|
6562
|
+
store.deleteRefsForFile(file);
|
|
6563
|
+
let content;
|
|
6564
|
+
try {
|
|
6565
|
+
content = await fs4.readFile(file, "utf8");
|
|
6566
|
+
} catch (e) {
|
|
6567
|
+
errors.push(`read error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
6568
|
+
continue;
|
|
6569
|
+
}
|
|
6570
|
+
let parsed;
|
|
6571
|
+
try {
|
|
6572
|
+
parsed = await parseFile(file, content, lang);
|
|
6573
|
+
} catch (e) {
|
|
6574
|
+
errors.push(`parse error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
if (parsed.symbols.length === 0) {
|
|
6578
|
+
store.upsertFile({
|
|
6579
|
+
file,
|
|
6580
|
+
lang,
|
|
6581
|
+
mtimeMs: Math.floor(stat11.mtimeMs),
|
|
6582
|
+
symbolCount: 0,
|
|
6583
|
+
lastIndexed: Date.now()
|
|
6584
|
+
});
|
|
6585
|
+
filesIndexed++;
|
|
6586
|
+
continue;
|
|
6587
|
+
}
|
|
6588
|
+
const nextId = store.getStats().totalSymbols + 1;
|
|
6589
|
+
const symbolsWithIds = parsed.symbols.map((s, i) => ({ ...s, id: nextId + i }));
|
|
6590
|
+
store.insertSymbols(symbolsWithIds, nextId);
|
|
6591
|
+
const count = symbolsWithIds.length;
|
|
6592
|
+
symbolsIndexed += count;
|
|
6593
|
+
langStats[lang] = (langStats[lang] ?? 0) + count;
|
|
6594
|
+
if (parsed.refs && parsed.refs.length > 0) {
|
|
6595
|
+
for (let i = 0; i < symbolsWithIds.length; i++) {
|
|
6596
|
+
const sym = symbolsWithIds[i];
|
|
6597
|
+
const symRefs = parsed.refs.filter((r) => r.line === sym.line);
|
|
6598
|
+
if (symRefs.length > 0) {
|
|
6599
|
+
const refsWithFromId = symRefs.map((r) => ({ ...r, fromId: sym.id }));
|
|
6600
|
+
store.insertRefs(sym.id, refsWithFromId);
|
|
6601
|
+
}
|
|
6602
|
+
}
|
|
6603
|
+
}
|
|
6604
|
+
store.upsertFile({
|
|
6605
|
+
file,
|
|
6606
|
+
lang,
|
|
6607
|
+
mtimeMs: Math.floor(stat11.mtimeMs),
|
|
6608
|
+
symbolCount: count,
|
|
6609
|
+
lastIndexed: Date.now()
|
|
6610
|
+
});
|
|
6611
|
+
filesIndexed++;
|
|
6612
|
+
}
|
|
6613
|
+
for (const [file_] of existingMeta) {
|
|
6614
|
+
try {
|
|
6615
|
+
await fs4.stat(file_);
|
|
6616
|
+
} catch {
|
|
6617
|
+
store.deleteFile(file_);
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
const durationMs = Date.now() - startMs;
|
|
6621
|
+
store.setLastIndexed(Date.now());
|
|
6622
|
+
store.close();
|
|
6623
|
+
return {
|
|
6624
|
+
filesIndexed,
|
|
6625
|
+
symbolsIndexed,
|
|
6626
|
+
langStats,
|
|
6627
|
+
durationMs,
|
|
6628
|
+
errors
|
|
6629
|
+
};
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
// src/codebase-index/codebase-index-tool.ts
|
|
6633
|
+
var codebaseIndexTool = {
|
|
6634
|
+
name: "codebase-index",
|
|
6635
|
+
category: "Project",
|
|
6636
|
+
description: "Build or update the symbol index for the project. Runs incrementally by default \u2014 only re-indexes files that changed since the last run.",
|
|
6637
|
+
usageHint: "Call with `force: true` to wipe and rebuild the index from scratch. Use `langs` to limit to specific languages. First call without arguments to do an incremental index.",
|
|
6638
|
+
permission: "auto",
|
|
6639
|
+
mutating: true,
|
|
6640
|
+
timeoutMs: 12e4,
|
|
6641
|
+
inputSchema: {
|
|
6642
|
+
type: "object",
|
|
6643
|
+
properties: {
|
|
6644
|
+
force: {
|
|
6645
|
+
type: "boolean",
|
|
6646
|
+
description: "Force a full reindex \u2014 clears the index first and reindexes all files."
|
|
6647
|
+
},
|
|
6648
|
+
langs: {
|
|
6649
|
+
type: "array",
|
|
6650
|
+
items: { type: "string" },
|
|
6651
|
+
description: "Limit reindex to specific languages: ts, tsx, js, jsx, go, py, rs"
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
},
|
|
6655
|
+
async execute(input, ctx) {
|
|
6656
|
+
const result = await runIndexer(ctx, {
|
|
6657
|
+
projectRoot: ctx.projectRoot,
|
|
6658
|
+
force: input.force ?? false,
|
|
6659
|
+
langs: input.langs
|
|
6660
|
+
});
|
|
6661
|
+
return result;
|
|
6662
|
+
}
|
|
6663
|
+
};
|
|
6664
|
+
|
|
6665
|
+
// src/codebase-index/bm25.ts
|
|
6666
|
+
var K1 = 1.5;
|
|
6667
|
+
var B = 0.75;
|
|
6668
|
+
function tokenise(text) {
|
|
6669
|
+
const sanitised = text.replace(/[^\p{L}\p{N}$'_]/gu, " ").replace(/_/g, " ");
|
|
6670
|
+
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
6671
|
+
}
|
|
6672
|
+
function splitName(name) {
|
|
6673
|
+
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").trim();
|
|
6674
|
+
}
|
|
6675
|
+
function buildIndexableText(name, signature, docComment) {
|
|
6676
|
+
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
6677
|
+
}
|
|
6678
|
+
function buildBm25Index(docs) {
|
|
6679
|
+
const documents = docs.map((d) => {
|
|
6680
|
+
const tokens = tokenise(d.text);
|
|
6681
|
+
return { id: d.id, tokens, raw: d.text, len: tokens.length };
|
|
6682
|
+
});
|
|
6683
|
+
const df = {};
|
|
6684
|
+
for (const doc of documents) {
|
|
6685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6686
|
+
for (const t of doc.tokens) {
|
|
6687
|
+
if (!seen.has(t)) {
|
|
6688
|
+
df[t] = (df[t] ?? 0) + 1;
|
|
6689
|
+
seen.add(t);
|
|
6690
|
+
}
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
const N = documents.length;
|
|
6694
|
+
const totalLen = documents.reduce((sum, d) => sum + d.len, 0);
|
|
6695
|
+
const avgLen = N === 0 ? 0 : totalLen / N;
|
|
6696
|
+
return new Bm25Index(documents, df, N, avgLen);
|
|
6697
|
+
}
|
|
6698
|
+
var Bm25Index = class {
|
|
6699
|
+
constructor(documents, df, N, avgLen) {
|
|
6700
|
+
this.documents = documents;
|
|
6701
|
+
this.df = df;
|
|
6702
|
+
this.N = N;
|
|
6703
|
+
this.safeAvgLen = avgLen === 0 ? 1 : avgLen;
|
|
6704
|
+
}
|
|
6705
|
+
documents;
|
|
6706
|
+
df;
|
|
6707
|
+
N;
|
|
6708
|
+
safeAvgLen;
|
|
6709
|
+
score(query2, filter) {
|
|
6710
|
+
const qTokens = tokenise(query2);
|
|
6711
|
+
if (qTokens.length === 0) return [];
|
|
6712
|
+
const results = [];
|
|
6713
|
+
for (const doc of this.documents) {
|
|
6714
|
+
if (filter && !filter(doc.id)) continue;
|
|
6715
|
+
let docScore = 0;
|
|
6716
|
+
for (const qTerm of qTokens) {
|
|
6717
|
+
let tf = 0;
|
|
6718
|
+
for (const t of doc.tokens) {
|
|
6719
|
+
if (t === qTerm) tf++;
|
|
6720
|
+
}
|
|
6721
|
+
if (tf === 0) continue;
|
|
6722
|
+
const dfVal = this.df[qTerm] ?? 0;
|
|
6723
|
+
if (dfVal === 0) continue;
|
|
6724
|
+
const idf = Math.log((this.N - dfVal + 0.5) / (dfVal + 0.5) + 1);
|
|
6725
|
+
const lenRatio = B * (doc.len / this.safeAvgLen);
|
|
6726
|
+
const tfComponent = tf * (K1 + 1) / (tf + K1 * (1 - B + lenRatio));
|
|
6727
|
+
docScore += idf * tfComponent;
|
|
6728
|
+
}
|
|
6729
|
+
if (docScore > 0) results.push({ id: doc.id, score: docScore });
|
|
6730
|
+
}
|
|
6731
|
+
return results;
|
|
6732
|
+
}
|
|
6733
|
+
getDoc(id) {
|
|
6734
|
+
return this.documents.find((d) => d.id === id);
|
|
6735
|
+
}
|
|
6736
|
+
extractSnippet(docId, queryTokens, radius = 40) {
|
|
6737
|
+
const doc = this.getDoc(docId);
|
|
6738
|
+
if (!doc) return "";
|
|
6739
|
+
for (const tok of queryTokens) {
|
|
6740
|
+
const idx = doc.raw.toLowerCase().indexOf(tok);
|
|
6741
|
+
if (idx !== -1) {
|
|
6742
|
+
const start = Math.max(0, idx - radius);
|
|
6743
|
+
const end = Math.min(doc.raw.length, idx + tok.length + radius);
|
|
6744
|
+
const excerpt = doc.raw.slice(start, end);
|
|
6745
|
+
const ellipsis = "\u2026";
|
|
6746
|
+
return (start > 0 ? ellipsis : "") + excerpt + (end < doc.raw.length ? ellipsis : "");
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
return doc.raw.slice(0, radius * 2) + (doc.raw.length > radius * 2 ? "\u2026" : "");
|
|
6750
|
+
}
|
|
6751
|
+
};
|
|
6752
|
+
|
|
6753
|
+
// src/codebase-index/codebase-search-tool.ts
|
|
6754
|
+
var codebaseSearchTool = {
|
|
6755
|
+
name: "codebase-search",
|
|
6756
|
+
category: "Project",
|
|
6757
|
+
description: "Search indexed code symbols by name, signature, or doc comment. Uses BM25 ranking for relevance.",
|
|
6758
|
+
usageHint: "Pass `query` for keyword search. Filter with `kind` (class/function/interface/etc), `lang` (ts/js/etc), `file` (substring). `limit` caps results (default 20).",
|
|
6759
|
+
permission: "auto",
|
|
6760
|
+
mutating: false,
|
|
6761
|
+
timeoutMs: 1e4,
|
|
6762
|
+
inputSchema: {
|
|
6763
|
+
type: "object",
|
|
6764
|
+
properties: {
|
|
6765
|
+
query: {
|
|
6766
|
+
type: "string",
|
|
6767
|
+
description: "Search query \u2014 searches symbol names, signatures, and doc comments"
|
|
6768
|
+
},
|
|
6769
|
+
kind: {
|
|
6770
|
+
type: "string",
|
|
6771
|
+
description: "Filter by symbol kind: class, function, interface, method, const, let, var, property, type, enum"
|
|
6772
|
+
},
|
|
6773
|
+
lang: {
|
|
6774
|
+
type: "string",
|
|
6775
|
+
description: "Filter by language: ts, tsx, js, jsx"
|
|
6776
|
+
},
|
|
6777
|
+
lspKind: {
|
|
6778
|
+
type: "integer",
|
|
6779
|
+
description: "Filter by LSP SymbolKind number (e.g. 5=Class, 12=Function, 11=Interface, 10=Enum)"
|
|
6780
|
+
},
|
|
6781
|
+
file: {
|
|
6782
|
+
type: "string",
|
|
6783
|
+
description: "Filter to files matching this path substring"
|
|
6784
|
+
},
|
|
6785
|
+
limit: {
|
|
6786
|
+
type: "integer",
|
|
6787
|
+
description: "Maximum results to return (default 20, max 100)",
|
|
6788
|
+
minimum: 1,
|
|
6789
|
+
maximum: 100
|
|
6790
|
+
}
|
|
6791
|
+
},
|
|
6792
|
+
required: ["query"]
|
|
6793
|
+
},
|
|
6794
|
+
async execute(input, ctx) {
|
|
6795
|
+
const store = new IndexStore(ctx.projectRoot);
|
|
6796
|
+
try {
|
|
6797
|
+
const limit = Math.min(input.limit ?? 20, 100);
|
|
6798
|
+
const candidates = store.search(input.query, {
|
|
6799
|
+
kind: input.kind,
|
|
6800
|
+
lang: input.lang,
|
|
6801
|
+
file: input.file,
|
|
6802
|
+
lspKind: input.lspKind
|
|
6803
|
+
});
|
|
6804
|
+
if (candidates.length === 0) {
|
|
6805
|
+
return { results: [], total: 0, query: input.query };
|
|
6806
|
+
}
|
|
6807
|
+
const indexable = candidates.map((c) => ({
|
|
6808
|
+
id: c.id,
|
|
6809
|
+
text: buildIndexableText(c.name, c.signature, c.docComment)
|
|
6810
|
+
}));
|
|
6811
|
+
const bm25 = buildBm25Index(indexable);
|
|
6812
|
+
const scored = bm25.score(input.query, (id) => candidates.some((c) => c.id === id));
|
|
6813
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6814
|
+
const top = scored.slice(0, limit);
|
|
6815
|
+
const qTokens = tokenise(input.query);
|
|
6816
|
+
const results = top.map(({ id, score }) => {
|
|
6817
|
+
const c = candidates.find((c2) => c2.id === id);
|
|
6818
|
+
const snippet = bm25.extractSnippet(id, qTokens);
|
|
6819
|
+
return {
|
|
6820
|
+
...c,
|
|
6821
|
+
score,
|
|
6822
|
+
snippet
|
|
6823
|
+
};
|
|
6824
|
+
});
|
|
6825
|
+
return {
|
|
6826
|
+
results,
|
|
6827
|
+
total: candidates.length,
|
|
6828
|
+
query: input.query
|
|
6829
|
+
};
|
|
6830
|
+
} finally {
|
|
6831
|
+
store.close();
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6834
|
+
};
|
|
6835
|
+
|
|
6836
|
+
// src/codebase-index/codebase-stats-tool.ts
|
|
6837
|
+
var codebaseStatsTool = {
|
|
6838
|
+
name: "codebase-stats",
|
|
6839
|
+
category: "Project",
|
|
6840
|
+
description: "Return statistics about the symbol index: total symbols, files, breakdown by language and kind, index size, and last update time.",
|
|
6841
|
+
usageHint: "No arguments needed. Use to check if the index is stale or healthy before running a search.",
|
|
6842
|
+
permission: "auto",
|
|
6843
|
+
mutating: false,
|
|
6844
|
+
timeoutMs: 5e3,
|
|
6845
|
+
inputSchema: {
|
|
6846
|
+
type: "object",
|
|
6847
|
+
properties: {},
|
|
6848
|
+
additionalProperties: false
|
|
6849
|
+
},
|
|
6850
|
+
async execute(_input, ctx) {
|
|
6851
|
+
const store = new IndexStore(ctx.projectRoot);
|
|
6852
|
+
try {
|
|
6853
|
+
const stats = store.getStats();
|
|
6854
|
+
return {
|
|
6855
|
+
totalSymbols: stats.totalSymbols,
|
|
6856
|
+
totalFiles: stats.totalFiles,
|
|
6857
|
+
byLang: stats.byLang,
|
|
6858
|
+
byKind: stats.byKind,
|
|
6859
|
+
lastIndexed: stats.lastIndexed,
|
|
6860
|
+
sizeBytes: stats.sizeBytes,
|
|
6861
|
+
indexPath: stats.indexPath,
|
|
6862
|
+
version: stats.version
|
|
6863
|
+
};
|
|
6864
|
+
} finally {
|
|
6865
|
+
store.close();
|
|
6866
|
+
}
|
|
6867
|
+
}
|
|
6868
|
+
};
|
|
6869
|
+
|
|
4936
6870
|
// src/builtin.ts
|
|
4937
6871
|
var builtinTools = [
|
|
4938
6872
|
readTool,
|
|
@@ -4965,7 +6899,10 @@ var builtinTools = [
|
|
|
4965
6899
|
toolSearchTool,
|
|
4966
6900
|
toolUseTool,
|
|
4967
6901
|
batchToolUseTool,
|
|
4968
|
-
toolHelpTool
|
|
6902
|
+
toolHelpTool,
|
|
6903
|
+
codebaseIndexTool,
|
|
6904
|
+
codebaseSearchTool,
|
|
6905
|
+
codebaseStatsTool
|
|
4969
6906
|
];
|
|
4970
6907
|
|
|
4971
6908
|
// src/pack.ts
|
|
@@ -4975,6 +6912,6 @@ var builtinToolsPack = {
|
|
|
4975
6912
|
tools: builtinTools
|
|
4976
6913
|
};
|
|
4977
6914
|
|
|
4978
|
-
export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, createModeTool, diffTool, documentTool, editTool, execTool, fetchTool, forgetTool, formatTool, getProcessRegistry, gitTool, globTool, grepTool, installTool, jsonTool, lintTool, logsTool, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
6915
|
+
export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, execTool, fetchTool, forgetTool, formatTool, getProcessRegistry, gitTool, globTool, grepTool, installTool, jsonTool, lintTool, logsTool, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
4979
6916
|
//# sourceMappingURL=index.js.map
|
|
4980
6917
|
//# sourceMappingURL=index.js.map
|