@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/builtin.js
CHANGED
|
@@ -721,7 +721,7 @@ var init_process_registry = __esm({
|
|
|
721
721
|
const p = this.processes.get(pid);
|
|
722
722
|
if (!p) return false;
|
|
723
723
|
if (p.killed) return true;
|
|
724
|
-
if (p.protected) return false;
|
|
724
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
725
725
|
if (opts.preserveBackground && p.background) return false;
|
|
726
726
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
727
727
|
const isWin5 = os.platform() === "win32";
|
|
@@ -772,9 +772,13 @@ var init_process_registry = __esm({
|
|
|
772
772
|
killAll(opts = {}) {
|
|
773
773
|
const pids = Array.from(this.processes.keys());
|
|
774
774
|
const killed = [];
|
|
775
|
+
const includeProtected = opts.includeProtected === true;
|
|
775
776
|
for (const pid of pids) {
|
|
776
777
|
const p = this.processes.get(pid);
|
|
777
|
-
if (
|
|
778
|
+
if (!p) continue;
|
|
779
|
+
if (p.protected && !includeProtected) continue;
|
|
780
|
+
if (opts.preserveBackground && p.background) continue;
|
|
781
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
778
782
|
}
|
|
779
783
|
return killed;
|
|
780
784
|
}
|
|
@@ -1138,18 +1142,18 @@ async function resolveRealInsideRoot(absPath, ctx) {
|
|
|
1138
1142
|
const realRoots = await Promise.all(
|
|
1139
1143
|
allowedRoots(ctx).map((r) => fsp2.realpath(r).catch(() => path3.resolve(r)))
|
|
1140
1144
|
);
|
|
1141
|
-
let
|
|
1145
|
+
let probe = absPath;
|
|
1142
1146
|
const pendingTail = [];
|
|
1143
1147
|
for (; ; ) {
|
|
1144
1148
|
let real;
|
|
1145
1149
|
try {
|
|
1146
|
-
real = await fsp2.realpath(
|
|
1150
|
+
real = await fsp2.realpath(probe);
|
|
1147
1151
|
} catch (err) {
|
|
1148
1152
|
if (err.code === "ENOENT") {
|
|
1149
|
-
const parent = path3.dirname(
|
|
1150
|
-
if (parent ===
|
|
1151
|
-
pendingTail.unshift(path3.basename(
|
|
1152
|
-
|
|
1153
|
+
const parent = path3.dirname(probe);
|
|
1154
|
+
if (parent === probe) return absPath;
|
|
1155
|
+
pendingTail.unshift(path3.basename(probe));
|
|
1156
|
+
probe = parent;
|
|
1153
1157
|
continue;
|
|
1154
1158
|
}
|
|
1155
1159
|
throw err;
|
|
@@ -4968,20 +4972,23 @@ var init_legacy_bridge = __esm({
|
|
|
4968
4972
|
});
|
|
4969
4973
|
|
|
4970
4974
|
// src/codebase-index/languages.ts
|
|
4971
|
-
import * as
|
|
4975
|
+
import * as path13 from "node:path";
|
|
4972
4976
|
function detectLang(file) {
|
|
4973
|
-
const base =
|
|
4977
|
+
const base = path13.basename(file);
|
|
4974
4978
|
const lowerBase = base.toLowerCase();
|
|
4975
4979
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4976
4980
|
return "ts";
|
|
4977
4981
|
}
|
|
4978
4982
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4979
4983
|
if (special) return special;
|
|
4980
|
-
const ext =
|
|
4984
|
+
const ext = path13.extname(base).toLowerCase();
|
|
4981
4985
|
if (!ext) return null;
|
|
4982
4986
|
return EXT_TO_LANG[ext] ?? null;
|
|
4983
4987
|
}
|
|
4984
|
-
|
|
4988
|
+
function languageFamily(lang) {
|
|
4989
|
+
return LANG_FAMILY[lang] ?? "other";
|
|
4990
|
+
}
|
|
4991
|
+
var EXT_TO_LANG, INDEXABLE_EXTENSIONS, SPECIAL_FILENAMES, LANG_FAMILY, LANG_FAMILY_ENTRIES;
|
|
4985
4992
|
var init_languages2 = __esm({
|
|
4986
4993
|
"src/codebase-index/languages.ts"() {
|
|
4987
4994
|
"use strict";
|
|
@@ -5072,6 +5079,52 @@ var init_languages2 = __esm({
|
|
|
5072
5079
|
procfile: "other",
|
|
5073
5080
|
justfile: "other"
|
|
5074
5081
|
};
|
|
5082
|
+
LANG_FAMILY = {
|
|
5083
|
+
// Single-compilation-unit family: a .vue/.svelte script block is JS/TS and
|
|
5084
|
+
// imports from — and is imported by — plain .ts files.
|
|
5085
|
+
ts: "js",
|
|
5086
|
+
tsx: "js",
|
|
5087
|
+
js: "js",
|
|
5088
|
+
jsx: "js",
|
|
5089
|
+
vue: "js",
|
|
5090
|
+
svelte: "js",
|
|
5091
|
+
go: "go",
|
|
5092
|
+
py: "py",
|
|
5093
|
+
rs: "rs",
|
|
5094
|
+
// The JVM resolves across languages: Kotlin and Scala call Java directly.
|
|
5095
|
+
java: "jvm",
|
|
5096
|
+
kotlin: "jvm",
|
|
5097
|
+
scala: "jvm",
|
|
5098
|
+
csharp: "dotnet",
|
|
5099
|
+
// A .h header is consumed by both C and C++ translation units.
|
|
5100
|
+
c: "c",
|
|
5101
|
+
cpp: "c",
|
|
5102
|
+
ruby: "ruby",
|
|
5103
|
+
php: "php",
|
|
5104
|
+
swift: "swift",
|
|
5105
|
+
dart: "dart",
|
|
5106
|
+
elixir: "elixir",
|
|
5107
|
+
haskell: "haskell",
|
|
5108
|
+
zig: "zig",
|
|
5109
|
+
lua: "lua",
|
|
5110
|
+
r: "r",
|
|
5111
|
+
shell: "shell",
|
|
5112
|
+
sql: "sql",
|
|
5113
|
+
json: "data",
|
|
5114
|
+
yaml: "data",
|
|
5115
|
+
toml: "data",
|
|
5116
|
+
html: "web",
|
|
5117
|
+
css: "web",
|
|
5118
|
+
proto: "proto",
|
|
5119
|
+
graphql: "graphql",
|
|
5120
|
+
md: "other",
|
|
5121
|
+
other: "other"
|
|
5122
|
+
};
|
|
5123
|
+
LANG_FAMILY_ENTRIES = Object.freeze(
|
|
5124
|
+
Object.entries(LANG_FAMILY).map(
|
|
5125
|
+
([lang, family]) => Object.freeze([lang, family])
|
|
5126
|
+
)
|
|
5127
|
+
);
|
|
5075
5128
|
}
|
|
5076
5129
|
});
|
|
5077
5130
|
|
|
@@ -5236,7 +5289,7 @@ function getTypeName(name) {
|
|
|
5236
5289
|
function deduplicateRefs(refs) {
|
|
5237
5290
|
const seen = /* @__PURE__ */ new Set();
|
|
5238
5291
|
return refs.filter((r) => {
|
|
5239
|
-
const key = `${r.toName}:${r.callType}:${r.line}`;
|
|
5292
|
+
const key = `${r.toName}:${r.callType}:${r.line}:${r.module ?? ""}`;
|
|
5240
5293
|
if (seen.has(key)) return false;
|
|
5241
5294
|
seen.add(key);
|
|
5242
5295
|
return true;
|
|
@@ -5246,10 +5299,16 @@ function getImportSpecifierName(spec) {
|
|
|
5246
5299
|
return spec.propertyName?.text ?? spec.name.text;
|
|
5247
5300
|
}
|
|
5248
5301
|
function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
5302
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
5249
5303
|
const clause = node.importClause;
|
|
5250
|
-
if (!clause)
|
|
5304
|
+
if (!clause) {
|
|
5305
|
+
if (module) {
|
|
5306
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
5307
|
+
}
|
|
5308
|
+
return;
|
|
5309
|
+
}
|
|
5251
5310
|
if (clause.name) {
|
|
5252
|
-
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5311
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
5253
5312
|
}
|
|
5254
5313
|
const bindings = clause.namedBindings;
|
|
5255
5314
|
if (!bindings) return;
|
|
@@ -5259,26 +5318,40 @@ function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
|
5259
5318
|
fromId: 0,
|
|
5260
5319
|
toName: getImportSpecifierName(element),
|
|
5261
5320
|
callType: "import",
|
|
5262
|
-
line: lineNum
|
|
5321
|
+
line: lineNum,
|
|
5322
|
+
module
|
|
5263
5323
|
});
|
|
5264
5324
|
}
|
|
5265
5325
|
} else if (ts.isNamespaceImport(bindings)) {
|
|
5266
|
-
refs.push({
|
|
5326
|
+
refs.push({
|
|
5327
|
+
fromId: 0,
|
|
5328
|
+
toName: bindings.name.text,
|
|
5329
|
+
callType: "import",
|
|
5330
|
+
line: lineNum,
|
|
5331
|
+
module
|
|
5332
|
+
});
|
|
5267
5333
|
}
|
|
5268
5334
|
}
|
|
5335
|
+
function moduleSpecifierOf(node) {
|
|
5336
|
+
return node && ts.isStringLiteral(node) ? node.text : void 0;
|
|
5337
|
+
}
|
|
5269
5338
|
function emitExportSpecifierRefs(node, refs, lineNum) {
|
|
5339
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
5270
5340
|
const clause = node.exportClause;
|
|
5271
5341
|
if (clause && ts.isNamespaceExport(clause)) {
|
|
5272
|
-
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5342
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
5273
5343
|
return;
|
|
5274
5344
|
}
|
|
5275
5345
|
if (clause && ts.isNamedExports(clause)) {
|
|
5276
5346
|
for (const element of clause.elements) {
|
|
5277
5347
|
const originalName = element.propertyName?.text ?? element.name.text;
|
|
5278
|
-
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum });
|
|
5348
|
+
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum, module });
|
|
5279
5349
|
}
|
|
5280
5350
|
return;
|
|
5281
5351
|
}
|
|
5352
|
+
if (module) {
|
|
5353
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
5354
|
+
}
|
|
5282
5355
|
}
|
|
5283
5356
|
var ts, tsLoad, kindMapCache;
|
|
5284
5357
|
var init_ts_parser = __esm({
|
|
@@ -5290,6 +5363,82 @@ var init_ts_parser = __esm({
|
|
|
5290
5363
|
}
|
|
5291
5364
|
});
|
|
5292
5365
|
|
|
5366
|
+
// src/codebase-index/parser-output.ts
|
|
5367
|
+
function coerceSymbols(value) {
|
|
5368
|
+
if (!Array.isArray(value)) return [];
|
|
5369
|
+
return value.flatMap((entry) => {
|
|
5370
|
+
const candidate = entry;
|
|
5371
|
+
if (typeof candidate.name !== "string" || typeof candidate.kind !== "string") return [];
|
|
5372
|
+
return [
|
|
5373
|
+
{
|
|
5374
|
+
name: candidate.name,
|
|
5375
|
+
kind: candidate.kind,
|
|
5376
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
5377
|
+
col: typeof candidate.col === "number" ? candidate.col : 0,
|
|
5378
|
+
signature: typeof candidate.signature === "string" ? candidate.signature : "",
|
|
5379
|
+
scope: typeof candidate.scope === "string" ? candidate.scope : ""
|
|
5380
|
+
}
|
|
5381
|
+
];
|
|
5382
|
+
});
|
|
5383
|
+
}
|
|
5384
|
+
function coerceRefs(value, lang) {
|
|
5385
|
+
if (!Array.isArray(value)) return [];
|
|
5386
|
+
return value.flatMap((entry) => {
|
|
5387
|
+
const candidate = entry;
|
|
5388
|
+
if (typeof candidate.toName !== "string" || !candidate.toName) return [];
|
|
5389
|
+
if (typeof candidate.callType !== "string" || !CALL_TYPES.has(candidate.callType)) return [];
|
|
5390
|
+
const module = typeof candidate.module === "string" && candidate.module ? candidate.module : void 0;
|
|
5391
|
+
return [
|
|
5392
|
+
{
|
|
5393
|
+
fromId: 0,
|
|
5394
|
+
toName: candidate.toName,
|
|
5395
|
+
callType: candidate.callType,
|
|
5396
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
5397
|
+
lang,
|
|
5398
|
+
module
|
|
5399
|
+
}
|
|
5400
|
+
];
|
|
5401
|
+
});
|
|
5402
|
+
}
|
|
5403
|
+
function parseParserOutput(stdout, lang) {
|
|
5404
|
+
const trimmed = stdout.trim();
|
|
5405
|
+
if (!trimmed) return { symbols: [], refs: [] };
|
|
5406
|
+
let parsed;
|
|
5407
|
+
try {
|
|
5408
|
+
parsed = JSON.parse(trimmed);
|
|
5409
|
+
} catch {
|
|
5410
|
+
return { symbols: [], refs: [] };
|
|
5411
|
+
}
|
|
5412
|
+
if (Array.isArray(parsed)) return { symbols: coerceSymbols(parsed), refs: [] };
|
|
5413
|
+
const record = parsed;
|
|
5414
|
+
return {
|
|
5415
|
+
symbols: coerceSymbols(record.symbols),
|
|
5416
|
+
refs: dedupeRefs(coerceRefs(record.refs, lang))
|
|
5417
|
+
};
|
|
5418
|
+
}
|
|
5419
|
+
function dedupeRefs(refs) {
|
|
5420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5421
|
+
return refs.filter((ref) => {
|
|
5422
|
+
const key = `${ref.toName}:${ref.callType}:${ref.line}:${ref.module ?? ""}`;
|
|
5423
|
+
if (seen.has(key)) return false;
|
|
5424
|
+
seen.add(key);
|
|
5425
|
+
return true;
|
|
5426
|
+
});
|
|
5427
|
+
}
|
|
5428
|
+
var CALL_TYPES;
|
|
5429
|
+
var init_parser_output = __esm({
|
|
5430
|
+
"src/codebase-index/parser-output.ts"() {
|
|
5431
|
+
"use strict";
|
|
5432
|
+
CALL_TYPES = /* @__PURE__ */ new Set([
|
|
5433
|
+
"call",
|
|
5434
|
+
"type_ref",
|
|
5435
|
+
"inherit",
|
|
5436
|
+
"implement",
|
|
5437
|
+
"import"
|
|
5438
|
+
]);
|
|
5439
|
+
}
|
|
5440
|
+
});
|
|
5441
|
+
|
|
5293
5442
|
// src/codebase-index/spawn-gate.ts
|
|
5294
5443
|
function withSpawnGate(fn) {
|
|
5295
5444
|
const run = chain.then(fn, fn);
|
|
@@ -5315,8 +5464,8 @@ __export(go_parser_exports, {
|
|
|
5315
5464
|
});
|
|
5316
5465
|
import { spawn as spawn5 } from "node:child_process";
|
|
5317
5466
|
import * as os6 from "node:os";
|
|
5318
|
-
import * as
|
|
5319
|
-
import * as
|
|
5467
|
+
import * as path20 from "node:path";
|
|
5468
|
+
import * as fs15 from "node:fs/promises";
|
|
5320
5469
|
async function parseSymbols2(opts) {
|
|
5321
5470
|
const { file, content, lang } = opts;
|
|
5322
5471
|
try {
|
|
@@ -5324,7 +5473,8 @@ async function parseSymbols2(opts) {
|
|
|
5324
5473
|
if (parsed.symbols.length > 0) {
|
|
5325
5474
|
return parsed;
|
|
5326
5475
|
}
|
|
5327
|
-
|
|
5476
|
+
const fallback = fallbackParse(file, content, lang);
|
|
5477
|
+
return parsed.refs?.length ? { ...fallback, refs: parsed.refs } : fallback;
|
|
5328
5478
|
} catch {
|
|
5329
5479
|
return fallbackParse(file, content, lang);
|
|
5330
5480
|
}
|
|
@@ -5388,9 +5538,9 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5388
5538
|
try {
|
|
5389
5539
|
let scriptPath = _cachedGoScriptPath;
|
|
5390
5540
|
if (!scriptPath) {
|
|
5391
|
-
const tmpDir = await
|
|
5392
|
-
scriptPath =
|
|
5393
|
-
await
|
|
5541
|
+
const tmpDir = await fs15.mkdtemp(path20.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5542
|
+
scriptPath = path20.join(tmpDir, "parse.go");
|
|
5543
|
+
await fs15.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5394
5544
|
_cachedGoScriptPath = scriptPath;
|
|
5395
5545
|
}
|
|
5396
5546
|
const goBinary = resolveWin32Command("go");
|
|
@@ -5432,8 +5582,8 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5432
5582
|
if (code !== 0 || !stdout.trim()) {
|
|
5433
5583
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5434
5584
|
}
|
|
5435
|
-
const
|
|
5436
|
-
const symbols =
|
|
5585
|
+
const { symbols: rawSymbols, refs } = parseParserOutput(stdout, lang);
|
|
5586
|
+
const symbols = rawSymbols.map((s) => ({
|
|
5437
5587
|
id: 0,
|
|
5438
5588
|
lang,
|
|
5439
5589
|
kind: s.kind,
|
|
@@ -5446,7 +5596,7 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5446
5596
|
scope: s.scope ?? "",
|
|
5447
5597
|
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
5448
5598
|
}));
|
|
5449
|
-
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
5599
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
5450
5600
|
} catch {
|
|
5451
5601
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
5452
5602
|
}
|
|
@@ -5456,6 +5606,7 @@ var init_go_parser = __esm({
|
|
|
5456
5606
|
"src/codebase-index/go-parser.ts"() {
|
|
5457
5607
|
"use strict";
|
|
5458
5608
|
init_win32_resolve();
|
|
5609
|
+
init_parser_output();
|
|
5459
5610
|
init_spawn_gate();
|
|
5460
5611
|
init_languages2();
|
|
5461
5612
|
GO_PARSE_SCRIPT = `
|
|
@@ -5469,6 +5620,7 @@ import (
|
|
|
5469
5620
|
"go/token"
|
|
5470
5621
|
"io"
|
|
5471
5622
|
"os"
|
|
5623
|
+
"strconv"
|
|
5472
5624
|
"strings"
|
|
5473
5625
|
)
|
|
5474
5626
|
|
|
@@ -5481,16 +5633,34 @@ type Sym struct {
|
|
|
5481
5633
|
Scope string \`json:"scope"\`
|
|
5482
5634
|
}
|
|
5483
5635
|
|
|
5636
|
+
// Ref is a cross-reference emitted alongside the symbols, so one \`go run\`
|
|
5637
|
+
// yields both. Module is the import path for CallType "import", else empty.
|
|
5638
|
+
type Ref struct {
|
|
5639
|
+
ToName string \`json:"toName"\`
|
|
5640
|
+
CallType string \`json:"callType"\`
|
|
5641
|
+
Line int \`json:"line"\`
|
|
5642
|
+
Module string \`json:"module"\`
|
|
5643
|
+
}
|
|
5644
|
+
|
|
5645
|
+
type Result struct {
|
|
5646
|
+
Symbols []Sym \`json:"symbols"\`
|
|
5647
|
+
Refs []Ref \`json:"refs"\`
|
|
5648
|
+
}
|
|
5649
|
+
|
|
5650
|
+
func emptyResult() string {
|
|
5651
|
+
return "{\\"symbols\\":[],\\"refs\\":[]}"
|
|
5652
|
+
}
|
|
5653
|
+
|
|
5484
5654
|
func main() {
|
|
5485
5655
|
src, err := io.ReadAll(os.Stdin)
|
|
5486
5656
|
if err != nil {
|
|
5487
|
-
fmt.Print(
|
|
5657
|
+
fmt.Print(emptyResult())
|
|
5488
5658
|
return
|
|
5489
5659
|
}
|
|
5490
5660
|
fset := token.NewFileSet()
|
|
5491
5661
|
node, err := parser.ParseFile(fset, "src.go", src, 0)
|
|
5492
5662
|
if err != nil {
|
|
5493
|
-
fmt.Print(
|
|
5663
|
+
fmt.Print(emptyResult())
|
|
5494
5664
|
return
|
|
5495
5665
|
}
|
|
5496
5666
|
|
|
@@ -5554,9 +5724,43 @@ func main() {
|
|
|
5554
5724
|
}
|
|
5555
5725
|
}
|
|
5556
5726
|
|
|
5557
|
-
|
|
5727
|
+
refs := []Ref{}
|
|
5728
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
5729
|
+
switch expr := n.(type) {
|
|
5730
|
+
case *ast.CallExpr:
|
|
5731
|
+
line := fset.Position(expr.Pos()).Line
|
|
5732
|
+
switch fun := expr.Fun.(type) {
|
|
5733
|
+
case *ast.Ident:
|
|
5734
|
+
refs = append(refs, Ref{ToName: fun.Name, CallType: "call", Line: line})
|
|
5735
|
+
case *ast.SelectorExpr:
|
|
5736
|
+
// Record the selected name (\`Join\` of \`filepath.Join\`): it is the
|
|
5737
|
+
// declared symbol name, so it resolves the same way the TypeScript
|
|
5738
|
+
// and Python extractors' call refs do.
|
|
5739
|
+
refs = append(refs, Ref{ToName: fun.Sel.Name, CallType: "call", Line: line})
|
|
5740
|
+
}
|
|
5741
|
+
case *ast.ImportSpec:
|
|
5742
|
+
if expr.Path != nil {
|
|
5743
|
+
if importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {
|
|
5744
|
+
line := fset.Position(expr.Pos()).Line
|
|
5745
|
+
// A Go import names a package, not a symbol; the package's
|
|
5746
|
+
// last path segment is the name it is referenced by.
|
|
5747
|
+
name := importPath
|
|
5748
|
+
if idx := strings.LastIndex(importPath, "/"); idx >= 0 {
|
|
5749
|
+
name = importPath[idx+1:]
|
|
5750
|
+
}
|
|
5751
|
+
refs = append(refs, Ref{ToName: name, CallType: "import", Line: line, Module: importPath})
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
return true
|
|
5756
|
+
})
|
|
5757
|
+
|
|
5758
|
+
if syms == nil {
|
|
5759
|
+
syms = []Sym{}
|
|
5760
|
+
}
|
|
5761
|
+
data, err := json.Marshal(Result{Symbols: syms, Refs: refs})
|
|
5558
5762
|
if err != nil {
|
|
5559
|
-
fmt.Print(
|
|
5763
|
+
fmt.Print(emptyResult())
|
|
5560
5764
|
return
|
|
5561
5765
|
}
|
|
5562
5766
|
fmt.Print(string(data))
|
|
@@ -5908,9 +6112,13 @@ var init_generic_parser = __esm({
|
|
|
5908
6112
|
],
|
|
5909
6113
|
elixir: [
|
|
5910
6114
|
{ re: /\bdef(?:p|macro|macrop)?\s+([A-Za-z_]\w*[!?]?)/g, kind: "function" },
|
|
5911
|
-
|
|
6115
|
+
// Dotted module names must be captured whole: `alias Foo.Bar` resolves
|
|
6116
|
+
// against this symbol, and a `Foo`-only capture never matches it.
|
|
6117
|
+
{ re: /\bdefmodule\s+([A-Z][\w.]*)/g, kind: "namespace" }
|
|
5912
6118
|
],
|
|
5913
6119
|
haskell: [
|
|
6120
|
+
// Target of `import Data.List`.
|
|
6121
|
+
{ re: /^module\s+([A-Z][\w.]*)/gm, kind: "namespace" },
|
|
5914
6122
|
{ re: /^([A-Za-z_]\w*)\s*::/gm, kind: "function" },
|
|
5915
6123
|
{ re: /\bdata\s+([A-Za-z_]\w*)/g, kind: "type" },
|
|
5916
6124
|
{ re: /\btype\s+(?:family\s+)?([A-Za-z_]\w*)/g, kind: "type" },
|
|
@@ -6001,9 +6209,9 @@ __export(py_parser_exports, {
|
|
|
6001
6209
|
parseSymbols: () => parseSymbols4
|
|
6002
6210
|
});
|
|
6003
6211
|
import { spawn as spawn6 } from "node:child_process";
|
|
6004
|
-
import * as
|
|
6212
|
+
import * as fs16 from "node:fs/promises";
|
|
6005
6213
|
import * as os7 from "node:os";
|
|
6006
|
-
import * as
|
|
6214
|
+
import * as path21 from "node:path";
|
|
6007
6215
|
async function parseSymbols4(opts) {
|
|
6008
6216
|
const { file, content, lang } = opts;
|
|
6009
6217
|
try {
|
|
@@ -6081,10 +6289,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
6081
6289
|
async function syncPyParse(filePath, content, lang) {
|
|
6082
6290
|
try {
|
|
6083
6291
|
if (!_cachedScriptPath) {
|
|
6084
|
-
const tmpDir =
|
|
6085
|
-
await
|
|
6086
|
-
_cachedScriptPath =
|
|
6087
|
-
await
|
|
6292
|
+
const tmpDir = path21.join(os7.tmpdir(), "ws-py-parse");
|
|
6293
|
+
await fs16.mkdir(tmpDir, { recursive: true });
|
|
6294
|
+
_cachedScriptPath = path21.join(tmpDir, "parse.py");
|
|
6295
|
+
await fs16.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
6088
6296
|
}
|
|
6089
6297
|
cachedPyBinary ??= resolvePython();
|
|
6090
6298
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6098,7 +6306,7 @@ async function syncPyParse(filePath, content, lang) {
|
|
|
6098
6306
|
if (code !== 0 || !stdout.trim()) {
|
|
6099
6307
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
6100
6308
|
}
|
|
6101
|
-
const raw =
|
|
6309
|
+
const { symbols: raw, refs } = parseParserOutput(stdout, lang);
|
|
6102
6310
|
const symbols = raw.map((s) => ({
|
|
6103
6311
|
id: 0,
|
|
6104
6312
|
lang,
|
|
@@ -6112,7 +6320,7 @@ async function syncPyParse(filePath, content, lang) {
|
|
|
6112
6320
|
scope: s.scope ?? "",
|
|
6113
6321
|
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
6114
6322
|
}));
|
|
6115
|
-
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
6323
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
6116
6324
|
} catch {
|
|
6117
6325
|
return null;
|
|
6118
6326
|
}
|
|
@@ -6123,6 +6331,7 @@ var init_py_parser = __esm({
|
|
|
6123
6331
|
"use strict";
|
|
6124
6332
|
init_win32_resolve();
|
|
6125
6333
|
init_generic_parser();
|
|
6334
|
+
init_parser_output();
|
|
6126
6335
|
init_spawn_gate();
|
|
6127
6336
|
init_languages2();
|
|
6128
6337
|
PY_PARSE_SCRIPT = `import ast, json, sys, os
|
|
@@ -6184,7 +6393,18 @@ class Sym:
|
|
|
6184
6393
|
def is_private(name):
|
|
6185
6394
|
return name.startswith("__") and not name.endswith("__")
|
|
6186
6395
|
|
|
6396
|
+
def leaf_name(node):
|
|
6397
|
+
# Declared name of the callee: \`join\` of \`os.path.join\`. Matches how the
|
|
6398
|
+
# TypeScript and Go extractors record call refs, so resolution behaves the
|
|
6399
|
+
# same across languages.
|
|
6400
|
+
if isinstance(node, ast.Attribute):
|
|
6401
|
+
return node.attr
|
|
6402
|
+
if isinstance(node, ast.Name):
|
|
6403
|
+
return node.id
|
|
6404
|
+
return get_name(node).split(".")[-1]
|
|
6405
|
+
|
|
6187
6406
|
syms = []
|
|
6407
|
+
refs = []
|
|
6188
6408
|
errors = []
|
|
6189
6409
|
|
|
6190
6410
|
try:
|
|
@@ -6192,7 +6412,7 @@ try:
|
|
|
6192
6412
|
tree = ast.parse(source, filename=sys.argv[1])
|
|
6193
6413
|
except Exception as e:
|
|
6194
6414
|
errors.append(str(e))
|
|
6195
|
-
print("[]")
|
|
6415
|
+
print(json.dumps({"symbols": [], "refs": []}))
|
|
6196
6416
|
sys.exit(0)
|
|
6197
6417
|
|
|
6198
6418
|
# Module-level scope
|
|
@@ -6326,7 +6546,42 @@ class ModuleVisitor(ast.NodeVisitor):
|
|
|
6326
6546
|
visitor = ModuleVisitor()
|
|
6327
6547
|
visitor.visit(tree)
|
|
6328
6548
|
|
|
6329
|
-
|
|
6549
|
+
# Refs need a separate full walk: ModuleVisitor deliberately does not descend
|
|
6550
|
+
# into function bodies (it would index locals as symbols), but that is exactly
|
|
6551
|
+
# where the calls are.
|
|
6552
|
+
for node in ast.walk(tree):
|
|
6553
|
+
if isinstance(node, ast.Call):
|
|
6554
|
+
name = leaf_name(node.func)
|
|
6555
|
+
if name:
|
|
6556
|
+
refs.append({"toName": name, "callType": "call", "line": node.lineno})
|
|
6557
|
+
elif isinstance(node, ast.Import):
|
|
6558
|
+
for alias in node.names:
|
|
6559
|
+
refs.append({
|
|
6560
|
+
"toName": alias.name.split(".")[-1],
|
|
6561
|
+
"callType": "import",
|
|
6562
|
+
"line": node.lineno,
|
|
6563
|
+
"module": alias.name,
|
|
6564
|
+
})
|
|
6565
|
+
elif isinstance(node, ast.ImportFrom):
|
|
6566
|
+
# PEP 328: node.level is the number of leading dots. Preserving them is
|
|
6567
|
+
# what lets the resolver walk up from the importing file's package \u2014
|
|
6568
|
+
# dropping them made \`from .foo import X\` indistinguishable from an
|
|
6569
|
+
# absolute \`foo\`.
|
|
6570
|
+
module = ("." * (node.level or 0)) + (node.module or "")
|
|
6571
|
+
for alias in node.names:
|
|
6572
|
+
refs.append({
|
|
6573
|
+
"toName": alias.name,
|
|
6574
|
+
"callType": "import",
|
|
6575
|
+
"line": node.lineno,
|
|
6576
|
+
"module": module,
|
|
6577
|
+
})
|
|
6578
|
+
elif isinstance(node, ast.ClassDef):
|
|
6579
|
+
for base in node.bases:
|
|
6580
|
+
name = leaf_name(base)
|
|
6581
|
+
if name:
|
|
6582
|
+
refs.append({"toName": name, "callType": "inherit", "line": node.lineno})
|
|
6583
|
+
|
|
6584
|
+
print(json.dumps({"symbols": [s.to_dict() for s in syms], "refs": refs}))
|
|
6330
6585
|
`;
|
|
6331
6586
|
_cachedScriptPath = null;
|
|
6332
6587
|
}
|
|
@@ -6339,107 +6594,10 @@ __export(rs_parser_exports, {
|
|
|
6339
6594
|
parseSymbols: () => parseSymbols5
|
|
6340
6595
|
});
|
|
6341
6596
|
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6342
|
-
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6343
|
-
import * as fs16 from "node:fs/promises";
|
|
6344
|
-
import * as path21 from "node:path";
|
|
6345
6597
|
async function parseSymbols5(opts) {
|
|
6346
6598
|
const { file, content, lang } = opts;
|
|
6347
|
-
const nativeAvailable = await checkNativeParser();
|
|
6348
|
-
if (nativeAvailable) {
|
|
6349
|
-
const result = await withSpawnGate(() => tryNativeParse(file, content));
|
|
6350
|
-
if (result) return result;
|
|
6351
|
-
}
|
|
6352
6599
|
return regexParse({ file, content, lang });
|
|
6353
6600
|
}
|
|
6354
|
-
function probe(command, args) {
|
|
6355
|
-
return new Promise((resolve16, reject) => {
|
|
6356
|
-
execFile(command, args, { timeout: 1e4, windowsHide: true }, (error) => {
|
|
6357
|
-
if (error) reject(error);
|
|
6358
|
-
else resolve16();
|
|
6359
|
-
});
|
|
6360
|
-
});
|
|
6361
|
-
}
|
|
6362
|
-
function checkNativeParser() {
|
|
6363
|
-
nativeParserAvailability ??= (async () => {
|
|
6364
|
-
try {
|
|
6365
|
-
await probe("rustc", ["--version"]);
|
|
6366
|
-
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6367
|
-
await probe(
|
|
6368
|
-
"cargo",
|
|
6369
|
-
[
|
|
6370
|
-
"metadata",
|
|
6371
|
-
"--no-deps",
|
|
6372
|
-
"--format-version",
|
|
6373
|
-
"1",
|
|
6374
|
-
"--manifest-path",
|
|
6375
|
-
path21.join(toolsDir, "Cargo.toml")
|
|
6376
|
-
]
|
|
6377
|
-
);
|
|
6378
|
-
return true;
|
|
6379
|
-
} catch {
|
|
6380
|
-
return false;
|
|
6381
|
-
}
|
|
6382
|
-
})();
|
|
6383
|
-
return nativeParserAvailability;
|
|
6384
|
-
}
|
|
6385
|
-
async function tryNativeParse(file, content) {
|
|
6386
|
-
try {
|
|
6387
|
-
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6388
|
-
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6389
|
-
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6390
|
-
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6391
|
-
const cargoBinary = resolveWin32Command("cargo");
|
|
6392
|
-
const result = await new Promise(
|
|
6393
|
-
(resolve16, reject) => {
|
|
6394
|
-
let settled = false;
|
|
6395
|
-
const proc = spawn7(
|
|
6396
|
-
cargoBinary,
|
|
6397
|
-
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6398
|
-
{
|
|
6399
|
-
cwd: process.cwd(),
|
|
6400
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
6401
|
-
windowsHide: true
|
|
6402
|
-
}
|
|
6403
|
-
);
|
|
6404
|
-
proc.on("error", (err) => {
|
|
6405
|
-
if (settled) return;
|
|
6406
|
-
settled = true;
|
|
6407
|
-
reject(err);
|
|
6408
|
-
});
|
|
6409
|
-
let stdout2 = "";
|
|
6410
|
-
proc.stdout?.on("data", (chunk) => {
|
|
6411
|
-
stdout2 += chunk.toString();
|
|
6412
|
-
});
|
|
6413
|
-
proc.stderr?.resume();
|
|
6414
|
-
const timer = setTimeout(() => {
|
|
6415
|
-
if (settled) return;
|
|
6416
|
-
settled = true;
|
|
6417
|
-
proc.kill("SIGKILL");
|
|
6418
|
-
reject(new Error("timeout"));
|
|
6419
|
-
}, 15e3);
|
|
6420
|
-
timer.unref?.();
|
|
6421
|
-
proc.on("close", (c) => {
|
|
6422
|
-
if (settled) return;
|
|
6423
|
-
settled = true;
|
|
6424
|
-
clearTimeout(timer);
|
|
6425
|
-
resolve16({ code: c, stdout: stdout2 });
|
|
6426
|
-
});
|
|
6427
|
-
}
|
|
6428
|
-
);
|
|
6429
|
-
const { code, stdout } = result;
|
|
6430
|
-
if (code === 0 && stdout.trim()) {
|
|
6431
|
-
const symbols = JSON.parse(stdout.trim());
|
|
6432
|
-
return {
|
|
6433
|
-
file,
|
|
6434
|
-
lang: "rs",
|
|
6435
|
-
symbols: symbols.map((s) => ({ ...s, id: 0, lang: "rs" })),
|
|
6436
|
-
mtimeMs: Date.now()
|
|
6437
|
-
};
|
|
6438
|
-
}
|
|
6439
|
-
} catch {
|
|
6440
|
-
}
|
|
6441
|
-
return null;
|
|
6442
|
-
}
|
|
6443
6601
|
function regexParse(opts) {
|
|
6444
6602
|
const { file, content, lang } = opts;
|
|
6445
6603
|
const symbols = [];
|
|
@@ -6495,12 +6653,10 @@ function regexParse(opts) {
|
|
|
6495
6653
|
});
|
|
6496
6654
|
return { file, lang, symbols: deduped, mtimeMs: Date.now() };
|
|
6497
6655
|
}
|
|
6498
|
-
var
|
|
6656
|
+
var RS_PATTERNS;
|
|
6499
6657
|
var init_rs_parser = __esm({
|
|
6500
6658
|
"src/codebase-index/rs-parser.ts"() {
|
|
6501
6659
|
"use strict";
|
|
6502
|
-
init_win32_resolve();
|
|
6503
|
-
init_spawn_gate();
|
|
6504
6660
|
init_languages2();
|
|
6505
6661
|
RS_PATTERNS = [
|
|
6506
6662
|
{ regex: /fn\s+(\w+)\s*\([^)]*\)/g, kind: "function" },
|
|
@@ -7082,6 +7238,10 @@ async function acquireLock(lockfilePath, timeoutMs = 5e3) {
|
|
|
7082
7238
|
const start = Date.now();
|
|
7083
7239
|
const pidStr = String(process.pid);
|
|
7084
7240
|
const hostStr = os2.hostname();
|
|
7241
|
+
try {
|
|
7242
|
+
await fs6.mkdir(path9.dirname(lockfilePath), { recursive: true });
|
|
7243
|
+
} catch {
|
|
7244
|
+
}
|
|
7085
7245
|
while (Date.now() - start < timeoutMs) {
|
|
7086
7246
|
try {
|
|
7087
7247
|
await fs6.writeFile(lockfilePath, `${pidStr}:${hostStr}:${Date.now()}`, { flag: "wx" });
|
|
@@ -7747,6 +7907,16 @@ function parseKillCommand(command) {
|
|
|
7747
7907
|
if (pgrepMatch) {
|
|
7748
7908
|
return null;
|
|
7749
7909
|
}
|
|
7910
|
+
const posixScriptMatch = normalized.match(SCRIPT_KILL_RE_POSIX);
|
|
7911
|
+
if (posixScriptMatch) {
|
|
7912
|
+
return {
|
|
7913
|
+
name: "kill-script",
|
|
7914
|
+
signal: "FORCE",
|
|
7915
|
+
isGroupKill: false,
|
|
7916
|
+
isAllKill: false,
|
|
7917
|
+
originalCommand: command
|
|
7918
|
+
};
|
|
7919
|
+
}
|
|
7750
7920
|
return null;
|
|
7751
7921
|
}
|
|
7752
7922
|
async function getProtectedEntries() {
|
|
@@ -9940,7 +10110,7 @@ for (const tool of browserTools) tool.timeoutMs ??= 6e4;
|
|
|
9940
10110
|
|
|
9941
10111
|
// src/codebase-index/project-server-client.ts
|
|
9942
10112
|
import { spawn as spawn4 } from "node:child_process";
|
|
9943
|
-
import * as
|
|
10113
|
+
import * as fs13 from "node:fs";
|
|
9944
10114
|
import * as net3 from "node:net";
|
|
9945
10115
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
9946
10116
|
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
@@ -10027,16 +10197,16 @@ var indexCircuitBreaker = new IndexCircuitBreaker();
|
|
|
10027
10197
|
|
|
10028
10198
|
// src/codebase-index/project-server-endpoint.ts
|
|
10029
10199
|
import { createHash as createHash4 } from "node:crypto";
|
|
10030
|
-
import * as
|
|
10200
|
+
import * as fs12 from "node:fs";
|
|
10031
10201
|
import * as os5 from "node:os";
|
|
10032
|
-
import * as
|
|
10202
|
+
import * as path17 from "node:path";
|
|
10033
10203
|
import { fileURLToPath } from "node:url";
|
|
10034
10204
|
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
10035
10205
|
|
|
10036
10206
|
// src/codebase-index/writer.ts
|
|
10037
10207
|
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
10038
|
-
import * as
|
|
10039
|
-
import * as
|
|
10208
|
+
import * as fs11 from "node:fs";
|
|
10209
|
+
import * as path16 from "node:path";
|
|
10040
10210
|
|
|
10041
10211
|
// src/codebase-index/bm25.ts
|
|
10042
10212
|
var K1 = 1.5;
|
|
@@ -10127,6 +10297,9 @@ var Bm25Index = class {
|
|
|
10127
10297
|
}
|
|
10128
10298
|
};
|
|
10129
10299
|
|
|
10300
|
+
// src/codebase-index/writer.ts
|
|
10301
|
+
init_languages2();
|
|
10302
|
+
|
|
10130
10303
|
// src/codebase-index/lsp-kind.ts
|
|
10131
10304
|
function lspKindToInternalKind(k) {
|
|
10132
10305
|
switch (k) {
|
|
@@ -10161,7 +10334,7 @@ function lspKindToInternalKind(k) {
|
|
|
10161
10334
|
}
|
|
10162
10335
|
|
|
10163
10336
|
// src/codebase-index/schema.ts
|
|
10164
|
-
var SCHEMA_VERSION =
|
|
10337
|
+
var SCHEMA_VERSION = 4;
|
|
10165
10338
|
|
|
10166
10339
|
// src/codebase-index/sqlite-runtime.ts
|
|
10167
10340
|
import { createRequire } from "node:module";
|
|
@@ -10232,7 +10405,7 @@ function runSqliteWithRetry(fn) {
|
|
|
10232
10405
|
|
|
10233
10406
|
// src/codebase-index/writer-admin.ts
|
|
10234
10407
|
import * as fs9 from "node:fs";
|
|
10235
|
-
import * as
|
|
10408
|
+
import * as path14 from "node:path";
|
|
10236
10409
|
var DB_FILE = "index.db";
|
|
10237
10410
|
function getAllIndexableWithStatement(stmt) {
|
|
10238
10411
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10291,7 +10464,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10291
10464
|
}
|
|
10292
10465
|
function getIndexDbSizeBytes(indexDir) {
|
|
10293
10466
|
try {
|
|
10294
|
-
return fs9.statSync(
|
|
10467
|
+
return fs9.statSync(path14.join(indexDir, DB_FILE)).size;
|
|
10295
10468
|
} catch {
|
|
10296
10469
|
return 0;
|
|
10297
10470
|
}
|
|
@@ -10342,49 +10515,345 @@ function bulkInsertFtsWithStatement(stmt, maxSqlVars, ftsAvailable, rows) {
|
|
|
10342
10515
|
}
|
|
10343
10516
|
function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
10344
10517
|
if (refs.length === 0) return;
|
|
10345
|
-
const chunkSize = Math.max(1, Math.floor(maxSqlVars /
|
|
10518
|
+
const chunkSize = Math.max(1, Math.floor(maxSqlVars / 8));
|
|
10346
10519
|
for (let i = 0; i < refs.length; i += chunkSize) {
|
|
10347
10520
|
const chunk = refs.slice(i, i + chunkSize);
|
|
10348
|
-
const placeholders = chunk.map(() => "(?, ?, ?, ?, ?)").join(", ");
|
|
10521
|
+
const placeholders = chunk.map(() => "(?, ?, ?, ?, ?, ?, ?, ?)").join(", ");
|
|
10349
10522
|
const insert = stmt(
|
|
10350
|
-
`INSERT INTO refs(from_id, to_name, to_id, call_type, line
|
|
10523
|
+
`INSERT INTO refs(from_id, to_name, to_id, call_type, line, lang, module, to_file)
|
|
10524
|
+
VALUES ${placeholders}`
|
|
10351
10525
|
);
|
|
10352
10526
|
const binds = [];
|
|
10353
10527
|
for (const ref of chunk) {
|
|
10354
|
-
binds.push(
|
|
10528
|
+
binds.push(
|
|
10529
|
+
ref.fromId,
|
|
10530
|
+
ref.toName,
|
|
10531
|
+
ref.toId ?? null,
|
|
10532
|
+
ref.callType,
|
|
10533
|
+
ref.line,
|
|
10534
|
+
ref.lang ?? "",
|
|
10535
|
+
ref.module ?? null,
|
|
10536
|
+
ref.toFile ?? null
|
|
10537
|
+
);
|
|
10355
10538
|
}
|
|
10356
10539
|
insert.run(...binds);
|
|
10357
10540
|
}
|
|
10358
10541
|
}
|
|
10359
10542
|
|
|
10360
|
-
// src/codebase-index/writer-graph-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10543
|
+
// src/codebase-index/writer-graph-reader.ts
|
|
10544
|
+
init_languages2();
|
|
10545
|
+
|
|
10546
|
+
// src/codebase-index/module-roots.ts
|
|
10547
|
+
init_languages2();
|
|
10548
|
+
import * as fs10 from "node:fs/promises";
|
|
10549
|
+
import * as path15 from "node:path";
|
|
10550
|
+
function toPortablePath(file) {
|
|
10551
|
+
return file.replace(/\\/g, "/");
|
|
10552
|
+
}
|
|
10553
|
+
async function readTextIfPresent(file) {
|
|
10554
|
+
try {
|
|
10555
|
+
return await fs10.readFile(file, "utf8");
|
|
10556
|
+
} catch {
|
|
10557
|
+
return void 0;
|
|
10558
|
+
}
|
|
10559
|
+
}
|
|
10560
|
+
function parsePackageJsonName(source) {
|
|
10561
|
+
try {
|
|
10562
|
+
const parsed = JSON.parse(source);
|
|
10563
|
+
return typeof parsed.name === "string" && parsed.name ? parsed.name : void 0;
|
|
10564
|
+
} catch {
|
|
10565
|
+
return void 0;
|
|
10566
|
+
}
|
|
10567
|
+
}
|
|
10568
|
+
function parseGoModulePath(source) {
|
|
10569
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
10570
|
+
const line = rawLine.replace(/\/\/.*$/, "").trim();
|
|
10571
|
+
const match = /^module\s+(\S+)/.exec(line);
|
|
10572
|
+
if (match?.[1]) return match[1].replace(/^["']|["']$/g, "");
|
|
10573
|
+
}
|
|
10574
|
+
return void 0;
|
|
10575
|
+
}
|
|
10576
|
+
function parseTomlTableName(source, tables) {
|
|
10577
|
+
let current = "";
|
|
10578
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
10579
|
+
const line = rawLine.replace(/#.*$/, "").trim();
|
|
10580
|
+
if (line.startsWith("[[")) {
|
|
10581
|
+
current = "\0";
|
|
10582
|
+
continue;
|
|
10583
|
+
}
|
|
10584
|
+
const table = /^\[([^\]]+)\]$/.exec(line);
|
|
10585
|
+
if (table?.[1]) {
|
|
10586
|
+
current = table[1].trim();
|
|
10587
|
+
continue;
|
|
10588
|
+
}
|
|
10589
|
+
if (!tables.includes(current)) continue;
|
|
10590
|
+
const match = /^name\s*=\s*["']([^"']+)["']/.exec(line);
|
|
10591
|
+
if (match?.[1]) return match[1];
|
|
10592
|
+
}
|
|
10593
|
+
return void 0;
|
|
10594
|
+
}
|
|
10595
|
+
function parsePomArtifactId(source) {
|
|
10596
|
+
const withoutParent = source.replace(/<parent>[\s\S]*?<\/parent>/g, "");
|
|
10597
|
+
return /<artifactId>\s*([^<\s]+)\s*<\/artifactId>/.exec(withoutParent)?.[1];
|
|
10598
|
+
}
|
|
10599
|
+
var LANGS_BY_KIND = {
|
|
10600
|
+
npm: ["ts", "tsx", "js", "jsx", "vue", "svelte"],
|
|
10601
|
+
cargo: ["rs"],
|
|
10602
|
+
go: ["go"],
|
|
10603
|
+
python: ["py"],
|
|
10604
|
+
maven: ["java", "kotlin", "scala"],
|
|
10605
|
+
gradle: ["java", "kotlin", "scala"],
|
|
10606
|
+
dotnet: ["csharp"]
|
|
10607
|
+
};
|
|
10608
|
+
function ancestorsOf(dir, stopAt) {
|
|
10609
|
+
const out = [];
|
|
10610
|
+
let current = dir;
|
|
10611
|
+
for (; ; ) {
|
|
10612
|
+
out.push(current);
|
|
10613
|
+
if (current === stopAt || current.length <= stopAt.length) break;
|
|
10614
|
+
const parent = path15.posix.dirname(current);
|
|
10615
|
+
if (parent === current) break;
|
|
10616
|
+
current = parent;
|
|
10617
|
+
}
|
|
10618
|
+
return out;
|
|
10619
|
+
}
|
|
10620
|
+
var MARKER_PROBES = [
|
|
10621
|
+
{
|
|
10622
|
+
kind: "npm",
|
|
10623
|
+
file: "package.json",
|
|
10624
|
+
build: (dir, source) => {
|
|
10625
|
+
const name = parsePackageJsonName(source) ?? path15.posix.basename(dir);
|
|
10626
|
+
return { name, importPath: name, sourceRoots: [dir] };
|
|
10627
|
+
}
|
|
10628
|
+
},
|
|
10629
|
+
{
|
|
10630
|
+
kind: "cargo",
|
|
10631
|
+
file: "Cargo.toml",
|
|
10632
|
+
build: (dir, source) => {
|
|
10633
|
+
const name = parseTomlTableName(source, ["package"]);
|
|
10634
|
+
if (!name) return void 0;
|
|
10635
|
+
return {
|
|
10636
|
+
name: `crate:${name}`,
|
|
10637
|
+
// Rust paths use underscores where crate names often use dashes.
|
|
10638
|
+
importPath: name.replace(/-/g, "_"),
|
|
10639
|
+
sourceRoots: [path15.posix.join(dir, "src")]
|
|
10640
|
+
};
|
|
10641
|
+
}
|
|
10642
|
+
},
|
|
10643
|
+
{
|
|
10644
|
+
kind: "go",
|
|
10645
|
+
file: "go.mod",
|
|
10646
|
+
build: (dir, source) => {
|
|
10647
|
+
const modulePath = parseGoModulePath(source);
|
|
10648
|
+
if (!modulePath) return void 0;
|
|
10649
|
+
return { name: `go:${modulePath}`, importPath: modulePath, sourceRoots: [dir] };
|
|
10650
|
+
}
|
|
10651
|
+
},
|
|
10652
|
+
{
|
|
10653
|
+
kind: "python",
|
|
10654
|
+
file: "pyproject.toml",
|
|
10655
|
+
build: (dir, source) => {
|
|
10656
|
+
const name = parseTomlTableName(source, ["project", "tool.poetry"]) ?? path15.posix.basename(dir);
|
|
10657
|
+
return {
|
|
10658
|
+
name: `py:${name}`,
|
|
10659
|
+
importPath: void 0,
|
|
10660
|
+
// `src/` layout is the packaging-guide default; the root itself covers
|
|
10661
|
+
// the flat layout. Both are probed, missing ones simply never match.
|
|
10662
|
+
sourceRoots: [path15.posix.join(dir, "src"), dir]
|
|
10663
|
+
};
|
|
10664
|
+
}
|
|
10665
|
+
},
|
|
10666
|
+
{
|
|
10667
|
+
kind: "python",
|
|
10668
|
+
file: "setup.py",
|
|
10669
|
+
build: (dir) => ({
|
|
10670
|
+
name: `py:${path15.posix.basename(dir)}`,
|
|
10671
|
+
importPath: void 0,
|
|
10672
|
+
sourceRoots: [path15.posix.join(dir, "src"), dir]
|
|
10673
|
+
})
|
|
10674
|
+
},
|
|
10675
|
+
{
|
|
10676
|
+
kind: "maven",
|
|
10677
|
+
file: "pom.xml",
|
|
10678
|
+
build: (dir, source) => {
|
|
10679
|
+
const artifactId = parsePomArtifactId(source) ?? path15.posix.basename(dir);
|
|
10680
|
+
return {
|
|
10681
|
+
name: `mvn:${artifactId}`,
|
|
10682
|
+
importPath: void 0,
|
|
10683
|
+
sourceRoots: [
|
|
10684
|
+
path15.posix.join(dir, "src/main/java"),
|
|
10685
|
+
path15.posix.join(dir, "src/main/kotlin"),
|
|
10686
|
+
path15.posix.join(dir, "src/main/scala"),
|
|
10687
|
+
path15.posix.join(dir, "src/test/java")
|
|
10688
|
+
]
|
|
10689
|
+
};
|
|
10690
|
+
}
|
|
10691
|
+
},
|
|
10692
|
+
{
|
|
10693
|
+
kind: "gradle",
|
|
10694
|
+
file: "build.gradle",
|
|
10695
|
+
build: (dir) => buildGradleRoot(dir)
|
|
10696
|
+
},
|
|
10697
|
+
{
|
|
10698
|
+
kind: "gradle",
|
|
10699
|
+
file: "build.gradle.kts",
|
|
10700
|
+
build: (dir) => buildGradleRoot(dir)
|
|
10701
|
+
}
|
|
10702
|
+
];
|
|
10703
|
+
function buildGradleRoot(dir) {
|
|
10704
|
+
return {
|
|
10705
|
+
name: `gradle:${path15.posix.basename(dir)}`,
|
|
10706
|
+
importPath: void 0,
|
|
10707
|
+
sourceRoots: [
|
|
10708
|
+
path15.posix.join(dir, "src/main/java"),
|
|
10709
|
+
path15.posix.join(dir, "src/main/kotlin"),
|
|
10710
|
+
path15.posix.join(dir, "src/main/scala")
|
|
10711
|
+
]
|
|
10712
|
+
};
|
|
10713
|
+
}
|
|
10714
|
+
async function probeDotnetRoot(dir) {
|
|
10715
|
+
let entries;
|
|
10716
|
+
try {
|
|
10717
|
+
entries = await fs10.readdir(dir);
|
|
10718
|
+
} catch {
|
|
10719
|
+
return void 0;
|
|
10720
|
+
}
|
|
10721
|
+
const project = entries.find((entry) => entry.toLowerCase().endsWith(".csproj"));
|
|
10722
|
+
if (!project) return void 0;
|
|
10723
|
+
const name = project.slice(0, -".csproj".length);
|
|
10724
|
+
return {
|
|
10725
|
+
dir,
|
|
10726
|
+
kind: "dotnet",
|
|
10727
|
+
name: `csproj:${name}`,
|
|
10728
|
+
importPath: void 0,
|
|
10729
|
+
sourceRoots: [dir]
|
|
10730
|
+
};
|
|
10731
|
+
}
|
|
10732
|
+
async function detectModuleRoots(projectRoot, files) {
|
|
10733
|
+
const root = toPortablePath(projectRoot).replace(/\/+$/, "");
|
|
10734
|
+
const langsByDir = /* @__PURE__ */ new Map();
|
|
10735
|
+
for (const file of files) {
|
|
10736
|
+
const portable = toPortablePath(file);
|
|
10737
|
+
const lang = detectLang(portable);
|
|
10738
|
+
if (!lang) continue;
|
|
10739
|
+
const dir = path15.posix.dirname(portable);
|
|
10740
|
+
let langs = langsByDir.get(dir);
|
|
10741
|
+
if (!langs) {
|
|
10742
|
+
langs = /* @__PURE__ */ new Set();
|
|
10743
|
+
langsByDir.set(dir, langs);
|
|
10744
|
+
}
|
|
10745
|
+
langs.add(lang);
|
|
10746
|
+
}
|
|
10747
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
10748
|
+
for (const [dir, langs] of langsByDir) {
|
|
10749
|
+
for (const ancestor of ancestorsOf(dir, root)) {
|
|
10750
|
+
let merged = candidates.get(ancestor);
|
|
10751
|
+
if (!merged) {
|
|
10752
|
+
merged = /* @__PURE__ */ new Set();
|
|
10753
|
+
candidates.set(ancestor, merged);
|
|
10754
|
+
}
|
|
10755
|
+
for (const lang of langs) merged.add(lang);
|
|
10756
|
+
}
|
|
10757
|
+
}
|
|
10758
|
+
const roots = [];
|
|
10759
|
+
await Promise.all(
|
|
10760
|
+
[...candidates].map(async ([dir, langs]) => {
|
|
10761
|
+
for (const probe of MARKER_PROBES) {
|
|
10762
|
+
if (!LANGS_BY_KIND[probe.kind].some((lang) => langs.has(lang))) continue;
|
|
10763
|
+
const source = await readTextIfPresent(path15.posix.join(dir, probe.file));
|
|
10764
|
+
if (source === void 0) continue;
|
|
10765
|
+
const built = probe.build(dir, source);
|
|
10766
|
+
if (built) roots.push({ dir, kind: probe.kind, ...built });
|
|
10767
|
+
}
|
|
10768
|
+
if (LANGS_BY_KIND.dotnet.some((lang) => langs.has(lang))) {
|
|
10769
|
+
const dotnet = await probeDotnetRoot(dir);
|
|
10770
|
+
if (dotnet) roots.push(dotnet);
|
|
10771
|
+
}
|
|
10772
|
+
})
|
|
10773
|
+
);
|
|
10774
|
+
roots.sort((a, b) => b.dir.length - a.dir.length || a.dir.localeCompare(b.dir));
|
|
10775
|
+
return { projectRoot: root, roots };
|
|
10776
|
+
}
|
|
10777
|
+
function findOwningRoot(structure, file, kinds) {
|
|
10778
|
+
const portable = toPortablePath(file);
|
|
10779
|
+
for (const root of structure.roots) {
|
|
10780
|
+
if (kinds && !kinds.includes(root.kind)) continue;
|
|
10781
|
+
if (portable === root.dir || portable.startsWith(`${root.dir}/`)) return root;
|
|
10782
|
+
}
|
|
10783
|
+
return void 0;
|
|
10784
|
+
}
|
|
10785
|
+
function derivePackageFromLayout(filePath) {
|
|
10786
|
+
const portable = toPortablePath(filePath);
|
|
10787
|
+
const packagesIdx = portable.indexOf("/packages/");
|
|
10788
|
+
if (packagesIdx !== -1) {
|
|
10789
|
+
const segment = portable.slice(packagesIdx + "/packages/".length).split("/")[0];
|
|
10790
|
+
if (segment) return `@wrongstack/${segment}`;
|
|
10791
|
+
}
|
|
10792
|
+
const appsIdx = portable.indexOf("/apps/");
|
|
10371
10793
|
if (appsIdx !== -1) {
|
|
10372
|
-
const
|
|
10373
|
-
|
|
10374
|
-
return seg ? `app:${seg}` : void 0;
|
|
10794
|
+
const segment = portable.slice(appsIdx + "/apps/".length).split("/")[0];
|
|
10795
|
+
if (segment) return `app:${segment}`;
|
|
10375
10796
|
}
|
|
10376
10797
|
return void 0;
|
|
10377
10798
|
}
|
|
10378
|
-
function
|
|
10379
|
-
|
|
10380
|
-
const
|
|
10381
|
-
|
|
10799
|
+
function pythonPackageLabel(structure, file, initDirs) {
|
|
10800
|
+
const portable = toPortablePath(file);
|
|
10801
|
+
const dir = path15.posix.dirname(portable);
|
|
10802
|
+
if (!initDirs.has(dir)) return void 0;
|
|
10803
|
+
const segments = [];
|
|
10804
|
+
let current = dir;
|
|
10805
|
+
while (initDirs.has(current) && current.length > structure.projectRoot.length) {
|
|
10806
|
+
segments.unshift(path15.posix.basename(current));
|
|
10807
|
+
current = path15.posix.dirname(current);
|
|
10808
|
+
}
|
|
10809
|
+
return segments.length > 0 ? `py:${segments.join(".")}` : void 0;
|
|
10382
10810
|
}
|
|
10383
|
-
function
|
|
10811
|
+
function assignPackageLabels(structure, files) {
|
|
10812
|
+
const initDirs = /* @__PURE__ */ new Set();
|
|
10813
|
+
for (const file of files) {
|
|
10814
|
+
const portable = toPortablePath(file);
|
|
10815
|
+
if (path15.posix.basename(portable) === "__init__.py") {
|
|
10816
|
+
initDirs.add(path15.posix.dirname(portable));
|
|
10817
|
+
}
|
|
10818
|
+
}
|
|
10819
|
+
const labels = /* @__PURE__ */ new Map();
|
|
10820
|
+
for (const file of files) {
|
|
10821
|
+
const portable = toPortablePath(file);
|
|
10822
|
+
const lang = detectLang(portable);
|
|
10823
|
+
if (lang === "go") {
|
|
10824
|
+
const owner3 = findOwningRoot(structure, portable, ["go"]);
|
|
10825
|
+
const dir = path15.posix.dirname(portable);
|
|
10826
|
+
if (owner3?.importPath) {
|
|
10827
|
+
const relative12 = path15.posix.relative(owner3.dir, dir);
|
|
10828
|
+
labels.set(file, relative12 ? `${owner3.importPath}/${relative12}` : owner3.importPath);
|
|
10829
|
+
} else {
|
|
10830
|
+
labels.set(file, `go:${path15.posix.relative(structure.projectRoot, dir) || "."}`);
|
|
10831
|
+
}
|
|
10832
|
+
continue;
|
|
10833
|
+
}
|
|
10834
|
+
if (lang === "py") {
|
|
10835
|
+
const dotted = pythonPackageLabel(structure, portable, initDirs);
|
|
10836
|
+
if (dotted) {
|
|
10837
|
+
labels.set(file, dotted);
|
|
10838
|
+
continue;
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
const owner2 = findOwningRoot(structure, portable);
|
|
10842
|
+
const label = owner2?.name ?? derivePackageFromLayout(portable) ?? "(root)";
|
|
10843
|
+
labels.set(file, label);
|
|
10844
|
+
}
|
|
10845
|
+
return labels;
|
|
10846
|
+
}
|
|
10847
|
+
|
|
10848
|
+
// src/codebase-index/writer-graph-helpers.ts
|
|
10849
|
+
function createPackageLabeller(stored) {
|
|
10850
|
+
return (file) => stored.get(file) ?? derivePackageFromLayout(file) ?? "(root)";
|
|
10851
|
+
}
|
|
10852
|
+
function buildPackageGraphNodes(fileCounts, files, packageOf) {
|
|
10384
10853
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
10385
10854
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
10386
10855
|
for (const { file, n } of fileCounts) {
|
|
10387
|
-
const pkg =
|
|
10856
|
+
const pkg = packageOf(file);
|
|
10388
10857
|
fileToPkg.set(file, pkg);
|
|
10389
10858
|
const node = pkgNodes.get(pkg);
|
|
10390
10859
|
if (node) {
|
|
@@ -10401,7 +10870,7 @@ function buildPackageGraphNodes(fileCounts, files) {
|
|
|
10401
10870
|
}
|
|
10402
10871
|
}
|
|
10403
10872
|
for (const { file } of files) {
|
|
10404
|
-
const pkg =
|
|
10873
|
+
const pkg = packageOf(file);
|
|
10405
10874
|
fileToPkg.set(file, pkg);
|
|
10406
10875
|
const node = pkgNodes.get(pkg);
|
|
10407
10876
|
if (node) {
|
|
@@ -10419,7 +10888,7 @@ function buildPackageGraphNodes(fileCounts, files) {
|
|
|
10419
10888
|
}
|
|
10420
10889
|
return { pkgNodes, fileToPkg };
|
|
10421
10890
|
}
|
|
10422
|
-
function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
10891
|
+
function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
|
|
10423
10892
|
const fileNodes = /* @__PURE__ */ new Map();
|
|
10424
10893
|
const symToFile = /* @__PURE__ */ new Map();
|
|
10425
10894
|
const fileStats = /* @__PURE__ */ new Map();
|
|
@@ -10438,7 +10907,7 @@ function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
|
10438
10907
|
id: `file:${file}`,
|
|
10439
10908
|
label: file.replace(/\\/g, "/").split("/").pop() ?? file,
|
|
10440
10909
|
kind: "file",
|
|
10441
|
-
package:
|
|
10910
|
+
package: packageOf(file),
|
|
10442
10911
|
file,
|
|
10443
10912
|
symbolCount: stats?.count ?? 0,
|
|
10444
10913
|
lang: stats?.lang,
|
|
@@ -10450,7 +10919,7 @@ function buildFileGraphNodeState(pkgSyms, localFiles) {
|
|
|
10450
10919
|
}
|
|
10451
10920
|
return { fileNodes, symToFile, fileStats, ensureFileNode };
|
|
10452
10921
|
}
|
|
10453
|
-
function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
10922
|
+
function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
|
|
10454
10923
|
return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
|
|
10455
10924
|
const aExternal = a.file === fileFilter ? 0 : 1;
|
|
10456
10925
|
const bExternal = b.file === fileFilter ? 0 : 1;
|
|
@@ -10462,7 +10931,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10462
10931
|
symbolId: s.id,
|
|
10463
10932
|
symbolKind: s.kind,
|
|
10464
10933
|
file: s.file,
|
|
10465
|
-
package:
|
|
10934
|
+
package: packageOf(s.file),
|
|
10466
10935
|
lang: s.lang,
|
|
10467
10936
|
line: s.line,
|
|
10468
10937
|
signature: s.signature,
|
|
@@ -10470,29 +10939,6 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10470
10939
|
external: s.file !== fileFilter
|
|
10471
10940
|
}));
|
|
10472
10941
|
}
|
|
10473
|
-
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10474
|
-
if (!moduleName.startsWith(".")) return void 0;
|
|
10475
|
-
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10476
|
-
const absolute = path14.posix.normalize(
|
|
10477
|
-
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10478
|
-
);
|
|
10479
|
-
const extension = path14.posix.extname(absolute);
|
|
10480
|
-
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10481
|
-
const candidates = [
|
|
10482
|
-
absolute,
|
|
10483
|
-
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10484
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10485
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10486
|
-
];
|
|
10487
|
-
const indexedByPortablePath = new Map(
|
|
10488
|
-
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
10489
|
-
);
|
|
10490
|
-
for (const candidate of candidates) {
|
|
10491
|
-
const indexed = indexedByPortablePath.get(candidate.toLocaleLowerCase());
|
|
10492
|
-
if (indexed) return indexed;
|
|
10493
|
-
}
|
|
10494
|
-
return void 0;
|
|
10495
|
-
}
|
|
10496
10942
|
function addWeightedEdge(edgeMap, source, target, callType, weight) {
|
|
10497
10943
|
const key = `${source}\0${target}`;
|
|
10498
10944
|
let edge = edgeMap.get(key);
|
|
@@ -10533,11 +10979,146 @@ function mapWriterRefRow(row) {
|
|
|
10533
10979
|
toName: row.to_name,
|
|
10534
10980
|
toId: row.to_id ?? void 0,
|
|
10535
10981
|
callType: row.call_type,
|
|
10536
|
-
line: row.line
|
|
10982
|
+
line: row.line,
|
|
10983
|
+
// `lang`/`module`/`to_file` are absent from the narrower column lists some
|
|
10984
|
+
// queries select; `undefined` keeps those rows valid Refs.
|
|
10985
|
+
lang: row.lang || void 0,
|
|
10986
|
+
module: row.module ?? void 0,
|
|
10987
|
+
toFile: row.to_file ?? void 0
|
|
10537
10988
|
};
|
|
10538
10989
|
}
|
|
10539
10990
|
|
|
10540
10991
|
// src/codebase-index/writer-graph-reader.ts
|
|
10992
|
+
var MAX_SQL_VARS = 900;
|
|
10993
|
+
function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
10994
|
+
const results = [];
|
|
10995
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
10996
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
10997
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
10998
|
+
const sql = buildSql(placeholders);
|
|
10999
|
+
results.push(...stmt(sql).all(...chunk, ...extraArgs));
|
|
11000
|
+
}
|
|
11001
|
+
return results;
|
|
11002
|
+
}
|
|
11003
|
+
function chunkedIdScalar(stmt, ids, buildSql, extraArgs = []) {
|
|
11004
|
+
let total = 0;
|
|
11005
|
+
for (let start = 0; start < ids.length; start += MAX_SQL_VARS) {
|
|
11006
|
+
const chunk = ids.slice(start, start + MAX_SQL_VARS);
|
|
11007
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
11008
|
+
const sql = buildSql(placeholders);
|
|
11009
|
+
const rows = stmt(sql).all(...chunk, ...extraArgs);
|
|
11010
|
+
total += rows[0]?.n ?? 0;
|
|
11011
|
+
}
|
|
11012
|
+
return total;
|
|
11013
|
+
}
|
|
11014
|
+
function mapCallSiteRow(row) {
|
|
11015
|
+
return {
|
|
11016
|
+
symbol: {
|
|
11017
|
+
id: row.sym_id,
|
|
11018
|
+
name: row.sym_name,
|
|
11019
|
+
kind: row.sym_kind,
|
|
11020
|
+
lang: row.sym_lang,
|
|
11021
|
+
file: row.sym_file,
|
|
11022
|
+
line: row.sym_line,
|
|
11023
|
+
signature: row.sym_signature
|
|
11024
|
+
},
|
|
11025
|
+
callType: row.call_type,
|
|
11026
|
+
line: row.ref_line
|
|
11027
|
+
};
|
|
11028
|
+
}
|
|
11029
|
+
function resolveSymbolIds(stmt, symbolName, file) {
|
|
11030
|
+
const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
|
|
11031
|
+
const args = file ? [symbolName, file] : [symbolName];
|
|
11032
|
+
const rows = stmt(baseSql).all(...args);
|
|
11033
|
+
return rows.map((r) => r.id);
|
|
11034
|
+
}
|
|
11035
|
+
function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
11036
|
+
const targetIds = resolveSymbolIds(stmt, symbolName, file);
|
|
11037
|
+
if (targetIds.length === 0) return { calls: [], symbolFound: false, ambiguous: false, totalMatches: 0 };
|
|
11038
|
+
let matchIds = targetIds;
|
|
11039
|
+
let ambiguous = false;
|
|
11040
|
+
if (file !== void 0) {
|
|
11041
|
+
const allNamedIds = resolveSymbolIds(stmt, symbolName, void 0);
|
|
11042
|
+
if (allNamedIds.length > targetIds.length) {
|
|
11043
|
+
matchIds = allNamedIds;
|
|
11044
|
+
ambiguous = true;
|
|
11045
|
+
}
|
|
11046
|
+
}
|
|
11047
|
+
const useFallback = !file;
|
|
11048
|
+
const rows = chunkedIdQuery(
|
|
11049
|
+
stmt,
|
|
11050
|
+
matchIds,
|
|
11051
|
+
(ph) => `SELECT
|
|
11052
|
+
s.id AS sym_id,
|
|
11053
|
+
s.name AS sym_name,
|
|
11054
|
+
s.kind AS sym_kind,
|
|
11055
|
+
s.lang AS sym_lang,
|
|
11056
|
+
s.file AS sym_file,
|
|
11057
|
+
s.line AS sym_line,
|
|
11058
|
+
s.signature AS sym_signature,
|
|
11059
|
+
r.call_type,
|
|
11060
|
+
r.line AS ref_line
|
|
11061
|
+
FROM refs r
|
|
11062
|
+
JOIN symbols s ON s.id = r.from_id
|
|
11063
|
+
WHERE r.to_id IN (${ph})
|
|
11064
|
+
ORDER BY r.line, r.id`,
|
|
11065
|
+
[]
|
|
11066
|
+
);
|
|
11067
|
+
if (useFallback) {
|
|
11068
|
+
const fallbackRows = stmt(
|
|
11069
|
+
`SELECT
|
|
11070
|
+
s.id AS sym_id,
|
|
11071
|
+
s.name AS sym_name,
|
|
11072
|
+
s.kind AS sym_kind,
|
|
11073
|
+
s.lang AS sym_lang,
|
|
11074
|
+
s.file AS sym_file,
|
|
11075
|
+
s.line AS sym_line,
|
|
11076
|
+
s.signature AS sym_signature,
|
|
11077
|
+
r.call_type,
|
|
11078
|
+
r.line AS ref_line
|
|
11079
|
+
FROM refs r
|
|
11080
|
+
JOIN symbols s ON s.id = r.from_id
|
|
11081
|
+
WHERE r.to_id IS NULL AND r.to_name = ?
|
|
11082
|
+
ORDER BY r.line, r.id`
|
|
11083
|
+
).all(symbolName);
|
|
11084
|
+
rows.push(...fallbackRows);
|
|
11085
|
+
}
|
|
11086
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
11087
|
+
const allCalls = rows.map(mapCallSiteRow);
|
|
11088
|
+
return { calls: allCalls.slice(0, limit), symbolFound: true, ambiguous, totalMatches: allCalls.length };
|
|
11089
|
+
}
|
|
11090
|
+
function findOutgoingCallsByName(stmt, symbolName, file, limit) {
|
|
11091
|
+
const sourceIds = resolveSymbolIds(stmt, symbolName, file);
|
|
11092
|
+
if (sourceIds.length === 0) return { calls: [], symbolFound: false, unresolvedCount: 0, totalMatches: 0 };
|
|
11093
|
+
const unresolvedCount = chunkedIdScalar(
|
|
11094
|
+
stmt,
|
|
11095
|
+
sourceIds,
|
|
11096
|
+
(ph) => `SELECT COUNT(*) AS n FROM refs WHERE from_id IN (${ph}) AND to_id IS NULL`
|
|
11097
|
+
);
|
|
11098
|
+
const rows = chunkedIdQuery(
|
|
11099
|
+
stmt,
|
|
11100
|
+
sourceIds,
|
|
11101
|
+
(ph) => `SELECT
|
|
11102
|
+
s.id AS sym_id,
|
|
11103
|
+
s.name AS sym_name,
|
|
11104
|
+
s.kind AS sym_kind,
|
|
11105
|
+
s.lang AS sym_lang,
|
|
11106
|
+
s.file AS sym_file,
|
|
11107
|
+
s.line AS sym_line,
|
|
11108
|
+
s.signature AS sym_signature,
|
|
11109
|
+
r.call_type,
|
|
11110
|
+
r.line AS ref_line
|
|
11111
|
+
FROM refs r
|
|
11112
|
+
JOIN symbols s ON s.id = r.to_id
|
|
11113
|
+
WHERE r.from_id IN (${ph})
|
|
11114
|
+
AND r.to_id IS NOT NULL -- INNER JOIN already excludes NULL to_id; this is defensive belt-and-suspenders
|
|
11115
|
+
ORDER BY r.line, r.id`,
|
|
11116
|
+
[]
|
|
11117
|
+
);
|
|
11118
|
+
rows.sort((a, b) => a.ref_line - b.ref_line || a.sym_id - b.sym_id);
|
|
11119
|
+
const calls = rows.map(mapCallSiteRow).slice(0, limit);
|
|
11120
|
+
return { calls, symbolFound: true, unresolvedCount, totalMatches: rows.length };
|
|
11121
|
+
}
|
|
10541
11122
|
function findRefsToWithStatement(stmt, symbolId) {
|
|
10542
11123
|
return stmt(
|
|
10543
11124
|
"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 = ?)"
|
|
@@ -10551,7 +11132,8 @@ function findRefsFromWithStatement(stmt, symbolId) {
|
|
|
10551
11132
|
function getPackageGraphWithStatement(stmt) {
|
|
10552
11133
|
const fileCounts = stmt("SELECT file, COUNT(*) AS n FROM symbols GROUP BY file").all();
|
|
10553
11134
|
const files = stmt("SELECT DISTINCT file FROM files").all();
|
|
10554
|
-
const
|
|
11135
|
+
const packageOf = readPackageLabeller(stmt);
|
|
11136
|
+
const { pkgNodes, fileToPkg } = buildPackageGraphNodes(fileCounts, files, packageOf);
|
|
10555
11137
|
const refRows = stmt(
|
|
10556
11138
|
`SELECT r.call_type, sf.file AS from_file, st.file AS to_file, COUNT(*) AS n
|
|
10557
11139
|
FROM refs r
|
|
@@ -10562,32 +11144,42 @@ function getPackageGraphWithStatement(stmt) {
|
|
|
10562
11144
|
).all();
|
|
10563
11145
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
10564
11146
|
for (const r of refRows) {
|
|
10565
|
-
const fromPkg = fileToPkg.get(r.from_file) ??
|
|
10566
|
-
const toPkg = fileToPkg.get(r.to_file) ??
|
|
11147
|
+
const fromPkg = fileToPkg.get(r.from_file) ?? packageOf(r.from_file);
|
|
11148
|
+
const toPkg = fileToPkg.get(r.to_file) ?? packageOf(r.to_file);
|
|
10567
11149
|
if (fromPkg === toPkg) continue;
|
|
10568
11150
|
const n = Number(r.n) || 0;
|
|
10569
11151
|
addWeightedEdge(edgeMap, fromPkg, toPkg, r.call_type, n);
|
|
10570
11152
|
}
|
|
10571
11153
|
const importRows = stmt(
|
|
10572
|
-
`SELECT
|
|
11154
|
+
`SELECT s.file AS from_file,
|
|
11155
|
+
COALESCE(r.to_file, st.file) AS to_file,
|
|
11156
|
+
COUNT(*) AS n
|
|
10573
11157
|
FROM refs r
|
|
10574
11158
|
JOIN symbols s ON s.id = r.from_id
|
|
11159
|
+
LEFT JOIN symbols st ON st.id = r.to_id
|
|
10575
11160
|
WHERE r.call_type = 'import'
|
|
10576
|
-
|
|
11161
|
+
AND COALESCE(r.to_file, st.file) IS NOT NULL
|
|
11162
|
+
GROUP BY s.file, COALESCE(r.to_file, st.file)`
|
|
10577
11163
|
).all();
|
|
10578
11164
|
for (const r of importRows) {
|
|
10579
|
-
const fromPkg = fileToPkg.get(r.from_file) ??
|
|
10580
|
-
const toPkg =
|
|
10581
|
-
if (
|
|
11165
|
+
const fromPkg = fileToPkg.get(r.from_file) ?? packageOf(r.from_file);
|
|
11166
|
+
const toPkg = fileToPkg.get(r.to_file) ?? packageOf(r.to_file);
|
|
11167
|
+
if (fromPkg === toPkg || !pkgNodes.has(toPkg)) continue;
|
|
10582
11168
|
const n = Number(r.n) || 0;
|
|
10583
11169
|
addWeightedEdge(edgeMap, fromPkg, toPkg, "import", n);
|
|
10584
11170
|
}
|
|
10585
11171
|
const edges = materializeWeightedEdges(edgeMap, "pkg");
|
|
10586
11172
|
return { nodes: [...pkgNodes.values()], edges };
|
|
10587
11173
|
}
|
|
11174
|
+
function readPackageLabeller(stmt) {
|
|
11175
|
+
const rows = stmt("SELECT file, package FROM files WHERE package != ''").all();
|
|
11176
|
+
return createPackageLabeller(new Map(rows.map((row) => [row.file, row.package])));
|
|
11177
|
+
}
|
|
10588
11178
|
function getFileGraphWithStatement(stmt, packageFilter) {
|
|
10589
11179
|
const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
|
|
10590
|
-
const
|
|
11180
|
+
const packageOf = readPackageLabeller(stmt);
|
|
11181
|
+
const langOf = (file) => detectLang(file) ?? "other";
|
|
11182
|
+
const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
|
|
10591
11183
|
const localFiles = new Set(pkgFilePaths);
|
|
10592
11184
|
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
10593
11185
|
const filePlaceholders = [...localFiles].map(() => "?").join(",");
|
|
@@ -10596,9 +11188,9 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
10596
11188
|
).all(...pkgFilePaths);
|
|
10597
11189
|
const { fileNodes, symToFile, fileStats, ensureFileNode } = buildFileGraphNodeState(
|
|
10598
11190
|
pkgSyms,
|
|
10599
|
-
localFiles
|
|
11191
|
+
localFiles,
|
|
11192
|
+
packageOf
|
|
10600
11193
|
);
|
|
10601
|
-
const indexedFiles = new Set(allFiles.map((f) => f.file));
|
|
10602
11194
|
const refRows = stmt(
|
|
10603
11195
|
`SELECT r.from_id, r.to_id, r.call_type, COUNT(*) AS n
|
|
10604
11196
|
FROM refs r
|
|
@@ -10621,7 +11213,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
10621
11213
|
for (const x of extras) {
|
|
10622
11214
|
symToFile.set(x.id, x.file);
|
|
10623
11215
|
if (!fileStats.has(x.file)) {
|
|
10624
|
-
fileStats.set(x.file, { count: 0, lang:
|
|
11216
|
+
fileStats.set(x.file, { count: 0, lang: langOf(x.file) });
|
|
10625
11217
|
}
|
|
10626
11218
|
}
|
|
10627
11219
|
}
|
|
@@ -10638,17 +11230,22 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
10638
11230
|
addWeightedEdge(edgeMap, fromFile, toFile, r.call_type, n);
|
|
10639
11231
|
}
|
|
10640
11232
|
const importRows = stmt(
|
|
10641
|
-
`SELECT r.from_id, r.
|
|
11233
|
+
`SELECT r.from_id, COALESCE(r.to_file, st.file) AS to_file, COUNT(*) AS n
|
|
10642
11234
|
FROM refs r
|
|
11235
|
+
LEFT JOIN symbols st ON st.id = r.to_id
|
|
10643
11236
|
WHERE r.call_type = 'import'
|
|
11237
|
+
AND COALESCE(r.to_file, st.file) IS NOT NULL
|
|
10644
11238
|
AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
10645
|
-
GROUP BY r.from_id, r.
|
|
11239
|
+
GROUP BY r.from_id, COALESCE(r.to_file, st.file)`
|
|
10646
11240
|
).all(...pkgFilePaths);
|
|
10647
11241
|
for (const r of importRows) {
|
|
10648
11242
|
const fromFile = symToFile.get(r.from_id);
|
|
10649
11243
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
10650
|
-
const toFile =
|
|
11244
|
+
const toFile = r.to_file;
|
|
10651
11245
|
if (!toFile || fromFile === toFile) continue;
|
|
11246
|
+
if (!fileStats.has(toFile)) {
|
|
11247
|
+
fileStats.set(toFile, { count: 0, lang: langOf(toFile) });
|
|
11248
|
+
}
|
|
10652
11249
|
ensureFileNode(fromFile);
|
|
10653
11250
|
ensureFileNode(toFile);
|
|
10654
11251
|
const n = Number(r.n) || 0;
|
|
@@ -10698,7 +11295,7 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
|
10698
11295
|
).all(...missingIds);
|
|
10699
11296
|
for (const s of extras) symById.set(s.id, s);
|
|
10700
11297
|
}
|
|
10701
|
-
const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter);
|
|
11298
|
+
const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
|
|
10702
11299
|
return { nodes, edges };
|
|
10703
11300
|
}
|
|
10704
11301
|
|
|
@@ -10720,7 +11317,7 @@ function assignRefsToSymbols(refs, symbols) {
|
|
|
10720
11317
|
}
|
|
10721
11318
|
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
10722
11319
|
if (!owner2 || owner2.id <= 0) continue;
|
|
10723
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
11320
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
|
|
10724
11321
|
if (seen.has(key)) continue;
|
|
10725
11322
|
seen.add(key);
|
|
10726
11323
|
assigned.push({ ...ref, fromId: owner2.id });
|
|
@@ -10766,7 +11363,11 @@ var CORE_TABLES_SQL = `
|
|
|
10766
11363
|
lang TEXT NOT NULL,
|
|
10767
11364
|
mtime_ms INTEGER NOT NULL,
|
|
10768
11365
|
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
10769
|
-
last_indexed INTEGER NOT NULL
|
|
11366
|
+
last_indexed INTEGER NOT NULL,
|
|
11367
|
+
-- Code Atlas grouping label, computed at index time from the ecosystem's
|
|
11368
|
+
-- own manifests (package.json, go.mod, Cargo.toml, \u2026). Stored rather than
|
|
11369
|
+
-- re-derived per query because the evidence lives on disk, not in the DB.
|
|
11370
|
+
package TEXT NOT NULL DEFAULT ''
|
|
10770
11371
|
);
|
|
10771
11372
|
CREATE TABLE IF NOT EXISTS symbols (
|
|
10772
11373
|
id INTEGER PRIMARY KEY,
|
|
@@ -10783,6 +11384,9 @@ var CORE_TABLES_SQL = `
|
|
|
10783
11384
|
file_fk TEXT NOT NULL
|
|
10784
11385
|
);
|
|
10785
11386
|
`;
|
|
11387
|
+
var FILE_INDEX_SQL = [
|
|
11388
|
+
"CREATE INDEX IF NOT EXISTS idx_f_package ON files(package)"
|
|
11389
|
+
];
|
|
10786
11390
|
var SYMBOL_INDEX_SQL = [
|
|
10787
11391
|
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
10788
11392
|
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
@@ -10799,15 +11403,32 @@ var REFS_TABLE_SQL = `
|
|
|
10799
11403
|
to_name TEXT NOT NULL,
|
|
10800
11404
|
to_id INTEGER,
|
|
10801
11405
|
call_type TEXT NOT NULL,
|
|
10802
|
-
line INTEGER NOT NULL
|
|
11406
|
+
line INTEGER NOT NULL,
|
|
11407
|
+
lang TEXT NOT NULL DEFAULT '',
|
|
11408
|
+
module TEXT,
|
|
11409
|
+
to_file TEXT
|
|
10803
11410
|
);
|
|
10804
11411
|
`;
|
|
10805
11412
|
var REFS_INDEX_SQL = [
|
|
10806
11413
|
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
10807
11414
|
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
10808
11415
|
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
10809
|
-
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
11416
|
+
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)",
|
|
11417
|
+
// Name resolution matches (to_name, lang) pairs; the composite keeps the
|
|
11418
|
+
// language-scoped UPDATE from degrading into a scan of every same-named row.
|
|
11419
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_name_lang ON refs(to_name, lang)",
|
|
11420
|
+
// The post-index module resolution pass groups unresolved import refs by
|
|
11421
|
+
// (module, lang); graph readers then read to_file back.
|
|
11422
|
+
"CREATE INDEX IF NOT EXISTS idx_r_module ON refs(module)",
|
|
11423
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_file ON refs(to_file)"
|
|
10810
11424
|
];
|
|
11425
|
+
var LANG_FAMILY_TABLE_SQL = `
|
|
11426
|
+
CREATE TABLE IF NOT EXISTS lang_family (
|
|
11427
|
+
lang TEXT PRIMARY KEY,
|
|
11428
|
+
family TEXT NOT NULL
|
|
11429
|
+
);
|
|
11430
|
+
`;
|
|
11431
|
+
var LANG_FAMILY_WILDCARD = "*";
|
|
10811
11432
|
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
10812
11433
|
|
|
10813
11434
|
// src/codebase-index/writer-search-helpers.ts
|
|
@@ -11019,15 +11640,69 @@ var IndexStore = class _IndexStore {
|
|
|
11019
11640
|
}
|
|
11020
11641
|
constructor(projectRoot, opts = {}) {
|
|
11021
11642
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
11022
|
-
|
|
11643
|
+
fs11.mkdirSync(this.indexDir, { recursive: true });
|
|
11023
11644
|
const Database = loadDatabaseSync();
|
|
11024
|
-
this.db = new Database(
|
|
11645
|
+
this.db = new Database(path16.join(this.indexDir, DB_FILE2));
|
|
11025
11646
|
applyIndexStorePragmas(this.db);
|
|
11026
11647
|
this.initSchema();
|
|
11027
11648
|
}
|
|
11028
11649
|
runWithRetry(fn) {
|
|
11029
11650
|
return runSqliteWithRetry(fn);
|
|
11030
11651
|
}
|
|
11652
|
+
/**
|
|
11653
|
+
* Mirror the in-process language→family map into SQLite.
|
|
11654
|
+
*
|
|
11655
|
+
* Rewritten on every open rather than only on schema bumps: the mapping is
|
|
11656
|
+
* static lookup data, so a code-side change (a new language, a language
|
|
11657
|
+
* moving families) must take effect without forcing a full reindex.
|
|
11658
|
+
*/
|
|
11659
|
+
seedLangFamilies() {
|
|
11660
|
+
const insert = this.stmt("INSERT OR REPLACE INTO lang_family(lang, family) VALUES (?, ?)");
|
|
11661
|
+
for (const [lang, family] of LANG_FAMILY_ENTRIES) insert.run(lang, family);
|
|
11662
|
+
insert.run("", LANG_FAMILY_WILDCARD);
|
|
11663
|
+
}
|
|
11664
|
+
/**
|
|
11665
|
+
* Add any column the current schema expects but the on-disk table lacks.
|
|
11666
|
+
*
|
|
11667
|
+
* `CREATE TABLE IF NOT EXISTS` silently keeps an existing table's old shape,
|
|
11668
|
+
* and the version check above only rebuilds on a version *mismatch*. That
|
|
11669
|
+
* leaves a real gap: several wstack processes share this database, and while
|
|
11670
|
+
* a version upgrade is rolling out one of them may still be running the
|
|
11671
|
+
* previous build. That older process sees the newer version number, drops the
|
|
11672
|
+
* tables, and recreates them from *its* DDL — without the newer columns —
|
|
11673
|
+
* while the metadata row still reads the new version. Every later query for
|
|
11674
|
+
* one of those columns then fails with `no such column`, and no amount of
|
|
11675
|
+
* reindexing fixes it, because the version numbers already agree.
|
|
11676
|
+
*
|
|
11677
|
+
* Repairing column-by-column makes the schema self-healing from any of those
|
|
11678
|
+
* states. Table and column names are compile-time literals from this module,
|
|
11679
|
+
* never user input.
|
|
11680
|
+
*/
|
|
11681
|
+
repairMissingColumns() {
|
|
11682
|
+
const expected = [
|
|
11683
|
+
{ table: "files", columns: [["package", "TEXT NOT NULL DEFAULT ''"]] },
|
|
11684
|
+
{
|
|
11685
|
+
table: "refs",
|
|
11686
|
+
columns: [
|
|
11687
|
+
["lang", "TEXT NOT NULL DEFAULT ''"],
|
|
11688
|
+
["module", "TEXT"],
|
|
11689
|
+
["to_file", "TEXT"]
|
|
11690
|
+
]
|
|
11691
|
+
}
|
|
11692
|
+
];
|
|
11693
|
+
for (const { table, columns } of expected) {
|
|
11694
|
+
const present = new Set(
|
|
11695
|
+
this.db.prepare(`PRAGMA table_info(${table})`).all().flatMap(
|
|
11696
|
+
(row) => typeof row.name === "string" ? [row.name] : []
|
|
11697
|
+
)
|
|
11698
|
+
);
|
|
11699
|
+
if (present.size === 0) continue;
|
|
11700
|
+
for (const [name, type] of columns) {
|
|
11701
|
+
if (present.has(name)) continue;
|
|
11702
|
+
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${type}`);
|
|
11703
|
+
}
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11031
11706
|
initSchema() {
|
|
11032
11707
|
this.db.exec(METADATA_TABLE_SQL);
|
|
11033
11708
|
const storedRows = this.stmt("SELECT value FROM metadata WHERE key = ?").all("version");
|
|
@@ -11050,9 +11725,13 @@ var IndexStore = class _IndexStore {
|
|
|
11050
11725
|
);
|
|
11051
11726
|
}
|
|
11052
11727
|
this.db.exec(CORE_TABLES_SQL);
|
|
11053
|
-
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
11054
11728
|
this.db.exec(REFS_TABLE_SQL);
|
|
11729
|
+
this.repairMissingColumns();
|
|
11730
|
+
for (const sql of FILE_INDEX_SQL) this.db.exec(sql);
|
|
11731
|
+
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
11055
11732
|
for (const sql of REFS_INDEX_SQL) this.db.exec(sql);
|
|
11733
|
+
this.db.exec(LANG_FAMILY_TABLE_SQL);
|
|
11734
|
+
this.seedLangFamilies();
|
|
11056
11735
|
try {
|
|
11057
11736
|
this.db.exec(SYMBOLS_FTS_SQL);
|
|
11058
11737
|
this.ftsAvailable = true;
|
|
@@ -11087,6 +11766,18 @@ var IndexStore = class _IndexStore {
|
|
|
11087
11766
|
static NEXT_SYMBOL_ID_KEY = "next_symbol_id";
|
|
11088
11767
|
/** Stay under typical SQLite SQLITE_MAX_VARIABLE_NUMBER (often 999). */
|
|
11089
11768
|
static MAX_SQL_VARS = 900;
|
|
11769
|
+
/**
|
|
11770
|
+
* Correlated predicate: the ref in `refs` and the candidate symbol aliased
|
|
11771
|
+
* `sym` belong to the same language family — or the ref carries no language,
|
|
11772
|
+
* in which case the wildcard bind matches everything.
|
|
11773
|
+
*
|
|
11774
|
+
* Each textual occurrence consumes one `?` bind of {@link LANG_FAMILY_WILDCARD}.
|
|
11775
|
+
*/
|
|
11776
|
+
static FAMILY_MATCH_SQL = `(
|
|
11777
|
+
(SELECT family FROM lang_family WHERE lang = refs.lang) = ?
|
|
11778
|
+
OR (SELECT family FROM lang_family WHERE lang = sym.lang)
|
|
11779
|
+
= (SELECT family FROM lang_family WHERE lang = refs.lang)
|
|
11780
|
+
)`;
|
|
11090
11781
|
/**
|
|
11091
11782
|
* Ensure `metadata.next_symbol_id` exists. Safe to call outside a write
|
|
11092
11783
|
* transaction on open; the first concurrent writer under BEGIN IMMEDIATE
|
|
@@ -11150,9 +11841,12 @@ var IndexStore = class _IndexStore {
|
|
|
11150
11841
|
const placeholders = chunk.map(() => "?").join(",");
|
|
11151
11842
|
const result = this.stmt(
|
|
11152
11843
|
`UPDATE refs
|
|
11153
|
-
SET to_id = (
|
|
11844
|
+
SET to_id = (
|
|
11845
|
+
SELECT MIN(sym.id) FROM symbols sym
|
|
11846
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
11847
|
+
)
|
|
11154
11848
|
WHERE to_name IN (${placeholders})`
|
|
11155
|
-
).run(...chunk);
|
|
11849
|
+
).run(LANG_FAMILY_WILDCARD, ...chunk);
|
|
11156
11850
|
changes += result.changes ?? 0;
|
|
11157
11851
|
}
|
|
11158
11852
|
return changes;
|
|
@@ -11279,6 +11973,115 @@ var IndexStore = class _IndexStore {
|
|
|
11279
11973
|
getAllFileMetas() {
|
|
11280
11974
|
return getAllFileMetasWithStatement((sql) => this.stmt(sql));
|
|
11281
11975
|
}
|
|
11976
|
+
// ─── Project structure & module resolution ──────────────────────────────────
|
|
11977
|
+
/** Store the Code Atlas grouping label for each indexed file. */
|
|
11978
|
+
setFilePackages(entries) {
|
|
11979
|
+
if (entries.size === 0) return;
|
|
11980
|
+
this.runWithRetry(() => {
|
|
11981
|
+
const update = this.stmt("UPDATE files SET package = ? WHERE file = ?");
|
|
11982
|
+
for (const [file, label] of entries) update.run(label, file);
|
|
11983
|
+
});
|
|
11984
|
+
}
|
|
11985
|
+
/**
|
|
11986
|
+
* Every indexed `namespace`/`module` declaration, for ecosystems whose import
|
|
11987
|
+
* specifiers name a namespace rather than a path (C#, PHP, Elixir, Haskell).
|
|
11988
|
+
* Ordered so the resolver's choice among duplicate declarations is stable.
|
|
11989
|
+
*/
|
|
11990
|
+
getNamespaceDeclarations() {
|
|
11991
|
+
return this.stmt(
|
|
11992
|
+
`SELECT name, file FROM symbols WHERE kind = 'namespace' ORDER BY file, id`
|
|
11993
|
+
).all();
|
|
11994
|
+
}
|
|
11995
|
+
/** `file → package` for every indexed file that has a label. */
|
|
11996
|
+
getFilePackages() {
|
|
11997
|
+
const rows = this.stmt("SELECT file, package FROM files WHERE package != ''").all();
|
|
11998
|
+
return new Map(rows.map((row) => [row.file, row.package]));
|
|
11999
|
+
}
|
|
12000
|
+
/**
|
|
12001
|
+
* Distinct `(fromFile, lang, module)` triples needing module resolution.
|
|
12002
|
+
*
|
|
12003
|
+
* Distinct rather than per-ref because resolution depends only on these three
|
|
12004
|
+
* values: a file importing the same module twenty times resolves it once.
|
|
12005
|
+
*/
|
|
12006
|
+
getUnresolvedImports(onlyFiles) {
|
|
12007
|
+
const base = `SELECT DISTINCT s.file AS fromFile, r.lang AS lang, r.module AS module
|
|
12008
|
+
FROM refs r
|
|
12009
|
+
JOIN symbols s ON s.id = r.from_id
|
|
12010
|
+
WHERE r.call_type = 'import' AND r.module IS NOT NULL`;
|
|
12011
|
+
if (!onlyFiles?.length) {
|
|
12012
|
+
return this.stmt(base).all();
|
|
12013
|
+
}
|
|
12014
|
+
const out = [];
|
|
12015
|
+
for (let i = 0; i < onlyFiles.length; i += _IndexStore.MAX_SQL_VARS) {
|
|
12016
|
+
const chunk = onlyFiles.slice(i, i + _IndexStore.MAX_SQL_VARS);
|
|
12017
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
12018
|
+
out.push(
|
|
12019
|
+
...this.stmt(`${base} AND s.file IN (${placeholders})`).all(...chunk)
|
|
12020
|
+
);
|
|
12021
|
+
}
|
|
12022
|
+
return out;
|
|
12023
|
+
}
|
|
12024
|
+
/**
|
|
12025
|
+
* Write resolved import targets back onto `refs.to_file`.
|
|
12026
|
+
*
|
|
12027
|
+
* Applied through a temp table and a single UPDATE: one statement per
|
|
12028
|
+
* resolution would mean thousands of round-trips on a first index.
|
|
12029
|
+
*/
|
|
12030
|
+
applyImportResolutions(resolutions) {
|
|
12031
|
+
if (resolutions.length === 0) return 0;
|
|
12032
|
+
return this.runWithRetry(() => {
|
|
12033
|
+
this.db.exec("DROP TABLE IF EXISTS temp.import_resolution");
|
|
12034
|
+
this.db.exec(
|
|
12035
|
+
`CREATE TEMP TABLE import_resolution (
|
|
12036
|
+
from_file TEXT NOT NULL,
|
|
12037
|
+
lang TEXT NOT NULL,
|
|
12038
|
+
module TEXT NOT NULL,
|
|
12039
|
+
to_file TEXT NOT NULL
|
|
12040
|
+
)`
|
|
12041
|
+
);
|
|
12042
|
+
const chunkSize = Math.max(1, Math.floor(_IndexStore.MAX_SQL_VARS / 4));
|
|
12043
|
+
for (let i = 0; i < resolutions.length; i += chunkSize) {
|
|
12044
|
+
const chunk = resolutions.slice(i, i + chunkSize);
|
|
12045
|
+
const placeholders = chunk.map(() => "(?, ?, ?, ?)").join(", ");
|
|
12046
|
+
const binds = [];
|
|
12047
|
+
for (const entry of chunk) {
|
|
12048
|
+
binds.push(entry.fromFile, entry.lang, entry.module, entry.toFile);
|
|
12049
|
+
}
|
|
12050
|
+
this.stmt(
|
|
12051
|
+
`INSERT INTO temp.import_resolution(from_file, lang, module, to_file)
|
|
12052
|
+
VALUES ${placeholders}`
|
|
12053
|
+
).run(...binds);
|
|
12054
|
+
}
|
|
12055
|
+
this.db.exec(
|
|
12056
|
+
`CREATE INDEX IF NOT EXISTS temp.idx_ir
|
|
12057
|
+
ON import_resolution(module, lang, from_file)`
|
|
12058
|
+
);
|
|
12059
|
+
const result = this.stmt(
|
|
12060
|
+
`UPDATE refs
|
|
12061
|
+
SET to_file = (
|
|
12062
|
+
SELECT ir.to_file
|
|
12063
|
+
FROM temp.import_resolution ir
|
|
12064
|
+
JOIN symbols s ON s.id = refs.from_id
|
|
12065
|
+
WHERE ir.module = refs.module
|
|
12066
|
+
AND ir.lang = refs.lang
|
|
12067
|
+
AND ir.from_file = s.file
|
|
12068
|
+
LIMIT 1
|
|
12069
|
+
)
|
|
12070
|
+
WHERE refs.call_type = 'import'
|
|
12071
|
+
AND refs.module IS NOT NULL
|
|
12072
|
+
AND EXISTS (
|
|
12073
|
+
SELECT 1
|
|
12074
|
+
FROM temp.import_resolution ir
|
|
12075
|
+
JOIN symbols s ON s.id = refs.from_id
|
|
12076
|
+
WHERE ir.module = refs.module
|
|
12077
|
+
AND ir.lang = refs.lang
|
|
12078
|
+
AND ir.from_file = s.file
|
|
12079
|
+
)`
|
|
12080
|
+
).run();
|
|
12081
|
+
this.db.exec("DROP TABLE IF EXISTS temp.import_resolution");
|
|
12082
|
+
return result.changes ?? 0;
|
|
12083
|
+
});
|
|
12084
|
+
}
|
|
11282
12085
|
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
11283
12086
|
search(query, filter, opts) {
|
|
11284
12087
|
const built = this.buildSearchWhere(query, filter);
|
|
@@ -11665,9 +12468,12 @@ var IndexStore = class _IndexStore {
|
|
|
11665
12468
|
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
11666
12469
|
* Call this after all symbols have been inserted to fill in cross-references.
|
|
11667
12470
|
*
|
|
11668
|
-
*
|
|
11669
|
-
* the
|
|
11670
|
-
*
|
|
12471
|
+
* A match additionally requires the referencing ref and the target symbol to
|
|
12472
|
+
* be in the same {@link LangFamily}. Without that guard a name match is a
|
|
12473
|
+
* cross-language accident waiting to happen — `main`, `New`, `Parse` and
|
|
12474
|
+
* `Config` are declared in most languages at once, and each collision draws a
|
|
12475
|
+
* Code Atlas edge between files that never reference each other. Refs stored
|
|
12476
|
+
* without a language keep the old global behaviour via the `'*'` wildcard row.
|
|
11671
12477
|
*/
|
|
11672
12478
|
resolveRefs() {
|
|
11673
12479
|
return this.runWithRetry(() => {
|
|
@@ -11676,20 +12482,35 @@ var IndexStore = class _IndexStore {
|
|
|
11676
12482
|
`UPDATE refs
|
|
11677
12483
|
SET to_id = s.id
|
|
11678
12484
|
FROM (
|
|
11679
|
-
|
|
11680
|
-
|
|
12485
|
+
SELECT sym.name AS name, lf.family AS family, MIN(sym.id) AS id
|
|
12486
|
+
FROM symbols sym
|
|
12487
|
+
JOIN lang_family lf ON lf.lang = sym.lang
|
|
12488
|
+
GROUP BY sym.name, lf.family
|
|
12489
|
+
UNION ALL
|
|
12490
|
+
SELECT sym.name AS name, '${LANG_FAMILY_WILDCARD}' AS family, MIN(sym.id) AS id
|
|
12491
|
+
FROM symbols sym
|
|
12492
|
+
GROUP BY sym.name
|
|
12493
|
+
) AS s,
|
|
12494
|
+
lang_family AS rf
|
|
11681
12495
|
WHERE refs.to_id IS NULL
|
|
11682
12496
|
AND refs.to_name IS NOT NULL
|
|
11683
|
-
AND
|
|
12497
|
+
AND rf.lang = refs.lang
|
|
12498
|
+
AND s.name = refs.to_name
|
|
12499
|
+
AND s.family = rf.family`
|
|
11684
12500
|
).run();
|
|
11685
12501
|
return result.changes ?? 0;
|
|
11686
12502
|
} catch {
|
|
11687
12503
|
const result = this.stmt(
|
|
11688
12504
|
`UPDATE refs SET to_id = (
|
|
11689
|
-
SELECT id FROM symbols
|
|
12505
|
+
SELECT sym.id FROM symbols sym
|
|
12506
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
12507
|
+
ORDER BY sym.id LIMIT 1
|
|
11690
12508
|
) WHERE to_id IS NULL AND to_name IS NOT NULL
|
|
11691
|
-
AND
|
|
11692
|
-
|
|
12509
|
+
AND EXISTS (
|
|
12510
|
+
SELECT 1 FROM symbols sym
|
|
12511
|
+
WHERE sym.name = refs.to_name AND ${_IndexStore.FAMILY_MATCH_SQL}
|
|
12512
|
+
)`
|
|
12513
|
+
).run(LANG_FAMILY_WILDCARD, LANG_FAMILY_WILDCARD);
|
|
11693
12514
|
return result.changes ?? 0;
|
|
11694
12515
|
}
|
|
11695
12516
|
});
|
|
@@ -11773,6 +12594,20 @@ var IndexStore = class _IndexStore {
|
|
|
11773
12594
|
return false;
|
|
11774
12595
|
}
|
|
11775
12596
|
}
|
|
12597
|
+
/**
|
|
12598
|
+
* Find all symbols that reference the named target symbol (incoming callers).
|
|
12599
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
12600
|
+
*/
|
|
12601
|
+
findIncomingCallsByName(symbolName, file, limit = 100) {
|
|
12602
|
+
return findIncomingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
12603
|
+
}
|
|
12604
|
+
/**
|
|
12605
|
+
* Find all symbols that the named source symbol references (outgoing callees).
|
|
12606
|
+
* Accepts a name instead of an id so the agent doesn't need a prior lookup.
|
|
12607
|
+
*/
|
|
12608
|
+
findOutgoingCallsByName(symbolName, file, limit = 100) {
|
|
12609
|
+
return findOutgoingCallsByName((sql) => this.stmt(sql), symbolName, file, limit);
|
|
12610
|
+
}
|
|
11776
12611
|
/**
|
|
11777
12612
|
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
11778
12613
|
*/
|
|
@@ -11864,21 +12699,21 @@ var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
|
11864
12699
|
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
11865
12700
|
var buildIdCache;
|
|
11866
12701
|
function projectIndexServerBuildId(entrypoint) {
|
|
11867
|
-
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) :
|
|
12702
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path17.resolve(entrypoint);
|
|
11868
12703
|
try {
|
|
11869
|
-
const stat18 =
|
|
12704
|
+
const stat18 = fs12.statSync(file);
|
|
11870
12705
|
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
11871
12706
|
return buildIdCache.buildId;
|
|
11872
12707
|
}
|
|
11873
|
-
const buildId = createHash4("sha256").update(
|
|
12708
|
+
const buildId = createHash4("sha256").update(fs12.readFileSync(file)).digest("hex").slice(0, 24);
|
|
11874
12709
|
buildIdCache = { file, mtimeMs: stat18.mtimeMs, size: stat18.size, buildId };
|
|
11875
12710
|
return buildId;
|
|
11876
12711
|
} catch {
|
|
11877
|
-
return `unreadable:${
|
|
12712
|
+
return `unreadable:${path17.basename(file)}`;
|
|
11878
12713
|
}
|
|
11879
12714
|
}
|
|
11880
12715
|
function normalizeLocalPath(value) {
|
|
11881
|
-
const resolved =
|
|
12716
|
+
const resolved = path17.resolve(value);
|
|
11882
12717
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11883
12718
|
}
|
|
11884
12719
|
function projectIndexServerKey(projectRoot, indexDir) {
|
|
@@ -11890,11 +12725,11 @@ function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
|
11890
12725
|
if (process.platform === "win32") {
|
|
11891
12726
|
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
11892
12727
|
}
|
|
11893
|
-
return
|
|
12728
|
+
return path17.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
11894
12729
|
}
|
|
11895
12730
|
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
11896
|
-
return
|
|
11897
|
-
|
|
12731
|
+
return path17.join(
|
|
12732
|
+
path17.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
11898
12733
|
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
11899
12734
|
);
|
|
11900
12735
|
}
|
|
@@ -11934,7 +12769,7 @@ function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
|
11934
12769
|
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
11935
12770
|
try {
|
|
11936
12771
|
const url = new URL(rel, import.meta.url);
|
|
11937
|
-
if (url.protocol === "file:" &&
|
|
12772
|
+
if (url.protocol === "file:" && fs13.existsSync(fileURLToPath2(url))) {
|
|
11938
12773
|
builtUrl = url;
|
|
11939
12774
|
break;
|
|
11940
12775
|
}
|
|
@@ -12191,7 +13026,7 @@ var ProjectServerConnection = class {
|
|
|
12191
13026
|
currentAuthToken() {
|
|
12192
13027
|
if (this.authToken === void 0) {
|
|
12193
13028
|
try {
|
|
12194
|
-
const raw =
|
|
13029
|
+
const raw = fs13.readFileSync(
|
|
12195
13030
|
projectIndexServerMetadataPath(this.projectRoot, this.indexDir),
|
|
12196
13031
|
"utf8"
|
|
12197
13032
|
);
|
|
@@ -12456,7 +13291,7 @@ var ProjectServerConnection = class {
|
|
|
12456
13291
|
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12457
13292
|
if (process.platform !== "win32") {
|
|
12458
13293
|
try {
|
|
12459
|
-
|
|
13294
|
+
fs13.rmSync(this.endpoint, { force: true });
|
|
12460
13295
|
} catch {
|
|
12461
13296
|
}
|
|
12462
13297
|
}
|
|
@@ -12480,8 +13315,8 @@ var ProjectServerConnection = class {
|
|
|
12480
13315
|
process.kill(pid);
|
|
12481
13316
|
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12482
13317
|
try {
|
|
12483
|
-
const metadata = JSON.parse(
|
|
12484
|
-
if (metadata.pid === pid)
|
|
13318
|
+
const metadata = JSON.parse(fs13.readFileSync(metadataPath, "utf8"));
|
|
13319
|
+
if (metadata.pid === pid) fs13.rmSync(metadataPath, { force: true });
|
|
12485
13320
|
} catch {
|
|
12486
13321
|
}
|
|
12487
13322
|
return true;
|
|
@@ -12551,7 +13386,7 @@ import { Worker } from "node:worker_threads";
|
|
|
12551
13386
|
|
|
12552
13387
|
// src/codebase-index/indexer.ts
|
|
12553
13388
|
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12554
|
-
import { execFile
|
|
13389
|
+
import { execFile } from "node:child_process";
|
|
12555
13390
|
import * as fs17 from "node:fs/promises";
|
|
12556
13391
|
import { availableParallelism } from "node:os";
|
|
12557
13392
|
import * as path23 from "node:path";
|
|
@@ -12562,8 +13397,8 @@ import {
|
|
|
12562
13397
|
} from "@wrongstack/core/utils";
|
|
12563
13398
|
|
|
12564
13399
|
// src/codebase-index/gitignore.ts
|
|
12565
|
-
import * as
|
|
12566
|
-
import * as
|
|
13400
|
+
import * as fs14 from "node:fs/promises";
|
|
13401
|
+
import * as path18 from "node:path";
|
|
12567
13402
|
import { compileGlob } from "@wrongstack/core/utils";
|
|
12568
13403
|
function globBody(glob) {
|
|
12569
13404
|
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
@@ -12579,48 +13414,474 @@ function compileGitignore(lines) {
|
|
|
12579
13414
|
negated = true;
|
|
12580
13415
|
line = line.slice(1);
|
|
12581
13416
|
}
|
|
12582
|
-
let dirOnly = false;
|
|
12583
|
-
if (line.endsWith("/")) {
|
|
12584
|
-
dirOnly = true;
|
|
12585
|
-
line = line.slice(0, -1);
|
|
13417
|
+
let dirOnly = false;
|
|
13418
|
+
if (line.endsWith("/")) {
|
|
13419
|
+
dirOnly = true;
|
|
13420
|
+
line = line.slice(0, -1);
|
|
13421
|
+
}
|
|
13422
|
+
if (!line) continue;
|
|
13423
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
13424
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
13425
|
+
const body = globBody(line);
|
|
13426
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
13427
|
+
rules.push({
|
|
13428
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
13429
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
13430
|
+
negated,
|
|
13431
|
+
dirOnly
|
|
13432
|
+
});
|
|
13433
|
+
}
|
|
13434
|
+
return (relPath, isDir) => {
|
|
13435
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
13436
|
+
let ignored = false;
|
|
13437
|
+
for (const r of rules) {
|
|
13438
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
13439
|
+
if (re.test(p)) ignored = !r.negated;
|
|
13440
|
+
}
|
|
13441
|
+
return ignored;
|
|
13442
|
+
};
|
|
13443
|
+
}
|
|
13444
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
13445
|
+
let lines = [];
|
|
13446
|
+
try {
|
|
13447
|
+
const raw = await fs14.readFile(path18.join(projectRoot, ".gitignore"), "utf8");
|
|
13448
|
+
lines = raw.split("\n");
|
|
13449
|
+
} catch {
|
|
13450
|
+
}
|
|
13451
|
+
return compileGitignore(lines);
|
|
13452
|
+
}
|
|
13453
|
+
|
|
13454
|
+
// src/codebase-index/indexer.ts
|
|
13455
|
+
init_languages2();
|
|
13456
|
+
|
|
13457
|
+
// src/codebase-index/module-resolver.ts
|
|
13458
|
+
init_languages2();
|
|
13459
|
+
import * as path19 from "node:path";
|
|
13460
|
+
var EXTENSIONS = {
|
|
13461
|
+
js: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte"],
|
|
13462
|
+
py: [".py", ".pyi"],
|
|
13463
|
+
rs: [".rs"],
|
|
13464
|
+
jvm: [".java", ".kt", ".scala"],
|
|
13465
|
+
c: [".h", ".hpp", ".hh", ".hxx", ".c", ".cpp", ".cc", ".cxx"],
|
|
13466
|
+
ruby: [".rb"],
|
|
13467
|
+
go: [".go"]
|
|
13468
|
+
};
|
|
13469
|
+
var DIRECTORY_ENTRIES = {
|
|
13470
|
+
js: ["index"],
|
|
13471
|
+
py: ["__init__"],
|
|
13472
|
+
rs: ["mod"],
|
|
13473
|
+
ruby: ["index"]
|
|
13474
|
+
};
|
|
13475
|
+
function normalizeNamespace(value) {
|
|
13476
|
+
return value.replace(/::|[\\/]/g, ".").replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
13477
|
+
}
|
|
13478
|
+
var ModuleResolver = class {
|
|
13479
|
+
structure;
|
|
13480
|
+
/** Lowercased portable path → the path as indexed (case is preserved). */
|
|
13481
|
+
byPath;
|
|
13482
|
+
/** Lowercased portable directory → files directly inside it, as indexed. */
|
|
13483
|
+
byDir;
|
|
13484
|
+
/** Normalized namespace → the file declaring it (first by path, stable). */
|
|
13485
|
+
byNamespace;
|
|
13486
|
+
constructor(structure, files, namespaces = []) {
|
|
13487
|
+
this.structure = structure;
|
|
13488
|
+
this.byPath = /* @__PURE__ */ new Map();
|
|
13489
|
+
this.byDir = /* @__PURE__ */ new Map();
|
|
13490
|
+
this.byNamespace = /* @__PURE__ */ new Map();
|
|
13491
|
+
const dirsByKey = /* @__PURE__ */ new Map();
|
|
13492
|
+
for (const file of files) {
|
|
13493
|
+
const portable = toPortablePath(file);
|
|
13494
|
+
const pathKey = portable.toLowerCase();
|
|
13495
|
+
const priorPath = this.byPath.get(pathKey);
|
|
13496
|
+
if (priorPath !== void 0 && priorPath !== file) this.byPath.delete(pathKey);
|
|
13497
|
+
else this.byPath.set(pathKey, file);
|
|
13498
|
+
const dir = path19.posix.dirname(portable);
|
|
13499
|
+
const dirKey = dir.toLowerCase();
|
|
13500
|
+
const knownDir = dirsByKey.get(dirKey);
|
|
13501
|
+
if (knownDir === void 0) {
|
|
13502
|
+
dirsByKey.set(dirKey, dir);
|
|
13503
|
+
this.byDir.set(dirKey, [file]);
|
|
13504
|
+
} else if (knownDir === dir) {
|
|
13505
|
+
this.byDir.get(dirKey)?.push(file);
|
|
13506
|
+
} else {
|
|
13507
|
+
dirsByKey.delete(dirKey);
|
|
13508
|
+
this.byDir.delete(dirKey);
|
|
13509
|
+
}
|
|
13510
|
+
}
|
|
13511
|
+
for (const { name, file } of namespaces) {
|
|
13512
|
+
const lang = detectLang(file);
|
|
13513
|
+
if (!lang) continue;
|
|
13514
|
+
const key = `${languageFamily(lang)}:${normalizeNamespace(name)}`;
|
|
13515
|
+
if (normalizeNamespace(name) && !this.byNamespace.has(key)) {
|
|
13516
|
+
this.byNamespace.set(key, file);
|
|
13517
|
+
}
|
|
13518
|
+
}
|
|
13519
|
+
}
|
|
13520
|
+
/**
|
|
13521
|
+
* Resolve `specifier` as written in `fromFile`.
|
|
13522
|
+
* Returns the indexed target path, or `undefined` when it is external or
|
|
13523
|
+
* cannot be located.
|
|
13524
|
+
*/
|
|
13525
|
+
resolve(fromFile, lang, specifier) {
|
|
13526
|
+
const spec = specifier.trim().replace(/\\/g, "/");
|
|
13527
|
+
if (!spec) return void 0;
|
|
13528
|
+
const from = toPortablePath(fromFile);
|
|
13529
|
+
switch (languageFamily(lang)) {
|
|
13530
|
+
case "js":
|
|
13531
|
+
return this.resolveJs(from, spec);
|
|
13532
|
+
case "go":
|
|
13533
|
+
return this.resolveGo(spec);
|
|
13534
|
+
case "py":
|
|
13535
|
+
return this.resolvePython(from, spec);
|
|
13536
|
+
case "rs":
|
|
13537
|
+
return this.resolveRust(from, spec);
|
|
13538
|
+
case "jvm":
|
|
13539
|
+
return this.resolveJvm(spec);
|
|
13540
|
+
case "c":
|
|
13541
|
+
return this.resolveInclude(from, spec);
|
|
13542
|
+
case "ruby":
|
|
13543
|
+
return this.resolveRuby(from, spec);
|
|
13544
|
+
case "dotnet":
|
|
13545
|
+
case "php":
|
|
13546
|
+
case "elixir":
|
|
13547
|
+
case "haskell":
|
|
13548
|
+
return this.resolveNamespace(lang, spec);
|
|
13549
|
+
default:
|
|
13550
|
+
return void 0;
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
/**
|
|
13554
|
+
* Resolve a namespace specifier to the file declaring it.
|
|
13555
|
+
*
|
|
13556
|
+
* Tried whole first, then with the trailing segment dropped: `using Foo.Bar`
|
|
13557
|
+
* names a namespace outright, while PHP's `use App\Models\User` names a
|
|
13558
|
+
* *class* inside `App\Models`, so the prefix is what was declared.
|
|
13559
|
+
*/
|
|
13560
|
+
resolveNamespace(lang, spec) {
|
|
13561
|
+
const family = languageFamily(lang);
|
|
13562
|
+
const normalized = normalizeNamespace(spec);
|
|
13563
|
+
const exact = this.byNamespace.get(`${family}:${normalized}`);
|
|
13564
|
+
if (exact) return exact;
|
|
13565
|
+
const segments = normalized.split(".").filter(Boolean);
|
|
13566
|
+
if (segments.length < 2) return void 0;
|
|
13567
|
+
return this.byNamespace.get(`${family}:${segments.slice(0, -1).join(".")}`);
|
|
13568
|
+
}
|
|
13569
|
+
// ─── Lookup primitives ──────────────────────────────────────────────────────
|
|
13570
|
+
lookup(candidate) {
|
|
13571
|
+
return this.byPath.get(path19.posix.normalize(candidate).toLowerCase());
|
|
13572
|
+
}
|
|
13573
|
+
/**
|
|
13574
|
+
* Try `base` verbatim, then `base` + each extension, then each directory
|
|
13575
|
+
* entry point inside `base`.
|
|
13576
|
+
*/
|
|
13577
|
+
lookupWithExtensions(base, family) {
|
|
13578
|
+
const direct = this.lookup(base);
|
|
13579
|
+
if (direct) return direct;
|
|
13580
|
+
const extensions = EXTENSIONS[family] ?? [];
|
|
13581
|
+
const suffix = path19.posix.extname(base);
|
|
13582
|
+
const stem = suffix && extensions.includes(suffix) ? base.slice(0, -suffix.length) : base;
|
|
13583
|
+
for (const ext of extensions) {
|
|
13584
|
+
const hit = this.lookup(`${stem}${ext}`);
|
|
13585
|
+
if (hit) return hit;
|
|
13586
|
+
}
|
|
13587
|
+
for (const entry of DIRECTORY_ENTRIES[family] ?? []) {
|
|
13588
|
+
for (const ext of extensions) {
|
|
13589
|
+
const hit = this.lookup(path19.posix.join(base, `${entry}${ext}`));
|
|
13590
|
+
if (hit) return hit;
|
|
13591
|
+
}
|
|
13592
|
+
}
|
|
13593
|
+
return void 0;
|
|
13594
|
+
}
|
|
13595
|
+
/**
|
|
13596
|
+
* A representative indexed file inside `dir`, for ecosystems whose import
|
|
13597
|
+
* unit is a directory rather than a file (Go packages, JVM wildcard imports).
|
|
13598
|
+
*
|
|
13599
|
+
* The choice is deterministic — a file named after the directory, else the
|
|
13600
|
+
* first by name — so the same import always produces the same edge. Package
|
|
13601
|
+
* grouping is unaffected either way: every file in the directory carries the
|
|
13602
|
+
* same package label, so the package-level edge is exact regardless of which
|
|
13603
|
+
* member represents it.
|
|
13604
|
+
*/
|
|
13605
|
+
representativeIn(dir, family) {
|
|
13606
|
+
const members = this.byDir.get(path19.posix.normalize(dir).toLowerCase());
|
|
13607
|
+
if (!members?.length) return void 0;
|
|
13608
|
+
const extensions = EXTENSIONS[family] ?? [];
|
|
13609
|
+
const eligible = members.filter((file) => extensions.includes(path19.posix.extname(toPortablePath(file)).toLowerCase())).sort((a, b) => toPortablePath(a).localeCompare(toPortablePath(b)));
|
|
13610
|
+
if (eligible.length === 0) return void 0;
|
|
13611
|
+
const base = path19.posix.basename(path19.posix.normalize(dir)).toLowerCase();
|
|
13612
|
+
const named = eligible.find(
|
|
13613
|
+
(file) => path19.posix.basename(toPortablePath(file)).split(".")[0]?.toLowerCase() === base
|
|
13614
|
+
);
|
|
13615
|
+
return named ?? eligible[0];
|
|
13616
|
+
}
|
|
13617
|
+
// ─── Per-family resolution ──────────────────────────────────────────────────
|
|
13618
|
+
/** Relative specifiers, then workspace package names and their subpaths. */
|
|
13619
|
+
resolveJs(fromFile, spec) {
|
|
13620
|
+
if (spec.startsWith(".")) {
|
|
13621
|
+
const absolute = path19.posix.join(path19.posix.dirname(fromFile), spec);
|
|
13622
|
+
return this.lookupWithExtensions(absolute, "js");
|
|
13623
|
+
}
|
|
13624
|
+
const owner2 = this.structure.roots.find(
|
|
13625
|
+
(root) => root.kind === "npm" && root.importPath !== void 0 && (spec === root.importPath || spec.startsWith(`${root.importPath}/`))
|
|
13626
|
+
);
|
|
13627
|
+
if (!owner2?.importPath) return void 0;
|
|
13628
|
+
const subpath = spec.slice(owner2.importPath.length).replace(/^\//, "");
|
|
13629
|
+
if (!subpath) {
|
|
13630
|
+
return this.lookupWithExtensions(path19.posix.join(owner2.dir, "src/index"), "js") ?? this.lookupWithExtensions(path19.posix.join(owner2.dir, "index"), "js");
|
|
13631
|
+
}
|
|
13632
|
+
return this.lookupWithExtensions(path19.posix.join(owner2.dir, subpath), "js") ?? this.lookupWithExtensions(path19.posix.join(owner2.dir, "src", subpath), "js");
|
|
13633
|
+
}
|
|
13634
|
+
/** Go import paths are absolute module paths; a package is a directory. */
|
|
13635
|
+
resolveGo(spec) {
|
|
13636
|
+
const owner2 = this.structure.roots.find(
|
|
13637
|
+
(root) => root.kind === "go" && root.importPath !== void 0 && (spec === root.importPath || spec.startsWith(`${root.importPath}/`))
|
|
13638
|
+
);
|
|
13639
|
+
if (!owner2?.importPath) return void 0;
|
|
13640
|
+
const subpath = spec.slice(owner2.importPath.length).replace(/^\//, "");
|
|
13641
|
+
return this.representativeIn(path19.posix.join(owner2.dir, subpath), "go");
|
|
13642
|
+
}
|
|
13643
|
+
/**
|
|
13644
|
+
* `import a.b.c` / `from a.b import c`, plus PEP 328 relative imports whose
|
|
13645
|
+
* leading dots the extractor preserves (`.sibling`, `..parent.mod`).
|
|
13646
|
+
*/
|
|
13647
|
+
resolvePython(fromFile, spec) {
|
|
13648
|
+
const leadingDots = /^\.*/.exec(spec)?.[0].length ?? 0;
|
|
13649
|
+
if (leadingDots > 0) {
|
|
13650
|
+
let base = path19.posix.dirname(fromFile);
|
|
13651
|
+
for (let i = 1; i < leadingDots; i++) base = path19.posix.dirname(base);
|
|
13652
|
+
const rest = spec.slice(leadingDots).split(".").filter(Boolean);
|
|
13653
|
+
return this.lookupWithExtensions(path19.posix.join(base, ...rest), "py");
|
|
13654
|
+
}
|
|
13655
|
+
const segments = spec.split(".").filter(Boolean);
|
|
13656
|
+
if (segments.length === 0) return void 0;
|
|
13657
|
+
const sourceRoots = this.structure.roots.filter((root) => root.kind === "python").flatMap((root) => root.sourceRoots);
|
|
13658
|
+
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
13659
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, ...segments), "py");
|
|
13660
|
+
if (hit) return hit;
|
|
13661
|
+
if (segments.length > 1) {
|
|
13662
|
+
const parent = this.lookupWithExtensions(
|
|
13663
|
+
path19.posix.join(base, ...segments.slice(0, -1)),
|
|
13664
|
+
"py"
|
|
13665
|
+
);
|
|
13666
|
+
if (parent) return parent;
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
return void 0;
|
|
13670
|
+
}
|
|
13671
|
+
/** `use crate::a::b`, `use super::x`, `use self::y`, `use other_crate::z`. */
|
|
13672
|
+
resolveRust(fromFile, spec) {
|
|
13673
|
+
const segments = spec.split("::").filter(Boolean);
|
|
13674
|
+
if (segments.length === 0) return void 0;
|
|
13675
|
+
const head = segments[0];
|
|
13676
|
+
if (head === "self" || head === "super") {
|
|
13677
|
+
let base = path19.posix.dirname(fromFile);
|
|
13678
|
+
for (const segment of segments) {
|
|
13679
|
+
if (segment === "super") base = path19.posix.dirname(base);
|
|
13680
|
+
else if (segment !== "self") break;
|
|
13681
|
+
}
|
|
13682
|
+
const rest2 = segments.filter((segment) => segment !== "self" && segment !== "super");
|
|
13683
|
+
return this.lookupWithExtensions(path19.posix.join(base, ...rest2), "rs");
|
|
13684
|
+
}
|
|
13685
|
+
const owningCrate = findOwningRoot(this.structure, fromFile, ["cargo"]);
|
|
13686
|
+
const crate = head === "crate" ? owningCrate : this.structure.roots.find(
|
|
13687
|
+
(root) => root.kind === "cargo" && root.importPath === head?.replace(/-/g, "_")
|
|
13688
|
+
);
|
|
13689
|
+
if (!crate) {
|
|
13690
|
+
return this.lookupWithExtensions(
|
|
13691
|
+
path19.posix.join(path19.posix.dirname(fromFile), ...segments),
|
|
13692
|
+
"rs"
|
|
13693
|
+
);
|
|
12586
13694
|
}
|
|
12587
|
-
|
|
12588
|
-
const
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
negated,
|
|
12596
|
-
dirOnly
|
|
12597
|
-
});
|
|
13695
|
+
const rest = segments.slice(1);
|
|
13696
|
+
for (const base of crate.sourceRoots) {
|
|
13697
|
+
const parent = rest.length > 1 ? this.lookupWithExtensions(path19.posix.join(base, ...rest.slice(0, -1)), "rs") : void 0;
|
|
13698
|
+
const exact = this.lookupWithExtensions(path19.posix.join(base, ...rest), "rs");
|
|
13699
|
+
const hit = exact ?? parent ?? this.lookupWithExtensions(path19.posix.join(base, "lib"), "rs");
|
|
13700
|
+
if (hit) return hit;
|
|
13701
|
+
}
|
|
13702
|
+
return void 0;
|
|
12598
13703
|
}
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
13704
|
+
/** `com.example.Thing` and `com.example.*` against JVM source roots. */
|
|
13705
|
+
resolveJvm(spec) {
|
|
13706
|
+
const segments = spec.split(".").filter(Boolean);
|
|
13707
|
+
if (segments.length === 0) return void 0;
|
|
13708
|
+
const sourceRoots = this.structure.roots.filter((root) => root.kind === "maven" || root.kind === "gradle").flatMap((root) => root.sourceRoots);
|
|
13709
|
+
const wildcard = segments[segments.length - 1] === "*";
|
|
13710
|
+
const parts = wildcard ? segments.slice(0, -1) : segments;
|
|
13711
|
+
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
13712
|
+
const target = path19.posix.join(base, ...parts);
|
|
13713
|
+
const hit = wildcard ? this.representativeIn(target, "jvm") : this.lookupWithExtensions(target, "jvm");
|
|
13714
|
+
if (hit) return hit;
|
|
12605
13715
|
}
|
|
12606
|
-
return
|
|
12607
|
-
}
|
|
13716
|
+
return void 0;
|
|
13717
|
+
}
|
|
13718
|
+
/** `#include "foo/bar.h"` — quoted form only; `<…>` is a system header. */
|
|
13719
|
+
resolveInclude(fromFile, spec) {
|
|
13720
|
+
const relative12 = this.lookupWithExtensions(
|
|
13721
|
+
path19.posix.join(path19.posix.dirname(fromFile), spec),
|
|
13722
|
+
"c"
|
|
13723
|
+
);
|
|
13724
|
+
if (relative12) return relative12;
|
|
13725
|
+
for (const base of [
|
|
13726
|
+
path19.posix.join(this.structure.projectRoot, "include"),
|
|
13727
|
+
this.structure.projectRoot
|
|
13728
|
+
]) {
|
|
13729
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, spec), "c");
|
|
13730
|
+
if (hit) return hit;
|
|
13731
|
+
}
|
|
13732
|
+
return void 0;
|
|
13733
|
+
}
|
|
13734
|
+
/** `require_relative 'x'` is relative; `require 'x'` is looked up under lib/. */
|
|
13735
|
+
resolveRuby(fromFile, spec) {
|
|
13736
|
+
const relative12 = this.lookupWithExtensions(
|
|
13737
|
+
path19.posix.join(path19.posix.dirname(fromFile), spec),
|
|
13738
|
+
"ruby"
|
|
13739
|
+
);
|
|
13740
|
+
if (relative12) return relative12;
|
|
13741
|
+
for (const base of [
|
|
13742
|
+
path19.posix.join(this.structure.projectRoot, "lib"),
|
|
13743
|
+
this.structure.projectRoot
|
|
13744
|
+
]) {
|
|
13745
|
+
const hit = this.lookupWithExtensions(path19.posix.join(base, spec), "ruby");
|
|
13746
|
+
if (hit) return hit;
|
|
13747
|
+
}
|
|
13748
|
+
return void 0;
|
|
13749
|
+
}
|
|
13750
|
+
};
|
|
13751
|
+
|
|
13752
|
+
// src/codebase-index/import-extractor.ts
|
|
13753
|
+
var IMPORT_MAX_FILE_CHARS = 512 * 1024;
|
|
13754
|
+
var IMPORT_MAX_PER_FILE = 400;
|
|
13755
|
+
var DOTTED_IMPORT = [
|
|
13756
|
+
{ re: /^[ \t]*import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;?\s*$/gm }
|
|
13757
|
+
];
|
|
13758
|
+
var LANG_IMPORTS = {
|
|
13759
|
+
// Go and Python have real AST extractors; these patterns are the fallback for
|
|
13760
|
+
// machines with no Go toolchain or Python interpreter installed, where the
|
|
13761
|
+
// parser degrades to regex symbols and would otherwise contribute no edges.
|
|
13762
|
+
go: [
|
|
13763
|
+
{ re: /^[ \t]*import\s+(?:[\w.]+\s+)?"([^"]+)"/gm },
|
|
13764
|
+
// Grouped form: inside `import ( … )` each line is an optional alias plus a
|
|
13765
|
+
// quoted path. A stray match elsewhere resolves to no file and is dropped.
|
|
13766
|
+
{ re: /^[ \t]*(?:[A-Za-z_.]\w*\s+)?"([^"]+)"\s*$/gm }
|
|
13767
|
+
],
|
|
13768
|
+
py: [
|
|
13769
|
+
{ re: /^[ \t]*import\s+([\w.]+)/gm },
|
|
13770
|
+
{ re: /^[ \t]*from\s+([.\w]+)\s+import\b/gm }
|
|
13771
|
+
],
|
|
13772
|
+
rs: [
|
|
13773
|
+
// use a::b::C; | use a::b::{C, D}; → the path before any brace
|
|
13774
|
+
{ re: /^[ \t]*(?:pub\s+)?use\s+([\w:]+?)(?:::\{|\s*;|\s+as\b)/gm },
|
|
13775
|
+
// mod foo; (a declaration *and* a dependency on foo.rs / foo/mod.rs)
|
|
13776
|
+
{ re: /^[ \t]*(?:pub\s+)?mod\s+(\w+)\s*;/gm }
|
|
13777
|
+
],
|
|
13778
|
+
java: DOTTED_IMPORT,
|
|
13779
|
+
kotlin: DOTTED_IMPORT,
|
|
13780
|
+
scala: [{ re: /^[ \t]*import\s+([\w.]+(?:\.[_*])?)\s*$/gm }],
|
|
13781
|
+
csharp: [
|
|
13782
|
+
// using Foo.Bar; | using static Foo.Bar; | using Alias = Foo.Bar;
|
|
13783
|
+
{ re: /^[ \t]*global\s+using\s+(?:static\s+)?([\w.]+)\s*;/gm, name: "full" },
|
|
13784
|
+
{ re: /^[ \t]*using\s+(?:static\s+)?(?:\w+\s*=\s*)?([\w.]+)\s*;/gm, name: "full" }
|
|
13785
|
+
],
|
|
13786
|
+
// Quoted includes only: <stdio.h> is a system header with no indexed file.
|
|
13787
|
+
c: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
13788
|
+
cpp: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
13789
|
+
ruby: [{ re: /^[ \t]*(?:require|require_relative|load)\s*\(?\s*['"]([^'"]+)['"]/gm }],
|
|
13790
|
+
php: [
|
|
13791
|
+
// `use A\B\C` imports the class C, which is what the index has a symbol
|
|
13792
|
+
// for — the namespace symbol only covers the `A\B` prefix.
|
|
13793
|
+
{ re: /^[ \t]*use\s+(?:function\s+|const\s+)?([\w\\]+)/gm },
|
|
13794
|
+
{ re: /^[ \t]*(?:require|require_once|include|include_once)\s*\(?\s*['"]([^'"]+)['"]/gm }
|
|
13795
|
+
],
|
|
13796
|
+
swift: [{ re: /^[ \t]*import\s+(?:struct\s+|class\s+|func\s+)?([\w.]+)\s*$/gm }],
|
|
13797
|
+
dart: [{ re: /^[ \t]*(?:import|export|part)\s+['"]([^'"]+)['"]/gm }],
|
|
13798
|
+
lua: [{ re: /\brequire\s*\(?\s*['"]([^'"]+)['"]/g }],
|
|
13799
|
+
elixir: [{ re: /^[ \t]*(?:alias|import|require|use)\s+([A-Z][\w.]*)/gm, name: "full" }],
|
|
13800
|
+
haskell: [{ re: /^[ \t]*import\s+(?:qualified\s+)?([\w.]+)/gm, name: "full" }],
|
|
13801
|
+
zig: [{ re: /@import\s*\(\s*"([^"]+)"\s*\)/g }],
|
|
13802
|
+
proto: [{ re: /^[ \t]*import\s+(?:public\s+|weak\s+)?"([^"]+)"\s*;/gm }],
|
|
13803
|
+
// `@import "x"`, `@use "x"`, `@forward "x"` — Sass and plain CSS alike.
|
|
13804
|
+
css: [{ re: /^[ \t]*@(?:import|use|forward)\s+(?:url\()?\s*['"]([^'"]+)['"]/gm }],
|
|
13805
|
+
// A .vue/.svelte file's <script> block is JS; the same ESM syntax applies.
|
|
13806
|
+
vue: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
13807
|
+
svelte: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
13808
|
+
html: [
|
|
13809
|
+
{ re: /<script[^>]+src\s*=\s*['"]([^'"]+)['"]/g },
|
|
13810
|
+
{ re: /<link[^>]+href\s*=\s*['"]([^'"]+)['"]/g }
|
|
13811
|
+
],
|
|
13812
|
+
shell: [{ re: /^[ \t]*(?:source|\.)\s+(\S+)/gm }],
|
|
13813
|
+
r: [{ re: /\b(?:source|library|require)\s*\(\s*['"]?([\w./-]+)['"]?\s*\)/g }]
|
|
13814
|
+
};
|
|
13815
|
+
function lastSegment(specifier) {
|
|
13816
|
+
const pathLike = /[/\\]|::/.test(specifier);
|
|
13817
|
+
const segments = specifier.split(/[/\\]|::/).filter(Boolean);
|
|
13818
|
+
let last = segments[segments.length - 1] ?? specifier;
|
|
13819
|
+
if (last === "*" || last === "_") {
|
|
13820
|
+
last = segments[segments.length - 2] ?? specifier;
|
|
13821
|
+
}
|
|
13822
|
+
if (pathLike) return last.replace(/\.[A-Za-z0-9]{1,8}$/, "") || last;
|
|
13823
|
+
const dotted = last.split(".").filter((part) => part && part !== "*" && part !== "_");
|
|
13824
|
+
return dotted[dotted.length - 1] ?? last;
|
|
12608
13825
|
}
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
lines = raw.split("\n");
|
|
12614
|
-
} catch {
|
|
13826
|
+
function newlineOffsets(content) {
|
|
13827
|
+
const offsets = [];
|
|
13828
|
+
for (let i = 0; i < content.length; i++) {
|
|
13829
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
12615
13830
|
}
|
|
12616
|
-
return
|
|
13831
|
+
return offsets;
|
|
13832
|
+
}
|
|
13833
|
+
function lineAt(offsets, index) {
|
|
13834
|
+
let low = 0;
|
|
13835
|
+
let high = offsets.length;
|
|
13836
|
+
while (low < high) {
|
|
13837
|
+
const mid = low + high >>> 1;
|
|
13838
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
13839
|
+
else high = mid;
|
|
13840
|
+
}
|
|
13841
|
+
return low + 1;
|
|
13842
|
+
}
|
|
13843
|
+
function hasImportPatterns(lang) {
|
|
13844
|
+
return LANG_IMPORTS[lang] !== void 0;
|
|
13845
|
+
}
|
|
13846
|
+
function extractImports(opts) {
|
|
13847
|
+
const patterns = LANG_IMPORTS[opts.lang];
|
|
13848
|
+
if (!patterns || !opts.content) return [];
|
|
13849
|
+
const limit = opts.maxImports ?? IMPORT_MAX_PER_FILE;
|
|
13850
|
+
const content = opts.content.length > IMPORT_MAX_FILE_CHARS ? opts.content.slice(0, IMPORT_MAX_FILE_CHARS) : opts.content;
|
|
13851
|
+
const refs = [];
|
|
13852
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13853
|
+
const offsets = newlineOffsets(content);
|
|
13854
|
+
for (const pattern of patterns) {
|
|
13855
|
+
const re = new RegExp(pattern.re.source, pattern.re.flags);
|
|
13856
|
+
for (const match of content.matchAll(re)) {
|
|
13857
|
+
if (refs.length >= limit) return refs;
|
|
13858
|
+
const specifier = match[1]?.trim();
|
|
13859
|
+
if (!specifier) continue;
|
|
13860
|
+
const module = specifier;
|
|
13861
|
+
const toName = pattern.name === "full" ? module : lastSegment(module);
|
|
13862
|
+
if (!toName) continue;
|
|
13863
|
+
const key = `${module}\0${toName}`;
|
|
13864
|
+
if (seen.has(key)) continue;
|
|
13865
|
+
seen.add(key);
|
|
13866
|
+
refs.push({
|
|
13867
|
+
fromId: 0,
|
|
13868
|
+
toName,
|
|
13869
|
+
callType: "import",
|
|
13870
|
+
line: lineAt(offsets, match.index ?? 0),
|
|
13871
|
+
lang: opts.lang,
|
|
13872
|
+
module
|
|
13873
|
+
});
|
|
13874
|
+
}
|
|
13875
|
+
}
|
|
13876
|
+
return refs;
|
|
12617
13877
|
}
|
|
12618
|
-
|
|
12619
|
-
// src/codebase-index/indexer.ts
|
|
12620
|
-
init_languages2();
|
|
12621
13878
|
|
|
12622
13879
|
// src/codebase-index/parser-dispatch.ts
|
|
12623
13880
|
async function parseFileContent(file, content, lang) {
|
|
13881
|
+
const parsed = await dispatch(file, content, lang);
|
|
13882
|
+
return withRelations(parsed, content, lang);
|
|
13883
|
+
}
|
|
13884
|
+
async function dispatch(file, content, lang) {
|
|
12624
13885
|
switch (lang) {
|
|
12625
13886
|
case "ts":
|
|
12626
13887
|
case "tsx":
|
|
@@ -12655,6 +13916,13 @@ async function parseFileContent(file, content, lang) {
|
|
|
12655
13916
|
}
|
|
12656
13917
|
}
|
|
12657
13918
|
}
|
|
13919
|
+
function withRelations(parsed, content, lang) {
|
|
13920
|
+
let refs = parsed.refs ?? [];
|
|
13921
|
+
if (refs.length === 0 && hasImportPatterns(lang)) {
|
|
13922
|
+
refs = extractImports({ content, lang });
|
|
13923
|
+
}
|
|
13924
|
+
return { ...parsed, refs: refs.map((ref) => ref.lang ? ref : { ...ref, lang }) };
|
|
13925
|
+
}
|
|
12658
13926
|
|
|
12659
13927
|
// src/codebase-index/indexer.ts
|
|
12660
13928
|
var YIELD_EVERY_N = 50;
|
|
@@ -12691,7 +13959,7 @@ function normalizeComparablePath(value) {
|
|
|
12691
13959
|
}
|
|
12692
13960
|
function gitOutput(projectRoot, args) {
|
|
12693
13961
|
return new Promise((resolve16, reject) => {
|
|
12694
|
-
|
|
13962
|
+
execFile(
|
|
12695
13963
|
"git",
|
|
12696
13964
|
["-C", projectRoot, ...args],
|
|
12697
13965
|
{
|
|
@@ -12822,13 +14090,40 @@ function assignRefsToSymbols2(refs, symbols) {
|
|
|
12822
14090
|
}
|
|
12823
14091
|
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12824
14092
|
if (!owner2 || owner2.id <= 0) continue;
|
|
12825
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
14093
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
|
|
12826
14094
|
if (seen.has(key)) continue;
|
|
12827
14095
|
seen.add(key);
|
|
12828
14096
|
assigned.push({ ...ref, fromId: owner2.id });
|
|
12829
14097
|
}
|
|
12830
14098
|
return assigned;
|
|
12831
14099
|
}
|
|
14100
|
+
async function resolveProjectRelations(store, projectRoot, opts) {
|
|
14101
|
+
if (opts.signal?.aborted) return;
|
|
14102
|
+
try {
|
|
14103
|
+
const indexedFiles = store.getAllFileMetas().map((meta) => meta.file);
|
|
14104
|
+
if (indexedFiles.length === 0) return;
|
|
14105
|
+
const structure = await detectModuleRoots(projectRoot, indexedFiles);
|
|
14106
|
+
if (opts.signal?.aborted) return;
|
|
14107
|
+
store.setFilePackages(assignPackageLabels(structure, indexedFiles));
|
|
14108
|
+
const resolver = new ModuleResolver(
|
|
14109
|
+
structure,
|
|
14110
|
+
indexedFiles,
|
|
14111
|
+
store.getNamespaceDeclarations()
|
|
14112
|
+
);
|
|
14113
|
+
const pending2 = store.getUnresolvedImports(opts.onlyFiles);
|
|
14114
|
+
const resolutions = [];
|
|
14115
|
+
for (const entry of pending2) {
|
|
14116
|
+
const toFile = resolver.resolve(entry.fromFile, entry.lang, entry.module);
|
|
14117
|
+
if (toFile && toFile !== entry.fromFile) {
|
|
14118
|
+
resolutions.push({ ...entry, toFile });
|
|
14119
|
+
}
|
|
14120
|
+
}
|
|
14121
|
+
if (opts.signal?.aborted) return;
|
|
14122
|
+
store.applyImportResolutions(resolutions);
|
|
14123
|
+
} catch (err) {
|
|
14124
|
+
opts.errors.push(`relation resolution: ${err instanceof Error ? err.message : String(err)}`);
|
|
14125
|
+
}
|
|
14126
|
+
}
|
|
12832
14127
|
async function runIndexerWithStore(store, opts) {
|
|
12833
14128
|
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
12834
14129
|
const relationGraphVersion = "2";
|
|
@@ -13073,6 +14368,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
13073
14368
|
}
|
|
13074
14369
|
}
|
|
13075
14370
|
if (needsFullRefResolution) store.resolveRefs();
|
|
14371
|
+
await resolveProjectRelations(store, projectRoot, {
|
|
14372
|
+
// A watcher run re-resolves only what it touched; a full run (or a contract
|
|
14373
|
+
// bump) re-resolves everything, because a newly indexed file can be the
|
|
14374
|
+
// target of imports written long before it.
|
|
14375
|
+
onlyFiles: needsFullRefResolution ? void 0 : opts.files,
|
|
14376
|
+
errors,
|
|
14377
|
+
signal
|
|
14378
|
+
});
|
|
13076
14379
|
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
13077
14380
|
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
13078
14381
|
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
@@ -13155,6 +14458,22 @@ function symbolGraphService(args) {
|
|
|
13155
14458
|
indexStorePool.release(store);
|
|
13156
14459
|
}
|
|
13157
14460
|
}
|
|
14461
|
+
function incomingCallsService(args) {
|
|
14462
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
14463
|
+
try {
|
|
14464
|
+
return store.findIncomingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
14465
|
+
} finally {
|
|
14466
|
+
indexStorePool.release(store);
|
|
14467
|
+
}
|
|
14468
|
+
}
|
|
14469
|
+
function outgoingCallsService(args) {
|
|
14470
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
14471
|
+
try {
|
|
14472
|
+
return store.findOutgoingCallsByName(args.symbol, args.file, args.limit ?? 100);
|
|
14473
|
+
} finally {
|
|
14474
|
+
indexStorePool.release(store);
|
|
14475
|
+
}
|
|
14476
|
+
}
|
|
13158
14477
|
|
|
13159
14478
|
// src/codebase-index/background-indexer.ts
|
|
13160
14479
|
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 24e4;
|
|
@@ -13366,6 +14685,10 @@ async function callInline(op, args, opts) {
|
|
|
13366
14685
|
return fileGraphService(args);
|
|
13367
14686
|
case "symbolGraph":
|
|
13368
14687
|
return symbolGraphService(args);
|
|
14688
|
+
case "incomingCalls":
|
|
14689
|
+
return incomingCallsService(args);
|
|
14690
|
+
case "outgoingCalls":
|
|
14691
|
+
return outgoingCallsService(args);
|
|
13369
14692
|
default:
|
|
13370
14693
|
throw new Error(`unknown index op: ${String(op)}`);
|
|
13371
14694
|
}
|
|
@@ -13461,6 +14784,12 @@ async function codebaseIndexStats(args, opts = {}) {
|
|
|
13461
14784
|
signal: opts.signal
|
|
13462
14785
|
});
|
|
13463
14786
|
}
|
|
14787
|
+
async function incomingCallsService2(args) {
|
|
14788
|
+
return callIndexOp("incomingCalls", args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });
|
|
14789
|
+
}
|
|
14790
|
+
async function outgoingCallsService2(args) {
|
|
14791
|
+
return callIndexOp("outgoingCalls", args, { timeoutMs: DEFAULT_QUERY_TIMEOUT_MS });
|
|
14792
|
+
}
|
|
13464
14793
|
|
|
13465
14794
|
// src/codebase-index/codebase-index-tool.ts
|
|
13466
14795
|
var codebaseIndexTool = {
|
|
@@ -13525,6 +14854,214 @@ var codebaseIndexTool = {
|
|
|
13525
14854
|
}
|
|
13526
14855
|
};
|
|
13527
14856
|
|
|
14857
|
+
// src/codebase-index/codebase-incoming-calls-tool.ts
|
|
14858
|
+
var codebaseIncomingCallsTool = {
|
|
14859
|
+
name: "codebase-incoming-calls",
|
|
14860
|
+
category: "Project",
|
|
14861
|
+
icon: "index",
|
|
14862
|
+
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.",
|
|
14863
|
+
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.',
|
|
14864
|
+
permission: "auto",
|
|
14865
|
+
mutating: false,
|
|
14866
|
+
capabilities: ["fs.read"],
|
|
14867
|
+
timeoutMs: 35e3,
|
|
14868
|
+
inputSchema: {
|
|
14869
|
+
type: "object",
|
|
14870
|
+
properties: {
|
|
14871
|
+
symbol: {
|
|
14872
|
+
type: "string",
|
|
14873
|
+
description: "The function/method/type name to find callers for"
|
|
14874
|
+
},
|
|
14875
|
+
file: {
|
|
14876
|
+
type: "string",
|
|
14877
|
+
description: "Scope to a specific file when multiple symbols share the same name"
|
|
14878
|
+
},
|
|
14879
|
+
limit: {
|
|
14880
|
+
type: "integer",
|
|
14881
|
+
description: "Maximum call sites to return (default 50, max 200)",
|
|
14882
|
+
minimum: 1,
|
|
14883
|
+
maximum: 200
|
|
14884
|
+
}
|
|
14885
|
+
},
|
|
14886
|
+
required: ["symbol"]
|
|
14887
|
+
},
|
|
14888
|
+
async execute(input, ctx) {
|
|
14889
|
+
const state = getIndexState();
|
|
14890
|
+
if (state.indexing && !state.ready) {
|
|
14891
|
+
return {
|
|
14892
|
+
symbol: input.symbol,
|
|
14893
|
+
calls: [],
|
|
14894
|
+
total: 0,
|
|
14895
|
+
indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
|
|
14896
|
+
};
|
|
14897
|
+
}
|
|
14898
|
+
if (state.lastError) {
|
|
14899
|
+
const circuit = state.circuit;
|
|
14900
|
+
const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s).` : "Try /codebase-reindex.";
|
|
14901
|
+
return {
|
|
14902
|
+
symbol: input.symbol,
|
|
14903
|
+
calls: [],
|
|
14904
|
+
total: 0,
|
|
14905
|
+
indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
|
|
14906
|
+
};
|
|
14907
|
+
}
|
|
14908
|
+
const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
|
|
14909
|
+
const { calls, symbolFound, ambiguous, totalMatches } = await incomingCallsService2(
|
|
14910
|
+
{
|
|
14911
|
+
projectRoot: ctx.projectRoot,
|
|
14912
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
14913
|
+
symbol: input.symbol,
|
|
14914
|
+
file: input.file,
|
|
14915
|
+
limit
|
|
14916
|
+
}
|
|
14917
|
+
);
|
|
14918
|
+
if (!symbolFound) {
|
|
14919
|
+
let hasPersistedIndex = state.ready;
|
|
14920
|
+
if (!hasPersistedIndex) {
|
|
14921
|
+
try {
|
|
14922
|
+
const stats = await codebaseIndexStats({
|
|
14923
|
+
projectRoot: ctx.projectRoot,
|
|
14924
|
+
indexDir: codebaseIndexDirOverride(ctx)
|
|
14925
|
+
});
|
|
14926
|
+
hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
|
|
14927
|
+
} catch {
|
|
14928
|
+
}
|
|
14929
|
+
}
|
|
14930
|
+
if (!hasPersistedIndex) {
|
|
14931
|
+
return {
|
|
14932
|
+
symbol: input.symbol,
|
|
14933
|
+
calls: [],
|
|
14934
|
+
total: 0,
|
|
14935
|
+
indexStatus: "No persisted index data found. Run codebase-index to build it."
|
|
14936
|
+
};
|
|
14937
|
+
}
|
|
14938
|
+
return {
|
|
14939
|
+
symbol: input.symbol,
|
|
14940
|
+
calls: [],
|
|
14941
|
+
total: 0,
|
|
14942
|
+
note: `Symbol "${input.symbol}" not found in the index. Use codebase-search to verify the name.`
|
|
14943
|
+
};
|
|
14944
|
+
}
|
|
14945
|
+
const notes = [];
|
|
14946
|
+
if (totalMatches > limit) {
|
|
14947
|
+
notes.push(`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`);
|
|
14948
|
+
}
|
|
14949
|
+
if (ambiguous) {
|
|
14950
|
+
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\`.`);
|
|
14951
|
+
}
|
|
14952
|
+
return {
|
|
14953
|
+
symbol: input.symbol,
|
|
14954
|
+
calls,
|
|
14955
|
+
total: calls.length,
|
|
14956
|
+
...notes.length > 0 ? { note: notes.join(" ") } : {}
|
|
14957
|
+
};
|
|
14958
|
+
}
|
|
14959
|
+
};
|
|
14960
|
+
|
|
14961
|
+
// src/codebase-index/codebase-outgoing-calls-tool.ts
|
|
14962
|
+
var codebaseOutgoingCallsTool = {
|
|
14963
|
+
name: "codebase-outgoing-calls",
|
|
14964
|
+
category: "Project",
|
|
14965
|
+
icon: "index",
|
|
14966
|
+
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.",
|
|
14967
|
+
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.',
|
|
14968
|
+
permission: "auto",
|
|
14969
|
+
mutating: false,
|
|
14970
|
+
capabilities: ["fs.read"],
|
|
14971
|
+
timeoutMs: 35e3,
|
|
14972
|
+
inputSchema: {
|
|
14973
|
+
type: "object",
|
|
14974
|
+
properties: {
|
|
14975
|
+
symbol: {
|
|
14976
|
+
type: "string",
|
|
14977
|
+
description: "The function/method/type name to find callees for"
|
|
14978
|
+
},
|
|
14979
|
+
file: {
|
|
14980
|
+
type: "string",
|
|
14981
|
+
description: "Scope to a specific file when multiple symbols share the same name"
|
|
14982
|
+
},
|
|
14983
|
+
limit: {
|
|
14984
|
+
type: "integer",
|
|
14985
|
+
description: "Maximum call sites to return (default 50, max 200)",
|
|
14986
|
+
minimum: 1,
|
|
14987
|
+
maximum: 200
|
|
14988
|
+
}
|
|
14989
|
+
},
|
|
14990
|
+
required: ["symbol"]
|
|
14991
|
+
},
|
|
14992
|
+
async execute(input, ctx) {
|
|
14993
|
+
const state = getIndexState();
|
|
14994
|
+
if (state.indexing && !state.ready) {
|
|
14995
|
+
return {
|
|
14996
|
+
symbol: input.symbol,
|
|
14997
|
+
calls: [],
|
|
14998
|
+
total: 0,
|
|
14999
|
+
indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
|
|
15000
|
+
};
|
|
15001
|
+
}
|
|
15002
|
+
if (state.lastError) {
|
|
15003
|
+
const circuit = state.circuit;
|
|
15004
|
+
const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s).` : "Try /codebase-reindex.";
|
|
15005
|
+
return {
|
|
15006
|
+
symbol: input.symbol,
|
|
15007
|
+
calls: [],
|
|
15008
|
+
total: 0,
|
|
15009
|
+
indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
|
|
15010
|
+
};
|
|
15011
|
+
}
|
|
15012
|
+
const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
|
|
15013
|
+
const { calls, symbolFound, unresolvedCount, totalMatches } = await outgoingCallsService2(
|
|
15014
|
+
{
|
|
15015
|
+
projectRoot: ctx.projectRoot,
|
|
15016
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
15017
|
+
symbol: input.symbol,
|
|
15018
|
+
file: input.file,
|
|
15019
|
+
limit
|
|
15020
|
+
}
|
|
15021
|
+
);
|
|
15022
|
+
if (!symbolFound) {
|
|
15023
|
+
let hasPersistedIndex = state.ready;
|
|
15024
|
+
if (!hasPersistedIndex) {
|
|
15025
|
+
try {
|
|
15026
|
+
const stats = await codebaseIndexStats({
|
|
15027
|
+
projectRoot: ctx.projectRoot,
|
|
15028
|
+
indexDir: codebaseIndexDirOverride(ctx)
|
|
15029
|
+
});
|
|
15030
|
+
hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
|
|
15031
|
+
} catch {
|
|
15032
|
+
}
|
|
15033
|
+
}
|
|
15034
|
+
if (!hasPersistedIndex) {
|
|
15035
|
+
return {
|
|
15036
|
+
symbol: input.symbol,
|
|
15037
|
+
calls: [],
|
|
15038
|
+
total: 0,
|
|
15039
|
+
indexStatus: "No persisted index data found. Run codebase-index to build it."
|
|
15040
|
+
};
|
|
15041
|
+
}
|
|
15042
|
+
return {
|
|
15043
|
+
symbol: input.symbol,
|
|
15044
|
+
calls: [],
|
|
15045
|
+
total: 0,
|
|
15046
|
+
note: `Symbol "${input.symbol}" not found in the index. Use codebase-search to verify the name.`
|
|
15047
|
+
};
|
|
15048
|
+
}
|
|
15049
|
+
const notes = [];
|
|
15050
|
+
if (totalMatches > limit) {
|
|
15051
|
+
notes.push(`Results capped at ${limit} of ${totalMatches} call sites. Increase \`limit\` or use \`file\` to narrow.`);
|
|
15052
|
+
}
|
|
15053
|
+
if (unresolvedCount > 0) {
|
|
15054
|
+
notes.push(`${unresolvedCount} unresolved reference(s) not shown \u2014 their targets could not be resolved during indexing.`);
|
|
15055
|
+
}
|
|
15056
|
+
return {
|
|
15057
|
+
symbol: input.symbol,
|
|
15058
|
+
calls,
|
|
15059
|
+
total: calls.length,
|
|
15060
|
+
...notes.length > 0 ? { note: notes.join(" ") } : {}
|
|
15061
|
+
};
|
|
15062
|
+
}
|
|
15063
|
+
};
|
|
15064
|
+
|
|
13528
15065
|
// src/codebase-index/codebase-search-tool.ts
|
|
13529
15066
|
var codebaseSearchTool = {
|
|
13530
15067
|
name: "codebase-search",
|
|
@@ -14245,17 +15782,17 @@ import {
|
|
|
14245
15782
|
} from "@wrongstack/core/design";
|
|
14246
15783
|
async function resolveReal(p) {
|
|
14247
15784
|
const resolved = path25.resolve(p);
|
|
14248
|
-
let
|
|
15785
|
+
let probe = resolved;
|
|
14249
15786
|
const missing = [];
|
|
14250
15787
|
for (; ; ) {
|
|
14251
15788
|
try {
|
|
14252
|
-
return path25.resolve(await fs20.realpath(
|
|
15789
|
+
return path25.resolve(await fs20.realpath(probe), ...missing);
|
|
14253
15790
|
} catch (err) {
|
|
14254
15791
|
if (err.code === "ENOENT") {
|
|
14255
|
-
const parent = path25.dirname(
|
|
14256
|
-
if (parent ===
|
|
14257
|
-
missing.unshift(path25.basename(
|
|
14258
|
-
|
|
15792
|
+
const parent = path25.dirname(probe);
|
|
15793
|
+
if (parent === probe) return resolved;
|
|
15794
|
+
missing.unshift(path25.basename(probe));
|
|
15795
|
+
probe = parent;
|
|
14259
15796
|
continue;
|
|
14260
15797
|
}
|
|
14261
15798
|
return resolved;
|
|
@@ -14530,7 +16067,7 @@ Replace off-palette colors with kit tokens (or the materialized CSS vars / token
|
|
|
14530
16067
|
|
|
14531
16068
|
// src/diff.ts
|
|
14532
16069
|
init_util();
|
|
14533
|
-
import { spawn as
|
|
16070
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
14534
16071
|
import { statSync as statSync3 } from "node:fs";
|
|
14535
16072
|
import * as fs21 from "node:fs/promises";
|
|
14536
16073
|
import * as path26 from "node:path";
|
|
@@ -14633,7 +16170,7 @@ function runGit(args, cwd, signal) {
|
|
|
14633
16170
|
return new Promise((resolve16) => {
|
|
14634
16171
|
let stdout = "";
|
|
14635
16172
|
let stderr = "";
|
|
14636
|
-
const child =
|
|
16173
|
+
const child = spawn7("git", args, {
|
|
14637
16174
|
cwd,
|
|
14638
16175
|
signal,
|
|
14639
16176
|
env: buildChildEnv3(),
|
|
@@ -14844,7 +16381,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
|
|
|
14844
16381
|
|
|
14845
16382
|
// src/e2e.ts
|
|
14846
16383
|
init_util();
|
|
14847
|
-
import { open, readdir as
|
|
16384
|
+
import { open, readdir as readdir7 } from "node:fs/promises";
|
|
14848
16385
|
import * as path27 from "node:path";
|
|
14849
16386
|
async function readBoundedText(filePath, maxBytes) {
|
|
14850
16387
|
let handle;
|
|
@@ -14962,7 +16499,7 @@ async function scanWorkspace(root, maxDepth, signal) {
|
|
|
14962
16499
|
}
|
|
14963
16500
|
let entries;
|
|
14964
16501
|
try {
|
|
14965
|
-
entries = await
|
|
16502
|
+
entries = await readdir7(current.directory, { withFileTypes: true });
|
|
14966
16503
|
} catch {
|
|
14967
16504
|
continue;
|
|
14968
16505
|
}
|
|
@@ -15021,7 +16558,7 @@ async function detectPackageManager3(projectRoot, scanRoot, declared) {
|
|
|
15021
16558
|
while (true) {
|
|
15022
16559
|
const names = /* @__PURE__ */ new Set();
|
|
15023
16560
|
try {
|
|
15024
|
-
for (const entry of await
|
|
16561
|
+
for (const entry of await readdir7(directory)) names.add(entry);
|
|
15025
16562
|
} catch {
|
|
15026
16563
|
}
|
|
15027
16564
|
if (names.has("pnpm-lock.yaml")) return "pnpm";
|
|
@@ -15089,7 +16626,7 @@ async function collectSpecs(root, framework, testDirectory, signal) {
|
|
|
15089
16626
|
if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };
|
|
15090
16627
|
let entries;
|
|
15091
16628
|
try {
|
|
15092
|
-
entries = await
|
|
16629
|
+
entries = await readdir7(directory, { withFileTypes: true });
|
|
15093
16630
|
} catch {
|
|
15094
16631
|
continue;
|
|
15095
16632
|
}
|
|
@@ -15290,7 +16827,7 @@ function findLadderMatches(fileLf, oldLf) {
|
|
|
15290
16827
|
const exact = [];
|
|
15291
16828
|
let idx = fileLf.indexOf(oldLf);
|
|
15292
16829
|
while (idx !== -1) {
|
|
15293
|
-
exact.push({ start: idx, end: idx + oldLf.length, startLine:
|
|
16830
|
+
exact.push({ start: idx, end: idx + oldLf.length, startLine: lineAt2(fileLf, idx) });
|
|
15294
16831
|
idx = fileLf.indexOf(oldLf, idx + 1);
|
|
15295
16832
|
}
|
|
15296
16833
|
if (exact.length > 0) return { tier: "exact", matches: exact };
|
|
@@ -15322,7 +16859,7 @@ function findLadderMatches(fileLf, oldLf) {
|
|
|
15322
16859
|
if (normalized.length > 0) return { tier: "whitespace-normalized", matches: normalized };
|
|
15323
16860
|
return fuzzyScan(fileLines, needleLines, offsets);
|
|
15324
16861
|
}
|
|
15325
|
-
function
|
|
16862
|
+
function lineAt2(text, pos) {
|
|
15326
16863
|
if (pos < 512) {
|
|
15327
16864
|
let line2 = 1;
|
|
15328
16865
|
for (let i = 0; i < pos; i++) {
|
|
@@ -15784,7 +17321,7 @@ Compare this against your old_string and retry with the file's actual text.` : "
|
|
|
15784
17321
|
};
|
|
15785
17322
|
|
|
15786
17323
|
// src/exec.ts
|
|
15787
|
-
import { spawn as
|
|
17324
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
15788
17325
|
import {
|
|
15789
17326
|
emitProcessCompleted as emitProcessCompleted3,
|
|
15790
17327
|
emitProcessOutput as emitProcessOutput3,
|
|
@@ -17001,6 +18538,26 @@ var BLOCKED_ARG_PATTERNS = {
|
|
|
17001
18538
|
pnpm: [],
|
|
17002
18539
|
npx: []
|
|
17003
18540
|
};
|
|
18541
|
+
var BLOCKED_OPTION_NAMES = {
|
|
18542
|
+
git: /* @__PURE__ */ new Set([
|
|
18543
|
+
"--exec",
|
|
18544
|
+
"--upload-pack",
|
|
18545
|
+
"--receive-pack",
|
|
18546
|
+
"--exec-path",
|
|
18547
|
+
"--git-dir",
|
|
18548
|
+
"--work-tree",
|
|
18549
|
+
"--namespace",
|
|
18550
|
+
"-c",
|
|
18551
|
+
"--config",
|
|
18552
|
+
"--config-env",
|
|
18553
|
+
"-C"
|
|
18554
|
+
]),
|
|
18555
|
+
find: /* @__PURE__ */ new Set(["-exec", "-ok", "-execdir"])
|
|
18556
|
+
};
|
|
18557
|
+
function optionName(arg) {
|
|
18558
|
+
const eq = arg.indexOf("=");
|
|
18559
|
+
return eq > 0 ? arg.slice(0, eq) : arg;
|
|
18560
|
+
}
|
|
17004
18561
|
var BLOCKED_SUBCOMMANDS = {
|
|
17005
18562
|
docker: /* @__PURE__ */ new Set(["push"]),
|
|
17006
18563
|
podman: /* @__PURE__ */ new Set(["push"]),
|
|
@@ -17038,6 +18595,15 @@ function validateArgs(cmd, args) {
|
|
|
17038
18595
|
const blocked2 = blockedSequences.find((seq) => seq.every((part, idx) => actual[idx] === part));
|
|
17039
18596
|
if (blocked2) return `Blocked subcommand "${blocked2.join(" ")}" for command "${cmd}"`;
|
|
17040
18597
|
}
|
|
18598
|
+
const blockedOptions = BLOCKED_OPTION_NAMES[cmd];
|
|
18599
|
+
if (blockedOptions) {
|
|
18600
|
+
for (const arg of args) {
|
|
18601
|
+
if (arg === "--") break;
|
|
18602
|
+
if (blockedOptions.has(optionName(arg))) {
|
|
18603
|
+
return `Blocked option "${optionName(arg)}" for command "${cmd}"`;
|
|
18604
|
+
}
|
|
18605
|
+
}
|
|
18606
|
+
}
|
|
17041
18607
|
const blocked = BLOCKED_ARG_PATTERNS[cmd];
|
|
17042
18608
|
if (!blocked) return null;
|
|
17043
18609
|
for (const arg of args) {
|
|
@@ -17220,7 +18786,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
17220
18786
|
};
|
|
17221
18787
|
let child;
|
|
17222
18788
|
try {
|
|
17223
|
-
child =
|
|
18789
|
+
child = spawn8(spawnCmd, spawnArgs, {
|
|
17224
18790
|
cwd,
|
|
17225
18791
|
env: buildChildEnv2(sessionId),
|
|
17226
18792
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -17854,7 +19420,7 @@ async function detectFixer(cwd) {
|
|
|
17854
19420
|
|
|
17855
19421
|
// src/git.ts
|
|
17856
19422
|
init_util();
|
|
17857
|
-
import { spawn as
|
|
19423
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
17858
19424
|
import { statSync as statSync4 } from "node:fs";
|
|
17859
19425
|
import { dirname as dirname14, resolve as resolve13, sep as sep6 } from "node:path";
|
|
17860
19426
|
import { assessCommitSafety } from "@wrongstack/core/coordination";
|
|
@@ -18118,7 +19684,7 @@ function runGit2(args, cwd, signal) {
|
|
|
18118
19684
|
return new Promise((resolve16) => {
|
|
18119
19685
|
let stdout = "";
|
|
18120
19686
|
let stderr = "";
|
|
18121
|
-
const child =
|
|
19687
|
+
const child = spawn9("git", args, {
|
|
18122
19688
|
cwd,
|
|
18123
19689
|
signal,
|
|
18124
19690
|
env: buildChildEnv4(),
|
|
@@ -18309,7 +19875,7 @@ var globTool = {
|
|
|
18309
19875
|
};
|
|
18310
19876
|
|
|
18311
19877
|
// src/grep.ts
|
|
18312
|
-
import { spawn as
|
|
19878
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
18313
19879
|
import * as fs25 from "node:fs/promises";
|
|
18314
19880
|
import * as path31 from "node:path";
|
|
18315
19881
|
import { ToolValidationError as ToolValidationError4 } from "@wrongstack/core/types";
|
|
@@ -18466,7 +20032,7 @@ var grepTool = {
|
|
|
18466
20032
|
async function detectRg(signal) {
|
|
18467
20033
|
return new Promise((resolve16) => {
|
|
18468
20034
|
try {
|
|
18469
|
-
const p =
|
|
20035
|
+
const p = spawn10("rg", ["--version"], { env: buildChildEnv5(), stdio: "ignore", signal, windowsHide: true });
|
|
18470
20036
|
p.on("error", () => resolve16(false));
|
|
18471
20037
|
p.on("close", (code) => resolve16(code === 0));
|
|
18472
20038
|
} catch {
|
|
@@ -18500,7 +20066,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
18500
20066
|
const FLUSH_AT = 16;
|
|
18501
20067
|
const MAX_BUF_BYTES = 1e6;
|
|
18502
20068
|
let bufOverflow = false;
|
|
18503
|
-
const child =
|
|
20069
|
+
const child = spawn10("rg", args, {
|
|
18504
20070
|
signal,
|
|
18505
20071
|
env: buildChildEnv5(),
|
|
18506
20072
|
// rg diagnostics are not part of the tool result. Ignoring stderr avoids
|
|
@@ -18797,7 +20363,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
18797
20363
|
init_spawn_stream();
|
|
18798
20364
|
init_util();
|
|
18799
20365
|
init_legacy_bridge();
|
|
18800
|
-
import { join as
|
|
20366
|
+
import { join as join24 } from "node:path";
|
|
18801
20367
|
import {
|
|
18802
20368
|
detectEcosystem as detectPackageEcosystem,
|
|
18803
20369
|
recordPackageAction
|
|
@@ -18981,17 +20547,17 @@ function resolveManifestPath(cwd, pkgManager) {
|
|
|
18981
20547
|
case "pnpm":
|
|
18982
20548
|
case "yarn":
|
|
18983
20549
|
case "npm":
|
|
18984
|
-
return
|
|
20550
|
+
return join24(cwd, "package.json");
|
|
18985
20551
|
/* v8 ignore next 2 -- pkgManager is always pnpm/yarn/npm; the default is defensive. */
|
|
18986
20552
|
default:
|
|
18987
|
-
return
|
|
20553
|
+
return join24(cwd, "package.json");
|
|
18988
20554
|
}
|
|
18989
20555
|
}
|
|
18990
20556
|
|
|
18991
20557
|
// src/json.ts
|
|
18992
|
-
init_util();
|
|
18993
20558
|
import * as fs26 from "node:fs/promises";
|
|
18994
20559
|
import { deepMerge as deepMergeCore } from "@wrongstack/core/utils";
|
|
20560
|
+
init_util();
|
|
18995
20561
|
var MAX_JSON_FILE_BYTES = 16 * 1024 * 1024;
|
|
18996
20562
|
var MAX_JSON_FILE_BYTES_HUMAN = "16 MiB";
|
|
18997
20563
|
var JsonFileTooLargeError = class extends Error {
|
|
@@ -19430,8 +20996,12 @@ function validateJsonSchema(data, schema) {
|
|
|
19430
20996
|
}
|
|
19431
20997
|
}
|
|
19432
20998
|
if (typeof value === "string" && s["pattern"]) {
|
|
19433
|
-
const
|
|
19434
|
-
if (!
|
|
20999
|
+
const compiled = compileUserRegex(s["pattern"], "");
|
|
21000
|
+
if (!compiled.ok) {
|
|
21001
|
+
errors.push(`${path38}: invalid schema pattern \u2014 ${compiled.reason}`);
|
|
21002
|
+
} else if (!compiled.regex.test(capSubject(value))) {
|
|
21003
|
+
errors.push(`${path38}: does not match pattern ${s["pattern"]}`);
|
|
21004
|
+
}
|
|
19435
21005
|
}
|
|
19436
21006
|
if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
|
|
19437
21007
|
errors.push(`${path38}: string too short (min ${s["minLength"]})`);
|
|
@@ -21416,7 +22986,7 @@ async function detectLinter(cwd) {
|
|
|
21416
22986
|
}
|
|
21417
22987
|
|
|
21418
22988
|
// src/logs.ts
|
|
21419
|
-
import { spawn as
|
|
22989
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
21420
22990
|
import { buildChildEnv as buildChildEnv6 } from "@wrongstack/core/utils";
|
|
21421
22991
|
init_util();
|
|
21422
22992
|
var logsTool = {
|
|
@@ -21523,7 +23093,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
21523
23093
|
clearTimeout(timer);
|
|
21524
23094
|
resolve16(result);
|
|
21525
23095
|
};
|
|
21526
|
-
const child =
|
|
23096
|
+
const child = spawn11("docker", args, { cwd, signal, env: buildChildEnv6(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
21527
23097
|
const timer = setTimeout(() => {
|
|
21528
23098
|
child.kill("SIGTERM");
|
|
21529
23099
|
finish(empty());
|
|
@@ -21632,7 +23202,7 @@ function parseLine(line) {
|
|
|
21632
23202
|
// src/outdated.ts
|
|
21633
23203
|
init_util();
|
|
21634
23204
|
init_win32_resolve();
|
|
21635
|
-
import { spawn as
|
|
23205
|
+
import { spawn as spawn12 } from "node:child_process";
|
|
21636
23206
|
import { buildChildEnv as buildChildEnv7 } from "@wrongstack/core/utils";
|
|
21637
23207
|
var outdatedTool = {
|
|
21638
23208
|
name: "outdated",
|
|
@@ -21752,7 +23322,7 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
21752
23322
|
const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
|
|
21753
23323
|
const spawnCmd = shim?.command ?? resolved;
|
|
21754
23324
|
const spawnArgs = shim?.args ?? args;
|
|
21755
|
-
const child =
|
|
23325
|
+
const child = spawn12(spawnCmd, spawnArgs, {
|
|
21756
23326
|
cwd,
|
|
21757
23327
|
signal,
|
|
21758
23328
|
env: buildChildEnv7(),
|
|
@@ -21818,7 +23388,7 @@ function parseOutdatedOutput(json2, exitCode) {
|
|
|
21818
23388
|
|
|
21819
23389
|
// src/patch.ts
|
|
21820
23390
|
init_util();
|
|
21821
|
-
import { spawn as
|
|
23391
|
+
import { spawn as spawn13 } from "node:child_process";
|
|
21822
23392
|
import * as fs27 from "node:fs/promises";
|
|
21823
23393
|
import * as os9 from "node:os";
|
|
21824
23394
|
import * as path32 from "node:path";
|
|
@@ -21960,7 +23530,7 @@ function runPatch(args, cwd, signal) {
|
|
|
21960
23530
|
let stdout = "";
|
|
21961
23531
|
let stderr = "";
|
|
21962
23532
|
const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
|
|
21963
|
-
const child =
|
|
23533
|
+
const child = spawn13("patch", args, {
|
|
21964
23534
|
cwd,
|
|
21965
23535
|
signal,
|
|
21966
23536
|
env,
|
|
@@ -22557,7 +24127,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
|
|
|
22557
24127
|
}
|
|
22558
24128
|
|
|
22559
24129
|
// src/replace.ts
|
|
22560
|
-
import { spawn as
|
|
24130
|
+
import { spawn as spawn14 } from "node:child_process";
|
|
22561
24131
|
import * as fs29 from "node:fs/promises";
|
|
22562
24132
|
import * as path33 from "node:path";
|
|
22563
24133
|
import { ToolValidationError as ToolValidationError6 } from "@wrongstack/core/types";
|
|
@@ -22742,7 +24312,7 @@ async function globFiles(pattern, base, extraGlob) {
|
|
|
22742
24312
|
function checkRg() {
|
|
22743
24313
|
return new Promise((resolve16) => {
|
|
22744
24314
|
try {
|
|
22745
|
-
const p =
|
|
24315
|
+
const p = spawn14("rg", ["--version"], {
|
|
22746
24316
|
env: buildChildEnv9(),
|
|
22747
24317
|
stdio: "ignore",
|
|
22748
24318
|
windowsHide: true
|
|
@@ -22756,7 +24326,7 @@ function checkRg() {
|
|
|
22756
24326
|
}
|
|
22757
24327
|
function spawnRgFind(pattern, base) {
|
|
22758
24328
|
const args = ["--files", "--glob", pattern, base];
|
|
22759
|
-
const child =
|
|
24329
|
+
const child = spawn14("rg", args, {
|
|
22760
24330
|
signal: AbortSignal.timeout(3e4),
|
|
22761
24331
|
env: buildChildEnv9(),
|
|
22762
24332
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -24795,7 +26365,7 @@ var writeTool = {
|
|
|
24795
26365
|
required: ["path", "content"]
|
|
24796
26366
|
},
|
|
24797
26367
|
async execute(input, ctx, opts) {
|
|
24798
|
-
return
|
|
26368
|
+
return writeFile6(input, ctx, opts?.signal);
|
|
24799
26369
|
},
|
|
24800
26370
|
async *executeStream(input, ctx, opts) {
|
|
24801
26371
|
const prepared = await prepareWrite(input, ctx);
|
|
@@ -24808,7 +26378,7 @@ var writeTool = {
|
|
|
24808
26378
|
yield { type: "final", output: await finishWrite(input, ctx, prepared, opts?.signal) };
|
|
24809
26379
|
}
|
|
24810
26380
|
};
|
|
24811
|
-
async function
|
|
26381
|
+
async function writeFile6(input, ctx, signal) {
|
|
24812
26382
|
return finishWrite(input, ctx, await prepareWrite(input, ctx), signal);
|
|
24813
26383
|
}
|
|
24814
26384
|
async function prepareWrite(input, ctx) {
|
|
@@ -24896,6 +26466,8 @@ var TIER1_TOOLS = [
|
|
|
24896
26466
|
editTool,
|
|
24897
26467
|
codebaseStatsTool,
|
|
24898
26468
|
codebaseSearchTool,
|
|
26469
|
+
codebaseIncomingCallsTool,
|
|
26470
|
+
codebaseOutgoingCallsTool,
|
|
24899
26471
|
codebaseIndexTool,
|
|
24900
26472
|
bashTool,
|
|
24901
26473
|
grepTool,
|
|
@@ -24946,6 +26518,8 @@ var builtinTools = [
|
|
|
24946
26518
|
editTool,
|
|
24947
26519
|
codebaseStatsTool,
|
|
24948
26520
|
codebaseSearchTool,
|
|
26521
|
+
codebaseIncomingCallsTool,
|
|
26522
|
+
codebaseOutgoingCallsTool,
|
|
24949
26523
|
codebaseIndexTool,
|
|
24950
26524
|
deadCodeScanTool,
|
|
24951
26525
|
replaceTool,
|