@wrongstack/tools 0.298.3 → 0.300.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +6 -2
- package/dist/bash.js +20 -2
- package/dist/builtin.js +1901 -327
- package/dist/codebase-index/background-indexer.d.ts +6 -1
- package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +34 -0
- package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +34 -0
- package/dist/codebase-index/import-extractor.d.ts +39 -0
- package/dist/codebase-index/index-service.d.ts +24 -2
- package/dist/codebase-index/index.d.ts +4 -2
- package/dist/codebase-index/index.js +1779 -256
- package/dist/codebase-index/languages.d.ts +24 -0
- package/dist/codebase-index/module-resolver.d.ts +78 -0
- package/dist/codebase-index/module-roots.d.ts +81 -0
- package/dist/codebase-index/parser-output.d.ts +29 -0
- package/dist/codebase-index/project-server.js +1556 -237
- package/dist/codebase-index/rs-parser.d.ts +22 -0
- package/dist/codebase-index/schema.d.ts +47 -1
- package/dist/codebase-index/worker-protocol.d.ts +14 -0
- package/dist/codebase-index/worker.js +1545 -238
- package/dist/codebase-index/writer-graph-helpers.d.ts +17 -5
- package/dist/codebase-index/writer-graph-reader.d.ts +24 -1
- package/dist/codebase-index/writer-ref-mapper.d.ts +3 -0
- package/dist/codebase-index/writer-schema.d.ts +15 -3
- package/dist/codebase-index/writer.d.ts +97 -4
- package/dist/exec.js +39 -2
- package/dist/format.js +6 -2
- package/dist/index.js +1901 -327
- package/dist/install.js +6 -2
- package/dist/json.js +51 -2
- package/dist/languages/index.js +6 -2
- package/dist/lint.js +6 -2
- package/dist/outdated.js +6 -2
- package/dist/pack.js +1899 -327
- package/dist/process-registry.d.ts +6 -0
- package/dist/process-registry.js +6 -2
- package/dist/ps-slash.js +10 -2
- package/dist/read.js +1551 -244
- package/dist/test.js +6 -2
- package/dist/tool-tier.js +1901 -327
- package/dist/typecheck.js +6 -2
- package/package.json +3 -3
- package/dist/codebase-index/refs-extractor.d.ts +0 -11
package/dist/index.js
CHANGED
|
@@ -724,7 +724,7 @@ var init_process_registry = __esm({
|
|
|
724
724
|
const p = this.processes.get(pid);
|
|
725
725
|
if (!p) return false;
|
|
726
726
|
if (p.killed) return true;
|
|
727
|
-
if (p.protected) return false;
|
|
727
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
728
728
|
if (opts.preserveBackground && p.background) return false;
|
|
729
729
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
730
730
|
const isWin5 = os.platform() === "win32";
|
|
@@ -775,9 +775,13 @@ var init_process_registry = __esm({
|
|
|
775
775
|
killAll(opts = {}) {
|
|
776
776
|
const pids = Array.from(this.processes.keys());
|
|
777
777
|
const killed = [];
|
|
778
|
+
const includeProtected = opts.includeProtected === true;
|
|
778
779
|
for (const pid of pids) {
|
|
779
780
|
const p = this.processes.get(pid);
|
|
780
|
-
if (
|
|
781
|
+
if (!p) continue;
|
|
782
|
+
if (p.protected && !includeProtected) continue;
|
|
783
|
+
if (opts.preserveBackground && p.background) continue;
|
|
784
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
781
785
|
}
|
|
782
786
|
return killed;
|
|
783
787
|
}
|
|
@@ -1141,18 +1145,18 @@ async function resolveRealInsideRoot(absPath, ctx) {
|
|
|
1141
1145
|
const realRoots = await Promise.all(
|
|
1142
1146
|
allowedRoots(ctx).map((r) => fsp2.realpath(r).catch(() => path3.resolve(r)))
|
|
1143
1147
|
);
|
|
1144
|
-
let
|
|
1148
|
+
let probe = absPath;
|
|
1145
1149
|
const pendingTail = [];
|
|
1146
1150
|
for (; ; ) {
|
|
1147
1151
|
let real;
|
|
1148
1152
|
try {
|
|
1149
|
-
real = await fsp2.realpath(
|
|
1153
|
+
real = await fsp2.realpath(probe);
|
|
1150
1154
|
} catch (err) {
|
|
1151
1155
|
if (err.code === "ENOENT") {
|
|
1152
|
-
const parent = path3.dirname(
|
|
1153
|
-
if (parent ===
|
|
1154
|
-
pendingTail.unshift(path3.basename(
|
|
1155
|
-
|
|
1156
|
+
const parent = path3.dirname(probe);
|
|
1157
|
+
if (parent === probe) return absPath;
|
|
1158
|
+
pendingTail.unshift(path3.basename(probe));
|
|
1159
|
+
probe = parent;
|
|
1156
1160
|
continue;
|
|
1157
1161
|
}
|
|
1158
1162
|
throw err;
|
|
@@ -4971,23 +4975,26 @@ var init_legacy_bridge = __esm({
|
|
|
4971
4975
|
});
|
|
4972
4976
|
|
|
4973
4977
|
// src/codebase-index/languages.ts
|
|
4974
|
-
import * as
|
|
4978
|
+
import * as path13 from "node:path";
|
|
4975
4979
|
function detectLang(file) {
|
|
4976
|
-
const base =
|
|
4980
|
+
const base = path13.basename(file);
|
|
4977
4981
|
const lowerBase = base.toLowerCase();
|
|
4978
4982
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4979
4983
|
return "ts";
|
|
4980
4984
|
}
|
|
4981
4985
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4982
4986
|
if (special) return special;
|
|
4983
|
-
const ext =
|
|
4987
|
+
const ext = path13.extname(base).toLowerCase();
|
|
4984
4988
|
if (!ext) return null;
|
|
4985
4989
|
return EXT_TO_LANG[ext] ?? null;
|
|
4986
4990
|
}
|
|
4987
4991
|
function isIndexablePath(file) {
|
|
4988
4992
|
return detectLang(file) !== null;
|
|
4989
4993
|
}
|
|
4990
|
-
|
|
4994
|
+
function languageFamily(lang) {
|
|
4995
|
+
return LANG_FAMILY[lang] ?? "other";
|
|
4996
|
+
}
|
|
4997
|
+
var EXT_TO_LANG, INDEXABLE_EXTENSIONS, SPECIAL_FILENAMES, LANG_FAMILY, LANG_FAMILY_ENTRIES;
|
|
4991
4998
|
var init_languages2 = __esm({
|
|
4992
4999
|
"src/codebase-index/languages.ts"() {
|
|
4993
5000
|
"use strict";
|
|
@@ -5078,6 +5085,52 @@ var init_languages2 = __esm({
|
|
|
5078
5085
|
procfile: "other",
|
|
5079
5086
|
justfile: "other"
|
|
5080
5087
|
};
|
|
5088
|
+
LANG_FAMILY = {
|
|
5089
|
+
// Single-compilation-unit family: a .vue/.svelte script block is JS/TS and
|
|
5090
|
+
// imports from — and is imported by — plain .ts files.
|
|
5091
|
+
ts: "js",
|
|
5092
|
+
tsx: "js",
|
|
5093
|
+
js: "js",
|
|
5094
|
+
jsx: "js",
|
|
5095
|
+
vue: "js",
|
|
5096
|
+
svelte: "js",
|
|
5097
|
+
go: "go",
|
|
5098
|
+
py: "py",
|
|
5099
|
+
rs: "rs",
|
|
5100
|
+
// The JVM resolves across languages: Kotlin and Scala call Java directly.
|
|
5101
|
+
java: "jvm",
|
|
5102
|
+
kotlin: "jvm",
|
|
5103
|
+
scala: "jvm",
|
|
5104
|
+
csharp: "dotnet",
|
|
5105
|
+
// A .h header is consumed by both C and C++ translation units.
|
|
5106
|
+
c: "c",
|
|
5107
|
+
cpp: "c",
|
|
5108
|
+
ruby: "ruby",
|
|
5109
|
+
php: "php",
|
|
5110
|
+
swift: "swift",
|
|
5111
|
+
dart: "dart",
|
|
5112
|
+
elixir: "elixir",
|
|
5113
|
+
haskell: "haskell",
|
|
5114
|
+
zig: "zig",
|
|
5115
|
+
lua: "lua",
|
|
5116
|
+
r: "r",
|
|
5117
|
+
shell: "shell",
|
|
5118
|
+
sql: "sql",
|
|
5119
|
+
json: "data",
|
|
5120
|
+
yaml: "data",
|
|
5121
|
+
toml: "data",
|
|
5122
|
+
html: "web",
|
|
5123
|
+
css: "web",
|
|
5124
|
+
proto: "proto",
|
|
5125
|
+
graphql: "graphql",
|
|
5126
|
+
md: "other",
|
|
5127
|
+
other: "other"
|
|
5128
|
+
};
|
|
5129
|
+
LANG_FAMILY_ENTRIES = Object.freeze(
|
|
5130
|
+
Object.entries(LANG_FAMILY).map(
|
|
5131
|
+
([lang, family]) => Object.freeze([lang, family])
|
|
5132
|
+
)
|
|
5133
|
+
);
|
|
5081
5134
|
}
|
|
5082
5135
|
});
|
|
5083
5136
|
|
|
@@ -5242,7 +5295,7 @@ function getTypeName(name) {
|
|
|
5242
5295
|
function deduplicateRefs(refs) {
|
|
5243
5296
|
const seen = /* @__PURE__ */ new Set();
|
|
5244
5297
|
return refs.filter((r) => {
|
|
5245
|
-
const key = `${r.toName}:${r.callType}:${r.line}`;
|
|
5298
|
+
const key = `${r.toName}:${r.callType}:${r.line}:${r.module ?? ""}`;
|
|
5246
5299
|
if (seen.has(key)) return false;
|
|
5247
5300
|
seen.add(key);
|
|
5248
5301
|
return true;
|
|
@@ -5252,10 +5305,16 @@ function getImportSpecifierName(spec) {
|
|
|
5252
5305
|
return spec.propertyName?.text ?? spec.name.text;
|
|
5253
5306
|
}
|
|
5254
5307
|
function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
5308
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
5255
5309
|
const clause = node.importClause;
|
|
5256
|
-
if (!clause)
|
|
5310
|
+
if (!clause) {
|
|
5311
|
+
if (module) {
|
|
5312
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
5313
|
+
}
|
|
5314
|
+
return;
|
|
5315
|
+
}
|
|
5257
5316
|
if (clause.name) {
|
|
5258
|
-
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5317
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
5259
5318
|
}
|
|
5260
5319
|
const bindings2 = clause.namedBindings;
|
|
5261
5320
|
if (!bindings2) return;
|
|
@@ -5265,26 +5324,40 @@ function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
|
5265
5324
|
fromId: 0,
|
|
5266
5325
|
toName: getImportSpecifierName(element),
|
|
5267
5326
|
callType: "import",
|
|
5268
|
-
line: lineNum
|
|
5327
|
+
line: lineNum,
|
|
5328
|
+
module
|
|
5269
5329
|
});
|
|
5270
5330
|
}
|
|
5271
5331
|
} else if (ts.isNamespaceImport(bindings2)) {
|
|
5272
|
-
refs.push({
|
|
5332
|
+
refs.push({
|
|
5333
|
+
fromId: 0,
|
|
5334
|
+
toName: bindings2.name.text,
|
|
5335
|
+
callType: "import",
|
|
5336
|
+
line: lineNum,
|
|
5337
|
+
module
|
|
5338
|
+
});
|
|
5273
5339
|
}
|
|
5274
5340
|
}
|
|
5341
|
+
function moduleSpecifierOf(node) {
|
|
5342
|
+
return node && ts.isStringLiteral(node) ? node.text : void 0;
|
|
5343
|
+
}
|
|
5275
5344
|
function emitExportSpecifierRefs(node, refs, lineNum) {
|
|
5345
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
5276
5346
|
const clause = node.exportClause;
|
|
5277
5347
|
if (clause && ts.isNamespaceExport(clause)) {
|
|
5278
|
-
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5348
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
5279
5349
|
return;
|
|
5280
5350
|
}
|
|
5281
5351
|
if (clause && ts.isNamedExports(clause)) {
|
|
5282
5352
|
for (const element of clause.elements) {
|
|
5283
5353
|
const originalName = element.propertyName?.text ?? element.name.text;
|
|
5284
|
-
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum });
|
|
5354
|
+
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum, module });
|
|
5285
5355
|
}
|
|
5286
5356
|
return;
|
|
5287
5357
|
}
|
|
5358
|
+
if (module) {
|
|
5359
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
5360
|
+
}
|
|
5288
5361
|
}
|
|
5289
5362
|
var ts, tsLoad, kindMapCache;
|
|
5290
5363
|
var init_ts_parser = __esm({
|
|
@@ -5296,6 +5369,82 @@ var init_ts_parser = __esm({
|
|
|
5296
5369
|
}
|
|
5297
5370
|
});
|
|
5298
5371
|
|
|
5372
|
+
// src/codebase-index/parser-output.ts
|
|
5373
|
+
function coerceSymbols(value) {
|
|
5374
|
+
if (!Array.isArray(value)) return [];
|
|
5375
|
+
return value.flatMap((entry) => {
|
|
5376
|
+
const candidate = entry;
|
|
5377
|
+
if (typeof candidate.name !== "string" || typeof candidate.kind !== "string") return [];
|
|
5378
|
+
return [
|
|
5379
|
+
{
|
|
5380
|
+
name: candidate.name,
|
|
5381
|
+
kind: candidate.kind,
|
|
5382
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
5383
|
+
col: typeof candidate.col === "number" ? candidate.col : 0,
|
|
5384
|
+
signature: typeof candidate.signature === "string" ? candidate.signature : "",
|
|
5385
|
+
scope: typeof candidate.scope === "string" ? candidate.scope : ""
|
|
5386
|
+
}
|
|
5387
|
+
];
|
|
5388
|
+
});
|
|
5389
|
+
}
|
|
5390
|
+
function coerceRefs(value, lang) {
|
|
5391
|
+
if (!Array.isArray(value)) return [];
|
|
5392
|
+
return value.flatMap((entry) => {
|
|
5393
|
+
const candidate = entry;
|
|
5394
|
+
if (typeof candidate.toName !== "string" || !candidate.toName) return [];
|
|
5395
|
+
if (typeof candidate.callType !== "string" || !CALL_TYPES.has(candidate.callType)) return [];
|
|
5396
|
+
const module = typeof candidate.module === "string" && candidate.module ? candidate.module : void 0;
|
|
5397
|
+
return [
|
|
5398
|
+
{
|
|
5399
|
+
fromId: 0,
|
|
5400
|
+
toName: candidate.toName,
|
|
5401
|
+
callType: candidate.callType,
|
|
5402
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
5403
|
+
lang,
|
|
5404
|
+
module
|
|
5405
|
+
}
|
|
5406
|
+
];
|
|
5407
|
+
});
|
|
5408
|
+
}
|
|
5409
|
+
function parseParserOutput(stdout, lang) {
|
|
5410
|
+
const trimmed = stdout.trim();
|
|
5411
|
+
if (!trimmed) return { symbols: [], refs: [] };
|
|
5412
|
+
let parsed;
|
|
5413
|
+
try {
|
|
5414
|
+
parsed = JSON.parse(trimmed);
|
|
5415
|
+
} catch {
|
|
5416
|
+
return { symbols: [], refs: [] };
|
|
5417
|
+
}
|
|
5418
|
+
if (Array.isArray(parsed)) return { symbols: coerceSymbols(parsed), refs: [] };
|
|
5419
|
+
const record = parsed;
|
|
5420
|
+
return {
|
|
5421
|
+
symbols: coerceSymbols(record.symbols),
|
|
5422
|
+
refs: dedupeRefs(coerceRefs(record.refs, lang))
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
function dedupeRefs(refs) {
|
|
5426
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5427
|
+
return refs.filter((ref) => {
|
|
5428
|
+
const key = `${ref.toName}:${ref.callType}:${ref.line}:${ref.module ?? ""}`;
|
|
5429
|
+
if (seen.has(key)) return false;
|
|
5430
|
+
seen.add(key);
|
|
5431
|
+
return true;
|
|
5432
|
+
});
|
|
5433
|
+
}
|
|
5434
|
+
var CALL_TYPES;
|
|
5435
|
+
var init_parser_output = __esm({
|
|
5436
|
+
"src/codebase-index/parser-output.ts"() {
|
|
5437
|
+
"use strict";
|
|
5438
|
+
CALL_TYPES = /* @__PURE__ */ new Set([
|
|
5439
|
+
"call",
|
|
5440
|
+
"type_ref",
|
|
5441
|
+
"inherit",
|
|
5442
|
+
"implement",
|
|
5443
|
+
"import"
|
|
5444
|
+
]);
|
|
5445
|
+
}
|
|
5446
|
+
});
|
|
5447
|
+
|
|
5299
5448
|
// src/codebase-index/spawn-gate.ts
|
|
5300
5449
|
function withSpawnGate(fn) {
|
|
5301
5450
|
const run = chain.then(fn, fn);
|
|
@@ -5321,8 +5470,8 @@ __export(go_parser_exports, {
|
|
|
5321
5470
|
});
|
|
5322
5471
|
import { spawn as spawn5 } from "node:child_process";
|
|
5323
5472
|
import * as os6 from "node:os";
|
|
5324
|
-
import * as
|
|
5325
|
-
import * as
|
|
5473
|
+
import * as path20 from "node:path";
|
|
5474
|
+
import * as fs15 from "node:fs/promises";
|
|
5326
5475
|
async function parseSymbols2(opts) {
|
|
5327
5476
|
const { file, content, lang } = opts;
|
|
5328
5477
|
try {
|
|
@@ -5330,7 +5479,8 @@ async function parseSymbols2(opts) {
|
|
|
5330
5479
|
if (parsed.symbols.length > 0) {
|
|
5331
5480
|
return parsed;
|
|
5332
5481
|
}
|
|
5333
|
-
|
|
5482
|
+
const fallback = fallbackParse(file, content, lang);
|
|
5483
|
+
return parsed.refs?.length ? { ...fallback, refs: parsed.refs } : fallback;
|
|
5334
5484
|
} catch {
|
|
5335
5485
|
return fallbackParse(file, content, lang);
|
|
5336
5486
|
}
|
|
@@ -5394,9 +5544,9 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5394
5544
|
try {
|
|
5395
5545
|
let scriptPath = _cachedGoScriptPath;
|
|
5396
5546
|
if (!scriptPath) {
|
|
5397
|
-
const tmpDir = await
|
|
5398
|
-
scriptPath =
|
|
5399
|
-
await
|
|
5547
|
+
const tmpDir = await fs15.mkdtemp(path20.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5548
|
+
scriptPath = path20.join(tmpDir, "parse.go");
|
|
5549
|
+
await fs15.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5400
5550
|
_cachedGoScriptPath = scriptPath;
|
|
5401
5551
|
}
|
|
5402
5552
|
const goBinary = resolveWin32Command("go");
|
|
@@ -5438,8 +5588,8 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5438
5588
|
if (code !== 0 || !stdout.trim()) {
|
|
5439
5589
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5440
5590
|
}
|
|
5441
|
-
const
|
|
5442
|
-
const symbols =
|
|
5591
|
+
const { symbols: rawSymbols, refs } = parseParserOutput(stdout, lang);
|
|
5592
|
+
const symbols = rawSymbols.map((s) => ({
|
|
5443
5593
|
id: 0,
|
|
5444
5594
|
lang,
|
|
5445
5595
|
kind: s.kind,
|
|
@@ -5452,7 +5602,7 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5452
5602
|
scope: s.scope ?? "",
|
|
5453
5603
|
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
5454
5604
|
}));
|
|
5455
|
-
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
5605
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
5456
5606
|
} catch {
|
|
5457
5607
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5458
5608
|
}
|
|
@@ -5462,6 +5612,7 @@ var init_go_parser = __esm({
|
|
|
5462
5612
|
"src/codebase-index/go-parser.ts"() {
|
|
5463
5613
|
"use strict";
|
|
5464
5614
|
init_win32_resolve();
|
|
5615
|
+
init_parser_output();
|
|
5465
5616
|
init_spawn_gate();
|
|
5466
5617
|
init_languages2();
|
|
5467
5618
|
GO_PARSE_SCRIPT = `
|
|
@@ -5475,6 +5626,7 @@ import (
|
|
|
5475
5626
|
"go/token"
|
|
5476
5627
|
"io"
|
|
5477
5628
|
"os"
|
|
5629
|
+
"strconv"
|
|
5478
5630
|
"strings"
|
|
5479
5631
|
)
|
|
5480
5632
|
|
|
@@ -5487,16 +5639,34 @@ type Sym struct {
|
|
|
5487
5639
|
Scope string \`json:"scope"\`
|
|
5488
5640
|
}
|
|
5489
5641
|
|
|
5642
|
+
// Ref is a cross-reference emitted alongside the symbols, so one \`go run\`
|
|
5643
|
+
// yields both. Module is the import path for CallType "import", else empty.
|
|
5644
|
+
type Ref struct {
|
|
5645
|
+
ToName string \`json:"toName"\`
|
|
5646
|
+
CallType string \`json:"callType"\`
|
|
5647
|
+
Line int \`json:"line"\`
|
|
5648
|
+
Module string \`json:"module"\`
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5651
|
+
type Result struct {
|
|
5652
|
+
Symbols []Sym \`json:"symbols"\`
|
|
5653
|
+
Refs []Ref \`json:"refs"\`
|
|
5654
|
+
}
|
|
5655
|
+
|
|
5656
|
+
func emptyResult() string {
|
|
5657
|
+
return "{\\"symbols\\":[],\\"refs\\":[]}"
|
|
5658
|
+
}
|
|
5659
|
+
|
|
5490
5660
|
func main() {
|
|
5491
5661
|
src, err := io.ReadAll(os.Stdin)
|
|
5492
5662
|
if err != nil {
|
|
5493
|
-
fmt.Print(
|
|
5663
|
+
fmt.Print(emptyResult())
|
|
5494
5664
|
return
|
|
5495
5665
|
}
|
|
5496
5666
|
fset := token.NewFileSet()
|
|
5497
5667
|
node, err := parser.ParseFile(fset, "src.go", src, 0)
|
|
5498
5668
|
if err != nil {
|
|
5499
|
-
fmt.Print(
|
|
5669
|
+
fmt.Print(emptyResult())
|
|
5500
5670
|
return
|
|
5501
5671
|
}
|
|
5502
5672
|
|
|
@@ -5560,9 +5730,43 @@ func main() {
|
|
|
5560
5730
|
}
|
|
5561
5731
|
}
|
|
5562
5732
|
|
|
5563
|
-
|
|
5733
|
+
refs := []Ref{}
|
|
5734
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
5735
|
+
switch expr := n.(type) {
|
|
5736
|
+
case *ast.CallExpr:
|
|
5737
|
+
line := fset.Position(expr.Pos()).Line
|
|
5738
|
+
switch fun := expr.Fun.(type) {
|
|
5739
|
+
case *ast.Ident:
|
|
5740
|
+
refs = append(refs, Ref{ToName: fun.Name, CallType: "call", Line: line})
|
|
5741
|
+
case *ast.SelectorExpr:
|
|
5742
|
+
// Record the selected name (\`Join\` of \`filepath.Join\`): it is the
|
|
5743
|
+
// declared symbol name, so it resolves the same way the TypeScript
|
|
5744
|
+
// and Python extractors' call refs do.
|
|
5745
|
+
refs = append(refs, Ref{ToName: fun.Sel.Name, CallType: "call", Line: line})
|
|
5746
|
+
}
|
|
5747
|
+
case *ast.ImportSpec:
|
|
5748
|
+
if expr.Path != nil {
|
|
5749
|
+
if importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {
|
|
5750
|
+
line := fset.Position(expr.Pos()).Line
|
|
5751
|
+
// A Go import names a package, not a symbol; the package's
|
|
5752
|
+
// last path segment is the name it is referenced by.
|
|
5753
|
+
name := importPath
|
|
5754
|
+
if idx := strings.LastIndex(importPath, "/"); idx >= 0 {
|
|
5755
|
+
name = importPath[idx+1:]
|
|
5756
|
+
}
|
|
5757
|
+
refs = append(refs, Ref{ToName: name, CallType: "import", Line: line, Module: importPath})
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
return true
|
|
5762
|
+
})
|
|
5763
|
+
|
|
5764
|
+
if syms == nil {
|
|
5765
|
+
syms = []Sym{}
|
|
5766
|
+
}
|
|
5767
|
+
data, err := json.Marshal(Result{Symbols: syms, Refs: refs})
|
|
5564
5768
|
if err != nil {
|
|
5565
|
-
fmt.Print(
|
|
5769
|
+
fmt.Print(emptyResult())
|
|
5566
5770
|
return
|
|
5567
5771
|
}
|
|
5568
5772
|
fmt.Print(string(data))
|
|
@@ -5914,9 +6118,13 @@ var init_generic_parser = __esm({
|
|
|
5914
6118
|
],
|
|
5915
6119
|
elixir: [
|
|
5916
6120
|
{ re: /\bdef(?:p|macro|macrop)?\s+([A-Za-z_]\w*[!?]?)/g, kind: "function" },
|
|
5917
|
-
|
|
6121
|
+
// Dotted module names must be captured whole: `alias Foo.Bar` resolves
|
|
6122
|
+
// against this symbol, and a `Foo`-only capture never matches it.
|
|
6123
|
+
{ re: /\bdefmodule\s+([A-Z][\w.]*)/g, kind: "namespace" }
|
|
5918
6124
|
],
|
|
5919
6125
|
haskell: [
|
|
6126
|
+
// Target of `import Data.List`.
|
|
6127
|
+
{ re: /^module\s+([A-Z][\w.]*)/gm, kind: "namespace" },
|
|
5920
6128
|
{ re: /^([A-Za-z_]\w*)\s*::/gm, kind: "function" },
|
|
5921
6129
|
{ re: /\bdata\s+([A-Za-z_]\w*)/g, kind: "type" },
|
|
5922
6130
|
{ re: /\btype\s+(?:family\s+)?([A-Za-z_]\w*)/g, kind: "type" },
|
|
@@ -6007,9 +6215,9 @@ __export(py_parser_exports, {
|
|
|
6007
6215
|
parseSymbols: () => parseSymbols4
|
|
6008
6216
|
});
|
|
6009
6217
|
import { spawn as spawn6 } from "node:child_process";
|
|
6010
|
-
import * as
|
|
6218
|
+
import * as fs16 from "node:fs/promises";
|
|
6011
6219
|
import * as os7 from "node:os";
|
|
6012
|
-
import * as
|
|
6220
|
+
import * as path21 from "node:path";
|
|
6013
6221
|
async function parseSymbols4(opts) {
|
|
6014
6222
|
const { file, content, lang } = opts;
|
|
6015
6223
|
try {
|
|
@@ -6087,10 +6295,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
6087
6295
|
async function syncPyParse(filePath, content, lang) {
|
|
6088
6296
|
try {
|
|
6089
6297
|
if (!_cachedScriptPath) {
|
|
6090
|
-
const tmpDir =
|
|
6091
|
-
await
|
|
6092
|
-
_cachedScriptPath =
|
|
6093
|
-
await
|
|
6298
|
+
const tmpDir = path21.join(os7.tmpdir(), "ws-py-parse");
|
|
6299
|
+
await fs16.mkdir(tmpDir, { recursive: true });
|
|
6300
|
+
_cachedScriptPath = path21.join(tmpDir, "parse.py");
|
|
6301
|
+
await fs16.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
6094
6302
|
}
|
|
6095
6303
|
cachedPyBinary ??= resolvePython();
|
|
6096
6304
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6104,7 +6312,7 @@ async function syncPyParse(filePath, content, lang) {
|
|
|
6104
6312
|
if (code !== 0 || !stdout.trim()) {
|
|
6105
6313
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
6106
6314
|
}
|
|
6107
|
-
const raw =
|
|
6315
|
+
const { symbols: raw, refs } = parseParserOutput(stdout, lang);
|
|
6108
6316
|
const symbols = raw.map((s) => ({
|
|
6109
6317
|
id: 0,
|
|
6110
6318
|
lang,
|
|
@@ -6118,7 +6326,7 @@ async function syncPyParse(filePath, content, lang) {
|
|
|
6118
6326
|
scope: s.scope ?? "",
|
|
6119
6327
|
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
6120
6328
|
}));
|
|
6121
|
-
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
6329
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
6122
6330
|
} catch {
|
|
6123
6331
|
return null;
|
|
6124
6332
|
}
|
|
@@ -6129,6 +6337,7 @@ var init_py_parser = __esm({
|
|
|
6129
6337
|
"use strict";
|
|
6130
6338
|
init_win32_resolve();
|
|
6131
6339
|
init_generic_parser();
|
|
6340
|
+
init_parser_output();
|
|
6132
6341
|
init_spawn_gate();
|
|
6133
6342
|
init_languages2();
|
|
6134
6343
|
PY_PARSE_SCRIPT = `import ast, json, sys, os
|
|
@@ -6190,7 +6399,18 @@ class Sym:
|
|
|
6190
6399
|
def is_private(name):
|
|
6191
6400
|
return name.startswith("__") and not name.endswith("__")
|
|
6192
6401
|
|
|
6402
|
+
def leaf_name(node):
|
|
6403
|
+
# Declared name of the callee: \`join\` of \`os.path.join\`. Matches how the
|
|
6404
|
+
# TypeScript and Go extractors record call refs, so resolution behaves the
|
|
6405
|
+
# same across languages.
|
|
6406
|
+
if isinstance(node, ast.Attribute):
|
|
6407
|
+
return node.attr
|
|
6408
|
+
if isinstance(node, ast.Name):
|
|
6409
|
+
return node.id
|
|
6410
|
+
return get_name(node).split(".")[-1]
|
|
6411
|
+
|
|
6193
6412
|
syms = []
|
|
6413
|
+
refs = []
|
|
6194
6414
|
errors = []
|
|
6195
6415
|
|
|
6196
6416
|
try:
|
|
@@ -6198,7 +6418,7 @@ try:
|
|
|
6198
6418
|
tree = ast.parse(source, filename=sys.argv[1])
|
|
6199
6419
|
except Exception as e:
|
|
6200
6420
|
errors.append(str(e))
|
|
6201
|
-
print("[]")
|
|
6421
|
+
print(json.dumps({"symbols": [], "refs": []}))
|
|
6202
6422
|
sys.exit(0)
|
|
6203
6423
|
|
|
6204
6424
|
# Module-level scope
|
|
@@ -6332,7 +6552,42 @@ class ModuleVisitor(ast.NodeVisitor):
|
|
|
6332
6552
|
visitor = ModuleVisitor()
|
|
6333
6553
|
visitor.visit(tree)
|
|
6334
6554
|
|
|
6335
|
-
|
|
6555
|
+
# Refs need a separate full walk: ModuleVisitor deliberately does not descend
|
|
6556
|
+
# into function bodies (it would index locals as symbols), but that is exactly
|
|
6557
|
+
# where the calls are.
|
|
6558
|
+
for node in ast.walk(tree):
|
|
6559
|
+
if isinstance(node, ast.Call):
|
|
6560
|
+
name = leaf_name(node.func)
|
|
6561
|
+
if name:
|
|
6562
|
+
refs.append({"toName": name, "callType": "call", "line": node.lineno})
|
|
6563
|
+
elif isinstance(node, ast.Import):
|
|
6564
|
+
for alias in node.names:
|
|
6565
|
+
refs.append({
|
|
6566
|
+
"toName": alias.name.split(".")[-1],
|
|
6567
|
+
"callType": "import",
|
|
6568
|
+
"line": node.lineno,
|
|
6569
|
+
"module": alias.name,
|
|
6570
|
+
})
|
|
6571
|
+
elif isinstance(node, ast.ImportFrom):
|
|
6572
|
+
# PEP 328: node.level is the number of leading dots. Preserving them is
|
|
6573
|
+
# what lets the resolver walk up from the importing file's package \u2014
|
|
6574
|
+
# dropping them made \`from .foo import X\` indistinguishable from an
|
|
6575
|
+
# absolute \`foo\`.
|
|
6576
|
+
module = ("." * (node.level or 0)) + (node.module or "")
|
|
6577
|
+
for alias in node.names:
|
|
6578
|
+
refs.append({
|
|
6579
|
+
"toName": alias.name,
|
|
6580
|
+
"callType": "import",
|
|
6581
|
+
"line": node.lineno,
|
|
6582
|
+
"module": module,
|
|
6583
|
+
})
|
|
6584
|
+
elif isinstance(node, ast.ClassDef):
|
|
6585
|
+
for base in node.bases:
|
|
6586
|
+
name = leaf_name(base)
|
|
6587
|
+
if name:
|
|
6588
|
+
refs.append({"toName": name, "callType": "inherit", "line": node.lineno})
|
|
6589
|
+
|
|
6590
|
+
print(json.dumps({"symbols": [s.to_dict() for s in syms], "refs": refs}))
|
|
6336
6591
|
`;
|
|
6337
6592
|
_cachedScriptPath = null;
|
|
6338
6593
|
}
|
|
@@ -6345,107 +6600,10 @@ __export(rs_parser_exports, {
|
|
|
6345
6600
|
parseSymbols: () => parseSymbols5
|
|
6346
6601
|
});
|
|
6347
6602
|
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6348
|
-
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6349
|
-
import * as fs16 from "node:fs/promises";
|
|
6350
|
-
import * as path21 from "node:path";
|
|
6351
6603
|
async function parseSymbols5(opts) {
|
|
6352
6604
|
const { file, content, lang } = opts;
|
|
6353
|
-
const nativeAvailable = await checkNativeParser();
|
|
6354
|
-
if (nativeAvailable) {
|
|
6355
|
-
const result = await withSpawnGate(() => tryNativeParse(file, content));
|
|
6356
|
-
if (result) return result;
|
|
6357
|
-
}
|
|
6358
6605
|
return regexParse({ file, content, lang });
|
|
6359
6606
|
}
|
|
6360
|
-
function probe(command, args) {
|
|
6361
|
-
return new Promise((resolve17, reject) => {
|
|
6362
|
-
execFile(command, args, { timeout: 1e4, windowsHide: true }, (error) => {
|
|
6363
|
-
if (error) reject(error);
|
|
6364
|
-
else resolve17();
|
|
6365
|
-
});
|
|
6366
|
-
});
|
|
6367
|
-
}
|
|
6368
|
-
function checkNativeParser() {
|
|
6369
|
-
nativeParserAvailability ??= (async () => {
|
|
6370
|
-
try {
|
|
6371
|
-
await probe("rustc", ["--version"]);
|
|
6372
|
-
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6373
|
-
await probe(
|
|
6374
|
-
"cargo",
|
|
6375
|
-
[
|
|
6376
|
-
"metadata",
|
|
6377
|
-
"--no-deps",
|
|
6378
|
-
"--format-version",
|
|
6379
|
-
"1",
|
|
6380
|
-
"--manifest-path",
|
|
6381
|
-
path21.join(toolsDir, "Cargo.toml")
|
|
6382
|
-
]
|
|
6383
|
-
);
|
|
6384
|
-
return true;
|
|
6385
|
-
} catch {
|
|
6386
|
-
return false;
|
|
6387
|
-
}
|
|
6388
|
-
})();
|
|
6389
|
-
return nativeParserAvailability;
|
|
6390
|
-
}
|
|
6391
|
-
async function tryNativeParse(file, content) {
|
|
6392
|
-
try {
|
|
6393
|
-
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6394
|
-
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6395
|
-
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6396
|
-
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6397
|
-
const cargoBinary = resolveWin32Command("cargo");
|
|
6398
|
-
const result = await new Promise(
|
|
6399
|
-
(resolve17, reject) => {
|
|
6400
|
-
let settled = false;
|
|
6401
|
-
const proc = spawn7(
|
|
6402
|
-
cargoBinary,
|
|
6403
|
-
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6404
|
-
{
|
|
6405
|
-
cwd: process.cwd(),
|
|
6406
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
6407
|
-
windowsHide: true
|
|
6408
|
-
}
|
|
6409
|
-
);
|
|
6410
|
-
proc.on("error", (err) => {
|
|
6411
|
-
if (settled) return;
|
|
6412
|
-
settled = true;
|
|
6413
|
-
reject(err);
|
|
6414
|
-
});
|
|
6415
|
-
let stdout2 = "";
|
|
6416
|
-
proc.stdout?.on("data", (chunk) => {
|
|
6417
|
-
stdout2 += chunk.toString();
|
|
6418
|
-
});
|
|
6419
|
-
proc.stderr?.resume();
|
|
6420
|
-
const timer = setTimeout(() => {
|
|
6421
|
-
if (settled) return;
|
|
6422
|
-
settled = true;
|
|
6423
|
-
proc.kill("SIGKILL");
|
|
6424
|
-
reject(new Error("timeout"));
|
|
6425
|
-
}, 15e3);
|
|
6426
|
-
timer.unref?.();
|
|
6427
|
-
proc.on("close", (c) => {
|
|
6428
|
-
if (settled) return;
|
|
6429
|
-
settled = true;
|
|
6430
|
-
clearTimeout(timer);
|
|
6431
|
-
resolve17({ code: c, stdout: stdout2 });
|
|
6432
|
-
});
|
|
6433
|
-
}
|
|
6434
|
-
);
|
|
6435
|
-
const { code, stdout } = result;
|
|
6436
|
-
if (code === 0 && stdout.trim()) {
|
|
6437
|
-
const symbols = JSON.parse(stdout.trim());
|
|
6438
|
-
return {
|
|
6439
|
-
file,
|
|
6440
|
-
lang: "rs",
|
|
6441
|
-
symbols: symbols.map((s) => ({ ...s, id: 0, lang: "rs" })),
|
|
6442
|
-
mtimeMs: Date.now()
|
|
6443
|
-
};
|
|
6444
|
-
}
|
|
6445
|
-
} catch {
|
|
6446
|
-
}
|
|
6447
|
-
return null;
|
|
6448
|
-
}
|
|
6449
6607
|
function regexParse(opts) {
|
|
6450
6608
|
const { file, content, lang } = opts;
|
|
6451
6609
|
const symbols = [];
|
|
@@ -6501,12 +6659,10 @@ function regexParse(opts) {
|
|
|
6501
6659
|
});
|
|
6502
6660
|
return { file, lang, symbols: deduped, mtimeMs: Date.now() };
|
|
6503
6661
|
}
|
|
6504
|
-
var
|
|
6662
|
+
var RS_PATTERNS;
|
|
6505
6663
|
var init_rs_parser = __esm({
|
|
6506
6664
|
"src/codebase-index/rs-parser.ts"() {
|
|
6507
6665
|
"use strict";
|
|
6508
|
-
init_win32_resolve();
|
|
6509
|
-
init_spawn_gate();
|
|
6510
6666
|
init_languages2();
|
|
6511
6667
|
RS_PATTERNS = [
|
|
6512
6668
|
{ regex: /fn\s+(\w+)\s*\([^)]*\)/g, kind: "function" },
|
|
@@ -7440,6 +7596,10 @@ async function acquireLock(lockfilePath, timeoutMs = 5e3) {
|
|
|
7440
7596
|
const start = Date.now();
|
|
7441
7597
|
const pidStr = String(process.pid);
|
|
7442
7598
|
const hostStr = os2.hostname();
|
|
7599
|
+
try {
|
|
7600
|
+
await fs6.mkdir(path9.dirname(lockfilePath), { recursive: true });
|
|
7601
|
+
} catch {
|
|
7602
|
+
}
|
|
7443
7603
|
while (Date.now() - start < timeoutMs) {
|
|
7444
7604
|
try {
|
|
7445
7605
|
await fs6.writeFile(lockfilePath, `${pidStr}:${hostStr}:${Date.now()}`, { flag: "wx" });
|
|
@@ -8111,6 +8271,16 @@ function parseKillCommand(command) {
|
|
|
8111
8271
|
if (pgrepMatch) {
|
|
8112
8272
|
return null;
|
|
8113
8273
|
}
|
|
8274
|
+
const posixScriptMatch = normalized.match(SCRIPT_KILL_RE_POSIX);
|
|
8275
|
+
if (posixScriptMatch) {
|
|
8276
|
+
return {
|
|
8277
|
+
name: "kill-script",
|
|
8278
|
+
signal: "FORCE",
|
|
8279
|
+
isGroupKill: false,
|
|
8280
|
+
isAllKill: false,
|
|
8281
|
+
originalCommand: command
|
|
8282
|
+
};
|
|
8283
|
+
}
|
|
8114
8284
|
return null;
|
|
8115
8285
|
}
|
|
8116
8286
|
async function getProtectedEntries() {
|
|
@@ -10307,7 +10477,7 @@ async function shutdownBrowserTools() {
|
|
|
10307
10477
|
|
|
10308
10478
|
// src/codebase-index/project-server-client.ts
|
|
10309
10479
|
import { spawn as spawn4 } from "node:child_process";
|
|
10310
|
-
import * as
|
|
10480
|
+
import * as fs13 from "node:fs";
|
|
10311
10481
|
import * as net3 from "node:net";
|
|
10312
10482
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10313
10483
|
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
@@ -10397,16 +10567,16 @@ function resetIndexCircuitBreaker() {
|
|
|
10397
10567
|
|
|
10398
10568
|
// src/codebase-index/project-server-endpoint.ts
|
|
10399
10569
|
import { createHash as createHash4 } from "node:crypto";
|
|
10400
|
-
import * as
|
|
10570
|
+
import * as fs12 from "node:fs";
|
|
10401
10571
|
import * as os5 from "node:os";
|
|
10402
|
-
import * as
|
|
10572
|
+
import * as path17 from "node:path";
|
|
10403
10573
|
import { fileURLToPath } from "node:url";
|
|
10404
10574
|
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
10405
10575
|
|
|
10406
10576
|
// src/codebase-index/writer.ts
|
|
10407
10577
|
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
10408
|
-
import * as
|
|
10409
|
-
import * as
|
|
10578
|
+
import * as fs11 from "node:fs";
|
|
10579
|
+
import * as path16 from "node:path";
|
|
10410
10580
|
|
|
10411
10581
|
// src/codebase-index/bm25.ts
|
|
10412
10582
|
var K1 = 1.5;
|
|
@@ -10497,6 +10667,9 @@ var Bm25Index = class {
|
|
|
10497
10667
|
}
|
|
10498
10668
|
};
|
|
10499
10669
|
|
|
10670
|
+
// src/codebase-index/writer.ts
|
|
10671
|
+
init_languages2();
|
|
10672
|
+
|
|
10500
10673
|
// src/codebase-index/lsp-kind.ts
|
|
10501
10674
|
function lspKindToInternalKind(k) {
|
|
10502
10675
|
switch (k) {
|
|
@@ -10531,7 +10704,7 @@ function lspKindToInternalKind(k) {
|
|
|
10531
10704
|
}
|
|
10532
10705
|
|
|
10533
10706
|
// src/codebase-index/schema.ts
|
|
10534
|
-
var SCHEMA_VERSION =
|
|
10707
|
+
var SCHEMA_VERSION = 4;
|
|
10535
10708
|
|
|
10536
10709
|
// src/codebase-index/sqlite-runtime.ts
|
|
10537
10710
|
import { createRequire } from "node:module";
|
|
@@ -10602,7 +10775,7 @@ function runSqliteWithRetry(fn) {
|
|
|
10602
10775
|
|
|
10603
10776
|
// src/codebase-index/writer-admin.ts
|
|
10604
10777
|
import * as fs9 from "node:fs";
|
|
10605
|
-
import * as
|
|
10778
|
+
import * as path14 from "node:path";
|
|
10606
10779
|
var DB_FILE = "index.db";
|
|
10607
10780
|
function getAllIndexableWithStatement(stmt) {
|
|
10608
10781
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10661,7 +10834,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10661
10834
|
}
|
|
10662
10835
|
function getIndexDbSizeBytes(indexDir) {
|
|
10663
10836
|
try {
|
|
10664
|
-
return fs9.statSync(
|
|
10837
|
+
return fs9.statSync(path14.join(indexDir, DB_FILE)).size;
|
|
10665
10838
|
} catch {
|
|
10666
10839
|
return 0;
|
|
10667
10840
|
}
|
|
@@ -10712,49 +10885,345 @@ function bulkInsertFtsWithStatement(stmt, maxSqlVars, ftsAvailable, rows) {
|
|
|
10712
10885
|
}
|
|
10713
10886
|
function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
10714
10887
|
if (refs.length === 0) return;
|
|
10715
|
-
const chunkSize = Math.max(1, Math.floor(maxSqlVars /
|
|
10888
|
+
const chunkSize = Math.max(1, Math.floor(maxSqlVars / 8));
|
|
10716
10889
|
for (let i = 0; i < refs.length; i += chunkSize) {
|
|
10717
10890
|
const chunk = refs.slice(i, i + chunkSize);
|
|
10718
|
-
const placeholders = chunk.map(() => "(?, ?, ?, ?, ?)").join(", ");
|
|
10891
|
+
const placeholders = chunk.map(() => "(?, ?, ?, ?, ?, ?, ?, ?)").join(", ");
|
|
10719
10892
|
const insert = stmt(
|
|
10720
|
-
`INSERT INTO refs(from_id, to_name, to_id, call_type, line
|
|
10893
|
+
`INSERT INTO refs(from_id, to_name, to_id, call_type, line, lang, module, to_file)
|
|
10894
|
+
VALUES ${placeholders}`
|
|
10721
10895
|
);
|
|
10722
10896
|
const binds = [];
|
|
10723
10897
|
for (const ref of chunk) {
|
|
10724
|
-
binds.push(
|
|
10898
|
+
binds.push(
|
|
10899
|
+
ref.fromId,
|
|
10900
|
+
ref.toName,
|
|
10901
|
+
ref.toId ?? null,
|
|
10902
|
+
ref.callType,
|
|
10903
|
+
ref.line,
|
|
10904
|
+
ref.lang ?? "",
|
|
10905
|
+
ref.module ?? null,
|
|
10906
|
+
ref.toFile ?? null
|
|
10907
|
+
);
|
|
10725
10908
|
}
|
|
10726
10909
|
insert.run(...binds);
|
|
10727
10910
|
}
|
|
10728
10911
|
}
|
|
10729
10912
|
|
|
10730
|
-
// src/codebase-index/writer-graph-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10913
|
+
// src/codebase-index/writer-graph-reader.ts
|
|
10914
|
+
init_languages2();
|
|
10915
|
+
|
|
10916
|
+
// src/codebase-index/module-roots.ts
|
|
10917
|
+
init_languages2();
|
|
10918
|
+
import * as fs10 from "node:fs/promises";
|
|
10919
|
+
import * as path15 from "node:path";
|
|
10920
|
+
function toPortablePath(file) {
|
|
10921
|
+
return file.replace(/\\/g, "/");
|
|
10922
|
+
}
|
|
10923
|
+
async function readTextIfPresent(file) {
|
|
10924
|
+
try {
|
|
10925
|
+
return await fs10.readFile(file, "utf8");
|
|
10926
|
+
} catch {
|
|
10927
|
+
return void 0;
|
|
10928
|
+
}
|
|
10929
|
+
}
|
|
10930
|
+
function parsePackageJsonName(source) {
|
|
10931
|
+
try {
|
|
10932
|
+
const parsed = JSON.parse(source);
|
|
10933
|
+
return typeof parsed.name === "string" && parsed.name ? parsed.name : void 0;
|
|
10934
|
+
} catch {
|
|
10935
|
+
return void 0;
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
function parseGoModulePath(source) {
|
|
10939
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
10940
|
+
const line = rawLine.replace(/\/\/.*$/, "").trim();
|
|
10941
|
+
const match = /^module\s+(\S+)/.exec(line);
|
|
10942
|
+
if (match?.[1]) return match[1].replace(/^["']|["']$/g, "");
|
|
10943
|
+
}
|
|
10944
|
+
return void 0;
|
|
10945
|
+
}
|
|
10946
|
+
function parseTomlTableName(source, tables) {
|
|
10947
|
+
let current = "";
|
|
10948
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
10949
|
+
const line = rawLine.replace(/#.*$/, "").trim();
|
|
10950
|
+
if (line.startsWith("[[")) {
|
|
10951
|
+
current = "\0";
|
|
10952
|
+
continue;
|
|
10953
|
+
}
|
|
10954
|
+
const table = /^\[([^\]]+)\]$/.exec(line);
|
|
10955
|
+
if (table?.[1]) {
|
|
10956
|
+
current = table[1].trim();
|
|
10957
|
+
continue;
|
|
10958
|
+
}
|
|
10959
|
+
if (!tables.includes(current)) continue;
|
|
10960
|
+
const match = /^name\s*=\s*["']([^"']+)["']/.exec(line);
|
|
10961
|
+
if (match?.[1]) return match[1];
|
|
10962
|
+
}
|
|
10963
|
+
return void 0;
|
|
10964
|
+
}
|
|
10965
|
+
function parsePomArtifactId(source) {
|
|
10966
|
+
const withoutParent = source.replace(/<parent>[\s\S]*?<\/parent>/g, "");
|
|
10967
|
+
return /<artifactId>\s*([^<\s]+)\s*<\/artifactId>/.exec(withoutParent)?.[1];
|
|
10968
|
+
}
|
|
10969
|
+
var LANGS_BY_KIND = {
|
|
10970
|
+
npm: ["ts", "tsx", "js", "jsx", "vue", "svelte"],
|
|
10971
|
+
cargo: ["rs"],
|
|
10972
|
+
go: ["go"],
|
|
10973
|
+
python: ["py"],
|
|
10974
|
+
maven: ["java", "kotlin", "scala"],
|
|
10975
|
+
gradle: ["java", "kotlin", "scala"],
|
|
10976
|
+
dotnet: ["csharp"]
|
|
10977
|
+
};
|
|
10978
|
+
function ancestorsOf(dir, stopAt) {
|
|
10979
|
+
const out = [];
|
|
10980
|
+
let current = dir;
|
|
10981
|
+
for (; ; ) {
|
|
10982
|
+
out.push(current);
|
|
10983
|
+
if (current === stopAt || current.length <= stopAt.length) break;
|
|
10984
|
+
const parent = path15.posix.dirname(current);
|
|
10985
|
+
if (parent === current) break;
|
|
10986
|
+
current = parent;
|
|
10987
|
+
}
|
|
10988
|
+
return out;
|
|
10989
|
+
}
|
|
10990
|
+
var MARKER_PROBES = [
|
|
10991
|
+
{
|
|
10992
|
+
kind: "npm",
|
|
10993
|
+
file: "package.json",
|
|
10994
|
+
build: (dir, source) => {
|
|
10995
|
+
const name = parsePackageJsonName(source) ?? path15.posix.basename(dir);
|
|
10996
|
+
return { name, importPath: name, sourceRoots: [dir] };
|
|
10997
|
+
}
|
|
10998
|
+
},
|
|
10999
|
+
{
|
|
11000
|
+
kind: "cargo",
|
|
11001
|
+
file: "Cargo.toml",
|
|
11002
|
+
build: (dir, source) => {
|
|
11003
|
+
const name = parseTomlTableName(source, ["package"]);
|
|
11004
|
+
if (!name) return void 0;
|
|
11005
|
+
return {
|
|
11006
|
+
name: `crate:${name}`,
|
|
11007
|
+
// Rust paths use underscores where crate names often use dashes.
|
|
11008
|
+
importPath: name.replace(/-/g, "_"),
|
|
11009
|
+
sourceRoots: [path15.posix.join(dir, "src")]
|
|
11010
|
+
};
|
|
11011
|
+
}
|
|
11012
|
+
},
|
|
11013
|
+
{
|
|
11014
|
+
kind: "go",
|
|
11015
|
+
file: "go.mod",
|
|
11016
|
+
build: (dir, source) => {
|
|
11017
|
+
const modulePath = parseGoModulePath(source);
|
|
11018
|
+
if (!modulePath) return void 0;
|
|
11019
|
+
return { name: `go:${modulePath}`, importPath: modulePath, sourceRoots: [dir] };
|
|
11020
|
+
}
|
|
11021
|
+
},
|
|
11022
|
+
{
|
|
11023
|
+
kind: "python",
|
|
11024
|
+
file: "pyproject.toml",
|
|
11025
|
+
build: (dir, source) => {
|
|
11026
|
+
const name = parseTomlTableName(source, ["project", "tool.poetry"]) ?? path15.posix.basename(dir);
|
|
11027
|
+
return {
|
|
11028
|
+
name: `py:${name}`,
|
|
11029
|
+
importPath: void 0,
|
|
11030
|
+
// `src/` layout is the packaging-guide default; the root itself covers
|
|
11031
|
+
// the flat layout. Both are probed, missing ones simply never match.
|
|
11032
|
+
sourceRoots: [path15.posix.join(dir, "src"), dir]
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
},
|
|
11036
|
+
{
|
|
11037
|
+
kind: "python",
|
|
11038
|
+
file: "setup.py",
|
|
11039
|
+
build: (dir) => ({
|
|
11040
|
+
name: `py:${path15.posix.basename(dir)}`,
|
|
11041
|
+
importPath: void 0,
|
|
11042
|
+
sourceRoots: [path15.posix.join(dir, "src"), dir]
|
|
11043
|
+
})
|
|
11044
|
+
},
|
|
11045
|
+
{
|
|
11046
|
+
kind: "maven",
|
|
11047
|
+
file: "pom.xml",
|
|
11048
|
+
build: (dir, source) => {
|
|
11049
|
+
const artifactId = parsePomArtifactId(source) ?? path15.posix.basename(dir);
|
|
11050
|
+
return {
|
|
11051
|
+
name: `mvn:${artifactId}`,
|
|
11052
|
+
importPath: void 0,
|
|
11053
|
+
sourceRoots: [
|
|
11054
|
+
path15.posix.join(dir, "src/main/java"),
|
|
11055
|
+
path15.posix.join(dir, "src/main/kotlin"),
|
|
11056
|
+
path15.posix.join(dir, "src/main/scala"),
|
|
11057
|
+
path15.posix.join(dir, "src/test/java")
|
|
11058
|
+
]
|
|
11059
|
+
};
|
|
11060
|
+
}
|
|
11061
|
+
},
|
|
11062
|
+
{
|
|
11063
|
+
kind: "gradle",
|
|
11064
|
+
file: "build.gradle",
|
|
11065
|
+
build: (dir) => buildGradleRoot(dir)
|
|
11066
|
+
},
|
|
11067
|
+
{
|
|
11068
|
+
kind: "gradle",
|
|
11069
|
+
file: "build.gradle.kts",
|
|
11070
|
+
build: (dir) => buildGradleRoot(dir)
|
|
11071
|
+
}
|
|
11072
|
+
];
|
|
11073
|
+
function buildGradleRoot(dir) {
|
|
11074
|
+
return {
|
|
11075
|
+
name: `gradle:${path15.posix.basename(dir)}`,
|
|
11076
|
+
importPath: void 0,
|
|
11077
|
+
sourceRoots: [
|
|
11078
|
+
path15.posix.join(dir, "src/main/java"),
|
|
11079
|
+
path15.posix.join(dir, "src/main/kotlin"),
|
|
11080
|
+
path15.posix.join(dir, "src/main/scala")
|
|
11081
|
+
]
|
|
11082
|
+
};
|
|
11083
|
+
}
|
|
11084
|
+
async function probeDotnetRoot(dir) {
|
|
11085
|
+
let entries;
|
|
11086
|
+
try {
|
|
11087
|
+
entries = await fs10.readdir(dir);
|
|
11088
|
+
} catch {
|
|
11089
|
+
return void 0;
|
|
11090
|
+
}
|
|
11091
|
+
const project = entries.find((entry) => entry.toLowerCase().endsWith(".csproj"));
|
|
11092
|
+
if (!project) return void 0;
|
|
11093
|
+
const name = project.slice(0, -".csproj".length);
|
|
11094
|
+
return {
|
|
11095
|
+
dir,
|
|
11096
|
+
kind: "dotnet",
|
|
11097
|
+
name: `csproj:${name}`,
|
|
11098
|
+
importPath: void 0,
|
|
11099
|
+
sourceRoots: [dir]
|
|
11100
|
+
};
|
|
11101
|
+
}
|
|
11102
|
+
async function detectModuleRoots(projectRoot, files) {
|
|
11103
|
+
const root = toPortablePath(projectRoot).replace(/\/+$/, "");
|
|
11104
|
+
const langsByDir = /* @__PURE__ */ new Map();
|
|
11105
|
+
for (const file of files) {
|
|
11106
|
+
const portable = toPortablePath(file);
|
|
11107
|
+
const lang = detectLang(portable);
|
|
11108
|
+
if (!lang) continue;
|
|
11109
|
+
const dir = path15.posix.dirname(portable);
|
|
11110
|
+
let langs = langsByDir.get(dir);
|
|
11111
|
+
if (!langs) {
|
|
11112
|
+
langs = /* @__PURE__ */ new Set();
|
|
11113
|
+
langsByDir.set(dir, langs);
|
|
11114
|
+
}
|
|
11115
|
+
langs.add(lang);
|
|
11116
|
+
}
|
|
11117
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
11118
|
+
for (const [dir, langs] of langsByDir) {
|
|
11119
|
+
for (const ancestor of ancestorsOf(dir, root)) {
|
|
11120
|
+
let merged = candidates.get(ancestor);
|
|
11121
|
+
if (!merged) {
|
|
11122
|
+
merged = /* @__PURE__ */ new Set();
|
|
11123
|
+
candidates.set(ancestor, merged);
|
|
11124
|
+
}
|
|
11125
|
+
for (const lang of langs) merged.add(lang);
|
|
11126
|
+
}
|
|
11127
|
+
}
|
|
11128
|
+
const roots = [];
|
|
11129
|
+
await Promise.all(
|
|
11130
|
+
[...candidates].map(async ([dir, langs]) => {
|
|
11131
|
+
for (const probe of MARKER_PROBES) {
|
|
11132
|
+
if (!LANGS_BY_KIND[probe.kind].some((lang) => langs.has(lang))) continue;
|
|
11133
|
+
const source = await readTextIfPresent(path15.posix.join(dir, probe.file));
|
|
11134
|
+
if (source === void 0) continue;
|
|
11135
|
+
const built = probe.build(dir, source);
|
|
11136
|
+
if (built) roots.push({ dir, kind: probe.kind, ...built });
|
|
11137
|
+
}
|
|
11138
|
+
if (LANGS_BY_KIND.dotnet.some((lang) => langs.has(lang))) {
|
|
11139
|
+
const dotnet = await probeDotnetRoot(dir);
|
|
11140
|
+
if (dotnet) roots.push(dotnet);
|
|
11141
|
+
}
|
|
11142
|
+
})
|
|
11143
|
+
);
|
|
11144
|
+
roots.sort((a, b) => b.dir.length - a.dir.length || a.dir.localeCompare(b.dir));
|
|
11145
|
+
return { projectRoot: root, roots };
|
|
11146
|
+
}
|
|
11147
|
+
function findOwningRoot(structure, file, kinds) {
|
|
11148
|
+
const portable = toPortablePath(file);
|
|
11149
|
+
for (const root of structure.roots) {
|
|
11150
|
+
if (kinds && !kinds.includes(root.kind)) continue;
|
|
11151
|
+
if (portable === root.dir || portable.startsWith(`${root.dir}/`)) return root;
|
|
11152
|
+
}
|
|
11153
|
+
return void 0;
|
|
11154
|
+
}
|
|
11155
|
+
function derivePackageFromLayout(filePath) {
|
|
11156
|
+
const portable = toPortablePath(filePath);
|
|
11157
|
+
const packagesIdx = portable.indexOf("/packages/");
|
|
11158
|
+
if (packagesIdx !== -1) {
|
|
11159
|
+
const segment = portable.slice(packagesIdx + "/packages/".length).split("/")[0];
|
|
11160
|
+
if (segment) return `@wrongstack/${segment}`;
|
|
11161
|
+
}
|
|
11162
|
+
const appsIdx = portable.indexOf("/apps/");
|
|
10741
11163
|
if (appsIdx !== -1) {
|
|
10742
|
-
const
|
|
10743
|
-
|
|
10744
|
-
return seg ? `app:${seg}` : void 0;
|
|
11164
|
+
const segment = portable.slice(appsIdx + "/apps/".length).split("/")[0];
|
|
11165
|
+
if (segment) return `app:${segment}`;
|
|
10745
11166
|
}
|
|
10746
11167
|
return void 0;
|
|
10747
11168
|
}
|
|
10748
|
-
function
|
|
10749
|
-
|
|
10750
|
-
const
|
|
10751
|
-
|
|
11169
|
+
function pythonPackageLabel(structure, file, initDirs) {
|
|
11170
|
+
const portable = toPortablePath(file);
|
|
11171
|
+
const dir = path15.posix.dirname(portable);
|
|
11172
|
+
if (!initDirs.has(dir)) return void 0;
|
|
11173
|
+
const segments = [];
|
|
11174
|
+
let current = dir;
|
|
11175
|
+
while (initDirs.has(current) && current.length > structure.projectRoot.length) {
|
|
11176
|
+
segments.unshift(path15.posix.basename(current));
|
|
11177
|
+
current = path15.posix.dirname(current);
|
|
11178
|
+
}
|
|
11179
|
+
return segments.length > 0 ? `py:${segments.join(".")}` : void 0;
|
|
10752
11180
|
}
|
|
10753
|
-
function
|
|
11181
|
+
function assignPackageLabels(structure, files) {
|
|
11182
|
+
const initDirs = /* @__PURE__ */ new Set();
|
|
11183
|
+
for (const file of files) {
|
|
11184
|
+
const portable = toPortablePath(file);
|
|
11185
|
+
if (path15.posix.basename(portable) === "__init__.py") {
|
|
11186
|
+
initDirs.add(path15.posix.dirname(portable));
|
|
11187
|
+
}
|
|
11188
|
+
}
|
|
11189
|
+
const labels = /* @__PURE__ */ new Map();
|
|
11190
|
+
for (const file of files) {
|
|
11191
|
+
const portable = toPortablePath(file);
|
|
11192
|
+
const lang = detectLang(portable);
|
|
11193
|
+
if (lang === "go") {
|
|
11194
|
+
const owner3 = findOwningRoot(structure, portable, ["go"]);
|
|
11195
|
+
const dir = path15.posix.dirname(portable);
|
|
11196
|
+
if (owner3?.importPath) {
|
|
11197
|
+
const relative13 = path15.posix.relative(owner3.dir, dir);
|
|
11198
|
+
labels.set(file, relative13 ? `${owner3.importPath}/${relative13}` : owner3.importPath);
|
|
11199
|
+
} else {
|
|
11200
|
+
labels.set(file, `go:${path15.posix.relative(structure.projectRoot, dir) || "."}`);
|
|
11201
|
+
}
|
|
11202
|
+
continue;
|
|
11203
|
+
}
|
|
11204
|
+
if (lang === "py") {
|
|
11205
|
+
const dotted = pythonPackageLabel(structure, portable, initDirs);
|
|
11206
|
+
if (dotted) {
|
|
11207
|
+
labels.set(file, dotted);
|
|
11208
|
+
continue;
|
|
11209
|
+
}
|
|
11210
|
+
}
|
|
11211
|
+
const owner2 = findOwningRoot(structure, portable);
|
|
11212
|
+
const label = owner2?.name ?? derivePackageFromLayout(portable) ?? "(root)";
|
|
11213
|
+
labels.set(file, label);
|
|
11214
|
+
}
|
|
11215
|
+
return labels;
|
|
11216
|
+
}
|
|
11217
|
+
|
|
11218
|
+
// src/codebase-index/writer-graph-helpers.ts
|
|
11219
|
+
function createPackageLabeller(stored) {
|
|
11220
|
+
return (file) => stored.get(file) ?? derivePackageFromLayout(file) ?? "(root)";
|
|
11221
|
+
}
|
|
11222
|
+
function buildPackageGraphNodes(fileCounts, files, packageOf) {
|
|
10754
11223
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
10755
11224
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
10756
11225
|
for (const { file, n } of fileCounts) {
|
|
10757
|
-
const pkg =
|
|
11226
|
+
const pkg = packageOf(file);
|
|
10758
11227
|
fileToPkg.set(file, pkg);
|
|
10759
11228
|
const node = pkgNodes.get(pkg);
|
|
10760
11229
|
if (node) {
|
|
@@ -10771,7 +11240,7 @@ function buildPackageGraphNodes(fileCounts, files) {
|
|
|
10771
11240
|
}
|
|
10772
11241
|
}
|
|
10773
11242
|
for (const { file } of files) {
|
|
10774
|
-
const pkg =
|
|
11243
|
+
const pkg = packageOf(file);
|
|
10775
11244
|
fileToPkg.set(file, pkg);
|
|
10776
11245
|
const node = pkgNodes.get(pkg);
|
|
10777
11246
|
if (node) {
|
|
@@ -10789,7 +11258,7 @@ function buildPackageGraphNodes(fileCounts, files) {
|
|
|
10789
11258
|
}
|
|
10790
11259
|
return { pkgNodes, fileToPkg };
|
|
10791
11260
|
}
|
|
10792
|
-
function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
11261
|
+
function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
|
|
10793
11262
|
const fileNodes = /* @__PURE__ */ new Map();
|
|
10794
11263
|
const symToFile = /* @__PURE__ */ new Map();
|
|
10795
11264
|
const fileStats = /* @__PURE__ */ new Map();
|
|
@@ -10808,7 +11277,7 @@ function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
|
10808
11277
|
id: `file:${file}`,
|
|
10809
11278
|
label: file.replace(/\\/g, "/").split("/").pop() ?? file,
|
|
10810
11279
|
kind: "file",
|
|
10811
|
-
package:
|
|
11280
|
+
package: packageOf(file),
|
|
10812
11281
|
file,
|
|
10813
11282
|
symbolCount: stats?.count ?? 0,
|
|
10814
11283
|
lang: stats?.lang,
|
|
@@ -10820,7 +11289,7 @@ function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
|
10820
11289
|
}
|
|
10821
11290
|
return { fileNodes, symToFile, fileStats, ensureFileNode };
|
|
10822
11291
|
}
|
|
10823
|
-
function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
11292
|
+
function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
|
|
10824
11293
|
return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
|
|
10825
11294
|
const aExternal = a.file === fileFilter ? 0 : 1;
|
|
10826
11295
|
const bExternal = b.file === fileFilter ? 0 : 1;
|
|
@@ -10832,7 +11301,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10832
11301
|
symbolId: s.id,
|
|
10833
11302
|
symbolKind: s.kind,
|
|
10834
11303
|
file: s.file,
|
|
10835
|
-
package:
|
|
11304
|
+
package: packageOf(s.file),
|
|
10836
11305
|
lang: s.lang,
|
|
10837
11306
|
line: s.line,
|
|
10838
11307
|
signature: s.signature,
|
|
@@ -10840,29 +11309,6 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10840
11309
|
external: s.file !== fileFilter
|
|
10841
11310
|
}));
|
|
10842
11311
|
}
|
|
10843
|
-
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10844
|
-
if (!moduleName.startsWith(".")) return void 0;
|
|
10845
|
-
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10846
|
-
const absolute = path14.posix.normalize(
|
|
10847
|
-
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10848
|
-
);
|
|
10849
|
-
const extension = path14.posix.extname(absolute);
|
|
10850
|
-
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10851
|
-
const candidates = [
|
|
10852
|
-
absolute,
|
|
10853
|
-
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10854
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10855
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10856
|
-
];
|
|
10857
|
-
const indexedByPortablePath = new Map(
|
|
10858
|
-
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
10859
|
-
);
|
|
10860
|
-
for (const candidate of candidates) {
|
|
10861
|
-
const indexed = indexedByPortablePath.get(candidate.toLocaleLowerCase());
|
|
10862
|
-
if (indexed) return indexed;
|
|
10863
|
-
}
|
|
10864
|
-
return void 0;
|
|
10865
|
-
}
|
|
10866
11312
|
function addWeightedEdge(edgeMap, source, target, callType, weight) {
|
|
10867
11313
|
const key = `${source}\0${target}`;
|
|
10868
11314
|
let edge = edgeMap.get(key);
|
|
@@ -10903,11 +11349,146 @@ function mapWriterRefRow(row) {
|
|
|
10903
11349
|
toName: row.to_name,
|
|
10904
11350
|
toId: row.to_id ?? void 0,
|
|
10905
11351
|
callType: row.call_type,
|
|
10906
|
-
line: row.line
|
|
11352
|
+
line: row.line,
|
|
11353
|
+
// `lang`/`module`/`to_file` are absent from the narrower column lists some
|
|
11354
|
+
// queries select; `undefined` keeps those rows valid Refs.
|
|
11355
|
+
lang: row.lang || void 0,
|
|
11356
|
+
module: row.module ?? void 0,
|
|
11357
|
+
toFile: row.to_file ?? void 0
|
|
10907
11358
|
};
|
|
10908
11359
|
}
|
|
10909
11360
|
|
|
10910
11361
|
// src/codebase-index/writer-graph-reader.ts
|
|
11362
|
+
var MAX_SQL_VARS = 900;
|
|
11363
|
+
function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
11364
|
+
const results = [];
|
|
11365
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
11366
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
11367
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
11368
|
+
const sql = buildSql(placeholders);
|
|
11369
|
+
results.push(...stmt(sql).all(...chunk, ...extraArgs));
|
|
11370
|
+
}
|
|
11371
|
+
return results;
|
|
11372
|
+
}
|
|
11373
|
+
function chunkedIdScalar(stmt, ids, buildSql, extraArgs = []) {
|
|
11374
|
+
let total = 0;
|
|
11375
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
11376
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
11377
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
11378
|
+
const sql = buildSql(placeholders);
|
|
11379
|
+
const rows = stmt(sql).all(...chunk, ...extraArgs);
|
|
11380
|
+
total += rows[0]?.n ?? 0;
|
|
11381
|
+
}
|
|
11382
|
+
return total;
|
|
11383
|
+
}
|
|
11384
|
+
function mapCallSiteRow(row) {
|
|
11385
|
+
return {
|
|
11386
|
+
symbol: {
|
|
11387
|
+
id: row.sym_id,
|
|
11388
|
+
name: row.sym_name,
|
|
11389
|
+
kind: row.sym_kind,
|
|
11390
|
+
lang: row.sym_lang,
|
|
11391
|
+
file: row.sym_file,
|
|
11392
|
+
line: row.sym_line,
|
|
11393
|
+
signature: row.sym_signature
|
|
11394
|
+
},
|
|
11395
|
+
callType: row.call_type,
|
|
11396
|
+
line: row.ref_line
|
|
11397
|
+
};
|
|
11398
|
+
}
|
|
11399
|
+
function resolveSymbolIds(stmt, symbolName, file) {
|
|
11400
|
+
const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
|
|
11401
|
+
const args = file ? [symbolName, file] : [symbolName];
|
|
11402
|
+
const rows = stmt(baseSql).all(...args);
|
|
11403
|
+
return rows.map((r) => r.id);
|
|
11404
|
+
}
|
|
11405
|
+
function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
11406
|
+
const targetIds = resolveSymbolIds(stmt, symbolName, file);
|
|
11407
|
+
if (targetIds.length === 0) return { calls: [], symbolFound: false, ambiguous: false, totalMatches: 0 };
|
|
11408
|
+
let matchIds = targetIds;
|
|
11409
|
+
let ambiguous = false;
|
|
11410
|
+
if (file !== void 0) {
|
|
11411
|
+
const allNamedIds = resolveSymbolIds(stmt, symbolName, void 0);
|
|
11412
|
+
if (allNamedIds.length > targetIds.length) {
|
|
11413
|
+
matchIds = allNamedIds;
|
|
11414
|
+
ambiguous = true;
|
|
11415
|
+
}
|
|
11416
|
+
}
|
|
11417
|
+
const useFallback = !file;
|
|
11418
|
+
const rows = chunkedIdQuery(
|
|
11419
|
+
stmt,
|
|
11420
|
+
matchIds,
|
|
11421
|
+
(ph) => `SELECT
|
|
11422
|
+
s.id AS sym_id,
|
|
11423
|
+
s.name AS sym_name,
|
|
11424
|
+
s.kind AS sym_kind,
|
|
11425
|
+
s.lang AS sym_lang,
|
|
11426
|
+
s.file AS sym_file,
|
|
11427
|
+
s.line AS sym_line,
|
|
11428
|
+
s.signature AS sym_signature,
|
|
11429
|
+
r.call_type,
|
|
11430
|
+
r.line AS ref_line
|
|
11431
|
+
FROM refs r
|
|
11432
|
+
JOIN symbols s ON s.id = r.from_id
|
|
11433
|
+
WHERE r.to_id IN (${ph})
|
|
11434
|
+
ORDER BY r.line, r.id`,
|
|
11435
|
+
[]
|
|
11436
|
+
);
|
|
11437
|
+
if (useFallback) {
|
|
11438
|
+
const fallbackRows = stmt(
|
|
11439
|
+
`SELECT
|
|
11440
|
+
s.id AS sym_id,
|
|
11441
|
+
s.name AS sym_name,
|
|
11442
|
+
s.kind AS sym_kind,
|
|
11443
|
+
s.lang AS sym_lang,
|
|
11444
|
+
s.file AS sym_file,
|
|
11445
|
+
s.line AS sym_line,
|
|
11446
|
+
s.signature AS sym_signature,
|
|
11447
|
+
r.call_type,
|
|
11448
|
+
r.line AS ref_line
|
|
11449
|
+
FROM refs r
|
|
11450
|
+
JOIN symbols s ON s.id = r.from_id
|
|
11451
|
+
WHERE r.to_id IS NULL AND r.to_name = ?
|
|
11452
|
+
ORDER BY r.line, r.id`
|
|
11453
|
+
).all(symbolName);
|
|
11454
|
+
rows.push(...fallbackRows);
|
|
11455
|
+
}
|
|
11456
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
11457
|
+
const allCalls = rows.map(mapCallSiteRow);
|
|
11458
|
+
return { calls: allCalls.slice(0, limit), symbolFound: true, ambiguous, totalMatches: allCalls.length };
|
|
11459
|
+
}
|
|
11460
|
+
function findOutgoingCallsByName(stmt, symbolName, file, limit) {
|
|
11461
|
+
const sourceIds = resolveSymbolIds(stmt, symbolName, file);
|
|
11462
|
+
if (sourceIds.length === 0) return { calls: [], symbolFound: false, unresolvedCount: 0, totalMatches: 0 };
|
|
11463
|
+
const unresolvedCount = chunkedIdScalar(
|
|
11464
|
+
stmt,
|
|
11465
|
+
sourceIds,
|
|
11466
|
+
(ph) => `SELECT COUNT(*) AS n FROM refs WHERE from_id IN (${ph}) AND to_id IS NULL`
|
|
11467
|
+
);
|
|
11468
|
+
const rows = chunkedIdQuery(
|
|
11469
|
+
stmt,
|
|
11470
|
+
sourceIds,
|
|
11471
|
+
(ph) => `SELECT
|
|
11472
|
+
s.id AS sym_id,
|
|
11473
|
+
s.name AS sym_name,
|
|
11474
|
+
s.kind AS sym_kind,
|
|
11475
|
+
s.lang AS sym_lang,
|
|
11476
|
+
s.file AS sym_file,
|
|
11477
|
+
s.line AS sym_line,
|
|
11478
|
+
s.signature AS sym_signature,
|
|
11479
|
+
r.call_type,
|
|
11480
|
+
r.line AS ref_line
|
|
11481
|
+
FROM refs r
|
|
11482
|
+
JOIN symbols s ON s.id = r.to_id
|
|
11483
|
+
WHERE r.from_id IN (${ph})
|
|
11484
|
+
AND r.to_id IS NOT NULL -- INNER JOIN already excludes NULL to_id; this is defensive belt-and-suspenders
|
|
11485
|
+
ORDER BY r.line, r.id`,
|
|
11486
|
+
[]
|
|
11487
|
+
);
|
|
11488
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
11489
|
+
const calls = rows.map(mapCallSiteRow).slice(0, limit);
|
|
11490
|
+
return { calls, symbolFound: true, unresolvedCount, totalMatches: rows.length };
|
|
11491
|
+
}
|
|
10911
11492
|
function findRefsToWithStatement(stmt, symbolId) {
|
|
10912
11493
|
return stmt(
|
|
10913
11494
|
"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 = ?)"
|
|
@@ -10921,7 +11502,8 @@ function findRefsFromWithStatement(stmt, symbolId) {
|
|
|
10921
11502
|
function getPackageGraphWithStatement(stmt) {
|
|
10922
11503
|
const fileCounts = stmt("SELECT file, COUNT(*) AS n FROM symbols GROUP BY file").all();
|
|
10923
11504
|
const files = stmt("SELECT DISTINCT file FROM files").all();
|
|
10924
|
-
const
|
|
11505
|
+
const packageOf = readPackageLabeller(stmt);
|
|
11506
|
+
const { pkgNodes, fileToPkg } = buildPackageGraphNodes(fileCounts, files, packageOf);
|
|
10925
11507
|
const refRows = stmt(
|
|
10926
11508
|
`SELECT r.call_type, sf.file AS from_file, st.file AS to_file, COUNT(*) AS n
|
|
10927
11509
|
FROM refs r
|
|
@@ -10932,32 +11514,42 @@ function getPackageGraphWithStatement(stmt) {
|
|
|
10932
11514
|
).all();
|
|
10933
11515
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
10934
11516
|
for (const r of refRows) {
|
|
10935
|
-
const fromPkg = fileToPkg.get(r.from_file) ??
|
|
10936
|
-
const toPkg = fileToPkg.get(r.to_file) ??
|
|
11517
|
+
const fromPkg = fileToPkg.get(r.from_file) ?? packageOf(r.from_file);
|
|
11518
|
+
const toPkg = fileToPkg.get(r.to_file) ?? packageOf(r.to_file);
|
|
10937
11519
|
if (fromPkg === toPkg) continue;
|
|
10938
11520
|
const n = Number(r.n) || 0;
|
|
10939
11521
|
addWeightedEdge(edgeMap, fromPkg, toPkg, r.call_type, n);
|
|
10940
11522
|
}
|
|
10941
11523
|
const importRows = stmt(
|
|
10942
|
-
`SELECT
|
|
11524
|
+
`SELECT s.file AS from_file,
|
|
11525
|
+
COALESCE(r.to_file, st.file) AS to_file,
|
|
11526
|
+
COUNT(*) AS n
|
|
10943
11527
|
FROM refs r
|
|
10944
11528
|
JOIN symbols s ON s.id = r.from_id
|
|
11529
|
+
LEFT JOIN symbols st ON st.id = r.to_id
|
|
10945
11530
|
WHERE r.call_type = 'import'
|
|
10946
|
-
|
|
11531
|
+
AND COALESCE(r.to_file, st.file) IS NOT NULL
|
|
11532
|
+
GROUP BY s.file, COALESCE(r.to_file, st.file)`
|
|
10947
11533
|
).all();
|
|
10948
11534
|
for (const r of importRows) {
|
|
10949
|
-
const fromPkg = fileToPkg.get(r.from_file) ??
|
|
10950
|
-
const toPkg =
|
|
10951
|
-
if (
|
|
11535
|
+
const fromPkg = fileToPkg.get(r.from_file) ?? packageOf(r.from_file);
|
|
11536
|
+
const toPkg = fileToPkg.get(r.to_file) ?? packageOf(r.to_file);
|
|
11537
|
+
if (fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;
|
|
10952
11538
|
const n = Number(r.n) || 0;
|
|
10953
11539
|
addWeightedEdge(edgeMap, fromPkg, toPkg, "import", n);
|
|
10954
11540
|
}
|
|
10955
11541
|
const edges = materializeWeightedEdges(edgeMap, "pkg");
|
|
10956
11542
|
return { nodes: [...pkgNodes.values()], edges };
|
|
10957
11543
|
}
|
|
11544
|
+
function readPackageLabeller(stmt) {
|
|
11545
|
+
const rows = stmt("SELECT file, package FROM files WHERE package != ''").all();
|
|
11546
|
+
return createPackageLabeller(new Map(rows.map((row) => [row.file, row.package])));
|
|
11547
|
+
}
|
|
10958
11548
|
function getFileGraphWithStatement(stmt, packageFilter) {
|
|
10959
11549
|
const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
|
|
10960
|
-
const
|
|
11550
|
+
const packageOf = readPackageLabeller(stmt);
|
|
11551
|
+
const langOf = (file) => detectLang(file) ?? "other";
|
|
11552
|
+
const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
|
|
10961
11553
|
const localFiles = new Set(pkgFilePaths);
|
|
10962
11554
|
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
10963
11555
|
const filePlaceholders = [...localFiles].map(() => "?").join(",");
|
|
@@ -10966,9 +11558,9 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
10966
11558
|
).all(...pkgFilePaths);
|
|
10967
11559
|
const { fileNodes, symToFile, fileStats, ensureFileNode } = buildFileGraphNodeState(
|
|
10968
11560
|
pkgSyms,
|
|
10969
|
-
localFiles
|
|
11561
|
+
localFiles,
|
|
11562
|
+
packageOf
|
|
10970
11563
|
);
|
|
10971
|
-
const indexedFiles = new Set(allFiles.map((f) => f.file));
|
|
10972
11564
|
const refRows = stmt(
|
|
10973
11565
|
`SELECT r.from_id, r.to_id, r.call_type, COUNT(*) AS n
|
|
10974
11566
|
FROM refs r
|
|
@@ -10991,7 +11583,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
10991
11583
|
for (const x of extras) {
|
|
10992
11584
|
symToFile.set(x.id, x.file);
|
|
10993
11585
|
if (!fileStats.has(x.file)) {
|
|
10994
|
-
fileStats.set(x.file, { count: 0, lang:
|
|
11586
|
+
fileStats.set(x.file, { count: 0, lang: langOf(x.file) });
|
|
10995
11587
|
}
|
|
10996
11588
|
}
|
|
10997
11589
|
}
|
|
@@ -11008,17 +11600,22 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
11008
11600
|
addWeightedEdge(edgeMap, fromFile, toFile, r.call_type, n);
|
|
11009
11601
|
}
|
|
11010
11602
|
const importRows = stmt(
|
|
11011
|
-
`SELECT r.from_id, r.
|
|
11603
|
+
`SELECT r.from_id, COALESCE(r.to_file, st.file) AS to_file, COUNT(*) AS n
|
|
11012
11604
|
FROM refs r
|
|
11605
|
+
LEFT JOIN symbols st ON st.id = r.to_id
|
|
11013
11606
|
WHERE r.call_type = 'import'
|
|
11607
|
+
AND COALESCE(r.to_file, st.file) IS NOT NULL
|
|
11014
11608
|
AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
11015
|
-
GROUP BY r.from_id, r.
|
|
11609
|
+
GROUP BY r.from_id, COALESCE(r.to_file, st.file)`
|
|
11016
11610
|
).all(...pkgFilePaths);
|
|
11017
11611
|
for (const r of importRows) {
|
|
11018
11612
|
const fromFile = symToFile.get(r.from_id);
|
|
11019
11613
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
11020
|
-
const toFile =
|
|
11614
|
+
const toFile = r.to_file;
|
|
11021
11615
|
if (!toFile || fromFile === toFile) continue;
|
|
11616
|
+
if (!fileStats.has(toFile)) {
|
|
11617
|
+
fileStats.set(toFile, { count: 0, lang: langOf(toFile) });
|
|
11618
|
+
}
|
|
11022
11619
|
ensureFileNode(fromFile);
|
|
11023
11620
|
ensureFileNode(toFile);
|
|
11024
11621
|
const n = Number(r.n) || 0;
|
|
@@ -11068,7 +11665,7 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
|
11068
11665
|
).all(...missingIds);
|
|
11069
11666
|
for (const s of extras) symById.set(s.id, s);
|
|
11070
11667
|
}
|
|
11071
|
-
const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter);
|
|
11668
|
+
const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
|
|
11072
11669
|
return { nodes, edges };
|
|
11073
11670
|
}
|
|
11074
11671
|
|
|
@@ -11090,7 +11687,7 @@ function assignRefsToSymbols(refs, symbols) {
|
|
|
11090
11687
|
}
|
|
11091
11688
|
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
11092
11689
|
if (!owner2 || owner2.id <= 0) continue;
|
|
11093
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
11690
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
|
|
11094
11691
|
if (seen.has(key)) continue;
|
|
11095
11692
|
seen.add(key);
|
|
11096
11693
|
assigned.push({ ...ref, fromId: owner2.id });
|
|
@@ -11136,7 +11733,11 @@ var CORE_TABLES_SQL = `
|
|
|
11136
11733
|
lang TEXT NOT NULL,
|
|
11137
11734
|
mtime_ms INTEGER NOT NULL,
|
|
11138
11735
|
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
11139
|
-
last_indexed INTEGER NOT NULL
|
|
11736
|
+
last_indexed INTEGER NOT NULL,
|
|
11737
|
+
-- Code Atlas grouping label, computed at index time from the ecosystem's
|
|
11738
|
+
-- own manifests (package.json, go.mod, Cargo.toml, \u2026). Stored rather than
|
|
11739
|
+
-- re-derived per query because the evidence lives on disk, not in the DB.
|
|
11740
|
+
package TEXT NOT NULL DEFAULT ''
|
|
11140
11741
|
);
|
|
11141
11742
|
CREATE TABLE IF NOT EXISTS symbols (
|
|
11142
11743
|
id INTEGER PRIMARY KEY,
|
|
@@ -11153,6 +11754,9 @@ var CORE_TABLES_SQL = `
|
|
|
11153
11754
|
file_fk TEXT NOT NULL
|
|
11154
11755
|
);
|
|
11155
11756
|
`;
|
|
11757
|
+
var FILE_INDEX_SQL = [
|
|
11758
|
+
"CREATE INDEX IF NOT EXISTS idx_f_package ON files(package)"
|
|
11759
|
+
];
|
|
11156
11760
|
var SYMBOL_INDEX_SQL = [
|
|
11157
11761
|
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
11158
11762
|
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
@@ -11169,15 +11773,32 @@ var REFS_TABLE_SQL = `
|
|
|
11169
11773
|
to_name TEXT NOT NULL,
|
|
11170
11774
|
to_id INTEGER,
|
|
11171
11775
|
call_type TEXT NOT NULL,
|
|
11172
|
-
line INTEGER NOT NULL
|
|
11776
|
+
line INTEGER NOT NULL,
|
|
11777
|
+
lang TEXT NOT NULL DEFAULT '',
|
|
11778
|
+
module TEXT,
|
|
11779
|
+
to_file TEXT
|
|
11173
11780
|
);
|
|
11174
11781
|
`;
|
|
11175
11782
|
var REFS_INDEX_SQL = [
|
|
11176
11783
|
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
11177
11784
|
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
11178
11785
|
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
11179
|
-
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
11786
|
+
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)",
|
|
11787
|
+
// Name resolution matches (to_name, lang) pairs; the composite keeps the
|
|
11788
|
+
// language-scoped UPDATE from degrading into a scan of every same-named row.
|
|
11789
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_name_lang ON refs(to_name, lang)",
|
|
11790
|
+
// The post-index module resolution pass groups unresolved import refs by
|
|
11791
|
+
// (module, lang); graph readers then read to_file back.
|
|
11792
|
+
"CREATE INDEX IF NOT EXISTS idx_r_module ON refs(module)",
|
|
11793
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_file ON refs(to_file)"
|
|
11180
11794
|
];
|
|
11795
|
+
var LANG_FAMILY_TABLE_SQL = `
|
|
11796
|
+
CREATE TABLE IF NOT EXISTS lang_family (
|
|
11797
|
+
lang TEXT PRIMARY KEY,
|
|
11798
|
+
family TEXT NOT NULL
|
|
11799
|
+
);
|
|
11800
|
+
`;
|
|
11801
|
+
var LANG_FAMILY_WILDCARD = "*";
|
|
11181
11802
|
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
11182
11803
|
|
|
11183
11804
|
// src/codebase-index/writer-search-helpers.ts
|
|
@@ -11389,15 +12010,69 @@ var IndexStore = class _IndexStore {
|
|
|
11389
12010
|
}
|
|
11390
12011
|
constructor(projectRoot, opts = {}) {
|
|
11391
12012
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
11392
|
-
|
|
12013
|
+
fs11.mkdirSync(this.indexDir, { recursive: true });
|
|
11393
12014
|
const Database = loadDatabaseSync();
|
|
11394
|
-
this.db = new Database(
|
|
12015
|
+
this.db = new Database(path16.join(this.indexDir, DB_FILE2));
|
|
11395
12016
|
applyIndexStorePragmas(this.db);
|
|
11396
12017
|
this.initSchema();
|
|
11397
12018
|
}
|
|
11398
12019
|
runWithRetry(fn) {
|
|
11399
12020
|
return runSqliteWithRetry(fn);
|
|
11400
12021
|
}
|
|
12022
|
+
/**
|
|
12023
|
+
* Mirror the in-process language→family map into SQLite.
|
|
12024
|
+
*
|
|
12025
|
+
* Rewritten on every open rather than only on schema bumps: the mapping is
|
|
12026
|
+
* static lookup data, so a code-side change (a new language, a language
|
|
12027
|
+
* moving families) must take effect without forcing a full reindex.
|
|
12028
|
+
*/
|
|
12029
|
+
seedLangFamilies() {
|
|
12030
|
+
const insert = this.stmt("INSERT OR REPLACE INTO lang_family(lang, family) VALUES (?, ?)");
|
|
12031
|
+
for (const [lang, family] of LANG_FAMILY_ENTRIES) insert.run(lang, family);
|
|
12032
|
+
insert.run("", LANG_FAMILY_WILDCARD);
|
|
12033
|
+
}
|
|
12034
|
+
/**
|
|
12035
|
+
* Add any column the current schema expects but the on-disk table lacks.
|
|
12036
|
+
*
|
|
12037
|
+
* `CREATE TABLE IF NOT EXISTS` silently keeps an existing table's old shape,
|
|
12038
|
+
* and the version check above only rebuilds on a version *mismatch*. That
|
|
12039
|
+
* leaves a real gap: several wstack processes share this database, and while
|
|
12040
|
+
* a version upgrade is rolling out one of them may still be running the
|
|
12041
|
+
* previous build. That older process sees the newer version number, drops the
|
|
12042
|
+
* tables, and recreates them from *its* DDL — without the newer columns —
|
|
12043
|
+
* while the metadata row still reads the new version. Every later query for
|
|
12044
|
+
* one of those columns then fails with `no such column`, and no amount of
|
|
12045
|
+
* reindexing fixes it, because the version numbers already agree.
|
|
12046
|
+
*
|
|
12047
|
+
* Repairing column-by-column makes the schema self-healing from any of those
|
|
12048
|
+
* states. Table and column names are compile-time literals from this module,
|
|
12049
|
+
* never user input.
|
|
12050
|
+
*/
|
|
12051
|
+
repairMissingColumns() {
|
|
12052
|
+
const expected = [
|
|
12053
|
+
{ table: "files", columns: [["package", "TEXT NOT NULL DEFAULT ''"]] },
|
|
12054
|
+
{
|
|
12055
|
+
table: "refs",
|
|
12056
|
+
columns: [
|
|
12057
|
+
["lang", "TEXT NOT NULL DEFAULT ''"],
|
|
12058
|
+
["module", "TEXT"],
|
|
12059
|
+
["to_file", "TEXT"]
|
|
12060
|
+
]
|
|
12061
|
+
}
|
|
12062
|
+
];
|
|
12063
|
+
for (const { table, columns } of expected) {
|
|
12064
|
+
const present = new Set(
|
|
12065
|
+
this.db.prepare(`PRAGMA table_info(${table})`).all().flatMap(
|
|
12066
|
+
(row) => typeof row.name === "string" ? [row.name] : []
|
|
12067
|
+
)
|
|
12068
|
+
);
|
|
12069
|
+
if (present.size === 0) continue;
|
|
12070
|
+
for (const [name, type] of columns) {
|
|
12071
|
+
if (present.has(name)) continue;
|
|
12072
|
+
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${type}`);
|
|
12073
|
+
}
|
|
12074
|
+
}
|
|
12075
|
+
}
|
|
11401
12076
|
initSchema() {
|
|
11402
12077
|
this.db.exec(METADATA_TABLE_SQL);
|
|
11403
12078
|
const storedRows = this.stmt("SELECT value FROM metadata WHERE key = ?").all("version");
|
|
@@ -11420,9 +12095,13 @@ var IndexStore = class _IndexStore {
|
|
|
11420
12095
|
);
|
|
11421
12096
|
}
|
|
11422
12097
|
this.db.exec(CORE_TABLES_SQL);
|
|
11423
|
-
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
11424
12098
|
this.db.exec(REFS_TABLE_SQL);
|
|
12099
|
+
this.repairMissingColumns();
|
|
12100
|
+
for (const sql of FILE_INDEX_SQL) this.db.exec(sql);
|
|
12101
|
+
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
11425
12102
|
for (const sql of REFS_INDEX_SQL) this.db.exec(sql);
|
|
12103
|
+
this.db.exec(LANG_FAMILY_TABLE_SQL);
|
|
12104
|
+
this.seedLangFamilies();
|
|
11426
12105
|
try {
|
|
11427
12106
|
this.db.exec(SYMBOLS_FTS_SQL);
|
|
11428
12107
|
this.ftsAvailable = true;
|
|
@@ -11457,6 +12136,18 @@ var IndexStore = class _IndexStore {
|
|
|
11457
12136
|
static NEXT_SYMBOL_ID_KEY = "next_symbol_id";
|
|
11458
12137
|
/** Stay under typical SQLite SQLITE_MAX_VARIABLE_NUMBER (often 999). */
|
|
11459
12138
|
static MAX_SQL_VARS = 900;
|
|
12139
|
+
/**
|
|
12140
|
+
* Correlated predicate: the ref in `refs` and the candidate symbol aliased
|
|
12141
|
+
* `sym` belong to the same language family — or the ref carries no language,
|
|
12142
|
+
* in which case the wildcard bind matches everything.
|
|
12143
|
+
*
|
|
12144
|
+
* Each textual occurrence consumes one `?` bind of {@link LANG_FAMILY_WILDCARD}.
|
|
12145
|
+
*/
|
|
12146
|
+
static FAMILY_MATCH_SQL = `(
|
|
12147
|
+
(SELECT family FROM lang_family WHERE lang = refs.lang) = ?
|
|
12148
|
+
OR (SELECT family FROM lang_family WHERE lang = sym.lang)
|
|
12149
|
+
= (SELECT family FROM lang_family WHERE lang = refs.lang)
|
|
12150
|
+
)`;
|
|
11460
12151
|
/**
|
|
11461
12152
|
* Ensure `metadata.next_symbol_id` exists. Safe to call outside a write
|
|
11462
12153
|
* transaction on open; the first concurrent writer under BEGIN IMMEDIATE
|
|
@@ -11520,9 +12211,12 @@ var IndexStore = class _IndexStore {
|
|
|
11520
12211
|
const placeholders = chunk.map(() => "?").join(",");
|
|
11521
12212
|
const result = this.stmt(
|
|
11522
12213
|
`UPDATE refs
|
|
11523
|
-
SET to_id = (
|
|
12214
|
+
SET to_id = (
|
|
12215
|
+
SELECT MIN(sym.id) FROM symbols sym
|
|
12216
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
12217
|
+
)
|
|
11524
12218
|
WHERE to_name IN (${placeholders})`
|
|
11525
|
-
).run(...chunk);
|
|
12219
|
+
).run(LANG_FAMILY_WILDCARD, ...chunk);
|
|
11526
12220
|
changes += result.changes ?? 0;
|
|
11527
12221
|
}
|
|
11528
12222
|
return changes;
|
|
@@ -11649,6 +12343,115 @@ var IndexStore = class _IndexStore {
|
|
|
11649
12343
|
getAllFileMetas() {
|
|
11650
12344
|
return getAllFileMetasWithStatement((sql) => this.stmt(sql));
|
|
11651
12345
|
}
|
|
12346
|
+
// ─── Project structure & module resolution ──────────────────────────────────
|
|
12347
|
+
/** Store the Code Atlas grouping label for each indexed file. */
|
|
12348
|
+
setFilePackages(entries) {
|
|
12349
|
+
if (entries.size === 0) return;
|
|
12350
|
+
this.runWithRetry(() => {
|
|
12351
|
+
const update = this.stmt("UPDATE files SET package = ? WHERE file = ?");
|
|
12352
|
+
for (const [file, label] of entries) update.run(label, file);
|
|
12353
|
+
});
|
|
12354
|
+
}
|
|
12355
|
+
/**
|
|
12356
|
+
* Every indexed `namespace`/`module` declaration, for ecosystems whose import
|
|
12357
|
+
* specifiers name a namespace rather than a path (C#, PHP, Elixir, Haskell).
|
|
12358
|
+
* Ordered so the resolver's choice among duplicate declarations is stable.
|
|
12359
|
+
*/
|
|
12360
|
+
getNamespaceDeclarations() {
|
|
12361
|
+
return this.stmt(
|
|
12362
|
+
`SELECT name, file FROM symbols WHERE kind = 'namespace' ORDER BY file, id`
|
|
12363
|
+
).all();
|
|
12364
|
+
}
|
|
12365
|
+
/** `file → package` for every indexed file that has a label. */
|
|
12366
|
+
getFilePackages() {
|
|
12367
|
+
const rows = this.stmt("SELECT file, package FROM files WHERE package != ''").all();
|
|
12368
|
+
return new Map(rows.map((row) => [row.file, row.package]));
|
|
12369
|
+
}
|
|
12370
|
+
/**
|
|
12371
|
+
* Distinct `(fromFile, lang, module)` triples needing module resolution.
|
|
12372
|
+
*
|
|
12373
|
+
* Distinct rather than per-ref because resolution depends only on these three
|
|
12374
|
+
* values: a file importing the same module twenty times resolves it once.
|
|
12375
|
+
*/
|
|
12376
|
+
getUnresolvedImports(onlyFiles) {
|
|
12377
|
+
const base = `SELECT DISTINCT s.file AS fromFile, r.lang AS lang, r.module AS module
|
|
12378
|
+
FROM refs r
|
|
12379
|
+
JOIN symbols s ON s.id = r.from_id
|
|
12380
|
+
WHERE r.call_type = 'import' AND r.module IS NOT NULL`;
|
|
12381
|
+
if (!onlyFiles?.length) {
|
|
12382
|
+
return this.stmt(base).all();
|
|
12383
|
+
}
|
|
12384
|
+
const out = [];
|
|
12385
|
+
for (let i = 0; i < onlyFiles.length; i += _IndexStore.MAX_SQL_VARS) {
|
|
12386
|
+
const chunk = onlyFiles.slice(i, i + _IndexStore.MAX_SQL_VARS);
|
|
12387
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
12388
|
+
out.push(
|
|
12389
|
+
...this.stmt(`${base} AND s.file IN (${placeholders})`).all(...chunk)
|
|
12390
|
+
);
|
|
12391
|
+
}
|
|
12392
|
+
return out;
|
|
12393
|
+
}
|
|
12394
|
+
/**
|
|
12395
|
+
* Write resolved import targets back onto `refs.to_file`.
|
|
12396
|
+
*
|
|
12397
|
+
* Applied through a temp table and a single UPDATE: one statement per
|
|
12398
|
+
* resolution would mean thousands of round-trips on a first index.
|
|
12399
|
+
*/
|
|
12400
|
+
applyImportResolutions(resolutions) {
|
|
12401
|
+
if (resolutions.length === 0) return 0;
|
|
12402
|
+
return this.runWithRetry(() => {
|
|
12403
|
+
this.db.exec("DROP TABLE IF EXISTS temp.import_resolution");
|
|
12404
|
+
this.db.exec(
|
|
12405
|
+
`CREATE TEMP TABLE import_resolution (
|
|
12406
|
+
from_file TEXT NOT NULL,
|
|
12407
|
+
lang TEXT NOT NULL,
|
|
12408
|
+
module TEXT NOT NULL,
|
|
12409
|
+
to_file TEXT NOT NULL
|
|
12410
|
+
)`
|
|
12411
|
+
);
|
|
12412
|
+
const chunkSize = Math.max(1, Math.floor(_IndexStore.MAX_SQL_VARS / 4));
|
|
12413
|
+
for (let i = 0; i < resolutions.length; i += chunkSize) {
|
|
12414
|
+
const chunk = resolutions.slice(i, i + chunkSize);
|
|
12415
|
+
const placeholders = chunk.map(() => "(?, ?, ?, ?)").join(", ");
|
|
12416
|
+
const binds = [];
|
|
12417
|
+
for (const entry of chunk) {
|
|
12418
|
+
binds.push(entry.fromFile, entry.lang, entry.module, entry.toFile);
|
|
12419
|
+
}
|
|
12420
|
+
this.stmt(
|
|
12421
|
+
`INSERT INTO temp.import_resolution(from_file, lang, module, to_file)
|
|
12422
|
+
VALUES ${placeholders}`
|
|
12423
|
+
).run(...binds);
|
|
12424
|
+
}
|
|
12425
|
+
this.db.exec(
|
|
12426
|
+
`CREATE INDEX IF NOT EXISTS temp.idx_ir
|
|
12427
|
+
ON import_resolution(module, lang, from_file)`
|
|
12428
|
+
);
|
|
12429
|
+
const result = this.stmt(
|
|
12430
|
+
`UPDATE refs
|
|
12431
|
+
SET to_file = (
|
|
12432
|
+
SELECT ir.to_file
|
|
12433
|
+
FROM temp.import_resolution ir
|
|
12434
|
+
JOIN symbols s ON s.id = refs.from_id
|
|
12435
|
+
WHERE ir.module = refs.module
|
|
12436
|
+
AND ir.lang = refs.lang
|
|
12437
|
+
AND ir.from_file = s.file
|
|
12438
|
+
LIMIT 1
|
|
12439
|
+
)
|
|
12440
|
+
WHERE refs.call_type = 'import'
|
|
12441
|
+
AND refs.module IS NOT NULL
|
|
12442
|
+
AND EXISTS (
|
|
12443
|
+
SELECT 1
|
|
12444
|
+
FROM temp.import_resolution ir
|
|
12445
|
+
JOIN symbols s ON s.id = refs.from_id
|
|
12446
|
+
WHERE ir.module = refs.module
|
|
12447
|
+
AND ir.lang = refs.lang
|
|
12448
|
+
AND ir.from_file = s.file
|
|
12449
|
+
)`
|
|
12450
|
+
).run();
|
|
12451
|
+
this.db.exec("DROP TABLE IF EXISTS temp.import_resolution");
|
|
12452
|
+
return result.changes ?? 0;
|
|
12453
|
+
});
|
|
12454
|
+
}
|
|
11652
12455
|
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
11653
12456
|
search(query, filter, opts) {
|
|
11654
12457
|
const built = this.buildSearchWhere(query, filter);
|
|
@@ -12035,9 +12838,12 @@ var IndexStore = class _IndexStore {
|
|
|
12035
12838
|
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
12036
12839
|
* Call this after all symbols have been inserted to fill in cross-references.
|
|
12037
12840
|
*
|
|
12038
|
-
*
|
|
12039
|
-
* the
|
|
12040
|
-
*
|
|
12841
|
+
* A match additionally requires the referencing ref and the target symbol to
|
|
12842
|
+
* be in the same {@link LangFamily}. Without that guard a name match is a
|
|
12843
|
+
* cross-language accident waiting to happen — `main`, `New`, `Parse` and
|
|
12844
|
+
* `Config` are declared in most languages at once, and each collision draws a
|
|
12845
|
+
* Code Atlas edge between files that never reference each other. Refs stored
|
|
12846
|
+
* without a language keep the old global behaviour via the `'*'` wildcard row.
|
|
12041
12847
|
*/
|
|
12042
12848
|
resolveRefs() {
|
|
12043
12849
|
return this.runWithRetry(() => {
|
|
@@ -12046,20 +12852,35 @@ var IndexStore = class _IndexStore {
|
|
|
12046
12852
|
`UPDATE refs
|
|
12047
12853
|
SET to_id = s.id
|
|
12048
12854
|
FROM (
|
|
12049
|
-
|
|
12050
|
-
|
|
12855
|
+
SELECT sym.name AS name, lf.family AS family, MIN(sym.id) AS id
|
|
12856
|
+
FROM symbols sym
|
|
12857
|
+
JOIN lang_family lf ON lf.lang = sym.lang
|
|
12858
|
+
GROUP BY sym.name, lf.family
|
|
12859
|
+
UNION ALL
|
|
12860
|
+
SELECT sym.name AS name, '${LANG_FAMILY_WILDCARD}' AS family, MIN(sym.id) AS id
|
|
12861
|
+
FROM symbols sym
|
|
12862
|
+
GROUP BY sym.name
|
|
12863
|
+
) AS s,
|
|
12864
|
+
lang_family AS rf
|
|
12051
12865
|
WHERE refs.to_id IS NULL
|
|
12052
12866
|
AND refs.to_name IS NOT NULL
|
|
12053
|
-
AND
|
|
12867
|
+
AND rf.lang = refs.lang
|
|
12868
|
+
AND s.name = refs.to_name
|
|
12869
|
+
AND s.family = rf.family`
|
|
12054
12870
|
).run();
|
|
12055
12871
|
return result.changes ?? 0;
|
|
12056
12872
|
} catch {
|
|
12057
12873
|
const result = this.stmt(
|
|
12058
12874
|
`UPDATE refs SET to_id = (
|
|
12059
|
-
SELECT id FROM symbols
|
|
12875
|
+
SELECT sym.id FROM symbols sym
|
|
12876
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
12877
|
+
ORDER BY sym.id LIMIT 1
|
|
12060
12878
|
) WHERE to_id IS NULL AND to_name IS NOT NULL
|
|
12061
|
-
AND
|
|
12062
|
-
|
|
12879
|
+
AND EXISTS (
|
|
12880
|
+
SELECT 1 FROM symbols sym
|
|
12881
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
12882
|
+
)`
|
|
12883
|
+
).run(LANG_FAMILY_WILDCARD, LANG_FAMILY_WILDCARD);
|
|
12063
12884
|
return result.changes ?? 0;
|
|
12064
12885
|
}
|
|
12065
12886
|
});
|
|
@@ -12143,6 +12964,20 @@ var IndexStore = class _IndexStore {
|
|
|
12143
12964
|
return false;
|
|
12144
12965
|
}
|
|
12145
12966
|
}
|
|
12967
|
+
/**
|
|
12968
|
+
* Find all symbols that reference the named target symbol (incoming callers).
|
|
12969
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
12970
|
+
*/
|
|
12971
|
+
findIncomingCallsByName(symbolName, file, limit = 100) {
|
|
12972
|
+
return findIncomingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
12973
|
+
}
|
|
12974
|
+
/**
|
|
12975
|
+
* Find all symbols that the named source symbol references (outgoing callees).
|
|
12976
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
12977
|
+
*/
|
|
12978
|
+
findOutgoingCallsByName(symbolName, file, limit = 100) {
|
|
12979
|
+
return findOutgoingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
12980
|
+
}
|
|
12146
12981
|
/**
|
|
12147
12982
|
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
12148
12983
|
*/
|
|
@@ -12234,21 +13069,21 @@ var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
|
12234
13069
|
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
12235
13070
|
var buildIdCache;
|
|
12236
13071
|
function projectIndexServerBuildId(entrypoint) {
|
|
12237
|
-
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) :
|
|
13072
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path17.resolve(entrypoint);
|
|
12238
13073
|
try {
|
|
12239
|
-
const stat19 =
|
|
13074
|
+
const stat19 = fs12.statSync(file);
|
|
12240
13075
|
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat19.mtimeMs && buildIdCache.size === stat19.size) {
|
|
12241
13076
|
return buildIdCache.buildId;
|
|
12242
13077
|
}
|
|
12243
|
-
const buildId = createHash4("sha256").update(
|
|
13078
|
+
const buildId = createHash4("sha256").update(fs12.readFileSync(file)).digest("hex").slice(0, 24);
|
|
12244
13079
|
buildIdCache = { file, mtimeMs: stat19.mtimeMs, size: stat19.size, buildId };
|
|
12245
13080
|
return buildId;
|
|
12246
13081
|
} catch {
|
|
12247
|
-
return `unreadable:${
|
|
13082
|
+
return `unreadable:${path17.basename(file)}`;
|
|
12248
13083
|
}
|
|
12249
13084
|
}
|
|
12250
13085
|
function normalizeLocalPath(value) {
|
|
12251
|
-
const resolved =
|
|
13086
|
+
const resolved = path17.resolve(value);
|
|
12252
13087
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12253
13088
|
}
|
|
12254
13089
|
function projectIndexServerKey(projectRoot, indexDir) {
|
|
@@ -12260,11 +13095,11 @@ function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
|
12260
13095
|
if (process.platform === "win32") {
|
|
12261
13096
|
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
12262
13097
|
}
|
|
12263
|
-
return
|
|
13098
|
+
return path17.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
12264
13099
|
}
|
|
12265
13100
|
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
12266
|
-
return
|
|
12267
|
-
|
|
13101
|
+
return path17.join(
|
|
13102
|
+
path17.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
12268
13103
|
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
12269
13104
|
);
|
|
12270
13105
|
}
|
|
@@ -12304,7 +13139,7 @@ function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
|
12304
13139
|
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
12305
13140
|
try {
|
|
12306
13141
|
const url = new URL(rel, import.meta.url);
|
|
12307
|
-
if (url.protocol === "file:" &&
|
|
13142
|
+
if (url.protocol === "file:" && fs13.existsSync(fileURLToPath2(url))) {
|
|
12308
13143
|
builtUrl = url;
|
|
12309
13144
|
break;
|
|
12310
13145
|
}
|
|
@@ -12561,7 +13396,7 @@ var ProjectServerConnection = class {
|
|
|
12561
13396
|
currentAuthToken() {
|
|
12562
13397
|
if (this.authToken === void 0) {
|
|
12563
13398
|
try {
|
|
12564
|
-
const raw =
|
|
13399
|
+
const raw = fs13.readFileSync(
|
|
12565
13400
|
projectIndexServerMetadataPath(this.projectRoot, this.indexDir),
|
|
12566
13401
|
"utf8"
|
|
12567
13402
|
);
|
|
@@ -12826,7 +13661,7 @@ var ProjectServerConnection = class {
|
|
|
12826
13661
|
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12827
13662
|
if (process.platform !== "win32") {
|
|
12828
13663
|
try {
|
|
12829
|
-
|
|
13664
|
+
fs13.rmSync(this.endpoint, { force: true });
|
|
12830
13665
|
} catch {
|
|
12831
13666
|
}
|
|
12832
13667
|
}
|
|
@@ -12850,8 +13685,8 @@ var ProjectServerConnection = class {
|
|
|
12850
13685
|
process.kill(pid);
|
|
12851
13686
|
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12852
13687
|
try {
|
|
12853
|
-
const metadata = JSON.parse(
|
|
12854
|
-
if (metadata.pid === pid)
|
|
13688
|
+
const metadata = JSON.parse(fs13.readFileSync(metadataPath, "utf8"));
|
|
13689
|
+
if (metadata.pid === pid) fs13.rmSync(metadataPath, { force: true });
|
|
12855
13690
|
} catch {
|
|
12856
13691
|
}
|
|
12857
13692
|
return true;
|
|
@@ -12955,7 +13790,7 @@ import { Worker } from "node:worker_threads";
|
|
|
12955
13790
|
|
|
12956
13791
|
// src/codebase-index/indexer.ts
|
|
12957
13792
|
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12958
|
-
import { execFile
|
|
13793
|
+
import { execFile } from "node:child_process";
|
|
12959
13794
|
import * as fs17 from "node:fs/promises";
|
|
12960
13795
|
import { availableParallelism } from "node:os";
|
|
12961
13796
|
import * as path23 from "node:path";
|
|
@@ -12966,8 +13801,8 @@ import {
|
|
|
12966
13801
|
} from "@wrongstack/core/utils";
|
|
12967
13802
|
|
|
12968
13803
|
// src/codebase-index/gitignore.ts
|
|
12969
|
-
import * as
|
|
12970
|
-
import * as
|
|
13804
|
+
import * as fs14 from "node:fs/promises";
|
|
13805
|
+
import * as path18 from "node:path";
|
|
12971
13806
|
import { compileGlob } from "@wrongstack/core/utils";
|
|
12972
13807
|
function globBody(glob) {
|
|
12973
13808
|
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
@@ -12983,48 +13818,474 @@ function compileGitignore(lines) {
|
|
|
12983
13818
|
negated = true;
|
|
12984
13819
|
line = line.slice(1);
|
|
12985
13820
|
}
|
|
12986
|
-
let dirOnly = false;
|
|
12987
|
-
if (line.endsWith("/")) {
|
|
12988
|
-
dirOnly = true;
|
|
12989
|
-
line = line.slice(0, -1);
|
|
13821
|
+
let dirOnly = false;
|
|
13822
|
+
if (line.endsWith("/")) {
|
|
13823
|
+
dirOnly = true;
|
|
13824
|
+
line = line.slice(0, -1);
|
|
13825
|
+
}
|
|
13826
|
+
if (!line) continue;
|
|
13827
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
13828
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
13829
|
+
const body = globBody(line);
|
|
13830
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
13831
|
+
rules.push({
|
|
13832
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
13833
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
13834
|
+
negated,
|
|
13835
|
+
dirOnly
|
|
13836
|
+
});
|
|
13837
|
+
}
|
|
13838
|
+
return (relPath, isDir) => {
|
|
13839
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
13840
|
+
let ignored = false;
|
|
13841
|
+
for (const r of rules) {
|
|
13842
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
13843
|
+
if (re.test(p)) ignored = !r.negated;
|
|
13844
|
+
}
|
|
13845
|
+
return ignored;
|
|
13846
|
+
};
|
|
13847
|
+
}
|
|
13848
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
13849
|
+
let lines = [];
|
|
13850
|
+
try {
|
|
13851
|
+
const raw = await fs14.readFile(path18.join(projectRoot, ".gitignore"), "utf8");
|
|
13852
|
+
lines = raw.split("\n");
|
|
13853
|
+
} catch {
|
|
13854
|
+
}
|
|
13855
|
+
return compileGitignore(lines);
|
|
13856
|
+
}
|
|
13857
|
+
|
|
13858
|
+
// src/codebase-index/indexer.ts
|
|
13859
|
+
init_languages2();
|
|
13860
|
+
|
|
13861
|
+
// src/codebase-index/module-resolver.ts
|
|
13862
|
+
init_languages2();
|
|
13863
|
+
import * as path19 from "node:path";
|
|
13864
|
+
var EXTENSIONS = {
|
|
13865
|
+
js: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte"],
|
|
13866
|
+
py: [".py", ".pyi"],
|
|
13867
|
+
rs: [".rs"],
|
|
13868
|
+
jvm: [".java", ".kt", ".scala"],
|
|
13869
|
+
c: [".h", ".hpp", ".hh", ".hxx", ".c", ".cpp", ".cc", ".cxx"],
|
|
13870
|
+
ruby: [".rb"],
|
|
13871
|
+
go: [".go"]
|
|
13872
|
+
};
|
|
13873
|
+
var DIRECTORY_ENTRIES = {
|
|
13874
|
+
js: ["index"],
|
|
13875
|
+
py: ["__init__"],
|
|
13876
|
+
rs: ["mod"],
|
|
13877
|
+
ruby: ["index"]
|
|
13878
|
+
};
|
|
13879
|
+
function normalizeNamespace(value) {
|
|
13880
|
+
return value.replace(/::|[\\/]/g, ".").replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
13881
|
+
}
|
|
13882
|
+
var ModuleResolver = class {
|
|
13883
|
+
structure;
|
|
13884
|
+
/** Lowercased portable path → the path as indexed (case is preserved). */
|
|
13885
|
+
byPath;
|
|
13886
|
+
/** Lowercased portable directory → files directly inside it, as indexed. */
|
|
13887
|
+
byDir;
|
|
13888
|
+
/** Normalized namespace → the file declaring it (first by path, stable). */
|
|
13889
|
+
byNamespace;
|
|
13890
|
+
constructor(structure, files, namespaces = []) {
|
|
13891
|
+
this.structure = structure;
|
|
13892
|
+
this.byPath = /* @__PURE__ */ new Map();
|
|
13893
|
+
this.byDir = /* @__PURE__ */ new Map();
|
|
13894
|
+
this.byNamespace = /* @__PURE__ */ new Map();
|
|
13895
|
+
const dirsByKey = /* @__PURE__ */ new Map();
|
|
13896
|
+
for (const file of files) {
|
|
13897
|
+
const portable = toPortablePath(file);
|
|
13898
|
+
const pathKey = portable.toLowerCase();
|
|
13899
|
+
const priorPath = this.byPath.get(pathKey);
|
|
13900
|
+
if (priorPath !== void 0 && priorPath !== file) this.byPath.delete(pathKey);
|
|
13901
|
+
else this.byPath.set(pathKey, file);
|
|
13902
|
+
const dir = path19.posix.dirname(portable);
|
|
13903
|
+
const dirKey = dir.toLowerCase();
|
|
13904
|
+
const knownDir = dirsByKey.get(dirKey);
|
|
13905
|
+
if (knownDir === void 0) {
|
|
13906
|
+
dirsByKey.set(dirKey, dir);
|
|
13907
|
+
this.byDir.set(dirKey, [file]);
|
|
13908
|
+
} else if (knownDir === dir) {
|
|
13909
|
+
this.byDir.get(dirKey)?.push(file);
|
|
13910
|
+
} else {
|
|
13911
|
+
dirsByKey.delete(dirKey);
|
|
13912
|
+
this.byDir.delete(dirKey);
|
|
13913
|
+
}
|
|
13914
|
+
}
|
|
13915
|
+
for (const { name, file } of namespaces) {
|
|
13916
|
+
const lang = detectLang(file);
|
|
13917
|
+
if (!lang) continue;
|
|
13918
|
+
const key = `${languageFamily(lang)}:${normalizeNamespace(name)}`;
|
|
13919
|
+
if (normalizeNamespace(name) && !this.byNamespace.has(key)) {
|
|
13920
|
+
this.byNamespace.set(key, file);
|
|
13921
|
+
}
|
|
13922
|
+
}
|
|
13923
|
+
}
|
|
13924
|
+
/**
|
|
13925
|
+
* Resolve `specifier` as written in `fromFile`.
|
|
13926
|
+
* Returns the indexed target path, or `undefined` when it is external or
|
|
13927
|
+
* cannot be located.
|
|
13928
|
+
*/
|
|
13929
|
+
resolve(fromFile, lang, specifier) {
|
|
13930
|
+
const spec = specifier.trim().replace(/\\/g, "/");
|
|
13931
|
+
if (!spec) return void 0;
|
|
13932
|
+
const from = toPortablePath(fromFile);
|
|
13933
|
+
switch (languageFamily(lang)) {
|
|
13934
|
+
case "js":
|
|
13935
|
+
return this.resolveJs(from, spec);
|
|
13936
|
+
case "go":
|
|
13937
|
+
return this.resolveGo(spec);
|
|
13938
|
+
case "py":
|
|
13939
|
+
return this.resolvePython(from, spec);
|
|
13940
|
+
case "rs":
|
|
13941
|
+
return this.resolveRust(from, spec);
|
|
13942
|
+
case "jvm":
|
|
13943
|
+
return this.resolveJvm(spec);
|
|
13944
|
+
case "c":
|
|
13945
|
+
return this.resolveInclude(from, spec);
|
|
13946
|
+
case "ruby":
|
|
13947
|
+
return this.resolveRuby(from, spec);
|
|
13948
|
+
case "dotnet":
|
|
13949
|
+
case "php":
|
|
13950
|
+
case "elixir":
|
|
13951
|
+
case "haskell":
|
|
13952
|
+
return this.resolveNamespace(lang, spec);
|
|
13953
|
+
default:
|
|
13954
|
+
return void 0;
|
|
13955
|
+
}
|
|
13956
|
+
}
|
|
13957
|
+
/**
|
|
13958
|
+
* Resolve a namespace specifier to the file declaring it.
|
|
13959
|
+
*
|
|
13960
|
+
* Tried whole first, then with the trailing segment dropped: `using Foo.Bar`
|
|
13961
|
+
* names a namespace outright, while PHP's `use App\Models\User` names a
|
|
13962
|
+
* *class* inside `App\Models`, so the prefix is what was declared.
|
|
13963
|
+
*/
|
|
13964
|
+
resolveNamespace(lang, spec) {
|
|
13965
|
+
const family = languageFamily(lang);
|
|
13966
|
+
const normalized = normalizeNamespace(spec);
|
|
13967
|
+
const exact = this.byNamespace.get(`${family}:${normalized}`);
|
|
13968
|
+
if (exact) return exact;
|
|
13969
|
+
const segments = normalized.split(".").filter(Boolean);
|
|
13970
|
+
if (segments.length < 2) return void 0;
|
|
13971
|
+
return this.byNamespace.get(`${family}:${segments.slice(0, -1).join(".")}`);
|
|
13972
|
+
}
|
|
13973
|
+
// ─── Lookup primitives ──────────────────────────────────────────────────────
|
|
13974
|
+
lookup(candidate) {
|
|
13975
|
+
return this.byPath.get(path19.posix.normalize(candidate).toLowerCase());
|
|
13976
|
+
}
|
|
13977
|
+
/**
|
|
13978
|
+
* Try `base` verbatim, then `base` + each extension, then each directory
|
|
13979
|
+
* entry point inside `base`.
|
|
13980
|
+
*/
|
|
13981
|
+
lookupWithExtensions(base, family) {
|
|
13982
|
+
const direct = this.lookup(base);
|
|
13983
|
+
if (direct) return direct;
|
|
13984
|
+
const extensions = EXTENSIONS[family] ?? [];
|
|
13985
|
+
const suffix = path19.posix.extname(base);
|
|
13986
|
+
const stem = suffix && extensions.includes(suffix) ? base.slice(0, -suffix.length) : base;
|
|
13987
|
+
for (const ext of extensions) {
|
|
13988
|
+
const hit = this.lookup(`${stem}${ext}`);
|
|
13989
|
+
if (hit) return hit;
|
|
13990
|
+
}
|
|
13991
|
+
for (const entry of DIRECTORY_ENTRIES[family] ?? []) {
|
|
13992
|
+
for (const ext of extensions) {
|
|
13993
|
+
const hit = this.lookup(path19.posix.join(base, `${entry}${ext}`));
|
|
13994
|
+
if (hit) return hit;
|
|
13995
|
+
}
|
|
13996
|
+
}
|
|
13997
|
+
return void 0;
|
|
13998
|
+
}
|
|
13999
|
+
/**
|
|
14000
|
+
* A representative indexed file inside `dir`, for ecosystems whose import
|
|
14001
|
+
* unit is a directory rather than a file (Go packages, JVM wildcard imports).
|
|
14002
|
+
*
|
|
14003
|
+
* The choice is deterministic — a file named after the directory, else the
|
|
14004
|
+
* first by name — so the same import always produces the same edge. Package
|
|
14005
|
+
* grouping is unaffected either way: every file in the directory carries the
|
|
14006
|
+
* same package label, so the package-level edge is exact regardless of which
|
|
14007
|
+
* member represents it.
|
|
14008
|
+
*/
|
|
14009
|
+
representativeIn(dir, family) {
|
|
14010
|
+
const members = this.byDir.get(path19.posix.normalize(dir).toLowerCase());
|
|
14011
|
+
if (!members?.length) return void 0;
|
|
14012
|
+
const extensions = EXTENSIONS[family] ?? [];
|
|
14013
|
+
const eligible = members.filter((file) => extensions.includes(path19.posix.extname(toPortablePath(file)).toLowerCase())).sort((a, b) => toPortablePath(a).localeCompare(toPortablePath(b)));
|
|
14014
|
+
if (eligible.length === 0) return void 0;
|
|
14015
|
+
const base = path19.posix.basename(path19.posix.normalize(dir)).toLowerCase();
|
|
14016
|
+
const named = eligible.find(
|
|
14017
|
+
(file) => path19.posix.basename(toPortablePath(file)).split(".")[0]?.toLowerCase() === base
|
|
14018
|
+
);
|
|
14019
|
+
return named ?? eligible[0];
|
|
14020
|
+
}
|
|
14021
|
+
// ─── Per-family resolution ──────────────────────────────────────────────────
|
|
14022
|
+
/** Relative specifiers, then workspace package names and their subpaths. */
|
|
14023
|
+
resolveJs(fromFile, spec) {
|
|
14024
|
+
if (spec.startsWith(".")) {
|
|
14025
|
+
const absolute = path19.posix.join(path19.posix.dirname(fromFile), spec);
|
|
14026
|
+
return this.lookupWithExtensions(absolute, "js");
|
|
14027
|
+
}
|
|
14028
|
+
const owner2 = this.structure.roots.find(
|
|
14029
|
+
(root) => root.kind === "npm" && root.importPath !== void 0 && (spec === root.importPath || spec.startsWith(`${root.importPath}/`))
|
|
14030
|
+
);
|
|
14031
|
+
if (!owner2?.importPath) return void 0;
|
|
14032
|
+
const subpath = spec.slice(owner2.importPath.length).replace(/^\//, "");
|
|
14033
|
+
if (!subpath) {
|
|
14034
|
+
return this.lookupWithExtensions(path19.posix.join(owner2.dir, "src/index"), "js") ?? this.lookupWithExtensions(path19.posix.join(owner2.dir, "index"), "js");
|
|
14035
|
+
}
|
|
14036
|
+
return this.lookupWithExtensions(path19.posix.join(owner2.dir, subpath), "js") ?? this.lookupWithExtensions(path19.posix.join(owner2.dir, "src", subpath), "js");
|
|
14037
|
+
}
|
|
14038
|
+
/** Go import paths are absolute module paths; a package is a directory. */
|
|
14039
|
+
resolveGo(spec) {
|
|
14040
|
+
const owner2 = this.structure.roots.find(
|
|
14041
|
+
(root) => root.kind === "go" && root.importPath !== void 0 && (spec === root.importPath || spec.startsWith(`${root.importPath}/`))
|
|
14042
|
+
);
|
|
14043
|
+
if (!owner2?.importPath) return void 0;
|
|
14044
|
+
const subpath = spec.slice(owner2.importPath.length).replace(/^\//, "");
|
|
14045
|
+
return this.representativeIn(path19.posix.join(owner2.dir, subpath), "go");
|
|
14046
|
+
}
|
|
14047
|
+
/**
|
|
14048
|
+
* `import a.b.c` / `from a.b import c`, plus PEP 328 relative imports whose
|
|
14049
|
+
* leading dots the extractor preserves (`.sibling`, `..parent.mod`).
|
|
14050
|
+
*/
|
|
14051
|
+
resolvePython(fromFile, spec) {
|
|
14052
|
+
const leadingDots = /^\.*/.exec(spec)?.[0].length ?? 0;
|
|
14053
|
+
if (leadingDots > 0) {
|
|
14054
|
+
let base = path19.posix.dirname(fromFile);
|
|
14055
|
+
for (let i = 1; i < leadingDots; i++) base = path19.posix.dirname(base);
|
|
14056
|
+
const rest = spec.slice(leadingDots).split(".").filter(Boolean);
|
|
14057
|
+
return this.lookupWithExtensions(path19.posix.join(base, ...rest), "py");
|
|
14058
|
+
}
|
|
14059
|
+
const segments = spec.split(".").filter(Boolean);
|
|
14060
|
+
if (segments.length === 0) return void 0;
|
|
14061
|
+
const sourceRoots = this.structure.roots.filter((root) => root.kind === "python").flatMap((root) => root.sourceRoots);
|
|
14062
|
+
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
14063
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, ...segments), "py");
|
|
14064
|
+
if (hit) return hit;
|
|
14065
|
+
if (segments.length > 1) {
|
|
14066
|
+
const parent = this.lookupWithExtensions(
|
|
14067
|
+
path19.posix.join(base, ...segments.slice(0, -1)),
|
|
14068
|
+
"py"
|
|
14069
|
+
);
|
|
14070
|
+
if (parent) return parent;
|
|
14071
|
+
}
|
|
14072
|
+
}
|
|
14073
|
+
return void 0;
|
|
14074
|
+
}
|
|
14075
|
+
/** `use crate::a::b`, `use super::x`, `use self::y`, `use other_crate::z`. */
|
|
14076
|
+
resolveRust(fromFile, spec) {
|
|
14077
|
+
const segments = spec.split("::").filter(Boolean);
|
|
14078
|
+
if (segments.length === 0) return void 0;
|
|
14079
|
+
const head = segments[0];
|
|
14080
|
+
if (head === "self" || head === "super") {
|
|
14081
|
+
let base = path19.posix.dirname(fromFile);
|
|
14082
|
+
for (const segment of segments) {
|
|
14083
|
+
if (segment === "super") base = path19.posix.dirname(base);
|
|
14084
|
+
else if (segment !== "self") break;
|
|
14085
|
+
}
|
|
14086
|
+
const rest2 = segments.filter((segment) => segment !== "self" && segment !== "super");
|
|
14087
|
+
return this.lookupWithExtensions(path19.posix.join(base, ...rest2), "rs");
|
|
14088
|
+
}
|
|
14089
|
+
const owningCrate = findOwningRoot(this.structure, fromFile, ["cargo"]);
|
|
14090
|
+
const crate = head === "crate" ? owningCrate : this.structure.roots.find(
|
|
14091
|
+
(root) => root.kind === "cargo" && root.importPath === head?.replace(/-/g, "_")
|
|
14092
|
+
);
|
|
14093
|
+
if (!crate) {
|
|
14094
|
+
return this.lookupWithExtensions(
|
|
14095
|
+
path19.posix.join(path19.posix.dirname(fromFile), ...segments),
|
|
14096
|
+
"rs"
|
|
14097
|
+
);
|
|
12990
14098
|
}
|
|
12991
|
-
|
|
12992
|
-
const
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
negated,
|
|
13000
|
-
dirOnly
|
|
13001
|
-
});
|
|
14099
|
+
const rest = segments.slice(1);
|
|
14100
|
+
for (const base of crate.sourceRoots) {
|
|
14101
|
+
const parent = rest.length > 1 ? this.lookupWithExtensions(path19.posix.join(base, ...rest.slice(0, -1)), "rs") : void 0;
|
|
14102
|
+
const exact = this.lookupWithExtensions(path19.posix.join(base, ...rest), "rs");
|
|
14103
|
+
const hit = exact ?? parent ?? this.lookupWithExtensions(path19.posix.join(base, "lib"), "rs");
|
|
14104
|
+
if (hit) return hit;
|
|
14105
|
+
}
|
|
14106
|
+
return void 0;
|
|
13002
14107
|
}
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
14108
|
+
/** `com.example.Thing` and `com.example.*` against JVM source roots. */
|
|
14109
|
+
resolveJvm(spec) {
|
|
14110
|
+
const segments = spec.split(".").filter(Boolean);
|
|
14111
|
+
if (segments.length === 0) return void 0;
|
|
14112
|
+
const sourceRoots = this.structure.roots.filter((root) => root.kind === "maven" || root.kind === "gradle").flatMap((root) => root.sourceRoots);
|
|
14113
|
+
const wildcard = segments[segments.length - 1] === "*";
|
|
14114
|
+
const parts = wildcard ? segments.slice(0, -1) : segments;
|
|
14115
|
+
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
14116
|
+
const target = path19.posix.join(base, ...parts);
|
|
14117
|
+
const hit = wildcard ? this.representativeIn(target, "jvm") : this.lookupWithExtensions(target, "jvm");
|
|
14118
|
+
if (hit) return hit;
|
|
13009
14119
|
}
|
|
13010
|
-
return
|
|
13011
|
-
}
|
|
14120
|
+
return void 0;
|
|
14121
|
+
}
|
|
14122
|
+
/** `#include "foo/bar.h"` — quoted form only; `<…>` is a system header. */
|
|
14123
|
+
resolveInclude(fromFile, spec) {
|
|
14124
|
+
const relative13 = this.lookupWithExtensions(
|
|
14125
|
+
path19.posix.join(path19.posix.dirname(fromFile), spec),
|
|
14126
|
+
"c"
|
|
14127
|
+
);
|
|
14128
|
+
if (relative13) return relative13;
|
|
14129
|
+
for (const base of [
|
|
14130
|
+
path19.posix.join(this.structure.projectRoot, "include"),
|
|
14131
|
+
this.structure.projectRoot
|
|
14132
|
+
]) {
|
|
14133
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, spec), "c");
|
|
14134
|
+
if (hit) return hit;
|
|
14135
|
+
}
|
|
14136
|
+
return void 0;
|
|
14137
|
+
}
|
|
14138
|
+
/** `require_relative 'x'` is relative; `require 'x'` is looked up under lib/. */
|
|
14139
|
+
resolveRuby(fromFile, spec) {
|
|
14140
|
+
const relative13 = this.lookupWithExtensions(
|
|
14141
|
+
path19.posix.join(path19.posix.dirname(fromFile), spec),
|
|
14142
|
+
"ruby"
|
|
14143
|
+
);
|
|
14144
|
+
if (relative13) return relative13;
|
|
14145
|
+
for (const base of [
|
|
14146
|
+
path19.posix.join(this.structure.projectRoot, "lib"),
|
|
14147
|
+
this.structure.projectRoot
|
|
14148
|
+
]) {
|
|
14149
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, spec), "ruby");
|
|
14150
|
+
if (hit) return hit;
|
|
14151
|
+
}
|
|
14152
|
+
return void 0;
|
|
14153
|
+
}
|
|
14154
|
+
};
|
|
14155
|
+
|
|
14156
|
+
// src/codebase-index/import-extractor.ts
|
|
14157
|
+
var IMPORT_MAX_FILE_CHARS = 512 * 1024;
|
|
14158
|
+
var IMPORT_MAX_PER_FILE = 400;
|
|
14159
|
+
var DOTTED_IMPORT = [
|
|
14160
|
+
{ re: /^[ \t]*import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;?\s*$/gm }
|
|
14161
|
+
];
|
|
14162
|
+
var LANG_IMPORTS = {
|
|
14163
|
+
// Go and Python have real AST extractors; these patterns are the fallback for
|
|
14164
|
+
// machines with no Go toolchain or Python interpreter installed, where the
|
|
14165
|
+
// parser degrades to regex symbols and would otherwise contribute no edges.
|
|
14166
|
+
go: [
|
|
14167
|
+
{ re: /^[ \t]*import\s+(?:[\w.]+\s+)?"([^"]+)"/gm },
|
|
14168
|
+
// Grouped form: inside `import ( … )` each line is an optional alias plus a
|
|
14169
|
+
// quoted path. A stray match elsewhere resolves to no file and is dropped.
|
|
14170
|
+
{ re: /^[ \t]*(?:[A-Za-z_.]\w*\s+)?"([^"]+)"\s*$/gm }
|
|
14171
|
+
],
|
|
14172
|
+
py: [
|
|
14173
|
+
{ re: /^[ \t]*import\s+([\w.]+)/gm },
|
|
14174
|
+
{ re: /^[ \t]*from\s+([.\w]+)\s+import\b/gm }
|
|
14175
|
+
],
|
|
14176
|
+
rs: [
|
|
14177
|
+
// use a::b::C; | use a::b::{C, D}; → the path before any brace
|
|
14178
|
+
{ re: /^[ \t]*(?:pub\s+)?use\s+([\w:]+?)(?:::\{|\s*;|\s+as\b)/gm },
|
|
14179
|
+
// mod foo; (a declaration *and* a dependency on foo.rs / foo/mod.rs)
|
|
14180
|
+
{ re: /^[ \t]*(?:pub\s+)?mod\s+(\w+)\s*;/gm }
|
|
14181
|
+
],
|
|
14182
|
+
java: DOTTED_IMPORT,
|
|
14183
|
+
kotlin: DOTTED_IMPORT,
|
|
14184
|
+
scala: [{ re: /^[ \t]*import\s+([\w.]+(?:\.[_*])?)\s*$/gm }],
|
|
14185
|
+
csharp: [
|
|
14186
|
+
// using Foo.Bar; | using static Foo.Bar; | using Alias = Foo.Bar;
|
|
14187
|
+
{ re: /^[ \t]*global\s+using\s+(?:static\s+)?([\w.]+)\s*;/gm, name: "full" },
|
|
14188
|
+
{ re: /^[ \t]*using\s+(?:static\s+)?(?:\w+\s*=\s*)?([\w.]+)\s*;/gm, name: "full" }
|
|
14189
|
+
],
|
|
14190
|
+
// Quoted includes only: <stdio.h> is a system header with no indexed file.
|
|
14191
|
+
c: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
14192
|
+
cpp: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
14193
|
+
ruby: [{ re: /^[ \t]*(?:require|require_relative|load)\s*\(?\s*['"]([^'"]+)['"]/gm }],
|
|
14194
|
+
php: [
|
|
14195
|
+
// `use A\B\C` imports the class C, which is what the index has a symbol
|
|
14196
|
+
// for — the namespace symbol only covers the `A\B` prefix.
|
|
14197
|
+
{ re: /^[ \t]*use\s+(?:function\s+|const\s+)?([\w\\]+)/gm },
|
|
14198
|
+
{ re: /^[ \t]*(?:require|require_once|include|include_once)\s*\(?\s*['"]([^'"]+)['"]/gm }
|
|
14199
|
+
],
|
|
14200
|
+
swift: [{ re: /^[ \t]*import\s+(?:struct\s+|class\s+|func\s+)?([\w.]+)\s*$/gm }],
|
|
14201
|
+
dart: [{ re: /^[ \t]*(?:import|export|part)\s+['"]([^'"]+)['"]/gm }],
|
|
14202
|
+
lua: [{ re: /\brequire\s*\(?\s*['"]([^'"]+)['"]/g }],
|
|
14203
|
+
elixir: [{ re: /^[ \t]*(?:alias|import|require|use)\s+([A-Z][\w.]*)/gm, name: "full" }],
|
|
14204
|
+
haskell: [{ re: /^[ \t]*import\s+(?:qualified\s+)?([\w.]+)/gm, name: "full" }],
|
|
14205
|
+
zig: [{ re: /@import\s*\(\s*"([^"]+)"\s*\)/g }],
|
|
14206
|
+
proto: [{ re: /^[ \t]*import\s+(?:public\s+|weak\s+)?"([^"]+)"\s*;/gm }],
|
|
14207
|
+
// `@import "x"`, `@use "x"`, `@forward "x"` — Sass and plain CSS alike.
|
|
14208
|
+
css: [{ re: /^[ \t]*@(?:import|use|forward)\s+(?:url\()?\s*['"]([^'"]+)['"]/gm }],
|
|
14209
|
+
// A .vue/.svelte file's <script> block is JS; the same ESM syntax applies.
|
|
14210
|
+
vue: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
14211
|
+
svelte: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
14212
|
+
html: [
|
|
14213
|
+
{ re: /<script[^>]+src\s*=\s*['"]([^'"]+)['"]/g },
|
|
14214
|
+
{ re: /<link[^>]+href\s*=\s*['"]([^'"]+)['"]/g }
|
|
14215
|
+
],
|
|
14216
|
+
shell: [{ re: /^[ \t]*(?:source|\.)\s+(\S+)/gm }],
|
|
14217
|
+
r: [{ re: /\b(?:source|library|require)\s*\(\s*['"]?([\w./-]+)['"]?\s*\)/g }]
|
|
14218
|
+
};
|
|
14219
|
+
function lastSegment(specifier) {
|
|
14220
|
+
const pathLike = /[/\\]|::/.test(specifier);
|
|
14221
|
+
const segments = specifier.split(/[/\\]|::/).filter(Boolean);
|
|
14222
|
+
let last = segments[segments.length - 1] ?? specifier;
|
|
14223
|
+
if (last === "*" || last === "_") {
|
|
14224
|
+
last = segments[segments.length - 2] ?? specifier;
|
|
14225
|
+
}
|
|
14226
|
+
if (pathLike) return last.replace(/\.[A-Za-z0-9]{1,8}$/, "") || last;
|
|
14227
|
+
const dotted = last.split(".").filter((part) => part && part !== "*" && part !== "_");
|
|
14228
|
+
return dotted[dotted.length - 1] ?? last;
|
|
13012
14229
|
}
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
lines = raw.split("\n");
|
|
13018
|
-
} catch {
|
|
14230
|
+
function newlineOffsets(content) {
|
|
14231
|
+
const offsets = [];
|
|
14232
|
+
for (let i = 0; i < content.length; i++) {
|
|
14233
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
13019
14234
|
}
|
|
13020
|
-
return
|
|
14235
|
+
return offsets;
|
|
14236
|
+
}
|
|
14237
|
+
function lineAt(offsets, index) {
|
|
14238
|
+
let low = 0;
|
|
14239
|
+
let high = offsets.length;
|
|
14240
|
+
while (low < high) {
|
|
14241
|
+
const mid = low + high >>> 1;
|
|
14242
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
14243
|
+
else high = mid;
|
|
14244
|
+
}
|
|
14245
|
+
return low + 1;
|
|
14246
|
+
}
|
|
14247
|
+
function hasImportPatterns(lang) {
|
|
14248
|
+
return LANG_IMPORTS[lang] !== void 0;
|
|
14249
|
+
}
|
|
14250
|
+
function extractImports(opts) {
|
|
14251
|
+
const patterns = LANG_IMPORTS[opts.lang];
|
|
14252
|
+
if (!patterns || !opts.content) return [];
|
|
14253
|
+
const limit = opts.maxImports ?? IMPORT_MAX_PER_FILE;
|
|
14254
|
+
const content = opts.content.length > IMPORT_MAX_FILE_CHARS ? opts.content.slice(0, IMPORT_MAX_FILE_CHARS) : opts.content;
|
|
14255
|
+
const refs = [];
|
|
14256
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14257
|
+
const offsets = newlineOffsets(content);
|
|
14258
|
+
for (const pattern of patterns) {
|
|
14259
|
+
const re = new RegExp(pattern.re.source, pattern.re.flags);
|
|
14260
|
+
for (const match of content.matchAll(re)) {
|
|
14261
|
+
if (refs.length >= limit) return refs;
|
|
14262
|
+
const specifier = match[1]?.trim();
|
|
14263
|
+
if (!specifier) continue;
|
|
14264
|
+
const module = specifier;
|
|
14265
|
+
const toName = pattern.name === "full" ? module : lastSegment(module);
|
|
14266
|
+
if (!toName) continue;
|
|
14267
|
+
const key = `${module}\0${toName}`;
|
|
14268
|
+
if (seen.has(key)) continue;
|
|
14269
|
+
seen.add(key);
|
|
14270
|
+
refs.push({
|
|
14271
|
+
fromId: 0,
|
|
14272
|
+
toName,
|
|
14273
|
+
callType: "import",
|
|
14274
|
+
line: lineAt(offsets, match.index ?? 0),
|
|
14275
|
+
lang: opts.lang,
|
|
14276
|
+
module
|
|
14277
|
+
});
|
|
14278
|
+
}
|
|
14279
|
+
}
|
|
14280
|
+
return refs;
|
|
13021
14281
|
}
|
|
13022
|
-
|
|
13023
|
-
// src/codebase-index/indexer.ts
|
|
13024
|
-
init_languages2();
|
|
13025
14282
|
|
|
13026
14283
|
// src/codebase-index/parser-dispatch.ts
|
|
13027
14284
|
async function parseFileContent(file, content, lang) {
|
|
14285
|
+
const parsed = await dispatch(file, content, lang);
|
|
14286
|
+
return withRelations(parsed, content, lang);
|
|
14287
|
+
}
|
|
14288
|
+
async function dispatch(file, content, lang) {
|
|
13028
14289
|
switch (lang) {
|
|
13029
14290
|
case "ts":
|
|
13030
14291
|
case "tsx":
|
|
@@ -13059,6 +14320,13 @@ async function parseFileContent(file, content, lang) {
|
|
|
13059
14320
|
}
|
|
13060
14321
|
}
|
|
13061
14322
|
}
|
|
14323
|
+
function withRelations(parsed, content, lang) {
|
|
14324
|
+
let refs = parsed.refs ?? [];
|
|
14325
|
+
if (refs.length === 0 && hasImportPatterns(lang)) {
|
|
14326
|
+
refs = extractImports({ content, lang });
|
|
14327
|
+
}
|
|
14328
|
+
return { ...parsed, refs: refs.map((ref) => ref.lang ? ref : { ...ref, lang }) };
|
|
14329
|
+
}
|
|
13062
14330
|
|
|
13063
14331
|
// src/codebase-index/indexer.ts
|
|
13064
14332
|
var YIELD_EVERY_N = 50;
|
|
@@ -13095,7 +14363,7 @@ function normalizeComparablePath(value) {
|
|
|
13095
14363
|
}
|
|
13096
14364
|
function gitOutput(projectRoot, args) {
|
|
13097
14365
|
return new Promise((resolve17, reject) => {
|
|
13098
|
-
|
|
14366
|
+
execFile(
|
|
13099
14367
|
"git",
|
|
13100
14368
|
["-C", projectRoot, ...args],
|
|
13101
14369
|
{
|
|
@@ -13226,13 +14494,40 @@ function assignRefsToSymbols2(refs, symbols) {
|
|
|
13226
14494
|
}
|
|
13227
14495
|
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
13228
14496
|
if (!owner2 || owner2.id <= 0) continue;
|
|
13229
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
14497
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
|
|
13230
14498
|
if (seen.has(key)) continue;
|
|
13231
14499
|
seen.add(key);
|
|
13232
14500
|
assigned.push({ ...ref, fromId: owner2.id });
|
|
13233
14501
|
}
|
|
13234
14502
|
return assigned;
|
|
13235
14503
|
}
|
|
14504
|
+
async function resolveProjectRelations(store, projectRoot, opts) {
|
|
14505
|
+
if (opts.signal?.aborted) return;
|
|
14506
|
+
try {
|
|
14507
|
+
const indexedFiles = store.getAllFileMetas().map((meta) => meta.file);
|
|
14508
|
+
if (indexedFiles.length === 0) return;
|
|
14509
|
+
const structure = await detectModuleRoots(projectRoot, indexedFiles);
|
|
14510
|
+
if (opts.signal?.aborted) return;
|
|
14511
|
+
store.setFilePackages(assignPackageLabels(structure, indexedFiles));
|
|
14512
|
+
const resolver = new ModuleResolver(
|
|
14513
|
+
structure,
|
|
14514
|
+
indexedFiles,
|
|
14515
|
+
store.getNamespaceDeclarations()
|
|
14516
|
+
);
|
|
14517
|
+
const pending2 = store.getUnresolvedImports(opts.onlyFiles);
|
|
14518
|
+
const resolutions = [];
|
|
14519
|
+
for (const entry of pending2) {
|
|
14520
|
+
const toFile = resolver.resolve(entry.fromFile, entry.lang, entry.module);
|
|
14521
|
+
if (toFile && toFile !== entry.fromFile) {
|
|
14522
|
+
resolutions.push({ ...entry, toFile });
|
|
14523
|
+
}
|
|
14524
|
+
}
|
|
14525
|
+
if (opts.signal?.aborted) return;
|
|
14526
|
+
store.applyImportResolutions(resolutions);
|
|
14527
|
+
} catch (err) {
|
|
14528
|
+
opts.errors.push(`relation resolution: ${err instanceof Error ? err.message : String(err)}`);
|
|
14529
|
+
}
|
|
14530
|
+
}
|
|
13236
14531
|
async function runIndexerWithStore(store, opts) {
|
|
13237
14532
|
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
13238
14533
|
const relationGraphVersion = "2";
|
|
@@ -13477,6 +14772,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
13477
14772
|
}
|
|
13478
14773
|
}
|
|
13479
14774
|
if (needsFullRefResolution) store.resolveRefs();
|
|
14775
|
+
await resolveProjectRelations(store, projectRoot, {
|
|
14776
|
+
// A watcher run re-resolves only what it touched; a full run (or a contract
|
|
14777
|
+
// bump) re-resolves everything, because a newly indexed file can be the
|
|
14778
|
+
// target of imports written long before it.
|
|
14779
|
+
onlyFiles: needsFullRefResolution ? void 0 : opts.files,
|
|
14780
|
+
errors,
|
|
14781
|
+
signal
|
|
14782
|
+
});
|
|
13480
14783
|
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
13481
14784
|
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
13482
14785
|
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
@@ -13559,6 +14862,22 @@ function symbolGraphService(args) {
|
|
|
13559
14862
|
indexStorePool.release(store);
|
|
13560
14863
|
}
|
|
13561
14864
|
}
|
|
14865
|
+
function incomingCallsService(args) {
|
|
14866
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
14867
|
+
try {
|
|
14868
|
+
return store.findIncomingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
14869
|
+
} finally {
|
|
14870
|
+
indexStorePool.release(store);
|
|
14871
|
+
}
|
|
14872
|
+
}
|
|
14873
|
+
function outgoingCallsService(args) {
|
|
14874
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
14875
|
+
try {
|
|
14876
|
+
return store.findOutgoingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
14877
|
+
} finally {
|
|
14878
|
+
indexStorePool.release(store);
|
|
14879
|
+
}
|
|
14880
|
+
}
|
|
13562
14881
|
|
|
13563
14882
|
// src/codebase-index/background-indexer.ts
|
|
13564
14883
|
init_languages2();
|
|
@@ -13796,6 +15115,10 @@ async function callInline(op, args, opts) {
|
|
|
13796
15115
|
return fileGraphService(args);
|
|
13797
15116
|
case "symbolGraph":
|
|
13798
15117
|
return symbolGraphService(args);
|
|
15118
|
+
case "incomingCalls":
|
|
15119
|
+
return incomingCallsService(args);
|
|
15120
|
+
case "outgoingCalls":
|
|
15121
|
+
return outgoingCallsService(args);
|
|
13799
15122
|
default:
|
|
13800
15123
|
throw new Error(`unknown index op: ${String(op)}`);
|
|
13801
15124
|
}
|
|
@@ -13989,6 +15312,12 @@ async function fileGraphService2(args) {
|
|
|
13989
15312
|
async function symbolGraphService2(args) {
|
|
13990
15313
|
return callIndexOp("symbolGraph", args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });
|
|
13991
15314
|
}
|
|
15315
|
+
async function incomingCallsService2(args) {
|
|
15316
|
+
return callIndexOp("incomingCalls", args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });
|
|
15317
|
+
}
|
|
15318
|
+
async function outgoingCallsService2(args) {
|
|
15319
|
+
return callIndexOp("outgoingCalls", args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });
|
|
15320
|
+
}
|
|
13992
15321
|
function shutdownCodebaseIndexServer(projectRoot, indexDir, reason) {
|
|
13993
15322
|
return shutdownProjectIndexServer(projectRoot, indexDir, reason);
|
|
13994
15323
|
}
|
|
@@ -14072,6 +15401,214 @@ var codebaseIndexTool = {
|
|
|
14072
15401
|
}
|
|
14073
15402
|
};
|
|
14074
15403
|
|
|
15404
|
+
// src/codebase-index/codebase-incoming-calls-tool.ts
|
|
15405
|
+
var codebaseIncomingCallsTool = {
|
|
15406
|
+
name: "codebase-incoming-calls",
|
|
15407
|
+
category: "Project",
|
|
15408
|
+
icon: "index",
|
|
15409
|
+
description: "Find all callers of a function, method, or symbol \u2014 who invokes or references it. Uses the codebase index ref graph for instant, exact results. Always use this instead of grep when checking impact of a change.",
|
|
15410
|
+
usageHint: 'CALL THIS BEFORE REFACTORING OR CHANGING ANY FUNCTION:\n\n- NEVER use grep or manual line reading to check where a function is called.\n- ALWAYS call codebase-incoming-calls({ symbol: "funcName" }) first.\n- Returns exact files, line numbers, caller signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Combine with codebase-outgoing-calls to see what the symbol itself calls.\nIf the index is not built, run codebase-index first.',
|
|
15411
|
+
permission: "auto",
|
|
15412
|
+
mutating: false,
|
|
15413
|
+
capabilities: ["fs.read"],
|
|
15414
|
+
timeoutMs: 35e3,
|
|
15415
|
+
inputSchema: {
|
|
15416
|
+
type: "object",
|
|
15417
|
+
properties: {
|
|
15418
|
+
symbol: {
|
|
15419
|
+
type: "string",
|
|
15420
|
+
description: "The function/method/type name to find callers for"
|
|
15421
|
+
},
|
|
15422
|
+
file: {
|
|
15423
|
+
type: "string",
|
|
15424
|
+
description: "Scope to a specific file when multiple symbols share the same name"
|
|
15425
|
+
},
|
|
15426
|
+
limit: {
|
|
15427
|
+
type: "integer",
|
|
15428
|
+
description: "Maximum call sites to return (default 50, max 200)",
|
|
15429
|
+
minimum: 1,
|
|
15430
|
+
maximum: 200
|
|
15431
|
+
}
|
|
15432
|
+
},
|
|
15433
|
+
required: ["symbol"]
|
|
15434
|
+
},
|
|
15435
|
+
async execute(input, ctx) {
|
|
15436
|
+
const state = getIndexState();
|
|
15437
|
+
if (state.indexing && !state.ready) {
|
|
15438
|
+
return {
|
|
15439
|
+
symbol: input.symbol,
|
|
15440
|
+
calls: [],
|
|
15441
|
+
total: 0,
|
|
15442
|
+
indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
|
|
15443
|
+
};
|
|
15444
|
+
}
|
|
15445
|
+
if (state.lastError) {
|
|
15446
|
+
const circuit = state.circuit;
|
|
15447
|
+
const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s).` : "Try /codebase-reindex.";
|
|
15448
|
+
return {
|
|
15449
|
+
symbol: input.symbol,
|
|
15450
|
+
calls: [],
|
|
15451
|
+
total: 0,
|
|
15452
|
+
indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
|
|
15453
|
+
};
|
|
15454
|
+
}
|
|
15455
|
+
const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
|
|
15456
|
+
const { calls, symbolFound, ambiguous, totalMatches } = await incomingCallsService2(
|
|
15457
|
+
{
|
|
15458
|
+
projectRoot: ctx.projectRoot,
|
|
15459
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
15460
|
+
symbol: input.symbol,
|
|
15461
|
+
file: input.file,
|
|
15462
|
+
limit
|
|
15463
|
+
}
|
|
15464
|
+
);
|
|
15465
|
+
if (!symbolFound) {
|
|
15466
|
+
let hasPersistedIndex = state.ready;
|
|
15467
|
+
if (!hasPersistedIndex) {
|
|
15468
|
+
try {
|
|
15469
|
+
const stats = await codebaseIndexStats({
|
|
15470
|
+
projectRoot: ctx.projectRoot,
|
|
15471
|
+
indexDir: codebaseIndexDirOverride(ctx)
|
|
15472
|
+
});
|
|
15473
|
+
hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
|
|
15474
|
+
} catch {
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
if (!hasPersistedIndex) {
|
|
15478
|
+
return {
|
|
15479
|
+
symbol: input.symbol,
|
|
15480
|
+
calls: [],
|
|
15481
|
+
total: 0,
|
|
15482
|
+
indexStatus: "No persisted index data found. Run codebase-index to build it."
|
|
15483
|
+
};
|
|
15484
|
+
}
|
|
15485
|
+
return {
|
|
15486
|
+
symbol: input.symbol,
|
|
15487
|
+
calls: [],
|
|
15488
|
+
total: 0,
|
|
15489
|
+
note: `Symbol "${input.symbol}" not found in the index. Use codebase-search to verify the name.`
|
|
15490
|
+
};
|
|
15491
|
+
}
|
|
15492
|
+
const notes = [];
|
|
15493
|
+
if (totalMatches > limit) {
|
|
15494
|
+
notes.push(`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`);
|
|
15495
|
+
}
|
|
15496
|
+
if (ambiguous) {
|
|
15497
|
+
notes.push(`Symbol "${input.symbol}" exists in multiple files. Results include callers of all same-named symbols. Use codebase-search to find the exact file and pass it as \`file\`.`);
|
|
15498
|
+
}
|
|
15499
|
+
return {
|
|
15500
|
+
symbol: input.symbol,
|
|
15501
|
+
calls,
|
|
15502
|
+
total: calls.length,
|
|
15503
|
+
...notes.length > 0 ? { note: notes.join(" ") } : {}
|
|
15504
|
+
};
|
|
15505
|
+
}
|
|
15506
|
+
};
|
|
15507
|
+
|
|
15508
|
+
// src/codebase-index/codebase-outgoing-calls-tool.ts
|
|
15509
|
+
var codebaseOutgoingCallsTool = {
|
|
15510
|
+
name: "codebase-outgoing-calls",
|
|
15511
|
+
category: "Project",
|
|
15512
|
+
icon: "index",
|
|
15513
|
+
description: "Find all functions/methods/symbols that a given symbol calls or depends on \u2014 its callees. Uses the codebase index ref graph for instant, exact results. Use this to understand a function's dependencies before modifying it.",
|
|
15514
|
+
usageHint: 'USE THIS TO UNDERSTAND A FUNCTION\'S DEPENDENCIES:\n\n- Call codebase-outgoing-calls({ symbol: "funcName" }) to see everything it calls.\n- Returns exact files, line numbers, callee signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Pair with codebase-incoming-calls for a complete impact picture: incoming = who calls you, outgoing = what you call.\nIf the index is not built, run codebase-index first.',
|
|
15515
|
+
permission: "auto",
|
|
15516
|
+
mutating: false,
|
|
15517
|
+
capabilities: ["fs.read"],
|
|
15518
|
+
timeoutMs: 35e3,
|
|
15519
|
+
inputSchema: {
|
|
15520
|
+
type: "object",
|
|
15521
|
+
properties: {
|
|
15522
|
+
symbol: {
|
|
15523
|
+
type: "string",
|
|
15524
|
+
description: "The function/method/type name to find callees for"
|
|
15525
|
+
},
|
|
15526
|
+
file: {
|
|
15527
|
+
type: "string",
|
|
15528
|
+
description: "Scope to a specific file when multiple symbols share the same name"
|
|
15529
|
+
},
|
|
15530
|
+
limit: {
|
|
15531
|
+
type: "integer",
|
|
15532
|
+
description: "Maximum call sites to return (default 50, max 200)",
|
|
15533
|
+
minimum: 1,
|
|
15534
|
+
maximum: 200
|
|
15535
|
+
}
|
|
15536
|
+
},
|
|
15537
|
+
required: ["symbol"]
|
|
15538
|
+
},
|
|
15539
|
+
async execute(input, ctx) {
|
|
15540
|
+
const state = getIndexState();
|
|
15541
|
+
if (state.indexing && !state.ready) {
|
|
15542
|
+
return {
|
|
15543
|
+
symbol: input.symbol,
|
|
15544
|
+
calls: [],
|
|
15545
|
+
total: 0,
|
|
15546
|
+
indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
|
|
15547
|
+
};
|
|
15548
|
+
}
|
|
15549
|
+
if (state.lastError) {
|
|
15550
|
+
const circuit = state.circuit;
|
|
15551
|
+
const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s).` : "Try /codebase-reindex.";
|
|
15552
|
+
return {
|
|
15553
|
+
symbol: input.symbol,
|
|
15554
|
+
calls: [],
|
|
15555
|
+
total: 0,
|
|
15556
|
+
indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
|
|
15557
|
+
};
|
|
15558
|
+
}
|
|
15559
|
+
const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
|
|
15560
|
+
const { calls, symbolFound, unresolvedCount, totalMatches } = await outgoingCallsService2(
|
|
15561
|
+
{
|
|
15562
|
+
projectRoot: ctx.projectRoot,
|
|
15563
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
15564
|
+
symbol: input.symbol,
|
|
15565
|
+
file: input.file,
|
|
15566
|
+
limit
|
|
15567
|
+
}
|
|
15568
|
+
);
|
|
15569
|
+
if (!symbolFound) {
|
|
15570
|
+
let hasPersistedIndex = state.ready;
|
|
15571
|
+
if (!hasPersistedIndex) {
|
|
15572
|
+
try {
|
|
15573
|
+
const stats = await codebaseIndexStats({
|
|
15574
|
+
projectRoot: ctx.projectRoot,
|
|
15575
|
+
indexDir: codebaseIndexDirOverride(ctx)
|
|
15576
|
+
});
|
|
15577
|
+
hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
|
|
15578
|
+
} catch {
|
|
15579
|
+
}
|
|
15580
|
+
}
|
|
15581
|
+
if (!hasPersistedIndex) {
|
|
15582
|
+
return {
|
|
15583
|
+
symbol: input.symbol,
|
|
15584
|
+
calls: [],
|
|
15585
|
+
total: 0,
|
|
15586
|
+
indexStatus: "No persisted index data found. Run codebase-index to build it."
|
|
15587
|
+
};
|
|
15588
|
+
}
|
|
15589
|
+
return {
|
|
15590
|
+
symbol: input.symbol,
|
|
15591
|
+
calls: [],
|
|
15592
|
+
total: 0,
|
|
15593
|
+
note: `Symbol "${input.symbol}" not found in the index. Use codebase-search to verify the name.`
|
|
15594
|
+
};
|
|
15595
|
+
}
|
|
15596
|
+
const notes = [];
|
|
15597
|
+
if (totalMatches > limit) {
|
|
15598
|
+
notes.push(`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`);
|
|
15599
|
+
}
|
|
15600
|
+
if (unresolvedCount > 0) {
|
|
15601
|
+
notes.push(`${unresolvedCount} unresolved reference(s) not shown \u2014 their targets could not be resolved during indexing.`);
|
|
15602
|
+
}
|
|
15603
|
+
return {
|
|
15604
|
+
symbol: input.symbol,
|
|
15605
|
+
calls,
|
|
15606
|
+
total: calls.length,
|
|
15607
|
+
...notes.length > 0 ? { note: notes.join(" ") } : {}
|
|
15608
|
+
};
|
|
15609
|
+
}
|
|
15610
|
+
};
|
|
15611
|
+
|
|
14075
15612
|
// src/codebase-index/codebase-search-tool.ts
|
|
14076
15613
|
var codebaseSearchTool = {
|
|
14077
15614
|
name: "codebase-search",
|
|
@@ -14792,17 +16329,17 @@ import {
|
|
|
14792
16329
|
} from "@wrongstack/core/design";
|
|
14793
16330
|
async function resolveReal(p) {
|
|
14794
16331
|
const resolved = path25.resolve(p);
|
|
14795
|
-
let
|
|
16332
|
+
let probe = resolved;
|
|
14796
16333
|
const missing = [];
|
|
14797
16334
|
for (; ; ) {
|
|
14798
16335
|
try {
|
|
14799
|
-
return path25.resolve(await fs20.realpath(
|
|
16336
|
+
return path25.resolve(await fs20.realpath(probe), ...missing);
|
|
14800
16337
|
} catch (err) {
|
|
14801
16338
|
if (err.code === "ENOENT") {
|
|
14802
|
-
const parent = path25.dirname(
|
|
14803
|
-
if (parent ===
|
|
14804
|
-
missing.unshift(path25.basename(
|
|
14805
|
-
|
|
16339
|
+
const parent = path25.dirname(probe);
|
|
16340
|
+
if (parent === probe) return resolved;
|
|
16341
|
+
missing.unshift(path25.basename(probe));
|
|
16342
|
+
probe = parent;
|
|
14806
16343
|
continue;
|
|
14807
16344
|
}
|
|
14808
16345
|
return resolved;
|
|
@@ -15077,7 +16614,7 @@ Replace off-palette colors with kit tokens (or the materialized CSS vars / token
|
|
|
15077
16614
|
|
|
15078
16615
|
// src/diff.ts
|
|
15079
16616
|
init_util();
|
|
15080
|
-
import { spawn as
|
|
16617
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
15081
16618
|
import { statSync as statSync3 } from "node:fs";
|
|
15082
16619
|
import * as fs21 from "node:fs/promises";
|
|
15083
16620
|
import * as path26 from "node:path";
|
|
@@ -15180,7 +16717,7 @@ function runGit(args, cwd, signal) {
|
|
|
15180
16717
|
return new Promise((resolve17) => {
|
|
15181
16718
|
let stdout = "";
|
|
15182
16719
|
let stderr = "";
|
|
15183
|
-
const child =
|
|
16720
|
+
const child = spawn7("git", args, {
|
|
15184
16721
|
cwd,
|
|
15185
16722
|
signal,
|
|
15186
16723
|
env: buildChildEnv3(),
|
|
@@ -15391,7 +16928,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
|
|
|
15391
16928
|
|
|
15392
16929
|
// src/e2e.ts
|
|
15393
16930
|
init_util();
|
|
15394
|
-
import { open, readdir as
|
|
16931
|
+
import { open, readdir as readdir7 } from "node:fs/promises";
|
|
15395
16932
|
import * as path27 from "node:path";
|
|
15396
16933
|
async function readBoundedText(filePath, maxBytes) {
|
|
15397
16934
|
let handle;
|
|
@@ -15509,7 +17046,7 @@ async function scanWorkspace(root, maxDepth, signal) {
|
|
|
15509
17046
|
}
|
|
15510
17047
|
let entries;
|
|
15511
17048
|
try {
|
|
15512
|
-
entries = await
|
|
17049
|
+
entries = await readdir7(current.directory, { withFileTypes: true });
|
|
15513
17050
|
} catch {
|
|
15514
17051
|
continue;
|
|
15515
17052
|
}
|
|
@@ -15568,7 +17105,7 @@ async function detectPackageManager3(projectRoot, scanRoot, declared) {
|
|
|
15568
17105
|
while (true) {
|
|
15569
17106
|
const names = /* @__PURE__ */ new Set();
|
|
15570
17107
|
try {
|
|
15571
|
-
for (const entry of await
|
|
17108
|
+
for (const entry of await readdir7(directory)) names.add(entry);
|
|
15572
17109
|
} catch {
|
|
15573
17110
|
}
|
|
15574
17111
|
if (names.has("pnpm-lock.yaml")) return "pnpm";
|
|
@@ -15636,7 +17173,7 @@ async function collectSpecs(root, framework, testDirectory, signal) {
|
|
|
15636
17173
|
if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };
|
|
15637
17174
|
let entries;
|
|
15638
17175
|
try {
|
|
15639
|
-
entries = await
|
|
17176
|
+
entries = await readdir7(directory, { withFileTypes: true });
|
|
15640
17177
|
} catch {
|
|
15641
17178
|
continue;
|
|
15642
17179
|
}
|
|
@@ -15837,7 +17374,7 @@ function findLadderMatches(fileLf, oldLf) {
|
|
|
15837
17374
|
const exact = [];
|
|
15838
17375
|
let idx = fileLf.indexOf(oldLf);
|
|
15839
17376
|
while (idx !== -1) {
|
|
15840
|
-
exact.push({ start: idx, end: idx + oldLf.length, startLine:
|
|
17377
|
+
exact.push({ start: idx, end: idx + oldLf.length, startLine: lineAt2(fileLf, idx) });
|
|
15841
17378
|
idx = fileLf.indexOf(oldLf, idx + 1);
|
|
15842
17379
|
}
|
|
15843
17380
|
if (exact.length > 0) return { tier: "exact", matches: exact };
|
|
@@ -15869,7 +17406,7 @@ function findLadderMatches(fileLf, oldLf) {
|
|
|
15869
17406
|
if (normalized.length > 0) return { tier: "whitespace-normalized", matches: normalized };
|
|
15870
17407
|
return fuzzyScan(fileLines, needleLines, offsets);
|
|
15871
17408
|
}
|
|
15872
|
-
function
|
|
17409
|
+
function lineAt2(text, pos) {
|
|
15873
17410
|
if (pos < 512) {
|
|
15874
17411
|
let line2 = 1;
|
|
15875
17412
|
for (let i = 0; i < pos; i++) {
|
|
@@ -16331,7 +17868,7 @@ Compare this against your old_string and retry with the file's actual text.` : "
|
|
|
16331
17868
|
};
|
|
16332
17869
|
|
|
16333
17870
|
// src/exec.ts
|
|
16334
|
-
import { spawn as
|
|
17871
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
16335
17872
|
import {
|
|
16336
17873
|
emitProcessCompleted as emitProcessCompleted3,
|
|
16337
17874
|
emitProcessOutput as emitProcessOutput3,
|
|
@@ -17252,6 +18789,26 @@ var BLOCKED_ARG_PATTERNS = {
|
|
|
17252
18789
|
pnpm: [],
|
|
17253
18790
|
npx: []
|
|
17254
18791
|
};
|
|
18792
|
+
var BLOCKED_OPTION_NAMES = {
|
|
18793
|
+
git: /* @__PURE__ */ new Set([
|
|
18794
|
+
"--exec",
|
|
18795
|
+
"--upload-pack",
|
|
18796
|
+
"--receive-pack",
|
|
18797
|
+
"--exec-path",
|
|
18798
|
+
"--git-dir",
|
|
18799
|
+
"--work-tree",
|
|
18800
|
+
"--namespace",
|
|
18801
|
+
"-c",
|
|
18802
|
+
"--config",
|
|
18803
|
+
"--config-env",
|
|
18804
|
+
"-C"
|
|
18805
|
+
]),
|
|
18806
|
+
find: /* @__PURE__ */ new Set(["-exec", "-ok", "-execdir"])
|
|
18807
|
+
};
|
|
18808
|
+
function optionName(arg) {
|
|
18809
|
+
const eq = arg.indexOf("=");
|
|
18810
|
+
return eq > 0 ? arg.slice(0, eq) : arg;
|
|
18811
|
+
}
|
|
17255
18812
|
var BLOCKED_SUBCOMMANDS = {
|
|
17256
18813
|
docker: /* @__PURE__ */ new Set(["push"]),
|
|
17257
18814
|
podman: /* @__PURE__ */ new Set(["push"]),
|
|
@@ -17289,6 +18846,15 @@ function validateArgs(cmd, args) {
|
|
|
17289
18846
|
const blocked2 = blockedSequences.find((seq) => seq.every((part, idx) => actual[idx] === part));
|
|
17290
18847
|
if (blocked2) return `Blocked subcommand "${blocked2.join(" ")}" for command "${cmd}"`;
|
|
17291
18848
|
}
|
|
18849
|
+
const blockedOptions = BLOCKED_OPTION_NAMES[cmd];
|
|
18850
|
+
if (blockedOptions) {
|
|
18851
|
+
for (const arg of args) {
|
|
18852
|
+
if (arg === "--") break;
|
|
18853
|
+
if (blockedOptions.has(optionName(arg))) {
|
|
18854
|
+
return `Blocked option "${optionName(arg)}" for command "${cmd}"`;
|
|
18855
|
+
}
|
|
18856
|
+
}
|
|
18857
|
+
}
|
|
17292
18858
|
const blocked = BLOCKED_ARG_PATTERNS[cmd];
|
|
17293
18859
|
if (!blocked) return null;
|
|
17294
18860
|
for (const arg of args) {
|
|
@@ -17471,7 +19037,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
17471
19037
|
};
|
|
17472
19038
|
let child;
|
|
17473
19039
|
try {
|
|
17474
|
-
child =
|
|
19040
|
+
child = spawn8(spawnCmd, spawnArgs, {
|
|
17475
19041
|
cwd,
|
|
17476
19042
|
env: buildChildEnv2(sessionId),
|
|
17477
19043
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -18105,7 +19671,7 @@ async function detectFixer(cwd) {
|
|
|
18105
19671
|
|
|
18106
19672
|
// src/git.ts
|
|
18107
19673
|
init_util();
|
|
18108
|
-
import { spawn as
|
|
19674
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
18109
19675
|
import { statSync as statSync4 } from "node:fs";
|
|
18110
19676
|
import { dirname as dirname14, resolve as resolve13, sep as sep6 } from "node:path";
|
|
18111
19677
|
import { assessCommitSafety } from "@wrongstack/core/coordination";
|
|
@@ -18369,7 +19935,7 @@ function runGit2(args, cwd, signal) {
|
|
|
18369
19935
|
return new Promise((resolve17) => {
|
|
18370
19936
|
let stdout = "";
|
|
18371
19937
|
let stderr = "";
|
|
18372
|
-
const child =
|
|
19938
|
+
const child = spawn9("git", args, {
|
|
18373
19939
|
cwd,
|
|
18374
19940
|
signal,
|
|
18375
19941
|
env: buildChildEnv4(),
|
|
@@ -18560,7 +20126,7 @@ var globTool = {
|
|
|
18560
20126
|
};
|
|
18561
20127
|
|
|
18562
20128
|
// src/grep.ts
|
|
18563
|
-
import { spawn as
|
|
20129
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
18564
20130
|
import * as fs25 from "node:fs/promises";
|
|
18565
20131
|
import * as path31 from "node:path";
|
|
18566
20132
|
import { ToolValidationError as ToolValidationError4 } from "@wrongstack/core/types";
|
|
@@ -18717,7 +20283,7 @@ var grepTool = {
|
|
|
18717
20283
|
async function detectRg(signal) {
|
|
18718
20284
|
return new Promise((resolve17) => {
|
|
18719
20285
|
try {
|
|
18720
|
-
const p =
|
|
20286
|
+
const p = spawn10("rg", ["--version"], { env: buildChildEnv5(), stdio: "ignore", signal, windowsHide: true });
|
|
18721
20287
|
p.on("error", () => resolve17(false));
|
|
18722
20288
|
p.on("close", (code) => resolve17(code === 0));
|
|
18723
20289
|
} catch {
|
|
@@ -18751,7 +20317,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
18751
20317
|
const FLUSH_AT = 16;
|
|
18752
20318
|
const MAX_BUF_BYTES = 1e6;
|
|
18753
20319
|
let bufOverflow = false;
|
|
18754
|
-
const child =
|
|
20320
|
+
const child = spawn10("rg", args, {
|
|
18755
20321
|
signal,
|
|
18756
20322
|
env: buildChildEnv5(),
|
|
18757
20323
|
// rg diagnostics are not part of the tool result. Ignoring stderr avoids
|
|
@@ -19048,7 +20614,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
19048
20614
|
init_spawn_stream();
|
|
19049
20615
|
init_util();
|
|
19050
20616
|
init_legacy_bridge();
|
|
19051
|
-
import { join as
|
|
20617
|
+
import { join as join24 } from "node:path";
|
|
19052
20618
|
import {
|
|
19053
20619
|
detectEcosystem as detectPackageEcosystem,
|
|
19054
20620
|
recordPackageAction
|
|
@@ -19232,17 +20798,17 @@ function resolveManifestPath(cwd, pkgManager) {
|
|
|
19232
20798
|
case "pnpm":
|
|
19233
20799
|
case "yarn":
|
|
19234
20800
|
case "npm":
|
|
19235
|
-
return
|
|
20801
|
+
return join24(cwd, "package.json");
|
|
19236
20802
|
/* v8 ignore next 2 -- pkgManager is always pnpm/yarn/npm; the default is defensive. */
|
|
19237
20803
|
default:
|
|
19238
|
-
return
|
|
20804
|
+
return join24(cwd, "package.json");
|
|
19239
20805
|
}
|
|
19240
20806
|
}
|
|
19241
20807
|
|
|
19242
20808
|
// src/json.ts
|
|
19243
|
-
init_util();
|
|
19244
20809
|
import * as fs26 from "node:fs/promises";
|
|
19245
20810
|
import { deepMerge as deepMergeCore } from "@wrongstack/core/utils";
|
|
20811
|
+
init_util();
|
|
19246
20812
|
var MAX_JSON_FILE_BYTES = 16 * 1024 * 1024;
|
|
19247
20813
|
var MAX_JSON_FILE_BYTES_HUMAN = "16 MiB";
|
|
19248
20814
|
var JsonFileTooLargeError = class extends Error {
|
|
@@ -19681,8 +21247,12 @@ function validateJsonSchema(data, schema) {
|
|
|
19681
21247
|
}
|
|
19682
21248
|
}
|
|
19683
21249
|
if (typeof value === "string" && s["pattern"]) {
|
|
19684
|
-
const
|
|
19685
|
-
if (!
|
|
21250
|
+
const compiled = compileUserRegex(s["pattern"], "");
|
|
21251
|
+
if (!compiled.ok) {
|
|
21252
|
+
errors.push(`${path39}: invalid schema pattern \u2014 ${compiled.reason}`);
|
|
21253
|
+
} else if (!compiled.regex.test(capSubject(value))) {
|
|
21254
|
+
errors.push(`${path39}: does not match pattern ${s["pattern"]}`);
|
|
21255
|
+
}
|
|
19686
21256
|
}
|
|
19687
21257
|
if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
|
|
19688
21258
|
errors.push(`${path39}: string too short (min ${s["minLength"]})`);
|
|
@@ -22081,7 +23651,7 @@ async function detectLinter(cwd) {
|
|
|
22081
23651
|
}
|
|
22082
23652
|
|
|
22083
23653
|
// src/logs.ts
|
|
22084
|
-
import { spawn as
|
|
23654
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
22085
23655
|
import { buildChildEnv as buildChildEnv6 } from "@wrongstack/core/utils";
|
|
22086
23656
|
init_util();
|
|
22087
23657
|
var logsTool = {
|
|
@@ -22188,7 +23758,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
22188
23758
|
clearTimeout(timer);
|
|
22189
23759
|
resolve17(result);
|
|
22190
23760
|
};
|
|
22191
|
-
const child =
|
|
23761
|
+
const child = spawn11("docker", args, { cwd, signal, env: buildChildEnv6(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
22192
23762
|
const timer = setTimeout(() => {
|
|
22193
23763
|
child.kill("SIGTERM");
|
|
22194
23764
|
finish(empty());
|
|
@@ -22297,7 +23867,7 @@ function parseLine(line) {
|
|
|
22297
23867
|
// src/outdated.ts
|
|
22298
23868
|
init_util();
|
|
22299
23869
|
init_win32_resolve();
|
|
22300
|
-
import { spawn as
|
|
23870
|
+
import { spawn as spawn12 } from "node:child_process";
|
|
22301
23871
|
import { buildChildEnv as buildChildEnv7 } from "@wrongstack/core/utils";
|
|
22302
23872
|
var outdatedTool = {
|
|
22303
23873
|
name: "outdated",
|
|
@@ -22417,7 +23987,7 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
22417
23987
|
const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
|
|
22418
23988
|
const spawnCmd = shim?.command ?? resolved;
|
|
22419
23989
|
const spawnArgs = shim?.args ?? args;
|
|
22420
|
-
const child =
|
|
23990
|
+
const child = spawn12(spawnCmd, spawnArgs, {
|
|
22421
23991
|
cwd,
|
|
22422
23992
|
signal,
|
|
22423
23993
|
env: buildChildEnv7(),
|
|
@@ -22483,7 +24053,7 @@ function parseOutdatedOutput(json2, exitCode) {
|
|
|
22483
24053
|
|
|
22484
24054
|
// src/patch.ts
|
|
22485
24055
|
init_util();
|
|
22486
|
-
import { spawn as
|
|
24056
|
+
import { spawn as spawn13 } from "node:child_process";
|
|
22487
24057
|
import * as fs27 from "node:fs/promises";
|
|
22488
24058
|
import * as os9 from "node:os";
|
|
22489
24059
|
import * as path32 from "node:path";
|
|
@@ -22625,7 +24195,7 @@ function runPatch(args, cwd, signal) {
|
|
|
22625
24195
|
let stdout = "";
|
|
22626
24196
|
let stderr = "";
|
|
22627
24197
|
const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
|
|
22628
|
-
const child =
|
|
24198
|
+
const child = spawn13("patch", args, {
|
|
22629
24199
|
cwd,
|
|
22630
24200
|
signal,
|
|
22631
24201
|
env,
|
|
@@ -23222,7 +24792,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
|
|
|
23222
24792
|
}
|
|
23223
24793
|
|
|
23224
24794
|
// src/replace.ts
|
|
23225
|
-
import { spawn as
|
|
24795
|
+
import { spawn as spawn14 } from "node:child_process";
|
|
23226
24796
|
import * as fs29 from "node:fs/promises";
|
|
23227
24797
|
import * as path33 from "node:path";
|
|
23228
24798
|
import { ToolValidationError as ToolValidationError6 } from "@wrongstack/core/types";
|
|
@@ -23407,7 +24977,7 @@ async function globFiles(pattern, base, extraGlob) {
|
|
|
23407
24977
|
function checkRg() {
|
|
23408
24978
|
return new Promise((resolve17) => {
|
|
23409
24979
|
try {
|
|
23410
|
-
const p =
|
|
24980
|
+
const p = spawn14("rg", ["--version"], {
|
|
23411
24981
|
env: buildChildEnv9(),
|
|
23412
24982
|
stdio: "ignore",
|
|
23413
24983
|
windowsHide: true
|
|
@@ -23421,7 +24991,7 @@ function checkRg() {
|
|
|
23421
24991
|
}
|
|
23422
24992
|
function spawnRgFind(pattern, base) {
|
|
23423
24993
|
const args = ["--files", "--glob", pattern, base];
|
|
23424
|
-
const child =
|
|
24994
|
+
const child = spawn14("rg", args, {
|
|
23425
24995
|
signal: AbortSignal.timeout(3e4),
|
|
23426
24996
|
env: buildChildEnv9(),
|
|
23427
24997
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -25460,7 +27030,7 @@ var writeTool = {
|
|
|
25460
27030
|
required: ["path", "content"]
|
|
25461
27031
|
},
|
|
25462
27032
|
async execute(input, ctx, opts) {
|
|
25463
|
-
return
|
|
27033
|
+
return writeFile6(input, ctx, opts?.signal);
|
|
25464
27034
|
},
|
|
25465
27035
|
async *executeStream(input, ctx, opts) {
|
|
25466
27036
|
const prepared = await prepareWrite(input, ctx);
|
|
@@ -25473,7 +27043,7 @@ var writeTool = {
|
|
|
25473
27043
|
yield { type: "final", output: await finishWrite(input, ctx, prepared, opts?.signal) };
|
|
25474
27044
|
}
|
|
25475
27045
|
};
|
|
25476
|
-
async function
|
|
27046
|
+
async function writeFile6(input, ctx, signal) {
|
|
25477
27047
|
return finishWrite(input, ctx, await prepareWrite(input, ctx), signal);
|
|
25478
27048
|
}
|
|
25479
27049
|
async function prepareWrite(input, ctx) {
|
|
@@ -25561,6 +27131,8 @@ var TIER1_TOOLS = [
|
|
|
25561
27131
|
editTool,
|
|
25562
27132
|
codebaseStatsTool,
|
|
25563
27133
|
codebaseSearchTool,
|
|
27134
|
+
codebaseIncomingCallsTool,
|
|
27135
|
+
codebaseOutgoingCallsTool,
|
|
25564
27136
|
codebaseIndexTool,
|
|
25565
27137
|
bashTool,
|
|
25566
27138
|
grepTool,
|
|
@@ -25611,6 +27183,8 @@ var builtinTools = [
|
|
|
25611
27183
|
editTool,
|
|
25612
27184
|
codebaseStatsTool,
|
|
25613
27185
|
codebaseSearchTool,
|
|
27186
|
+
codebaseIncomingCallsTool,
|
|
27187
|
+
codebaseOutgoingCallsTool,
|
|
25614
27188
|
codebaseIndexTool,
|
|
25615
27189
|
deadCodeScanTool,
|
|
25616
27190
|
replaceTool,
|