@maxgfr/codeindex 2.13.0 → 2.14.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/README.md +30 -10
- package/docs/MIGRATION.md +118 -36
- package/package.json +1 -1
- package/scripts/engine.d.mts +39 -4
- package/scripts/engine.mjs +1116 -664
- package/src/ast/grammars-pull.ts +178 -0
- package/src/ast/loader.ts +77 -15
- package/src/bm25.ts +16 -6
- package/src/callers.ts +10 -14
- package/src/complexity.ts +7 -1
- package/src/deadcode.ts +5 -4
- package/src/derived.ts +128 -0
- package/src/engine-cli.ts +288 -46
- package/src/engine.ts +20 -3
- package/src/hash.ts +5 -2
- package/src/mcp.ts +190 -23
- package/src/pipeline.ts +16 -5
- package/src/query.ts +8 -5
- package/src/scan.ts +66 -8
- package/src/types.ts +1 -1
- package/src/walk.ts +26 -9
package/scripts/engine.mjs
CHANGED
|
@@ -14,7 +14,7 @@ var ENGINE_VERSION, SCHEMA_VERSION, EXTRACTOR_VERSION;
|
|
|
14
14
|
var init_types = __esm({
|
|
15
15
|
"src/types.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
-
ENGINE_VERSION = "2.
|
|
17
|
+
ENGINE_VERSION = "2.14.0";
|
|
18
18
|
SCHEMA_VERSION = 4;
|
|
19
19
|
EXTRACTOR_VERSION = 10;
|
|
20
20
|
}
|
|
@@ -348,23 +348,26 @@ function walk(root, opts = {}) {
|
|
|
348
348
|
if (!contained(real)) continue;
|
|
349
349
|
let entries;
|
|
350
350
|
try {
|
|
351
|
-
entries = readdirSync(frame.dir).sort(
|
|
351
|
+
entries = readdirSync(frame.dir, { withFileTypes: true }).sort(
|
|
352
|
+
(a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
353
|
+
);
|
|
352
354
|
} catch {
|
|
353
355
|
continue;
|
|
354
356
|
}
|
|
355
357
|
let rules = frame.rules;
|
|
356
|
-
if (useGitignore && entries.
|
|
358
|
+
if (useGitignore && entries.some((e) => e.name === ".gitignore")) {
|
|
357
359
|
const parsed = parseGitignore(readText(join(frame.dir, ".gitignore")), frame.rel);
|
|
358
360
|
if (parsed.length) rules = [...rules, ...parsed];
|
|
359
361
|
}
|
|
360
|
-
for (const
|
|
362
|
+
for (const entry of entries) {
|
|
363
|
+
const name2 = entry.name;
|
|
361
364
|
const abs = join(frame.dir, name2);
|
|
362
365
|
const rel = frame.rel ? `${frame.rel}/${name2}` : name2;
|
|
366
|
+
const isLink = entry.isSymbolicLink();
|
|
367
|
+
if (entry.isDirectory() && ignoreDirs.has(name2)) continue;
|
|
363
368
|
let st;
|
|
364
|
-
let isLink;
|
|
365
369
|
try {
|
|
366
|
-
st = statSync(abs);
|
|
367
|
-
isLink = lstatSync(abs).isSymbolicLink();
|
|
370
|
+
st = isLink ? statSync(abs) : lstatSync(abs);
|
|
368
371
|
} catch {
|
|
369
372
|
continue;
|
|
370
373
|
}
|
|
@@ -1772,13 +1775,13 @@ async function Module2(moduleArg = {}) {
|
|
|
1772
1775
|
}
|
|
1773
1776
|
readAsync = /* @__PURE__ */ __name(async (url) => {
|
|
1774
1777
|
if (isFileURI(url)) {
|
|
1775
|
-
return new Promise((
|
|
1778
|
+
return new Promise((resolve3, reject) => {
|
|
1776
1779
|
var xhr = new XMLHttpRequest();
|
|
1777
1780
|
xhr.open("GET", url, true);
|
|
1778
1781
|
xhr.responseType = "arraybuffer";
|
|
1779
1782
|
xhr.onload = () => {
|
|
1780
1783
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
1781
|
-
|
|
1784
|
+
resolve3(xhr.response);
|
|
1782
1785
|
return;
|
|
1783
1786
|
}
|
|
1784
1787
|
reject(xhr.status);
|
|
@@ -1974,9 +1977,9 @@ async function Module2(moduleArg = {}) {
|
|
|
1974
1977
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
1975
1978
|
var info2 = getWasmImports();
|
|
1976
1979
|
if (Module["instantiateWasm"]) {
|
|
1977
|
-
return new Promise((
|
|
1980
|
+
return new Promise((resolve3, reject) => {
|
|
1978
1981
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
1979
|
-
|
|
1982
|
+
resolve3(receiveInstance(mod, inst));
|
|
1980
1983
|
});
|
|
1981
1984
|
});
|
|
1982
1985
|
}
|
|
@@ -3307,8 +3310,8 @@ async function Module2(moduleArg = {}) {
|
|
|
3307
3310
|
if (runtimeInitialized) {
|
|
3308
3311
|
moduleRtn = Module;
|
|
3309
3312
|
} else {
|
|
3310
|
-
moduleRtn = new Promise((
|
|
3311
|
-
readyPromiseResolve =
|
|
3313
|
+
moduleRtn = new Promise((resolve3, reject) => {
|
|
3314
|
+
readyPromiseResolve = resolve3;
|
|
3312
3315
|
readyPromiseReject = reject;
|
|
3313
3316
|
});
|
|
3314
3317
|
}
|
|
@@ -5539,27 +5542,41 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
5539
5542
|
|
|
5540
5543
|
// src/ast/loader.ts
|
|
5541
5544
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
5545
|
+
import { homedir } from "os";
|
|
5542
5546
|
import { dirname, join as join2 } from "path";
|
|
5543
5547
|
import { fileURLToPath } from "url";
|
|
5544
5548
|
function grammarKeyForExt(ext) {
|
|
5545
5549
|
return EXT_GRAMMAR[ext];
|
|
5546
5550
|
}
|
|
5547
|
-
function
|
|
5548
|
-
const
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5551
|
+
function sharedGrammarsCacheDir() {
|
|
5552
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
5553
|
+
const base = xdg && xdg.trim() ? xdg.trim() : join2(homedir(), ".cache");
|
|
5554
|
+
return join2(base, "codeindex", "grammars", ENGINE_VERSION);
|
|
5555
|
+
}
|
|
5556
|
+
function resolveGrammarsTier(opts = {}) {
|
|
5557
|
+
const cacheDir = sharedGrammarsCacheDir();
|
|
5558
|
+
const legacy = process.env.CODEINDEX_GRAMMAR_DIR ?? process.env.ULTRAINDEX_GRAMMAR_DIR;
|
|
5559
|
+
if (legacy && legacy.trim() && existsSync(legacy)) return { tier: "env", dir: legacy, cacheDir };
|
|
5560
|
+
const here = opts.moduleDir ?? dirname(fileURLToPath(import.meta.url));
|
|
5561
|
+
const adjacent = [
|
|
5552
5562
|
join2(here, "grammars"),
|
|
5553
5563
|
// bundle: <...>/scripts/grammars
|
|
5554
5564
|
join2(here, "..", "..", "scripts", "grammars"),
|
|
5555
5565
|
// dev: src/ast → <repo>/scripts/grammars
|
|
5556
5566
|
join2(here, "..", "scripts", "grammars")
|
|
5557
5567
|
];
|
|
5558
|
-
for (const c2 of
|
|
5559
|
-
|
|
5568
|
+
for (const c2 of adjacent) if (existsSync(c2)) return { tier: "adjacent", dir: c2, cacheDir };
|
|
5569
|
+
const env = process.env.CODEINDEX_GRAMMARS_DIR;
|
|
5570
|
+
if (env && env.trim() && existsSync(env)) return { tier: "env", dir: env, cacheDir };
|
|
5571
|
+
if (existsSync(cacheDir)) return { tier: "cache", dir: cacheDir, cacheDir };
|
|
5572
|
+
return { tier: "none", cacheDir };
|
|
5573
|
+
}
|
|
5574
|
+
function resolveGrammarsDir(opts) {
|
|
5575
|
+
return resolveGrammarsTier(opts).dir;
|
|
5560
5576
|
}
|
|
5561
5577
|
async function ensureGrammars(keys) {
|
|
5562
|
-
const dir =
|
|
5578
|
+
const dir = resolveGrammarsDir();
|
|
5579
|
+
if (!dir) return;
|
|
5563
5580
|
if (!runtimeReady) {
|
|
5564
5581
|
const runtime = join2(dir, "web-tree-sitter.wasm");
|
|
5565
5582
|
if (!existsSync(runtime)) return;
|
|
@@ -5584,6 +5601,14 @@ async function ensureGrammars(keys) {
|
|
|
5584
5601
|
function allGrammarKeys() {
|
|
5585
5602
|
return [...new Set(Object.values(EXT_GRAMMAR))];
|
|
5586
5603
|
}
|
|
5604
|
+
function grammarKeysForExts(exts) {
|
|
5605
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5606
|
+
for (const ext of exts) {
|
|
5607
|
+
const key = EXT_GRAMMAR[ext];
|
|
5608
|
+
if (key !== void 0) keys.add(key);
|
|
5609
|
+
}
|
|
5610
|
+
return [...keys].sort();
|
|
5611
|
+
}
|
|
5587
5612
|
function grammarReady(key) {
|
|
5588
5613
|
return loaded.has(key);
|
|
5589
5614
|
}
|
|
@@ -5598,6 +5623,7 @@ var init_loader = __esm({
|
|
|
5598
5623
|
"src/ast/loader.ts"() {
|
|
5599
5624
|
"use strict";
|
|
5600
5625
|
init_web_tree_sitter();
|
|
5626
|
+
init_types();
|
|
5601
5627
|
EXT_GRAMMAR = {
|
|
5602
5628
|
".ts": "typescript",
|
|
5603
5629
|
".mts": "typescript",
|
|
@@ -6523,7 +6549,7 @@ function scanRepo(root, opts = {}) {
|
|
|
6523
6549
|
const scoped = opts.scope ? [...opts.include ?? [], `${opts.scope.replace(/\/+$/, "")}/**`] : opts.include;
|
|
6524
6550
|
const include = compileGlobs(scoped);
|
|
6525
6551
|
const exclude = compileGlobs(opts.exclude);
|
|
6526
|
-
const { files: walked, capped, excluded } = walk(root, {
|
|
6552
|
+
const { files: walked, capped, excluded } = opts.precomputedWalk ?? walk(root, {
|
|
6527
6553
|
maxFileBytes: opts.maxBytes,
|
|
6528
6554
|
maxFiles: opts.maxFiles,
|
|
6529
6555
|
gitignore: opts.gitignore,
|
|
@@ -6534,6 +6560,9 @@ function scanRepo(root, opts = {}) {
|
|
|
6534
6560
|
const languages = {};
|
|
6535
6561
|
const docText = /* @__PURE__ */ new Map();
|
|
6536
6562
|
const mtimes = /* @__PURE__ */ new Map();
|
|
6563
|
+
const cache = opts.cache;
|
|
6564
|
+
let allReused = cache !== void 0;
|
|
6565
|
+
let cacheDirty = cache === void 0;
|
|
6537
6566
|
for (const f of walked) {
|
|
6538
6567
|
if (outPrefix && (f.abs === opts.out || f.abs.startsWith(outPrefix))) continue;
|
|
6539
6568
|
if (include && !include(f.rel)) continue;
|
|
@@ -6552,8 +6581,11 @@ function scanRepo(root, opts = {}) {
|
|
|
6552
6581
|
if (cached && cached.hash === hash) {
|
|
6553
6582
|
files.push(cached.record);
|
|
6554
6583
|
if (kind === "doc" && content) docText.set(f.rel, content);
|
|
6584
|
+
if (cached.size !== f.size || cached.mtimeMs !== f.mtimeMs) cacheDirty = true;
|
|
6555
6585
|
continue;
|
|
6556
6586
|
}
|
|
6587
|
+
allReused = false;
|
|
6588
|
+
cacheDirty = true;
|
|
6557
6589
|
const record = {
|
|
6558
6590
|
rel: f.rel,
|
|
6559
6591
|
ext: f.ext,
|
|
@@ -6595,7 +6627,22 @@ function scanRepo(root, opts = {}) {
|
|
|
6595
6627
|
files.push(record);
|
|
6596
6628
|
}
|
|
6597
6629
|
files.sort(byKey((f) => f.rel));
|
|
6598
|
-
|
|
6630
|
+
if (cache !== void 0 && files.length !== cache.size) {
|
|
6631
|
+
allReused = false;
|
|
6632
|
+
cacheDirty = true;
|
|
6633
|
+
}
|
|
6634
|
+
return {
|
|
6635
|
+
root,
|
|
6636
|
+
commit: headCommit(root),
|
|
6637
|
+
files,
|
|
6638
|
+
languages,
|
|
6639
|
+
docText,
|
|
6640
|
+
mtimes,
|
|
6641
|
+
capped,
|
|
6642
|
+
excluded,
|
|
6643
|
+
contentUnchanged: allReused,
|
|
6644
|
+
cacheDirty
|
|
6645
|
+
};
|
|
6599
6646
|
}
|
|
6600
6647
|
var init_scan = __esm({
|
|
6601
6648
|
"src/scan.ts"() {
|
|
@@ -6614,7 +6661,7 @@ var init_scan = __esm({
|
|
|
6614
6661
|
|
|
6615
6662
|
// src/resolve.ts
|
|
6616
6663
|
import { posix } from "path";
|
|
6617
|
-
import { join as
|
|
6664
|
+
import { join as join4 } from "path";
|
|
6618
6665
|
function distToSrcCandidates(target) {
|
|
6619
6666
|
const segs = norm(target).split("/").filter((s) => s !== ".");
|
|
6620
6667
|
const out2 = [];
|
|
@@ -6701,7 +6748,7 @@ function resolveExtends(fileSet, fromDir, ext) {
|
|
|
6701
6748
|
function readTsConfig(root, fileSet, rel, warnings, seen) {
|
|
6702
6749
|
if (seen.has(rel)) return void 0;
|
|
6703
6750
|
seen.add(rel);
|
|
6704
|
-
const cfg = tolerantJsonParse(readText(
|
|
6751
|
+
const cfg = tolerantJsonParse(readText(join4(root, rel)));
|
|
6705
6752
|
if (cfg === void 0) {
|
|
6706
6753
|
warnings.push(`unparseable ${rel} \u2014 its path aliases were ignored`);
|
|
6707
6754
|
return void 0;
|
|
@@ -6830,7 +6877,7 @@ function buildResolveContext(scan2) {
|
|
|
6830
6877
|
const goModules = [];
|
|
6831
6878
|
for (const rel of fileSet) {
|
|
6832
6879
|
if (rel !== "go.mod" && !rel.endsWith("/go.mod")) continue;
|
|
6833
|
-
const text = readText(
|
|
6880
|
+
const text = readText(join4(scan2.root, rel));
|
|
6834
6881
|
const m = /^\s*module\s+(\S+)/m.exec(text);
|
|
6835
6882
|
if (!m) continue;
|
|
6836
6883
|
const dir = rel.includes("/") ? posix.dirname(rel) : "";
|
|
@@ -6840,7 +6887,7 @@ function buildResolveContext(scan2) {
|
|
|
6840
6887
|
const rustCrates = [];
|
|
6841
6888
|
for (const rel of fileSet) {
|
|
6842
6889
|
if (rel !== "Cargo.toml" && !rel.endsWith("/Cargo.toml")) continue;
|
|
6843
|
-
const text = readText(
|
|
6890
|
+
const text = readText(join4(scan2.root, rel));
|
|
6844
6891
|
const m = /\[package\][^[]*?^\s*name\s*=\s*"([^"]+)"/ms.exec(text);
|
|
6845
6892
|
if (!m) continue;
|
|
6846
6893
|
const dir = rel.includes("/") ? posix.dirname(rel) : "";
|
|
@@ -6867,7 +6914,7 @@ function buildResolveContext(scan2) {
|
|
|
6867
6914
|
const workspacePackages = [];
|
|
6868
6915
|
for (const rel of fileSet) {
|
|
6869
6916
|
if (rel !== "package.json" && !rel.endsWith("/package.json")) continue;
|
|
6870
|
-
const pkg = tolerantJsonParse(readText(
|
|
6917
|
+
const pkg = tolerantJsonParse(readText(join4(scan2.root, rel)));
|
|
6871
6918
|
if (pkg === void 0) {
|
|
6872
6919
|
warnings.push(`unparseable ${rel} \u2014 skipped for workspace resolution`);
|
|
6873
6920
|
continue;
|
|
@@ -6894,7 +6941,7 @@ function buildResolveContext(scan2) {
|
|
|
6894
6941
|
const phpPsr4 = [];
|
|
6895
6942
|
for (const rel of fileSet) {
|
|
6896
6943
|
if (rel !== "composer.json" && !rel.endsWith("/composer.json")) continue;
|
|
6897
|
-
const composer = tolerantJsonParse(readText(
|
|
6944
|
+
const composer = tolerantJsonParse(readText(join4(scan2.root, rel)));
|
|
6898
6945
|
if (!composer) {
|
|
6899
6946
|
warnings.push(`unparseable ${rel} \u2014 skipped for PHP PSR-4 resolution`);
|
|
6900
6947
|
continue;
|
|
@@ -7469,7 +7516,7 @@ var init_calls = __esm({
|
|
|
7469
7516
|
});
|
|
7470
7517
|
|
|
7471
7518
|
// src/graph.ts
|
|
7472
|
-
import { join as
|
|
7519
|
+
import { join as join5 } from "path";
|
|
7473
7520
|
function isDistinctive(name2) {
|
|
7474
7521
|
if (name2.length < 5) return false;
|
|
7475
7522
|
const internalUpper = /[a-z][A-Z]/.test(name2) || /[A-Z]{2}/.test(name2);
|
|
@@ -7548,7 +7595,7 @@ function buildGraph(scan2, ctx, modules, moduleOf, meta) {
|
|
|
7548
7595
|
if (unique.size) {
|
|
7549
7596
|
for (const f of scan2.files) {
|
|
7550
7597
|
if (f.kind !== "doc") continue;
|
|
7551
|
-
const content = scan2.docText.get(f.rel) ?? readText(
|
|
7598
|
+
const content = scan2.docText.get(f.rel) ?? readText(join5(scan2.root, f.rel));
|
|
7552
7599
|
if (!content) continue;
|
|
7553
7600
|
const tokens = /* @__PURE__ */ new Map();
|
|
7554
7601
|
for (const tok of content.split(/[^A-Za-z0-9_]+/)) {
|
|
@@ -7653,217 +7700,567 @@ var init_graph = __esm({
|
|
|
7653
7700
|
}
|
|
7654
7701
|
});
|
|
7655
7702
|
|
|
7656
|
-
// src/
|
|
7657
|
-
function
|
|
7658
|
-
const
|
|
7659
|
-
const
|
|
7703
|
+
// src/render/symbols-json.ts
|
|
7704
|
+
function computeSymbolRefs(scan2) {
|
|
7705
|
+
const unique = uniqueSymbolDefs(scan2);
|
|
7706
|
+
const refs = /* @__PURE__ */ new Map();
|
|
7707
|
+
if (!unique.size) return refs;
|
|
7708
|
+
const add = (name2, file) => {
|
|
7709
|
+
let set = refs.get(name2);
|
|
7710
|
+
if (!set) refs.set(name2, set = /* @__PURE__ */ new Set());
|
|
7711
|
+
set.add(file);
|
|
7712
|
+
};
|
|
7660
7713
|
for (const f of scan2.files) {
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7714
|
+
if (f.kind === "code" && f.idents) {
|
|
7715
|
+
for (const id of f.idents) {
|
|
7716
|
+
const target = unique.get(id);
|
|
7717
|
+
if (target && target !== f.rel) add(id, f.rel);
|
|
7718
|
+
}
|
|
7719
|
+
} else if (f.kind === "doc") {
|
|
7720
|
+
const content = scan2.docText.get(f.rel);
|
|
7721
|
+
if (!content) continue;
|
|
7722
|
+
for (const tok of content.split(/[^A-Za-z0-9_]+/)) {
|
|
7723
|
+
const target = unique.get(tok);
|
|
7724
|
+
if (target && target !== f.rel) add(tok, f.rel);
|
|
7725
|
+
}
|
|
7665
7726
|
}
|
|
7666
7727
|
}
|
|
7667
|
-
return
|
|
7728
|
+
return refs;
|
|
7668
7729
|
}
|
|
7669
|
-
function
|
|
7670
|
-
const
|
|
7671
|
-
const recall = opts.recall === true;
|
|
7672
|
-
const defs = /* @__PURE__ */ new Map();
|
|
7673
|
-
for (const f of scan2.files) {
|
|
7674
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7675
|
-
for (const s of f.symbols) {
|
|
7676
|
-
if (!s.exported || REFERENCE_KINDS3.has(s.kind)) continue;
|
|
7677
|
-
if (seen.has(s.name)) continue;
|
|
7678
|
-
seen.add(s.name);
|
|
7679
|
-
let arr = defs.get(s.name);
|
|
7680
|
-
if (!arr) defs.set(s.name, arr = []);
|
|
7681
|
-
arr.push(s);
|
|
7682
|
-
}
|
|
7683
|
-
}
|
|
7684
|
-
const localDefs = /* @__PURE__ */ new Map();
|
|
7730
|
+
function buildSymbolIndex(scan2, refs = /* @__PURE__ */ new Map()) {
|
|
7731
|
+
const defsByName = /* @__PURE__ */ new Map();
|
|
7685
7732
|
for (const f of scan2.files) {
|
|
7686
|
-
const byName = /* @__PURE__ */ new Map();
|
|
7687
7733
|
for (const s of f.symbols) {
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
if (!f.calls?.length) continue;
|
|
7700
|
-
const family = familyOf(f.lang);
|
|
7701
|
-
const own = localDefs.get(f.rel);
|
|
7702
|
-
for (const c2 of f.calls) {
|
|
7703
|
-
const local = own.get(c2.name);
|
|
7704
|
-
if (local) {
|
|
7705
|
-
if (local.line !== c2.line)
|
|
7706
|
-
record(local, recall ? { file: f.rel, line: c2.line, confidence: "corroborated" } : { file: f.rel, line: c2.line });
|
|
7707
|
-
continue;
|
|
7708
|
-
}
|
|
7709
|
-
const cands = (defs.get(c2.name) ?? []).filter((d) => familyOf(d.lang) === family && d.file !== f.rel).map((d) => ({ file: d.file, lang: d.lang }));
|
|
7710
|
-
if (!cands.length) continue;
|
|
7711
|
-
const imported = cands.filter((d) => pairs.has(`${f.rel}|${d.file}`));
|
|
7712
|
-
const chosen = family === "js" ? imported.length ? pickCandidate(f.rel, imported) : (
|
|
7713
|
-
// JS/TS gate: no corroborating import → no binding. Recall mode
|
|
7714
|
-
// relaxes this to a unique-repo-wide name match (issue #7).
|
|
7715
|
-
recall && cands.length === 1 ? cands[0] : void 0
|
|
7716
|
-
) : imported.length ? pickCandidate(f.rel, imported) : pickCandidate(f.rel, cands);
|
|
7717
|
-
if (!chosen) continue;
|
|
7718
|
-
const def = defs.get(c2.name).find((d) => d.file === chosen.file);
|
|
7719
|
-
record(
|
|
7720
|
-
def,
|
|
7721
|
-
recall ? { file: f.rel, line: c2.line, confidence: imported.length ? "corroborated" : "unique-name" } : { file: f.rel, line: c2.line }
|
|
7722
|
-
);
|
|
7734
|
+
let arr = defsByName.get(s.name);
|
|
7735
|
+
if (!arr) defsByName.set(s.name, arr = []);
|
|
7736
|
+
arr.push({
|
|
7737
|
+
file: s.file,
|
|
7738
|
+
line: s.line,
|
|
7739
|
+
...s.endLine !== void 0 ? { endLine: s.endLine } : {},
|
|
7740
|
+
kind: s.kind,
|
|
7741
|
+
exported: s.exported,
|
|
7742
|
+
lang: s.lang,
|
|
7743
|
+
...s.parent ? { parent: s.parent } : {}
|
|
7744
|
+
});
|
|
7723
7745
|
}
|
|
7724
7746
|
}
|
|
7725
|
-
const
|
|
7726
|
-
const
|
|
7727
|
-
|
|
7728
|
-
const { def, callers } = sites.get(key);
|
|
7729
|
-
callers.sort((a, b) => byStr(a.file, b.file) || a.line - b.line);
|
|
7730
|
-
if (!index.has(def.name)) index.set(def.name, { def, callers });
|
|
7731
|
-
else index.set(`${def.name}@${def.file}`, { def, callers });
|
|
7747
|
+
const defs = {};
|
|
7748
|
+
for (const name2 of [...defsByName.keys()].sort(byStr)) {
|
|
7749
|
+
defs[name2] = defsByName.get(name2).slice().sort((a, b) => byStr(a.file, b.file) || a.line - b.line || byStr(a.kind, b.kind));
|
|
7732
7750
|
}
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
if (!f?.symbols.length) return void 0;
|
|
7738
|
-
return enclosingAmong(f.symbols, line);
|
|
7739
|
-
}
|
|
7740
|
-
function enclosingAmong(symbols, line) {
|
|
7741
|
-
let best;
|
|
7742
|
-
for (const s of symbols) {
|
|
7743
|
-
if (REFERENCE_KINDS3.has(s.kind)) continue;
|
|
7744
|
-
if (s.line > line) continue;
|
|
7745
|
-
if (s.endLine !== void 0 && line > s.endLine) continue;
|
|
7746
|
-
if (!best || s.line > best.line || s.line === best.line && (s.endLine ?? Infinity) <= (best.endLine ?? Infinity)) {
|
|
7747
|
-
best = s;
|
|
7748
|
-
}
|
|
7751
|
+
const refsOut = {};
|
|
7752
|
+
for (const name2 of [...refs.keys()].sort(byStr)) {
|
|
7753
|
+
const files = [...refs.get(name2)].sort(byStr);
|
|
7754
|
+
if (files.length) refsOut[name2] = files;
|
|
7749
7755
|
}
|
|
7750
|
-
return
|
|
7756
|
+
return { schemaVersion: SCHEMA_VERSION, defs, refs: refsOut };
|
|
7751
7757
|
}
|
|
7752
|
-
function
|
|
7753
|
-
|
|
7754
|
-
for (const f of scan2.files) {
|
|
7755
|
-
if (!f.calls?.length) continue;
|
|
7756
|
-
const symbols = f.symbols.filter((s) => !REFERENCE_KINDS3.has(s.kind));
|
|
7757
|
-
for (const c2 of f.calls) {
|
|
7758
|
-
const site = { file: f.rel, line: c2.line };
|
|
7759
|
-
if (c2.receiver !== void 0) site.receiver = c2.receiver;
|
|
7760
|
-
const enc = enclosingAmong(symbols, c2.line);
|
|
7761
|
-
if (enc) site.enclosingSymbol = enc;
|
|
7762
|
-
let arr = byName.get(c2.name);
|
|
7763
|
-
if (!arr) byName.set(c2.name, arr = []);
|
|
7764
|
-
arr.push(site);
|
|
7765
|
-
}
|
|
7766
|
-
}
|
|
7767
|
-
const index = /* @__PURE__ */ new Map();
|
|
7768
|
-
for (const name2 of [...byName.keys()].sort(byStr)) {
|
|
7769
|
-
const sites = byName.get(name2);
|
|
7770
|
-
sites.sort((a, b) => byStr(a.file, b.file) || a.line - b.line);
|
|
7771
|
-
index.set(name2, sites);
|
|
7772
|
-
}
|
|
7773
|
-
return index;
|
|
7758
|
+
function renderSymbolsJson(index) {
|
|
7759
|
+
return JSON.stringify(index, null, 2) + "\n";
|
|
7774
7760
|
}
|
|
7775
|
-
var
|
|
7776
|
-
|
|
7777
|
-
"src/callers.ts"() {
|
|
7761
|
+
var init_symbols_json = __esm({
|
|
7762
|
+
"src/render/symbols-json.ts"() {
|
|
7778
7763
|
"use strict";
|
|
7779
|
-
|
|
7780
|
-
init_resolve();
|
|
7764
|
+
init_types();
|
|
7781
7765
|
init_sort();
|
|
7782
|
-
|
|
7766
|
+
init_graph();
|
|
7783
7767
|
}
|
|
7784
7768
|
});
|
|
7785
7769
|
|
|
7786
|
-
// src/
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
const f = scan2.files.find((x) => x.rel === rel);
|
|
7790
|
-
if (!f) return [];
|
|
7791
|
-
return [...f.symbols].filter((s) => !REFERENCE_KINDS4.has(s.kind)).sort((a, b) => a.line - b.line || byStr(a.name, b.name));
|
|
7792
|
-
}
|
|
7793
|
-
function findSymbol(scan2, namePath, opts = {}) {
|
|
7794
|
-
const segments = namePath.split("/").filter(Boolean);
|
|
7795
|
-
if (!segments.length) return [];
|
|
7796
|
-
const leaf = segments[segments.length - 1];
|
|
7797
|
-
const parents = segments.slice(0, -1);
|
|
7798
|
-
const matchName = (name2, wanted) => opts.substring ? name2.toLowerCase().includes(wanted.toLowerCase()) : name2 === wanted;
|
|
7770
|
+
// src/bm25.ts
|
|
7771
|
+
function subtokens(raw) {
|
|
7772
|
+
const folded = foldText(raw).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
|
|
7799
7773
|
const out2 = [];
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
(
|
|
7813
|
-
|
|
7814
|
-
const capped = out2.slice(0, opts.maxResults ?? 50);
|
|
7815
|
-
if (opts.includeBody) {
|
|
7816
|
-
for (const m of capped) {
|
|
7817
|
-
const end = m.endLine ?? m.line;
|
|
7818
|
-
const content = readText(join5(scan2.root, m.file));
|
|
7819
|
-
if (!content) continue;
|
|
7820
|
-
m.body = content.split("\n").slice(m.line - 1, end).join("\n");
|
|
7821
|
-
}
|
|
7774
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7775
|
+
const push = (t) => {
|
|
7776
|
+
if (t.length < 2 || seen.has(t)) return;
|
|
7777
|
+
seen.add(t);
|
|
7778
|
+
out2.push(t);
|
|
7779
|
+
};
|
|
7780
|
+
if (!/\s/.test(raw.trim())) push(foldText(raw).toLowerCase().replace(/[^a-z0-9_]+/g, ""));
|
|
7781
|
+
for (const part of folded.split(/[^A-Za-z0-9]+/)) push(part.toLowerCase());
|
|
7782
|
+
return out2;
|
|
7783
|
+
}
|
|
7784
|
+
function addTerms(doc, text) {
|
|
7785
|
+
for (const t of subtokens(text)) {
|
|
7786
|
+
doc.tf.set(t, (doc.tf.get(t) ?? 0) + 1);
|
|
7787
|
+
doc.len++;
|
|
7822
7788
|
}
|
|
7823
|
-
return capped;
|
|
7824
7789
|
}
|
|
7825
|
-
function
|
|
7826
|
-
const
|
|
7790
|
+
function buildDocs(scan2) {
|
|
7791
|
+
const docs = [];
|
|
7827
7792
|
for (const f of scan2.files) {
|
|
7793
|
+
const doc = { file: f.rel, tf: /* @__PURE__ */ new Map(), len: 0, symbols: [] };
|
|
7794
|
+
const seenSym = /* @__PURE__ */ new Set();
|
|
7828
7795
|
for (const s of f.symbols) {
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
const index = buildCallerIndex(scan2);
|
|
7834
|
-
const entry = index.get(name2);
|
|
7835
|
-
const callSites = entry ? entry.callers : [];
|
|
7836
|
-
const referencingFiles = /* @__PURE__ */ new Set();
|
|
7837
|
-
const unique = uniqueSymbolDefs(scan2);
|
|
7838
|
-
const defFile = unique.get(name2);
|
|
7839
|
-
for (const f of scan2.files) {
|
|
7840
|
-
if (f.rel === defFile) continue;
|
|
7841
|
-
if (f.kind === "code" && f.idents?.includes(name2)) referencingFiles.add(f.rel);
|
|
7842
|
-
else if (f.kind === "doc") {
|
|
7843
|
-
const content = scan2.docText.get(f.rel);
|
|
7844
|
-
if (content && new RegExp(`\\b${name2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(content)) {
|
|
7845
|
-
referencingFiles.add(f.rel);
|
|
7796
|
+
addTerms(doc, s.name);
|
|
7797
|
+
if (!seenSym.has(s.name)) {
|
|
7798
|
+
seenSym.add(s.name);
|
|
7799
|
+
doc.symbols.push(s.name);
|
|
7846
7800
|
}
|
|
7847
7801
|
}
|
|
7802
|
+
for (const seg of f.rel.split("/")) addTerms(doc, seg);
|
|
7803
|
+
for (const h of f.headings) addTerms(doc, h);
|
|
7804
|
+
if (f.summary) addTerms(doc, f.summary);
|
|
7805
|
+
docs.push(doc);
|
|
7848
7806
|
}
|
|
7849
|
-
|
|
7850
|
-
return { defs, callSites, referencingFiles: [...referencingFiles].sort(byStr) };
|
|
7807
|
+
return docs;
|
|
7851
7808
|
}
|
|
7852
|
-
|
|
7853
|
-
|
|
7809
|
+
function charTrigrams(term) {
|
|
7810
|
+
const padded = `^^${term}$$`;
|
|
7811
|
+
const grams = /* @__PURE__ */ new Set();
|
|
7812
|
+
for (let i2 = 0; i2 + 3 <= padded.length; i2++) grams.add(padded.slice(i2, i2 + 3));
|
|
7813
|
+
return grams;
|
|
7814
|
+
}
|
|
7815
|
+
function diceCoefficient(a, b) {
|
|
7816
|
+
if (!a.size || !b.size) return 0;
|
|
7817
|
+
let inter = 0;
|
|
7818
|
+
for (const g of a) if (b.has(g)) inter++;
|
|
7819
|
+
return 2 * inter / (a.size + b.size);
|
|
7820
|
+
}
|
|
7821
|
+
function buildTrigramIndex(docs) {
|
|
7822
|
+
const index = /* @__PURE__ */ new Map();
|
|
7823
|
+
for (const d of docs) {
|
|
7824
|
+
for (const term of d.tf.keys()) {
|
|
7825
|
+
if (!index.has(term)) index.set(term, charTrigrams(term));
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
return index;
|
|
7829
|
+
}
|
|
7830
|
+
function searchIndex(scan2, query, opts = {}) {
|
|
7831
|
+
const terms = [];
|
|
7832
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7833
|
+
for (const kw of keywords(query)) {
|
|
7834
|
+
for (const t of subtokens(kw)) {
|
|
7835
|
+
if (seen.has(t)) continue;
|
|
7836
|
+
seen.add(t);
|
|
7837
|
+
terms.push(t);
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7840
|
+
if (!terms.length) return [];
|
|
7841
|
+
const docs = bm25DocsFor(scan2);
|
|
7842
|
+
const n = docs.length;
|
|
7843
|
+
if (!n) return [];
|
|
7844
|
+
let totalLen = 0;
|
|
7845
|
+
for (const d of docs) totalLen += d.len;
|
|
7846
|
+
const avgLen = totalLen / n || 1;
|
|
7847
|
+
const df = /* @__PURE__ */ new Map();
|
|
7848
|
+
for (const t of terms) {
|
|
7849
|
+
let count = 0;
|
|
7850
|
+
for (const d of docs) if (d.tf.has(t)) count++;
|
|
7851
|
+
df.set(t, count);
|
|
7852
|
+
}
|
|
7853
|
+
const fuzzyEnabled = opts.fuzzy ?? true;
|
|
7854
|
+
const fuzzyCandidates = /* @__PURE__ */ new Map();
|
|
7855
|
+
if (fuzzyEnabled) {
|
|
7856
|
+
const unmatched = terms.filter((t) => df.get(t) === 0);
|
|
7857
|
+
if (unmatched.length) {
|
|
7858
|
+
const trigramIndex = bm25TrigramsFor(scan2);
|
|
7859
|
+
for (const t of unmatched) {
|
|
7860
|
+
const grams = charTrigrams(t);
|
|
7861
|
+
const candidates = [];
|
|
7862
|
+
for (const [vocabTerm, vocabGrams] of trigramIndex) {
|
|
7863
|
+
const dice = diceCoefficient(grams, vocabGrams);
|
|
7864
|
+
if (dice >= FUZZY_DICE_THRESHOLD) candidates.push({ term: vocabTerm, dice });
|
|
7865
|
+
}
|
|
7866
|
+
candidates.sort((a, b) => b.dice - a.dice || byStr(a.term, b.term));
|
|
7867
|
+
fuzzyCandidates.set(t, candidates.slice(0, FUZZY_CAP));
|
|
7868
|
+
}
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
const vocabDf = /* @__PURE__ */ new Map();
|
|
7872
|
+
const dfOfVocabTerm = (term) => {
|
|
7873
|
+
const known = df.get(term) ?? vocabDf.get(term);
|
|
7874
|
+
if (known !== void 0) return known;
|
|
7875
|
+
let count = 0;
|
|
7876
|
+
for (const d of docs) if (d.tf.has(term)) count++;
|
|
7877
|
+
vocabDf.set(term, count);
|
|
7878
|
+
return count;
|
|
7879
|
+
};
|
|
7880
|
+
const results = [];
|
|
7881
|
+
for (const d of docs) {
|
|
7882
|
+
let score = 0;
|
|
7883
|
+
const matched = [];
|
|
7884
|
+
const symbolTerms = /* @__PURE__ */ new Set();
|
|
7885
|
+
const fuzzyHit = /* @__PURE__ */ new Set();
|
|
7886
|
+
for (const t of terms) {
|
|
7887
|
+
const tf = d.tf.get(t);
|
|
7888
|
+
if (tf) {
|
|
7889
|
+
matched.push(t);
|
|
7890
|
+
symbolTerms.add(t);
|
|
7891
|
+
const idf = Math.log(1 + (n - df.get(t) + 0.5) / (df.get(t) + 0.5));
|
|
7892
|
+
score += idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * d.len / avgLen));
|
|
7893
|
+
continue;
|
|
7894
|
+
}
|
|
7895
|
+
const candidates = fuzzyCandidates.get(t);
|
|
7896
|
+
if (!candidates) continue;
|
|
7897
|
+
for (const cand of candidates) {
|
|
7898
|
+
const ctf = d.tf.get(cand.term);
|
|
7899
|
+
if (!ctf) continue;
|
|
7900
|
+
const cdf = dfOfVocabTerm(cand.term);
|
|
7901
|
+
const idf = Math.log(1 + (n - cdf + 0.5) / (cdf + 0.5));
|
|
7902
|
+
const contribution = idf * (ctf * (K1 + 1)) / (ctf + K1 * (1 - B + B * d.len / avgLen));
|
|
7903
|
+
score += contribution * cand.dice;
|
|
7904
|
+
symbolTerms.add(cand.term);
|
|
7905
|
+
fuzzyHit.add(t);
|
|
7906
|
+
}
|
|
7907
|
+
}
|
|
7908
|
+
if (!matched.length && !fuzzyHit.size) continue;
|
|
7909
|
+
const scored = d.symbols.map((name2) => {
|
|
7910
|
+
const toks = new Set(subtokens(name2));
|
|
7911
|
+
let hits = 0;
|
|
7912
|
+
for (const t of symbolTerms) if (toks.has(t)) hits++;
|
|
7913
|
+
return { name: name2, hits };
|
|
7914
|
+
}).filter((s) => s.hits > 0).sort((a, b) => b.hits - a.hits || byStr(a.name, b.name));
|
|
7915
|
+
const result = {
|
|
7916
|
+
file: d.file,
|
|
7917
|
+
score: Number(score.toFixed(4)),
|
|
7918
|
+
matchedTerms: matched.sort(byStr),
|
|
7919
|
+
topSymbols: scored.slice(0, TOP_SYMBOLS).map((s) => s.name)
|
|
7920
|
+
};
|
|
7921
|
+
if (fuzzyHit.size) result.fuzzyTerms = [...fuzzyHit].sort(byStr);
|
|
7922
|
+
results.push(result);
|
|
7923
|
+
}
|
|
7924
|
+
results.sort((a, b) => b.score - a.score || byStr(a.file, b.file));
|
|
7925
|
+
return results.slice(0, opts.limit ?? DEFAULT_LIMIT);
|
|
7926
|
+
}
|
|
7927
|
+
var K1, B, DEFAULT_LIMIT, TOP_SYMBOLS, FUZZY_DICE_THRESHOLD, FUZZY_CAP;
|
|
7928
|
+
var init_bm25 = __esm({
|
|
7929
|
+
"src/bm25.ts"() {
|
|
7930
|
+
"use strict";
|
|
7931
|
+
init_derived();
|
|
7932
|
+
init_util();
|
|
7933
|
+
init_sort();
|
|
7934
|
+
K1 = 1.2;
|
|
7935
|
+
B = 0.75;
|
|
7936
|
+
DEFAULT_LIMIT = 20;
|
|
7937
|
+
TOP_SYMBOLS = 5;
|
|
7938
|
+
FUZZY_DICE_THRESHOLD = 0.6;
|
|
7939
|
+
FUZZY_CAP = 3;
|
|
7940
|
+
}
|
|
7941
|
+
});
|
|
7942
|
+
|
|
7943
|
+
// src/complexity.ts
|
|
7944
|
+
import { join as join6 } from "path";
|
|
7945
|
+
function complexityOfSource(source) {
|
|
7946
|
+
return 1 + (source.match(BRANCH_RE) ?? []).length;
|
|
7947
|
+
}
|
|
7948
|
+
function symbolComplexity(scan2, rel, top = 50) {
|
|
7949
|
+
const out2 = [];
|
|
7950
|
+
for (const f of scan2.files) {
|
|
7951
|
+
if (f.kind !== "code") continue;
|
|
7952
|
+
if (rel && f.rel !== rel) continue;
|
|
7953
|
+
if (!f.symbols.length) continue;
|
|
7954
|
+
const lines = readText(join6(scan2.root, f.rel)).split("\n");
|
|
7955
|
+
for (const s of f.symbols) {
|
|
7956
|
+
if (s.kind === "reexport" || s.kind === "reexport-all") continue;
|
|
7957
|
+
const end = s.endLine ?? s.line;
|
|
7958
|
+
const body2 = lines.slice(s.line - 1, end).join("\n");
|
|
7959
|
+
const entry = { file: f.rel, name: s.name, line: s.line, complexity: complexityOfSource(body2) };
|
|
7960
|
+
if (s.endLine !== void 0) entry.endLine = s.endLine;
|
|
7961
|
+
out2.push(entry);
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7964
|
+
out2.sort((a, b) => b.complexity - a.complexity || byStr(a.file, b.file) || a.line - b.line);
|
|
7965
|
+
return out2.slice(0, top);
|
|
7966
|
+
}
|
|
7967
|
+
function riskHotspots(scan2, churn, top = 20) {
|
|
7968
|
+
const complexityByFile = fileComplexityFor(scan2);
|
|
7969
|
+
const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
|
|
7970
|
+
const complexity = complexityByFile.get(f.rel);
|
|
7971
|
+
const commits = churn.get(f.rel) ?? 0;
|
|
7972
|
+
return { file: f.rel, complexity, commits, score: (commits + 1) * complexity };
|
|
7973
|
+
});
|
|
7974
|
+
out2.sort((a, b) => b.score - a.score || byStr(a.file, b.file));
|
|
7975
|
+
return out2.slice(0, top);
|
|
7976
|
+
}
|
|
7977
|
+
var BRANCH_RE;
|
|
7978
|
+
var init_complexity = __esm({
|
|
7979
|
+
"src/complexity.ts"() {
|
|
7980
|
+
"use strict";
|
|
7981
|
+
init_derived();
|
|
7982
|
+
init_walk();
|
|
7983
|
+
init_sort();
|
|
7984
|
+
BRANCH_RE = /\b(if|elif|elsif|else\s+if|for|foreach|while|until|unless|case|when|match|catch|rescue|except)\b|&&|\|\||(?<![?:])\?(?![?.:])/g;
|
|
7985
|
+
}
|
|
7986
|
+
});
|
|
7987
|
+
|
|
7988
|
+
// src/derived.ts
|
|
7989
|
+
import { join as join7 } from "path";
|
|
7990
|
+
function cacheFor(scan2) {
|
|
7991
|
+
let c2 = caches.get(scan2);
|
|
7992
|
+
if (!c2) caches.set(scan2, c2 = {});
|
|
7993
|
+
return c2;
|
|
7994
|
+
}
|
|
7995
|
+
function resolveContextFor(scan2) {
|
|
7996
|
+
const c2 = cacheFor(scan2);
|
|
7997
|
+
return c2.resolveCtx ??= buildResolveContext(scan2);
|
|
7998
|
+
}
|
|
7999
|
+
function importPairsFor(scan2) {
|
|
8000
|
+
const c2 = cacheFor(scan2);
|
|
8001
|
+
if (!c2.importPairs) {
|
|
8002
|
+
const ctx = resolveContextFor(scan2);
|
|
8003
|
+
const pairs = /* @__PURE__ */ new Set();
|
|
8004
|
+
for (const f of scan2.files) {
|
|
8005
|
+
for (const ref of f.refs) {
|
|
8006
|
+
if (ref.kind !== "import") continue;
|
|
8007
|
+
const r = resolveImport(f.rel, f.ext, ref.spec, ctx);
|
|
8008
|
+
if (r.kind === "resolved" && r.target !== f.rel) pairs.add(`${f.rel}|${r.target}`);
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
c2.importPairs = pairs;
|
|
8012
|
+
}
|
|
8013
|
+
return c2.importPairs;
|
|
8014
|
+
}
|
|
8015
|
+
function uniqueDefsFor(scan2) {
|
|
8016
|
+
const c2 = cacheFor(scan2);
|
|
8017
|
+
return c2.uniqueDefs ??= uniqueSymbolDefs(scan2);
|
|
8018
|
+
}
|
|
8019
|
+
function symbolRefsFor(scan2) {
|
|
8020
|
+
const c2 = cacheFor(scan2);
|
|
8021
|
+
return c2.symbolRefs ??= computeSymbolRefs(scan2);
|
|
8022
|
+
}
|
|
8023
|
+
function callerIndexFor(scan2) {
|
|
8024
|
+
const c2 = cacheFor(scan2);
|
|
8025
|
+
return c2.callerIndex ??= buildCallerIndex(scan2, importPairsFor(scan2));
|
|
8026
|
+
}
|
|
8027
|
+
function bm25DocsFor(scan2) {
|
|
8028
|
+
const c2 = cacheFor(scan2);
|
|
8029
|
+
return (c2.bm25 ??= { docs: buildDocs(scan2) }).docs;
|
|
8030
|
+
}
|
|
8031
|
+
function bm25TrigramsFor(scan2) {
|
|
8032
|
+
const c2 = cacheFor(scan2);
|
|
8033
|
+
const bm25 = c2.bm25 ??= { docs: buildDocs(scan2) };
|
|
8034
|
+
return bm25.trigrams ??= buildTrigramIndex(bm25.docs);
|
|
8035
|
+
}
|
|
8036
|
+
function fileComplexityFor(scan2) {
|
|
8037
|
+
const c2 = cacheFor(scan2);
|
|
8038
|
+
if (!c2.fileComplexity) {
|
|
8039
|
+
const m = /* @__PURE__ */ new Map();
|
|
8040
|
+
for (const f of scan2.files) {
|
|
8041
|
+
if (f.kind !== "code") continue;
|
|
8042
|
+
m.set(f.rel, complexityOfSource(readText(join7(scan2.root, f.rel))));
|
|
8043
|
+
}
|
|
8044
|
+
c2.fileComplexity = m;
|
|
8045
|
+
}
|
|
8046
|
+
return c2.fileComplexity;
|
|
8047
|
+
}
|
|
8048
|
+
var caches;
|
|
8049
|
+
var init_derived = __esm({
|
|
8050
|
+
"src/derived.ts"() {
|
|
8051
|
+
"use strict";
|
|
8052
|
+
init_resolve();
|
|
8053
|
+
init_graph();
|
|
8054
|
+
init_symbols_json();
|
|
8055
|
+
init_callers();
|
|
8056
|
+
init_bm25();
|
|
8057
|
+
init_complexity();
|
|
8058
|
+
init_walk();
|
|
8059
|
+
caches = /* @__PURE__ */ new WeakMap();
|
|
8060
|
+
}
|
|
8061
|
+
});
|
|
8062
|
+
|
|
8063
|
+
// src/callers.ts
|
|
8064
|
+
function computeImportPairs(scan2) {
|
|
8065
|
+
return new Set(importPairsFor(scan2));
|
|
8066
|
+
}
|
|
8067
|
+
function buildCallerIndex(scan2, importPairs, opts = {}) {
|
|
8068
|
+
const pairs = importPairs ?? importPairsFor(scan2);
|
|
8069
|
+
const recall = opts.recall === true;
|
|
8070
|
+
const defs = /* @__PURE__ */ new Map();
|
|
8071
|
+
for (const f of scan2.files) {
|
|
8072
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8073
|
+
for (const s of f.symbols) {
|
|
8074
|
+
if (!s.exported || REFERENCE_KINDS3.has(s.kind)) continue;
|
|
8075
|
+
if (seen.has(s.name)) continue;
|
|
8076
|
+
seen.add(s.name);
|
|
8077
|
+
let arr = defs.get(s.name);
|
|
8078
|
+
if (!arr) defs.set(s.name, arr = []);
|
|
8079
|
+
arr.push(s);
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
8082
|
+
const localDefs = /* @__PURE__ */ new Map();
|
|
8083
|
+
for (const f of scan2.files) {
|
|
8084
|
+
const byName = /* @__PURE__ */ new Map();
|
|
8085
|
+
for (const s of f.symbols) {
|
|
8086
|
+
if (!REFERENCE_KINDS3.has(s.kind) && !byName.has(s.name)) byName.set(s.name, s);
|
|
8087
|
+
}
|
|
8088
|
+
localDefs.set(f.rel, byName);
|
|
8089
|
+
}
|
|
8090
|
+
const sites = /* @__PURE__ */ new Map();
|
|
8091
|
+
const record = (def, caller) => {
|
|
8092
|
+
let entry = sites.get(def.name + "\0" + def.file);
|
|
8093
|
+
if (!entry) sites.set(def.name + "\0" + def.file, entry = { def, callers: [] });
|
|
8094
|
+
entry.callers.push(caller);
|
|
8095
|
+
};
|
|
8096
|
+
for (const f of scan2.files) {
|
|
8097
|
+
if (!f.calls?.length) continue;
|
|
8098
|
+
const family = familyOf(f.lang);
|
|
8099
|
+
const own = localDefs.get(f.rel);
|
|
8100
|
+
for (const c2 of f.calls) {
|
|
8101
|
+
const local = own.get(c2.name);
|
|
8102
|
+
if (local) {
|
|
8103
|
+
if (local.line !== c2.line)
|
|
8104
|
+
record(local, recall ? { file: f.rel, line: c2.line, confidence: "corroborated" } : { file: f.rel, line: c2.line });
|
|
8105
|
+
continue;
|
|
8106
|
+
}
|
|
8107
|
+
const cands = (defs.get(c2.name) ?? []).filter((d) => familyOf(d.lang) === family && d.file !== f.rel).map((d) => ({ file: d.file, lang: d.lang }));
|
|
8108
|
+
if (!cands.length) continue;
|
|
8109
|
+
const imported = cands.filter((d) => pairs.has(`${f.rel}|${d.file}`));
|
|
8110
|
+
const chosen = family === "js" ? imported.length ? pickCandidate(f.rel, imported) : (
|
|
8111
|
+
// JS/TS gate: no corroborating import → no binding. Recall mode
|
|
8112
|
+
// relaxes this to a unique-repo-wide name match (issue #7).
|
|
8113
|
+
recall && cands.length === 1 ? cands[0] : void 0
|
|
8114
|
+
) : imported.length ? pickCandidate(f.rel, imported) : pickCandidate(f.rel, cands);
|
|
8115
|
+
if (!chosen) continue;
|
|
8116
|
+
const def = defs.get(c2.name).find((d) => d.file === chosen.file);
|
|
8117
|
+
record(
|
|
8118
|
+
def,
|
|
8119
|
+
recall ? { file: f.rel, line: c2.line, confidence: imported.length ? "corroborated" : "unique-name" } : { file: f.rel, line: c2.line }
|
|
8120
|
+
);
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
const index = /* @__PURE__ */ new Map();
|
|
8124
|
+
const keys = [...sites.keys()].sort(byStr);
|
|
8125
|
+
for (const key of keys) {
|
|
8126
|
+
const { def, callers } = sites.get(key);
|
|
8127
|
+
callers.sort((a, b) => byStr(a.file, b.file) || a.line - b.line);
|
|
8128
|
+
if (!index.has(def.name)) index.set(def.name, { def, callers });
|
|
8129
|
+
else index.set(`${def.name}@${def.file}`, { def, callers });
|
|
8130
|
+
}
|
|
8131
|
+
return index;
|
|
8132
|
+
}
|
|
8133
|
+
function enclosingSymbol(scan2, file, line) {
|
|
8134
|
+
const f = scan2.files.find((x) => x.rel === file);
|
|
8135
|
+
if (!f?.symbols.length) return void 0;
|
|
8136
|
+
return enclosingAmong(f.symbols, line);
|
|
8137
|
+
}
|
|
8138
|
+
function enclosingAmong(symbols, line) {
|
|
8139
|
+
let best;
|
|
8140
|
+
for (const s of symbols) {
|
|
8141
|
+
if (REFERENCE_KINDS3.has(s.kind)) continue;
|
|
8142
|
+
if (s.line > line) continue;
|
|
8143
|
+
if (s.endLine !== void 0 && line > s.endLine) continue;
|
|
8144
|
+
if (!best || s.line > best.line || s.line === best.line && (s.endLine ?? Infinity) <= (best.endLine ?? Infinity)) {
|
|
8145
|
+
best = s;
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
return best;
|
|
8149
|
+
}
|
|
8150
|
+
function buildRawCallerIndex(scan2) {
|
|
8151
|
+
const byName = /* @__PURE__ */ new Map();
|
|
8152
|
+
for (const f of scan2.files) {
|
|
8153
|
+
if (!f.calls?.length) continue;
|
|
8154
|
+
const symbols = f.symbols.filter((s) => !REFERENCE_KINDS3.has(s.kind));
|
|
8155
|
+
for (const c2 of f.calls) {
|
|
8156
|
+
const site = { file: f.rel, line: c2.line };
|
|
8157
|
+
if (c2.receiver !== void 0) site.receiver = c2.receiver;
|
|
8158
|
+
const enc = enclosingAmong(symbols, c2.line);
|
|
8159
|
+
if (enc) site.enclosingSymbol = enc;
|
|
8160
|
+
let arr = byName.get(c2.name);
|
|
8161
|
+
if (!arr) byName.set(c2.name, arr = []);
|
|
8162
|
+
arr.push(site);
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
8165
|
+
const index = /* @__PURE__ */ new Map();
|
|
8166
|
+
for (const name2 of [...byName.keys()].sort(byStr)) {
|
|
8167
|
+
const sites = byName.get(name2);
|
|
8168
|
+
sites.sort((a, b) => byStr(a.file, b.file) || a.line - b.line);
|
|
8169
|
+
index.set(name2, sites);
|
|
8170
|
+
}
|
|
8171
|
+
return index;
|
|
8172
|
+
}
|
|
8173
|
+
var REFERENCE_KINDS3;
|
|
8174
|
+
var init_callers = __esm({
|
|
8175
|
+
"src/callers.ts"() {
|
|
8176
|
+
"use strict";
|
|
8177
|
+
init_calls();
|
|
8178
|
+
init_derived();
|
|
8179
|
+
init_sort();
|
|
8180
|
+
REFERENCE_KINDS3 = /* @__PURE__ */ new Set(["reexport", "reexport-all", "default"]);
|
|
8181
|
+
}
|
|
8182
|
+
});
|
|
8183
|
+
|
|
8184
|
+
// src/query.ts
|
|
8185
|
+
import { join as join8 } from "path";
|
|
8186
|
+
function symbolsOverview(scan2, rel) {
|
|
8187
|
+
const f = scan2.files.find((x) => x.rel === rel);
|
|
8188
|
+
if (!f) return [];
|
|
8189
|
+
return [...f.symbols].filter((s) => !REFERENCE_KINDS4.has(s.kind)).sort((a, b) => a.line - b.line || byStr(a.name, b.name));
|
|
8190
|
+
}
|
|
8191
|
+
function findSymbol(scan2, namePath, opts = {}) {
|
|
8192
|
+
const segments = namePath.split("/").filter(Boolean);
|
|
8193
|
+
if (!segments.length) return [];
|
|
8194
|
+
const leaf = segments[segments.length - 1];
|
|
8195
|
+
const parents = segments.slice(0, -1);
|
|
8196
|
+
const matchName = (name2, wanted) => opts.substring ? name2.toLowerCase().includes(wanted.toLowerCase()) : name2 === wanted;
|
|
8197
|
+
const out2 = [];
|
|
8198
|
+
for (const f of scan2.files) {
|
|
8199
|
+
for (const s of f.symbols) {
|
|
8200
|
+
if (REFERENCE_KINDS4.has(s.kind)) continue;
|
|
8201
|
+
if (!matchName(s.name, leaf)) continue;
|
|
8202
|
+
if (parents.length) {
|
|
8203
|
+
const parent = parents[parents.length - 1];
|
|
8204
|
+
if (!s.parent || s.parent !== parent) continue;
|
|
8205
|
+
}
|
|
8206
|
+
out2.push({ ...s });
|
|
8207
|
+
}
|
|
8208
|
+
}
|
|
8209
|
+
out2.sort(
|
|
8210
|
+
(a, b) => Number(b.name === leaf) - Number(a.name === leaf) || byStr(a.file, b.file) || a.line - b.line
|
|
8211
|
+
);
|
|
8212
|
+
const capped = out2.slice(0, opts.maxResults ?? 50);
|
|
8213
|
+
if (opts.includeBody) {
|
|
8214
|
+
for (const m of capped) {
|
|
8215
|
+
const end = m.endLine ?? m.line;
|
|
8216
|
+
const content = readText(join8(scan2.root, m.file));
|
|
8217
|
+
if (!content) continue;
|
|
8218
|
+
m.body = content.split("\n").slice(m.line - 1, end).join("\n");
|
|
8219
|
+
}
|
|
8220
|
+
}
|
|
8221
|
+
return capped;
|
|
8222
|
+
}
|
|
8223
|
+
function findReferences(scan2, name2) {
|
|
8224
|
+
const defs = [];
|
|
8225
|
+
for (const f of scan2.files) {
|
|
8226
|
+
for (const s of f.symbols) {
|
|
8227
|
+
if (s.name === name2 && !REFERENCE_KINDS4.has(s.kind)) defs.push(s);
|
|
8228
|
+
}
|
|
8229
|
+
}
|
|
8230
|
+
defs.sort((a, b) => byStr(a.file, b.file) || a.line - b.line);
|
|
8231
|
+
const index = callerIndexFor(scan2);
|
|
8232
|
+
const entry = index.get(name2);
|
|
8233
|
+
const callSites = entry ? [...entry.callers] : [];
|
|
8234
|
+
const referencingFiles = /* @__PURE__ */ new Set();
|
|
8235
|
+
const unique = uniqueDefsFor(scan2);
|
|
8236
|
+
const defFile = unique.get(name2);
|
|
8237
|
+
for (const f of scan2.files) {
|
|
8238
|
+
if (f.rel === defFile) continue;
|
|
8239
|
+
if (f.kind === "code" && f.idents?.includes(name2)) referencingFiles.add(f.rel);
|
|
8240
|
+
else if (f.kind === "doc") {
|
|
8241
|
+
const content = scan2.docText.get(f.rel);
|
|
8242
|
+
if (content && new RegExp(`\\b${name2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(content)) {
|
|
8243
|
+
referencingFiles.add(f.rel);
|
|
8244
|
+
}
|
|
8245
|
+
}
|
|
8246
|
+
}
|
|
8247
|
+
for (const site of callSites) referencingFiles.add(site.file);
|
|
8248
|
+
return { defs, callSites, referencingFiles: [...referencingFiles].sort(byStr) };
|
|
8249
|
+
}
|
|
8250
|
+
var REFERENCE_KINDS4;
|
|
8251
|
+
var init_query = __esm({
|
|
7854
8252
|
"src/query.ts"() {
|
|
7855
8253
|
"use strict";
|
|
7856
8254
|
init_walk();
|
|
7857
|
-
|
|
7858
|
-
init_graph();
|
|
8255
|
+
init_derived();
|
|
7859
8256
|
init_sort();
|
|
7860
8257
|
REFERENCE_KINDS4 = /* @__PURE__ */ new Set(["reexport", "reexport-all", "default"]);
|
|
7861
8258
|
}
|
|
7862
8259
|
});
|
|
7863
8260
|
|
|
7864
8261
|
// src/edit.ts
|
|
7865
|
-
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
7866
|
-
import { join as
|
|
8262
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
8263
|
+
import { join as join9 } from "path";
|
|
7867
8264
|
function resolveUniqueSymbol(scan2, namePath, file) {
|
|
7868
8265
|
let matches = findSymbol(scan2, namePath);
|
|
7869
8266
|
if (file) matches = matches.filter((m) => m.file === file);
|
|
@@ -7881,15 +8278,15 @@ function readLines(abs) {
|
|
|
7881
8278
|
function replaceSymbolBody(scan2, namePath, body2, file) {
|
|
7882
8279
|
const sym = resolveUniqueSymbol(scan2, namePath, file);
|
|
7883
8280
|
const end = sym.endLine ?? sym.line;
|
|
7884
|
-
const abs =
|
|
8281
|
+
const abs = join9(scan2.root, sym.file);
|
|
7885
8282
|
const lines = readLines(abs);
|
|
7886
8283
|
const newLines = body2.replace(/^\n+|\n+$/g, "").split("\n");
|
|
7887
8284
|
lines.splice(sym.line - 1, end - sym.line + 1, ...newLines);
|
|
7888
|
-
|
|
8285
|
+
writeFileSync2(abs, lines.join("\n"));
|
|
7889
8286
|
return { file: sym.file, startLine: sym.line, endLine: sym.line + newLines.length - 1, lines: newLines.length };
|
|
7890
8287
|
}
|
|
7891
8288
|
function insertAt(scan2, sym, body2, index, blankBefore, blankAfter) {
|
|
7892
|
-
const abs =
|
|
8289
|
+
const abs = join9(scan2.root, sym.file);
|
|
7893
8290
|
const lines = readLines(abs);
|
|
7894
8291
|
const minGap = SEPARATED_KINDS.has(sym.kind) ? 1 : 0;
|
|
7895
8292
|
const newLines = body2.replace(/^\n+|\n+$/g, "").split("\n");
|
|
@@ -7898,7 +8295,7 @@ function insertAt(scan2, sym, body2, index, blankBefore, blankAfter) {
|
|
|
7898
8295
|
block.push(...newLines);
|
|
7899
8296
|
if (blankAfter && minGap && lines[index]?.trim() !== "") block.push("");
|
|
7900
8297
|
lines.splice(index, 0, ...block);
|
|
7901
|
-
|
|
8298
|
+
writeFileSync2(abs, lines.join("\n"));
|
|
7902
8299
|
return { file: sym.file, startLine: index + 1, endLine: index + block.length, lines: block.length };
|
|
7903
8300
|
}
|
|
7904
8301
|
function insertAfterSymbol(scan2, namePath, body2, file) {
|
|
@@ -7920,8 +8317,8 @@ var init_edit = __esm({
|
|
|
7920
8317
|
});
|
|
7921
8318
|
|
|
7922
8319
|
// src/memory.ts
|
|
7923
|
-
import { mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as
|
|
7924
|
-
import { dirname as
|
|
8320
|
+
import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
8321
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
7925
8322
|
function sanitize(name2) {
|
|
7926
8323
|
const clean = name2.replace(/^mem:/, "").replace(/\.md$/, "");
|
|
7927
8324
|
if (!clean) throw new Error("memory name is empty");
|
|
@@ -7935,12 +8332,12 @@ function sanitize(name2) {
|
|
|
7935
8332
|
return clean;
|
|
7936
8333
|
}
|
|
7937
8334
|
function memoryPath(repo, name2) {
|
|
7938
|
-
return
|
|
8335
|
+
return join10(repo, ...MEMORY_DIR, `${sanitize(name2)}.md`);
|
|
7939
8336
|
}
|
|
7940
8337
|
function writeMemory(repo, name2, content) {
|
|
7941
8338
|
const path = memoryPath(repo, name2);
|
|
7942
|
-
|
|
7943
|
-
|
|
8339
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
8340
|
+
writeFileSync3(path, content.endsWith("\n") ? content : content + "\n");
|
|
7944
8341
|
return sanitize(name2);
|
|
7945
8342
|
}
|
|
7946
8343
|
function readMemory(repo, name2) {
|
|
@@ -7961,7 +8358,7 @@ function deleteMemory(repo, name2) {
|
|
|
7961
8358
|
return true;
|
|
7962
8359
|
}
|
|
7963
8360
|
function listMemories(repo) {
|
|
7964
|
-
const root =
|
|
8361
|
+
const root = join10(repo, ...MEMORY_DIR);
|
|
7965
8362
|
const out2 = [];
|
|
7966
8363
|
const walk2 = (dir, prefix) => {
|
|
7967
8364
|
let entries;
|
|
@@ -7971,7 +8368,7 @@ function listMemories(repo) {
|
|
|
7971
8368
|
return;
|
|
7972
8369
|
}
|
|
7973
8370
|
for (const e of entries) {
|
|
7974
|
-
if (e.isDirectory()) walk2(
|
|
8371
|
+
if (e.isDirectory()) walk2(join10(dir, e.name), prefix ? `${prefix}/${e.name}` : e.name);
|
|
7975
8372
|
else if (e.name.endsWith(".md")) out2.push(prefix ? `${prefix}/${e.name.slice(0, -3)}` : e.name.slice(0, -3));
|
|
7976
8373
|
}
|
|
7977
8374
|
};
|
|
@@ -7988,7 +8385,7 @@ var init_memory = __esm({
|
|
|
7988
8385
|
|
|
7989
8386
|
// src/workspaces.ts
|
|
7990
8387
|
import { existsSync as existsSync2, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
7991
|
-
import { join as
|
|
8388
|
+
import { join as join11 } from "path";
|
|
7992
8389
|
function readJson(path, label, warnings) {
|
|
7993
8390
|
const raw = readText(path);
|
|
7994
8391
|
if (!raw) return void 0;
|
|
@@ -8039,7 +8436,7 @@ function wsGlobToRegExp(pat) {
|
|
|
8039
8436
|
return new RegExp(`^${re}($|/)`);
|
|
8040
8437
|
}
|
|
8041
8438
|
function probeNodePkg(root, dir, kind, warnings) {
|
|
8042
|
-
const path =
|
|
8439
|
+
const path = join11(root, dir, "package.json");
|
|
8043
8440
|
if (!existsSync2(path)) return void 0;
|
|
8044
8441
|
const manifest = `${dir}/package.json`;
|
|
8045
8442
|
const pkg = readJson(path, manifest, warnings);
|
|
@@ -8053,7 +8450,7 @@ function probeNodePkg(root, dir, kind, warnings) {
|
|
|
8053
8450
|
return out2;
|
|
8054
8451
|
}
|
|
8055
8452
|
function probeCargo(root, dir) {
|
|
8056
|
-
const path =
|
|
8453
|
+
const path = join11(root, dir, "Cargo.toml");
|
|
8057
8454
|
if (!existsSync2(path)) return void 0;
|
|
8058
8455
|
const body2 = tomlSectionBody(readText(path), "package");
|
|
8059
8456
|
const out2 = {
|
|
@@ -8067,18 +8464,18 @@ function probeCargo(root, dir) {
|
|
|
8067
8464
|
return out2;
|
|
8068
8465
|
}
|
|
8069
8466
|
function probeGoMod(root, dir) {
|
|
8070
|
-
const path =
|
|
8467
|
+
const path = join11(root, dir, "go.mod");
|
|
8071
8468
|
if (!existsSync2(path)) return void 0;
|
|
8072
8469
|
const name2 = readText(path).match(/^module\s+(\S+)/m)?.[1] ?? dir;
|
|
8073
8470
|
return { name: name2, dir, kind: "go", manifest: `${dir}/go.mod` };
|
|
8074
8471
|
}
|
|
8075
8472
|
function probeMaven(root, dir) {
|
|
8076
|
-
const path =
|
|
8473
|
+
const path = join11(root, dir, "pom.xml");
|
|
8077
8474
|
if (!existsSync2(path)) return void 0;
|
|
8078
8475
|
return { name: ownArtifactId(readText(path)) ?? dir, dir, kind: "maven", manifest: `${dir}/pom.xml` };
|
|
8079
8476
|
}
|
|
8080
8477
|
function probePyproject(root, dir) {
|
|
8081
|
-
const path =
|
|
8478
|
+
const path = join11(root, dir, "pyproject.toml");
|
|
8082
8479
|
if (!existsSync2(path)) return void 0;
|
|
8083
8480
|
const toml = readText(path);
|
|
8084
8481
|
const project = tomlSectionBody(toml, "project");
|
|
@@ -8094,7 +8491,7 @@ function probePyproject(root, dir) {
|
|
|
8094
8491
|
return out2;
|
|
8095
8492
|
}
|
|
8096
8493
|
function probeComposer(root, dir, warnings) {
|
|
8097
|
-
const path =
|
|
8494
|
+
const path = join11(root, dir, "composer.json");
|
|
8098
8495
|
if (!existsSync2(path)) return void 0;
|
|
8099
8496
|
const manifest = `${dir}/composer.json`;
|
|
8100
8497
|
const pkg = readJson(path, manifest, warnings);
|
|
@@ -8108,7 +8505,7 @@ function probeComposer(root, dir, warnings) {
|
|
|
8108
8505
|
return out2;
|
|
8109
8506
|
}
|
|
8110
8507
|
function probeNxProject(root, dir, warnings) {
|
|
8111
|
-
const path =
|
|
8508
|
+
const path = join11(root, dir, "project.json");
|
|
8112
8509
|
if (!existsSync2(path)) return void 0;
|
|
8113
8510
|
const manifest = `${dir}/project.json`;
|
|
8114
8511
|
const proj = readJson(path, manifest, warnings);
|
|
@@ -8121,7 +8518,7 @@ function probeNxProject(root, dir, warnings) {
|
|
|
8121
8518
|
}
|
|
8122
8519
|
function probeGradle(root, dir) {
|
|
8123
8520
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
8124
|
-
if (existsSync2(
|
|
8521
|
+
if (existsSync2(join11(root, dir, f))) {
|
|
8125
8522
|
return { name: dir, dir, kind: "gradle", manifest: `${dir}/${f}` };
|
|
8126
8523
|
}
|
|
8127
8524
|
}
|
|
@@ -8156,7 +8553,7 @@ function addPackage(root, dir, found, kind, warnings) {
|
|
|
8156
8553
|
}
|
|
8157
8554
|
function isDirAt(root, rel) {
|
|
8158
8555
|
try {
|
|
8159
|
-
return statSync3(
|
|
8556
|
+
return statSync3(join11(root, rel)).isDirectory();
|
|
8160
8557
|
} catch {
|
|
8161
8558
|
return false;
|
|
8162
8559
|
}
|
|
@@ -8164,7 +8561,7 @@ function isDirAt(root, rel) {
|
|
|
8164
8561
|
function subdirsOf(root, base) {
|
|
8165
8562
|
let entries;
|
|
8166
8563
|
try {
|
|
8167
|
-
entries = readdirSync3(base ?
|
|
8564
|
+
entries = readdirSync3(base ? join11(root, base) : root, { withFileTypes: true });
|
|
8168
8565
|
} catch {
|
|
8169
8566
|
return [];
|
|
8170
8567
|
}
|
|
@@ -8226,14 +8623,14 @@ function npmFamilyPatterns(root, warnings) {
|
|
|
8226
8623
|
if (t.startsWith("!")) negations.push(t.slice(1));
|
|
8227
8624
|
else positives.push({ pattern: t, kind });
|
|
8228
8625
|
};
|
|
8229
|
-
const pkg = readJson(
|
|
8626
|
+
const pkg = readJson(join11(root, "package.json"), "package.json", warnings);
|
|
8230
8627
|
const ws = pkg?.workspaces;
|
|
8231
8628
|
if (Array.isArray(ws)) {
|
|
8232
8629
|
for (const x of ws) if (typeof x === "string") push(x, "npm");
|
|
8233
8630
|
} else if (ws && typeof ws === "object" && Array.isArray(ws.packages)) {
|
|
8234
8631
|
for (const x of ws.packages) if (typeof x === "string") push(x, "npm");
|
|
8235
8632
|
}
|
|
8236
|
-
const pnpm = readText(
|
|
8633
|
+
const pnpm = readText(join11(root, "pnpm-workspace.yaml"));
|
|
8237
8634
|
let inPackages = false;
|
|
8238
8635
|
for (const line of pnpm.split(/\r?\n/)) {
|
|
8239
8636
|
if (/^\S/.test(line)) {
|
|
@@ -8247,11 +8644,11 @@ function npmFamilyPatterns(root, warnings) {
|
|
|
8247
8644
|
return { positives, negations };
|
|
8248
8645
|
}
|
|
8249
8646
|
function fallbackNpmPatterns(root, warnings) {
|
|
8250
|
-
const lerna = readJson(
|
|
8647
|
+
const lerna = readJson(join11(root, "lerna.json"), "lerna.json", warnings);
|
|
8251
8648
|
if (lerna && Array.isArray(lerna.packages)) {
|
|
8252
8649
|
return lerna.packages.filter((x) => typeof x === "string").map((pattern) => ({ pattern, kind: "lerna" }));
|
|
8253
8650
|
}
|
|
8254
|
-
const nx = readJson(
|
|
8651
|
+
const nx = readJson(join11(root, "nx.json"), "nx.json", warnings);
|
|
8255
8652
|
if (nx) {
|
|
8256
8653
|
const layout = nx.workspaceLayout ?? {};
|
|
8257
8654
|
const appsDir = typeof layout.appsDir === "string" ? layout.appsDir : "apps";
|
|
@@ -8261,7 +8658,7 @@ function fallbackNpmPatterns(root, warnings) {
|
|
|
8261
8658
|
return [];
|
|
8262
8659
|
}
|
|
8263
8660
|
function detectCargoMembers(root, found, warnings) {
|
|
8264
|
-
const toml = readText(
|
|
8661
|
+
const toml = readText(join11(root, "Cargo.toml"));
|
|
8265
8662
|
if (!toml) return;
|
|
8266
8663
|
const body2 = tomlSectionBody(toml, "workspace");
|
|
8267
8664
|
if (!body2) return;
|
|
@@ -8276,7 +8673,7 @@ function detectCargoMembers(root, found, warnings) {
|
|
|
8276
8673
|
}
|
|
8277
8674
|
}
|
|
8278
8675
|
function detectGoWork(root, found, warnings) {
|
|
8279
|
-
const gowork = readText(
|
|
8676
|
+
const gowork = readText(join11(root, "go.work"));
|
|
8280
8677
|
if (!gowork) return;
|
|
8281
8678
|
const dirs = [];
|
|
8282
8679
|
for (const block of gowork.matchAll(/^use\s*\(([\s\S]*?)\)/gm)) {
|
|
@@ -8292,7 +8689,7 @@ function detectGoWork(root, found, warnings) {
|
|
|
8292
8689
|
}
|
|
8293
8690
|
}
|
|
8294
8691
|
function detectMavenModules(root, found, warnings) {
|
|
8295
|
-
const pom = readText(
|
|
8692
|
+
const pom = readText(join11(root, "pom.xml"));
|
|
8296
8693
|
if (!pom) return;
|
|
8297
8694
|
const modules = pom.match(/<modules>([\s\S]*?)<\/modules>/)?.[1];
|
|
8298
8695
|
if (!modules) return;
|
|
@@ -8301,7 +8698,7 @@ function detectMavenModules(root, found, warnings) {
|
|
|
8301
8698
|
}
|
|
8302
8699
|
}
|
|
8303
8700
|
function detectUvMembers(root, found, warnings) {
|
|
8304
|
-
const toml = readText(
|
|
8701
|
+
const toml = readText(join11(root, "pyproject.toml"));
|
|
8305
8702
|
if (!toml) return;
|
|
8306
8703
|
const body2 = tomlSectionBody(toml, "tool.uv.workspace");
|
|
8307
8704
|
if (!body2) return;
|
|
@@ -8316,7 +8713,7 @@ function detectUvMembers(root, found, warnings) {
|
|
|
8316
8713
|
}
|
|
8317
8714
|
}
|
|
8318
8715
|
function detectComposerPathRepos(root, found, warnings) {
|
|
8319
|
-
const composer = readJson(
|
|
8716
|
+
const composer = readJson(join11(root, "composer.json"), "composer.json", warnings);
|
|
8320
8717
|
const repos = composer?.repositories;
|
|
8321
8718
|
if (!Array.isArray(repos)) return;
|
|
8322
8719
|
for (const r of repos) {
|
|
@@ -8327,7 +8724,7 @@ function detectComposerPathRepos(root, found, warnings) {
|
|
|
8327
8724
|
}
|
|
8328
8725
|
function detectGradleIncludes(root, found, warnings) {
|
|
8329
8726
|
for (const f of ["settings.gradle", "settings.gradle.kts"]) {
|
|
8330
|
-
const text = readText(
|
|
8727
|
+
const text = readText(join11(root, f));
|
|
8331
8728
|
if (!text) continue;
|
|
8332
8729
|
for (const line of text.split(/\r?\n/)) {
|
|
8333
8730
|
if (!/^\s*include[\s(]/.test(line)) continue;
|
|
@@ -8339,7 +8736,7 @@ function detectGradleIncludes(root, found, warnings) {
|
|
|
8339
8736
|
}
|
|
8340
8737
|
}
|
|
8341
8738
|
function npmEdges(root, pkg, byName, warnings) {
|
|
8342
|
-
const manifest = readJson(
|
|
8739
|
+
const manifest = readJson(join11(root, pkg.dir, "package.json"), `${pkg.dir}/package.json`, warnings);
|
|
8343
8740
|
if (!manifest) return [];
|
|
8344
8741
|
const edges = /* @__PURE__ */ new Set();
|
|
8345
8742
|
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
@@ -8362,7 +8759,7 @@ function normalizeDepPath(fromDir, rel) {
|
|
|
8362
8759
|
return out2.join("/");
|
|
8363
8760
|
}
|
|
8364
8761
|
function cargoEdges(root, pkg, byName, byDir) {
|
|
8365
|
-
const toml = readText(
|
|
8762
|
+
const toml = readText(join11(root, pkg.dir, "Cargo.toml"));
|
|
8366
8763
|
if (!toml) return [];
|
|
8367
8764
|
const edges = /* @__PURE__ */ new Set();
|
|
8368
8765
|
for (const section of ["dependencies", "dev-dependencies", "build-dependencies"]) {
|
|
@@ -8386,7 +8783,7 @@ function cargoEdges(root, pkg, byName, byDir) {
|
|
|
8386
8783
|
return [...edges];
|
|
8387
8784
|
}
|
|
8388
8785
|
function goPkgEdges(root, pkg, byName, byDir) {
|
|
8389
|
-
const gomod = readText(
|
|
8786
|
+
const gomod = readText(join11(root, pkg.dir, "go.mod"));
|
|
8390
8787
|
if (!gomod) return [];
|
|
8391
8788
|
const edges = /* @__PURE__ */ new Set();
|
|
8392
8789
|
for (const m of gomod.matchAll(/^\s*(?:require\s+)?([^\s/(][^\s]*)\s+v[^\s]+/gm)) {
|
|
@@ -8400,7 +8797,7 @@ function goPkgEdges(root, pkg, byName, byDir) {
|
|
|
8400
8797
|
return [...edges];
|
|
8401
8798
|
}
|
|
8402
8799
|
function mavenEdges(root, pkg, byName) {
|
|
8403
|
-
const pom = readText(
|
|
8800
|
+
const pom = readText(join11(root, pkg.dir, "pom.xml"));
|
|
8404
8801
|
if (!pom) return [];
|
|
8405
8802
|
const edges = /* @__PURE__ */ new Set();
|
|
8406
8803
|
for (const m of pom.replace(/<parent>[\s\S]*?<\/parent>/g, "").matchAll(/<dependency>([\s\S]*?)<\/dependency>/g)) {
|
|
@@ -8410,7 +8807,7 @@ function mavenEdges(root, pkg, byName) {
|
|
|
8410
8807
|
return [...edges];
|
|
8411
8808
|
}
|
|
8412
8809
|
function uvEdges(root, pkg, byName) {
|
|
8413
|
-
const toml = readText(
|
|
8810
|
+
const toml = readText(join11(root, pkg.dir, "pyproject.toml"));
|
|
8414
8811
|
if (!toml) return [];
|
|
8415
8812
|
const edges = /* @__PURE__ */ new Set();
|
|
8416
8813
|
const project = tomlSectionBody(toml, "project");
|
|
@@ -8430,7 +8827,7 @@ function uvEdges(root, pkg, byName) {
|
|
|
8430
8827
|
return [...edges];
|
|
8431
8828
|
}
|
|
8432
8829
|
function composerEdges(root, pkg, byName, warnings) {
|
|
8433
|
-
const manifest = readJson(
|
|
8830
|
+
const manifest = readJson(join11(root, pkg.dir, "composer.json"), `${pkg.dir}/composer.json`, warnings);
|
|
8434
8831
|
if (!manifest) return [];
|
|
8435
8832
|
const edges = /* @__PURE__ */ new Set();
|
|
8436
8833
|
for (const field of ["require", "require-dev"]) {
|
|
@@ -8444,7 +8841,7 @@ function composerEdges(root, pkg, byName, warnings) {
|
|
|
8444
8841
|
}
|
|
8445
8842
|
function gradleEdges(root, pkg, byName, byDir) {
|
|
8446
8843
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
8447
|
-
const text = readText(
|
|
8844
|
+
const text = readText(join11(root, pkg.dir, f));
|
|
8448
8845
|
if (!text) continue;
|
|
8449
8846
|
const edges = /* @__PURE__ */ new Set();
|
|
8450
8847
|
for (const m of text.matchAll(/project\s*\(\s*["']:?([^"']+)["']\s*\)/g)) {
|
|
@@ -9027,73 +9424,6 @@ var init_surprise = __esm({
|
|
|
9027
9424
|
}
|
|
9028
9425
|
});
|
|
9029
9426
|
|
|
9030
|
-
// src/render/symbols-json.ts
|
|
9031
|
-
function computeSymbolRefs(scan2) {
|
|
9032
|
-
const unique = uniqueSymbolDefs(scan2);
|
|
9033
|
-
const refs = /* @__PURE__ */ new Map();
|
|
9034
|
-
if (!unique.size) return refs;
|
|
9035
|
-
const add = (name2, file) => {
|
|
9036
|
-
let set = refs.get(name2);
|
|
9037
|
-
if (!set) refs.set(name2, set = /* @__PURE__ */ new Set());
|
|
9038
|
-
set.add(file);
|
|
9039
|
-
};
|
|
9040
|
-
for (const f of scan2.files) {
|
|
9041
|
-
if (f.kind === "code" && f.idents) {
|
|
9042
|
-
for (const id of f.idents) {
|
|
9043
|
-
const target = unique.get(id);
|
|
9044
|
-
if (target && target !== f.rel) add(id, f.rel);
|
|
9045
|
-
}
|
|
9046
|
-
} else if (f.kind === "doc") {
|
|
9047
|
-
const content = scan2.docText.get(f.rel);
|
|
9048
|
-
if (!content) continue;
|
|
9049
|
-
for (const tok of content.split(/[^A-Za-z0-9_]+/)) {
|
|
9050
|
-
const target = unique.get(tok);
|
|
9051
|
-
if (target && target !== f.rel) add(tok, f.rel);
|
|
9052
|
-
}
|
|
9053
|
-
}
|
|
9054
|
-
}
|
|
9055
|
-
return refs;
|
|
9056
|
-
}
|
|
9057
|
-
function buildSymbolIndex(scan2, refs = /* @__PURE__ */ new Map()) {
|
|
9058
|
-
const defsByName = /* @__PURE__ */ new Map();
|
|
9059
|
-
for (const f of scan2.files) {
|
|
9060
|
-
for (const s of f.symbols) {
|
|
9061
|
-
let arr = defsByName.get(s.name);
|
|
9062
|
-
if (!arr) defsByName.set(s.name, arr = []);
|
|
9063
|
-
arr.push({
|
|
9064
|
-
file: s.file,
|
|
9065
|
-
line: s.line,
|
|
9066
|
-
...s.endLine !== void 0 ? { endLine: s.endLine } : {},
|
|
9067
|
-
kind: s.kind,
|
|
9068
|
-
exported: s.exported,
|
|
9069
|
-
lang: s.lang,
|
|
9070
|
-
...s.parent ? { parent: s.parent } : {}
|
|
9071
|
-
});
|
|
9072
|
-
}
|
|
9073
|
-
}
|
|
9074
|
-
const defs = {};
|
|
9075
|
-
for (const name2 of [...defsByName.keys()].sort(byStr)) {
|
|
9076
|
-
defs[name2] = defsByName.get(name2).slice().sort((a, b) => byStr(a.file, b.file) || a.line - b.line || byStr(a.kind, b.kind));
|
|
9077
|
-
}
|
|
9078
|
-
const refsOut = {};
|
|
9079
|
-
for (const name2 of [...refs.keys()].sort(byStr)) {
|
|
9080
|
-
const files = [...refs.get(name2)].sort(byStr);
|
|
9081
|
-
if (files.length) refsOut[name2] = files;
|
|
9082
|
-
}
|
|
9083
|
-
return { schemaVersion: SCHEMA_VERSION, defs, refs: refsOut };
|
|
9084
|
-
}
|
|
9085
|
-
function renderSymbolsJson(index) {
|
|
9086
|
-
return JSON.stringify(index, null, 2) + "\n";
|
|
9087
|
-
}
|
|
9088
|
-
var init_symbols_json = __esm({
|
|
9089
|
-
"src/render/symbols-json.ts"() {
|
|
9090
|
-
"use strict";
|
|
9091
|
-
init_types();
|
|
9092
|
-
init_sort();
|
|
9093
|
-
init_graph();
|
|
9094
|
-
}
|
|
9095
|
-
});
|
|
9096
|
-
|
|
9097
9427
|
// src/render/graph-json.ts
|
|
9098
9428
|
function sortObject(obj) {
|
|
9099
9429
|
const out2 = {};
|
|
@@ -9113,8 +9443,10 @@ var init_graph_json = __esm({
|
|
|
9113
9443
|
|
|
9114
9444
|
// src/pipeline.ts
|
|
9115
9445
|
function buildIndexArtifacts(repo, opts = {}) {
|
|
9116
|
-
|
|
9117
|
-
|
|
9446
|
+
return buildArtifactsFromScan(scanRepo(repo, opts), opts);
|
|
9447
|
+
}
|
|
9448
|
+
function buildArtifactsFromScan(scan2, opts = {}) {
|
|
9449
|
+
const ctx = resolveContextFor(scan2);
|
|
9118
9450
|
const { modules, moduleOf } = buildModules(scan2);
|
|
9119
9451
|
const graph = buildGraph(scan2, ctx, modules, moduleOf, opts.meta);
|
|
9120
9452
|
const communities = detectCommunities(graph.modules, graph.moduleEdges, opts.previousCommunities);
|
|
@@ -9133,14 +9465,14 @@ function buildIndexArtifacts(repo, opts = {}) {
|
|
|
9133
9465
|
}
|
|
9134
9466
|
const surprises = computeSurprises(graph);
|
|
9135
9467
|
if (surprises.length) graph.surprises = surprises;
|
|
9136
|
-
const symbols = buildSymbolIndex(scan2,
|
|
9468
|
+
const symbols = buildSymbolIndex(scan2, symbolRefsFor(scan2));
|
|
9137
9469
|
return { scan: scan2, graph, symbols };
|
|
9138
9470
|
}
|
|
9139
9471
|
var init_pipeline = __esm({
|
|
9140
9472
|
"src/pipeline.ts"() {
|
|
9141
9473
|
"use strict";
|
|
9142
9474
|
init_scan();
|
|
9143
|
-
|
|
9475
|
+
init_derived();
|
|
9144
9476
|
init_modules();
|
|
9145
9477
|
init_graph();
|
|
9146
9478
|
init_community();
|
|
@@ -9180,241 +9512,69 @@ function rgBackend(root, pattern, opts) {
|
|
|
9180
9512
|
const user = opts.globs ?? [];
|
|
9181
9513
|
const anchor = (g) => g.startsWith("/") ? g : `/${g}`;
|
|
9182
9514
|
for (const g of user.filter((g2) => !g2.startsWith("!"))) args2.push("--glob", anchor(g));
|
|
9183
|
-
for (const g of user.filter((g2) => g2.startsWith("!"))) args2.push("--glob", `!${anchor(g.slice(1))}`);
|
|
9184
|
-
args2.push("--regexp", pattern, "./");
|
|
9185
|
-
const res = sh("rg", args2, { cwd: root });
|
|
9186
|
-
if (res.missing || !res.ok && res.status !== 1) return void 0;
|
|
9187
|
-
const hits = [];
|
|
9188
|
-
for (const line of res.stdout.split("\n")) {
|
|
9189
|
-
if (!line) continue;
|
|
9190
|
-
const nul = line.indexOf("\0");
|
|
9191
|
-
if (nul === -1) continue;
|
|
9192
|
-
const file = line.slice(0, nul).replace(/^\.\//, "");
|
|
9193
|
-
const rest = line.slice(nul + 1);
|
|
9194
|
-
const colon = rest.indexOf(":");
|
|
9195
|
-
if (colon === -1) continue;
|
|
9196
|
-
hits.push({ file, line: Number(rest.slice(0, colon)), text: rest.slice(colon + 1) });
|
|
9197
|
-
}
|
|
9198
|
-
return hits;
|
|
9199
|
-
}
|
|
9200
|
-
function jsBackend(root, re, opts) {
|
|
9201
|
-
const filter = compileGlobFilter(opts.globs?.map((g) => g.replace(/^(!?)\//, "$1")));
|
|
9202
|
-
const hits = [];
|
|
9203
|
-
for (const f of walk(root).files) {
|
|
9204
|
-
if (filter && !filter(f.rel)) continue;
|
|
9205
|
-
const content = readText(f.abs);
|
|
9206
|
-
if (!content) continue;
|
|
9207
|
-
const lines = content.split("\n");
|
|
9208
|
-
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
9209
|
-
if (re.test(lines[i2])) hits.push({ file: f.rel, line: i2 + 1, text: lines[i2] });
|
|
9210
|
-
}
|
|
9211
|
-
}
|
|
9212
|
-
return hits;
|
|
9213
|
-
}
|
|
9214
|
-
function grepRepo(root, pattern, opts = {}) {
|
|
9215
|
-
const re = new RegExp(pattern, opts.ignoreCase ? "i" : "");
|
|
9216
|
-
const max = opts.maxHits ?? DEFAULT_MAX_HITS;
|
|
9217
|
-
let hits;
|
|
9218
|
-
if (!opts.noRipgrep && have("rg")) hits = rgBackend(root, pattern, opts);
|
|
9219
|
-
hits ??= jsBackend(root, re, opts);
|
|
9220
|
-
return sortHits(hits).slice(0, max);
|
|
9221
|
-
}
|
|
9222
|
-
var DEFAULT_MAX_HITS;
|
|
9223
|
-
var init_grep = __esm({
|
|
9224
|
-
"src/grep.ts"() {
|
|
9225
|
-
"use strict";
|
|
9226
|
-
init_walk();
|
|
9227
|
-
init_glob();
|
|
9228
|
-
init_util();
|
|
9229
|
-
init_sort();
|
|
9230
|
-
DEFAULT_MAX_HITS = 200;
|
|
9231
|
-
}
|
|
9232
|
-
});
|
|
9233
|
-
|
|
9234
|
-
// src/bm25.ts
|
|
9235
|
-
function subtokens(raw) {
|
|
9236
|
-
const folded = foldText(raw).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
|
|
9237
|
-
const out2 = [];
|
|
9238
|
-
const seen = /* @__PURE__ */ new Set();
|
|
9239
|
-
const push = (t) => {
|
|
9240
|
-
if (t.length < 2 || seen.has(t)) return;
|
|
9241
|
-
seen.add(t);
|
|
9242
|
-
out2.push(t);
|
|
9243
|
-
};
|
|
9244
|
-
if (!/\s/.test(raw.trim())) push(foldText(raw).toLowerCase().replace(/[^a-z0-9_]+/g, ""));
|
|
9245
|
-
for (const part of folded.split(/[^A-Za-z0-9]+/)) push(part.toLowerCase());
|
|
9246
|
-
return out2;
|
|
9247
|
-
}
|
|
9248
|
-
function addTerms(doc, text) {
|
|
9249
|
-
for (const t of subtokens(text)) {
|
|
9250
|
-
doc.tf.set(t, (doc.tf.get(t) ?? 0) + 1);
|
|
9251
|
-
doc.len++;
|
|
9252
|
-
}
|
|
9253
|
-
}
|
|
9254
|
-
function buildDocs(scan2) {
|
|
9255
|
-
const docs = [];
|
|
9256
|
-
for (const f of scan2.files) {
|
|
9257
|
-
const doc = { file: f.rel, tf: /* @__PURE__ */ new Map(), len: 0, symbols: [] };
|
|
9258
|
-
const seenSym = /* @__PURE__ */ new Set();
|
|
9259
|
-
for (const s of f.symbols) {
|
|
9260
|
-
addTerms(doc, s.name);
|
|
9261
|
-
if (!seenSym.has(s.name)) {
|
|
9262
|
-
seenSym.add(s.name);
|
|
9263
|
-
doc.symbols.push(s.name);
|
|
9264
|
-
}
|
|
9265
|
-
}
|
|
9266
|
-
for (const seg of f.rel.split("/")) addTerms(doc, seg);
|
|
9267
|
-
for (const h of f.headings) addTerms(doc, h);
|
|
9268
|
-
if (f.summary) addTerms(doc, f.summary);
|
|
9269
|
-
docs.push(doc);
|
|
9270
|
-
}
|
|
9271
|
-
return docs;
|
|
9272
|
-
}
|
|
9273
|
-
function charTrigrams(term) {
|
|
9274
|
-
const padded = `^^${term}$$`;
|
|
9275
|
-
const grams = /* @__PURE__ */ new Set();
|
|
9276
|
-
for (let i2 = 0; i2 + 3 <= padded.length; i2++) grams.add(padded.slice(i2, i2 + 3));
|
|
9277
|
-
return grams;
|
|
9278
|
-
}
|
|
9279
|
-
function diceCoefficient(a, b) {
|
|
9280
|
-
if (!a.size || !b.size) return 0;
|
|
9281
|
-
let inter = 0;
|
|
9282
|
-
for (const g of a) if (b.has(g)) inter++;
|
|
9283
|
-
return 2 * inter / (a.size + b.size);
|
|
9284
|
-
}
|
|
9285
|
-
function buildTrigramIndex(docs) {
|
|
9286
|
-
const index = /* @__PURE__ */ new Map();
|
|
9287
|
-
for (const d of docs) {
|
|
9288
|
-
for (const term of d.tf.keys()) {
|
|
9289
|
-
if (!index.has(term)) index.set(term, charTrigrams(term));
|
|
9290
|
-
}
|
|
9291
|
-
}
|
|
9292
|
-
return index;
|
|
9293
|
-
}
|
|
9294
|
-
function searchIndex(scan2, query, opts = {}) {
|
|
9295
|
-
const terms = [];
|
|
9296
|
-
const seen = /* @__PURE__ */ new Set();
|
|
9297
|
-
for (const kw of keywords(query)) {
|
|
9298
|
-
for (const t of subtokens(kw)) {
|
|
9299
|
-
if (seen.has(t)) continue;
|
|
9300
|
-
seen.add(t);
|
|
9301
|
-
terms.push(t);
|
|
9302
|
-
}
|
|
9303
|
-
}
|
|
9304
|
-
if (!terms.length) return [];
|
|
9305
|
-
const docs = buildDocs(scan2);
|
|
9306
|
-
const n = docs.length;
|
|
9307
|
-
if (!n) return [];
|
|
9308
|
-
let totalLen = 0;
|
|
9309
|
-
for (const d of docs) totalLen += d.len;
|
|
9310
|
-
const avgLen = totalLen / n || 1;
|
|
9311
|
-
const df = /* @__PURE__ */ new Map();
|
|
9312
|
-
for (const t of terms) {
|
|
9313
|
-
let count = 0;
|
|
9314
|
-
for (const d of docs) if (d.tf.has(t)) count++;
|
|
9315
|
-
df.set(t, count);
|
|
9316
|
-
}
|
|
9317
|
-
const fuzzyEnabled = opts.fuzzy ?? true;
|
|
9318
|
-
const fuzzyCandidates = /* @__PURE__ */ new Map();
|
|
9319
|
-
if (fuzzyEnabled) {
|
|
9320
|
-
const unmatched = terms.filter((t) => df.get(t) === 0);
|
|
9321
|
-
if (unmatched.length) {
|
|
9322
|
-
const trigramIndex = buildTrigramIndex(docs);
|
|
9323
|
-
for (const t of unmatched) {
|
|
9324
|
-
const grams = charTrigrams(t);
|
|
9325
|
-
const candidates = [];
|
|
9326
|
-
for (const [vocabTerm, vocabGrams] of trigramIndex) {
|
|
9327
|
-
const dice = diceCoefficient(grams, vocabGrams);
|
|
9328
|
-
if (dice >= FUZZY_DICE_THRESHOLD) candidates.push({ term: vocabTerm, dice });
|
|
9329
|
-
}
|
|
9330
|
-
candidates.sort((a, b) => b.dice - a.dice || byStr(a.term, b.term));
|
|
9331
|
-
fuzzyCandidates.set(t, candidates.slice(0, FUZZY_CAP));
|
|
9332
|
-
}
|
|
9333
|
-
}
|
|
9515
|
+
for (const g of user.filter((g2) => g2.startsWith("!"))) args2.push("--glob", `!${anchor(g.slice(1))}`);
|
|
9516
|
+
args2.push("--regexp", pattern, "./");
|
|
9517
|
+
const res = sh("rg", args2, { cwd: root });
|
|
9518
|
+
if (res.missing || !res.ok && res.status !== 1) return void 0;
|
|
9519
|
+
const hits = [];
|
|
9520
|
+
for (const line of res.stdout.split("\n")) {
|
|
9521
|
+
if (!line) continue;
|
|
9522
|
+
const nul = line.indexOf("\0");
|
|
9523
|
+
if (nul === -1) continue;
|
|
9524
|
+
const file = line.slice(0, nul).replace(/^\.\//, "");
|
|
9525
|
+
const rest = line.slice(nul + 1);
|
|
9526
|
+
const colon = rest.indexOf(":");
|
|
9527
|
+
if (colon === -1) continue;
|
|
9528
|
+
hits.push({ file, line: Number(rest.slice(0, colon)), text: rest.slice(colon + 1) });
|
|
9334
9529
|
}
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
const matched = [];
|
|
9348
|
-
const symbolTerms = /* @__PURE__ */ new Set();
|
|
9349
|
-
const fuzzyHit = /* @__PURE__ */ new Set();
|
|
9350
|
-
for (const t of terms) {
|
|
9351
|
-
const tf = d.tf.get(t);
|
|
9352
|
-
if (tf) {
|
|
9353
|
-
matched.push(t);
|
|
9354
|
-
symbolTerms.add(t);
|
|
9355
|
-
const idf = Math.log(1 + (n - df.get(t) + 0.5) / (df.get(t) + 0.5));
|
|
9356
|
-
score += idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * d.len / avgLen));
|
|
9357
|
-
continue;
|
|
9358
|
-
}
|
|
9359
|
-
const candidates = fuzzyCandidates.get(t);
|
|
9360
|
-
if (!candidates) continue;
|
|
9361
|
-
for (const cand of candidates) {
|
|
9362
|
-
const ctf = d.tf.get(cand.term);
|
|
9363
|
-
if (!ctf) continue;
|
|
9364
|
-
const cdf = dfOfVocabTerm(cand.term);
|
|
9365
|
-
const idf = Math.log(1 + (n - cdf + 0.5) / (cdf + 0.5));
|
|
9366
|
-
const contribution = idf * (ctf * (K1 + 1)) / (ctf + K1 * (1 - B + B * d.len / avgLen));
|
|
9367
|
-
score += contribution * cand.dice;
|
|
9368
|
-
symbolTerms.add(cand.term);
|
|
9369
|
-
fuzzyHit.add(t);
|
|
9370
|
-
}
|
|
9530
|
+
return hits;
|
|
9531
|
+
}
|
|
9532
|
+
function jsBackend(root, re, opts) {
|
|
9533
|
+
const filter = compileGlobFilter(opts.globs?.map((g) => g.replace(/^(!?)\//, "$1")));
|
|
9534
|
+
const hits = [];
|
|
9535
|
+
for (const f of walk(root).files) {
|
|
9536
|
+
if (filter && !filter(f.rel)) continue;
|
|
9537
|
+
const content = readText(f.abs);
|
|
9538
|
+
if (!content) continue;
|
|
9539
|
+
const lines = content.split("\n");
|
|
9540
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
9541
|
+
if (re.test(lines[i2])) hits.push({ file: f.rel, line: i2 + 1, text: lines[i2] });
|
|
9371
9542
|
}
|
|
9372
|
-
if (!matched.length && !fuzzyHit.size) continue;
|
|
9373
|
-
const scored = d.symbols.map((name2) => {
|
|
9374
|
-
const toks = new Set(subtokens(name2));
|
|
9375
|
-
let hits = 0;
|
|
9376
|
-
for (const t of symbolTerms) if (toks.has(t)) hits++;
|
|
9377
|
-
return { name: name2, hits };
|
|
9378
|
-
}).filter((s) => s.hits > 0).sort((a, b) => b.hits - a.hits || byStr(a.name, b.name));
|
|
9379
|
-
const result = {
|
|
9380
|
-
file: d.file,
|
|
9381
|
-
score: Number(score.toFixed(4)),
|
|
9382
|
-
matchedTerms: matched.sort(byStr),
|
|
9383
|
-
topSymbols: scored.slice(0, TOP_SYMBOLS).map((s) => s.name)
|
|
9384
|
-
};
|
|
9385
|
-
if (fuzzyHit.size) result.fuzzyTerms = [...fuzzyHit].sort(byStr);
|
|
9386
|
-
results.push(result);
|
|
9387
9543
|
}
|
|
9388
|
-
|
|
9389
|
-
return results.slice(0, opts.limit ?? DEFAULT_LIMIT);
|
|
9544
|
+
return hits;
|
|
9390
9545
|
}
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9546
|
+
function grepRepo(root, pattern, opts = {}) {
|
|
9547
|
+
const re = new RegExp(pattern, opts.ignoreCase ? "i" : "");
|
|
9548
|
+
const max = opts.maxHits ?? DEFAULT_MAX_HITS;
|
|
9549
|
+
let hits;
|
|
9550
|
+
if (!opts.noRipgrep && have("rg")) hits = rgBackend(root, pattern, opts);
|
|
9551
|
+
hits ??= jsBackend(root, re, opts);
|
|
9552
|
+
return sortHits(hits).slice(0, max);
|
|
9553
|
+
}
|
|
9554
|
+
var DEFAULT_MAX_HITS;
|
|
9555
|
+
var init_grep = __esm({
|
|
9556
|
+
"src/grep.ts"() {
|
|
9394
9557
|
"use strict";
|
|
9558
|
+
init_walk();
|
|
9559
|
+
init_glob();
|
|
9395
9560
|
init_util();
|
|
9396
9561
|
init_sort();
|
|
9397
|
-
|
|
9398
|
-
B = 0.75;
|
|
9399
|
-
DEFAULT_LIMIT = 20;
|
|
9400
|
-
TOP_SYMBOLS = 5;
|
|
9401
|
-
FUZZY_DICE_THRESHOLD = 0.6;
|
|
9402
|
-
FUZZY_CAP = 3;
|
|
9562
|
+
DEFAULT_MAX_HITS = 200;
|
|
9403
9563
|
}
|
|
9404
9564
|
});
|
|
9405
9565
|
|
|
9406
9566
|
// src/embed/model.ts
|
|
9407
|
-
import { createHash as
|
|
9567
|
+
import { createHash as createHash3 } from "crypto";
|
|
9408
9568
|
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
9409
|
-
import { join as
|
|
9569
|
+
import { join as join13 } from "path";
|
|
9410
9570
|
function resolveEmbedModelDir(repo) {
|
|
9411
9571
|
const env = process.env.CODEINDEX_EMBED_DIR;
|
|
9412
9572
|
const candidates = [];
|
|
9413
9573
|
if (env) candidates.push(env);
|
|
9414
|
-
if (repo) candidates.push(
|
|
9415
|
-
candidates.push(
|
|
9574
|
+
if (repo) candidates.push(join13(repo, ".codeindex", DEFAULT_EMBED_DIRNAME));
|
|
9575
|
+
candidates.push(join13(process.cwd(), ".codeindex", DEFAULT_EMBED_DIRNAME));
|
|
9416
9576
|
for (const c2 of candidates) {
|
|
9417
|
-
if (existsSync3(
|
|
9577
|
+
if (existsSync3(join13(c2, "model.json"))) return c2;
|
|
9418
9578
|
}
|
|
9419
9579
|
return void 0;
|
|
9420
9580
|
}
|
|
@@ -9447,7 +9607,7 @@ function parseEmbedModel(raw, source) {
|
|
|
9447
9607
|
}
|
|
9448
9608
|
function loadEmbedModel(dir) {
|
|
9449
9609
|
if (!dir) return void 0;
|
|
9450
|
-
const path =
|
|
9610
|
+
const path = join13(dir, "model.json");
|
|
9451
9611
|
if (!existsSync3(path)) return void 0;
|
|
9452
9612
|
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
9453
9613
|
return parseEmbedModel(raw, path);
|
|
@@ -9462,7 +9622,7 @@ async function fetchEmbedModel(url, expectedSha256) {
|
|
|
9462
9622
|
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
|
|
9463
9623
|
const body2 = await res.text();
|
|
9464
9624
|
if (expectedSha256) {
|
|
9465
|
-
const got =
|
|
9625
|
+
const got = createHash3("sha256").update(body2).digest("hex");
|
|
9466
9626
|
if (got !== expectedSha256) {
|
|
9467
9627
|
throw new Error(`sha256 mismatch: expected ${expectedSha256}, got ${got}`);
|
|
9468
9628
|
}
|
|
@@ -10066,8 +10226,8 @@ var init_repomap = __esm({
|
|
|
10066
10226
|
|
|
10067
10227
|
// src/deadcode.ts
|
|
10068
10228
|
function findDeadCode(scan2) {
|
|
10069
|
-
const callers =
|
|
10070
|
-
const refs =
|
|
10229
|
+
const callers = callerIndexFor(scan2);
|
|
10230
|
+
const refs = symbolRefsFor(scan2);
|
|
10071
10231
|
const out2 = [];
|
|
10072
10232
|
const consider = (s) => s.exported && !REFERENCE_KINDS6.has(s.kind) && !isTestPath(s.file) && !ENTRYPOINT_RE.test(s.file);
|
|
10073
10233
|
for (const f of scan2.files) {
|
|
@@ -10086,8 +10246,7 @@ var REFERENCE_KINDS6, ENTRYPOINT_RE;
|
|
|
10086
10246
|
var init_deadcode = __esm({
|
|
10087
10247
|
"src/deadcode.ts"() {
|
|
10088
10248
|
"use strict";
|
|
10089
|
-
|
|
10090
|
-
init_symbols_json();
|
|
10249
|
+
init_derived();
|
|
10091
10250
|
init_tests_map();
|
|
10092
10251
|
init_sort();
|
|
10093
10252
|
REFERENCE_KINDS6 = /* @__PURE__ */ new Set(["reexport", "reexport-all", "default"]);
|
|
@@ -10095,49 +10254,6 @@ var init_deadcode = __esm({
|
|
|
10095
10254
|
}
|
|
10096
10255
|
});
|
|
10097
10256
|
|
|
10098
|
-
// src/complexity.ts
|
|
10099
|
-
import { join as join11 } from "path";
|
|
10100
|
-
function complexityOfSource(source) {
|
|
10101
|
-
return 1 + (source.match(BRANCH_RE) ?? []).length;
|
|
10102
|
-
}
|
|
10103
|
-
function symbolComplexity(scan2, rel, top = 50) {
|
|
10104
|
-
const out2 = [];
|
|
10105
|
-
for (const f of scan2.files) {
|
|
10106
|
-
if (f.kind !== "code") continue;
|
|
10107
|
-
if (rel && f.rel !== rel) continue;
|
|
10108
|
-
if (!f.symbols.length) continue;
|
|
10109
|
-
const lines = readText(join11(scan2.root, f.rel)).split("\n");
|
|
10110
|
-
for (const s of f.symbols) {
|
|
10111
|
-
if (s.kind === "reexport" || s.kind === "reexport-all") continue;
|
|
10112
|
-
const end = s.endLine ?? s.line;
|
|
10113
|
-
const body2 = lines.slice(s.line - 1, end).join("\n");
|
|
10114
|
-
const entry = { file: f.rel, name: s.name, line: s.line, complexity: complexityOfSource(body2) };
|
|
10115
|
-
if (s.endLine !== void 0) entry.endLine = s.endLine;
|
|
10116
|
-
out2.push(entry);
|
|
10117
|
-
}
|
|
10118
|
-
}
|
|
10119
|
-
out2.sort((a, b) => b.complexity - a.complexity || byStr(a.file, b.file) || a.line - b.line);
|
|
10120
|
-
return out2.slice(0, top);
|
|
10121
|
-
}
|
|
10122
|
-
function riskHotspots(scan2, churn, top = 20) {
|
|
10123
|
-
const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
|
|
10124
|
-
const complexity = complexityOfSource(readText(join11(scan2.root, f.rel)));
|
|
10125
|
-
const commits = churn.get(f.rel) ?? 0;
|
|
10126
|
-
return { file: f.rel, complexity, commits, score: (commits + 1) * complexity };
|
|
10127
|
-
});
|
|
10128
|
-
out2.sort((a, b) => b.score - a.score || byStr(a.file, b.file));
|
|
10129
|
-
return out2.slice(0, top);
|
|
10130
|
-
}
|
|
10131
|
-
var BRANCH_RE;
|
|
10132
|
-
var init_complexity = __esm({
|
|
10133
|
-
"src/complexity.ts"() {
|
|
10134
|
-
"use strict";
|
|
10135
|
-
init_walk();
|
|
10136
|
-
init_sort();
|
|
10137
|
-
BRANCH_RE = /\b(if|elif|elsif|else\s+if|for|foreach|while|until|unless|case|when|match|catch|rescue|except)\b|&&|\|\||(?<![?:])\?(?![?.:])/g;
|
|
10138
|
-
}
|
|
10139
|
-
});
|
|
10140
|
-
|
|
10141
10257
|
// src/viz.ts
|
|
10142
10258
|
function renderMermaid(graph, opts = {}) {
|
|
10143
10259
|
const maxEdges = opts.maxEdges ?? 80;
|
|
@@ -10178,13 +10294,17 @@ var init_viz = __esm({
|
|
|
10178
10294
|
// src/mcp.ts
|
|
10179
10295
|
var mcp_exports = {};
|
|
10180
10296
|
__export(mcp_exports, {
|
|
10297
|
+
getArtifacts: () => getArtifacts,
|
|
10298
|
+
getScan: () => getScan,
|
|
10181
10299
|
memoizedEmbedModel: () => memoizedEmbedModel,
|
|
10182
10300
|
memoizedEmbeddingIndex: () => memoizedEmbeddingIndex,
|
|
10183
10301
|
runMcpServer: () => runMcpServer,
|
|
10184
|
-
scanFingerprint: () => scanFingerprint
|
|
10302
|
+
scanFingerprint: () => scanFingerprint,
|
|
10303
|
+
toCacheMap: () => toCacheMap,
|
|
10304
|
+
warmGrammarsForRepo: () => warmGrammarsForRepo
|
|
10185
10305
|
});
|
|
10186
10306
|
import { statSync as statSync4 } from "fs";
|
|
10187
|
-
import { join as
|
|
10307
|
+
import { join as join14 } from "path";
|
|
10188
10308
|
import { createInterface } from "readline";
|
|
10189
10309
|
function str(v) {
|
|
10190
10310
|
return typeof v === "string" && v ? v : void 0;
|
|
@@ -10208,7 +10328,7 @@ async function memoizedEmbeddingIndex(key, build) {
|
|
|
10208
10328
|
function memoizedEmbedModel(modelDir) {
|
|
10209
10329
|
let stat;
|
|
10210
10330
|
try {
|
|
10211
|
-
stat = statSync4(
|
|
10331
|
+
stat = statSync4(join14(modelDir, "model.json"));
|
|
10212
10332
|
} catch {
|
|
10213
10333
|
return void 0;
|
|
10214
10334
|
}
|
|
@@ -10218,12 +10338,59 @@ function memoizedEmbedModel(modelDir) {
|
|
|
10218
10338
|
if (model) embedModelCache = { key, model };
|
|
10219
10339
|
return model;
|
|
10220
10340
|
}
|
|
10341
|
+
function sessionKey(repo, opts) {
|
|
10342
|
+
return repo + "\0" + JSON.stringify({
|
|
10343
|
+
scope: opts.scope,
|
|
10344
|
+
include: opts.include,
|
|
10345
|
+
exclude: opts.exclude,
|
|
10346
|
+
gitignore: opts.gitignore,
|
|
10347
|
+
ignoreDirs: opts.ignoreDirs,
|
|
10348
|
+
maxBytes: opts.maxBytes,
|
|
10349
|
+
maxFiles: opts.maxFiles,
|
|
10350
|
+
maxCallsPerFile: opts.maxCallsPerFile,
|
|
10351
|
+
out: opts.out,
|
|
10352
|
+
fullHash: opts.fullHash
|
|
10353
|
+
});
|
|
10354
|
+
}
|
|
10355
|
+
function toCacheMap(scan2) {
|
|
10356
|
+
const m = /* @__PURE__ */ new Map();
|
|
10357
|
+
for (const f of scan2.files) m.set(f.rel, { hash: f.hash, record: f, size: f.size, mtimeMs: scan2.mtimes.get(f.rel) });
|
|
10358
|
+
return m;
|
|
10359
|
+
}
|
|
10360
|
+
function getScan(repo, opts = {}) {
|
|
10361
|
+
const key = sessionKey(repo, opts);
|
|
10362
|
+
if (sessionCache && sessionCache.key === key) {
|
|
10363
|
+
const fresh = scanRepo(repo, { ...opts, cache: sessionCache.cacheMap });
|
|
10364
|
+
if (fresh.contentUnchanged) {
|
|
10365
|
+
if (fresh.cacheDirty) sessionCache.cacheMap = toCacheMap(fresh);
|
|
10366
|
+
if (sessionCache.scan.commit !== fresh.commit) sessionCache.scan.commit = fresh.commit;
|
|
10367
|
+
return sessionCache.scan;
|
|
10368
|
+
}
|
|
10369
|
+
sessionCache = { key, scan: fresh, cacheMap: toCacheMap(fresh) };
|
|
10370
|
+
return fresh;
|
|
10371
|
+
}
|
|
10372
|
+
const scan2 = scanRepo(repo, opts);
|
|
10373
|
+
sessionCache = { key, scan: scan2, cacheMap: toCacheMap(scan2) };
|
|
10374
|
+
return scan2;
|
|
10375
|
+
}
|
|
10376
|
+
function getArtifacts(repo, opts = {}) {
|
|
10377
|
+
const scan2 = getScan(repo, opts);
|
|
10378
|
+
if (sessionCache && sessionCache.scan === scan2) {
|
|
10379
|
+
return sessionCache.arts ??= buildArtifactsFromScan(scan2, opts);
|
|
10380
|
+
}
|
|
10381
|
+
return buildArtifactsFromScan(scan2, opts);
|
|
10382
|
+
}
|
|
10383
|
+
async function warmGrammarsForRepo(repo) {
|
|
10384
|
+
const { files } = walk(repo, {});
|
|
10385
|
+
await ensureGrammars(grammarKeysForExts(files.map((f) => f.ext)));
|
|
10386
|
+
}
|
|
10221
10387
|
async function callTool(name2, args2) {
|
|
10222
10388
|
const repo = str(args2.repo);
|
|
10223
10389
|
if (!repo) throw new Error("`repo` is required (absolute path to the repository root)");
|
|
10224
10390
|
const scanOpts = { scope: str(args2.scope), include: strArray(args2.include), exclude: strArray(args2.exclude) };
|
|
10391
|
+
if (!SCANLESS_TOOLS.has(name2)) await warmGrammarsForRepo(repo);
|
|
10225
10392
|
if (name2 === "scan_summary") {
|
|
10226
|
-
const scan2 =
|
|
10393
|
+
const scan2 = getScan(repo, scanOpts);
|
|
10227
10394
|
return JSON.stringify(
|
|
10228
10395
|
{ engineVersion: ENGINE_VERSION, commit: scan2.commit, fileCount: scan2.files.length, languages: scan2.languages, capped: scan2.capped },
|
|
10229
10396
|
null,
|
|
@@ -10231,10 +10398,10 @@ async function callTool(name2, args2) {
|
|
|
10231
10398
|
);
|
|
10232
10399
|
}
|
|
10233
10400
|
if (name2 === "graph") {
|
|
10234
|
-
return renderGraphJson(
|
|
10401
|
+
return renderGraphJson(getArtifacts(repo, scanOpts).graph);
|
|
10235
10402
|
}
|
|
10236
10403
|
if (name2 === "symbols") {
|
|
10237
|
-
const { symbols } =
|
|
10404
|
+
const { symbols } = getArtifacts(repo, scanOpts);
|
|
10238
10405
|
const lookup = str(args2.name);
|
|
10239
10406
|
if (lookup) {
|
|
10240
10407
|
return JSON.stringify({ name: lookup, defs: symbols.defs[lookup] ?? [], refs: symbols.refs[lookup] ?? [] }, null, 2);
|
|
@@ -10242,7 +10409,7 @@ async function callTool(name2, args2) {
|
|
|
10242
10409
|
return JSON.stringify(symbols, null, 2);
|
|
10243
10410
|
}
|
|
10244
10411
|
if (name2 === "callers") {
|
|
10245
|
-
const index = buildCallerIndex(
|
|
10412
|
+
const index = buildCallerIndex(getScan(repo, scanOpts));
|
|
10246
10413
|
const lookup = str(args2.name);
|
|
10247
10414
|
if (lookup) {
|
|
10248
10415
|
const entry = index.get(lookup);
|
|
@@ -10265,12 +10432,12 @@ async function callTool(name2, args2) {
|
|
|
10265
10432
|
if (name2 === "symbols_overview") {
|
|
10266
10433
|
const file = str(args2.file);
|
|
10267
10434
|
if (!file) throw new Error("`file` is required");
|
|
10268
|
-
return JSON.stringify(symbolsOverview(
|
|
10435
|
+
return JSON.stringify(symbolsOverview(getScan(repo, scanOpts), file), null, 2);
|
|
10269
10436
|
}
|
|
10270
10437
|
if (name2 === "find_symbol") {
|
|
10271
10438
|
const namePath = str(args2.namePath);
|
|
10272
10439
|
if (!namePath) throw new Error("`namePath` is required");
|
|
10273
|
-
const matches = findSymbol(
|
|
10440
|
+
const matches = findSymbol(getScan(repo, scanOpts), namePath, {
|
|
10274
10441
|
substring: args2.substring === true,
|
|
10275
10442
|
includeBody: args2.includeBody === true
|
|
10276
10443
|
});
|
|
@@ -10279,15 +10446,17 @@ async function callTool(name2, args2) {
|
|
|
10279
10446
|
if (name2 === "find_references") {
|
|
10280
10447
|
const symName = str(args2.name);
|
|
10281
10448
|
if (!symName) throw new Error("`name` is required");
|
|
10282
|
-
return JSON.stringify(findReferences(
|
|
10449
|
+
return JSON.stringify(findReferences(getScan(repo, scanOpts), symName), null, 2);
|
|
10283
10450
|
}
|
|
10284
10451
|
if (name2 === "replace_symbol_body" || name2 === "insert_after_symbol" || name2 === "insert_before_symbol") {
|
|
10285
10452
|
const namePath = str(args2.namePath);
|
|
10286
10453
|
const body2 = typeof args2.body === "string" ? args2.body : void 0;
|
|
10287
10454
|
if (!namePath || body2 === void 0) throw new Error("`namePath` and `body` are required");
|
|
10288
|
-
const scan2 =
|
|
10455
|
+
const scan2 = getScan(repo, scanOpts);
|
|
10289
10456
|
const fn = name2 === "replace_symbol_body" ? replaceSymbolBody : name2 === "insert_after_symbol" ? insertAfterSymbol : insertBeforeSymbol;
|
|
10290
|
-
|
|
10457
|
+
const result = fn(scan2, namePath, body2, str(args2.file));
|
|
10458
|
+
sessionCache = void 0;
|
|
10459
|
+
return JSON.stringify(result, null, 2);
|
|
10291
10460
|
}
|
|
10292
10461
|
if (name2 === "write_memory") {
|
|
10293
10462
|
const memName = str(args2.name);
|
|
@@ -10311,10 +10480,10 @@ async function callTool(name2, args2) {
|
|
|
10311
10480
|
return JSON.stringify({ deleted: deleteMemory(repo, memName) }, null, 2);
|
|
10312
10481
|
}
|
|
10313
10482
|
if (name2 === "dead_code") {
|
|
10314
|
-
return JSON.stringify(findDeadCode(
|
|
10483
|
+
return JSON.stringify(findDeadCode(getScan(repo, scanOpts)), null, 2);
|
|
10315
10484
|
}
|
|
10316
10485
|
if (name2 === "complexity") {
|
|
10317
|
-
const scan2 =
|
|
10486
|
+
const scan2 = getScan(repo, scanOpts);
|
|
10318
10487
|
if (args2.risk === true) {
|
|
10319
10488
|
const { churn, ok } = gitChurn(repo);
|
|
10320
10489
|
return JSON.stringify({ churnOk: ok, risks: riskHotspots(scan2, churn) }, null, 2);
|
|
@@ -10322,15 +10491,15 @@ async function callTool(name2, args2) {
|
|
|
10322
10491
|
return JSON.stringify(symbolComplexity(scan2, str(args2.file)), null, 2);
|
|
10323
10492
|
}
|
|
10324
10493
|
if (name2 === "mermaid") {
|
|
10325
|
-
const { graph } =
|
|
10494
|
+
const { graph } = getArtifacts(repo, scanOpts);
|
|
10326
10495
|
return renderMermaid(graph, { module: str(args2.module) });
|
|
10327
10496
|
}
|
|
10328
10497
|
if (name2 === "repo_map") {
|
|
10329
|
-
const { scan: scan2, graph } =
|
|
10498
|
+
const { scan: scan2, graph } = getArtifacts(repo, scanOpts);
|
|
10330
10499
|
return renderRepoMap(scan2, graph, { budgetTokens: typeof args2.budgetTokens === "number" ? args2.budgetTokens : void 0 });
|
|
10331
10500
|
}
|
|
10332
10501
|
if (name2 === "hotspots") {
|
|
10333
|
-
const scan2 =
|
|
10502
|
+
const scan2 = getScan(repo, scanOpts);
|
|
10334
10503
|
const { churn, ok } = gitChurn(repo, { since: str(args2.since) });
|
|
10335
10504
|
return JSON.stringify({ churnOk: ok, hotspots: rankHotspots(scan2, churn) }, null, 2);
|
|
10336
10505
|
}
|
|
@@ -10351,7 +10520,7 @@ async function callTool(name2, args2) {
|
|
|
10351
10520
|
if (name2 === "search") {
|
|
10352
10521
|
const query = str(args2.query);
|
|
10353
10522
|
if (!query) throw new Error("`query` is required");
|
|
10354
|
-
const scan2 =
|
|
10523
|
+
const scan2 = getScan(repo, scanOpts);
|
|
10355
10524
|
const limit = typeof args2.limit === "number" ? args2.limit : void 0;
|
|
10356
10525
|
const fuzzy = typeof args2.fuzzy === "boolean" ? args2.fuzzy : void 0;
|
|
10357
10526
|
if (args2.semantic === true) {
|
|
@@ -10406,13 +10575,16 @@ async function callTool(name2, args2) {
|
|
|
10406
10575
|
}
|
|
10407
10576
|
if (name2 === "check_rules") {
|
|
10408
10577
|
const rules = parseRules(args2.rules);
|
|
10409
|
-
const { graph } =
|
|
10578
|
+
const { graph } = getArtifacts(repo, scanOpts);
|
|
10410
10579
|
return JSON.stringify(checkRules(graph, rules), null, 2);
|
|
10411
10580
|
}
|
|
10412
10581
|
throw new Error(`unknown tool: ${name2}`);
|
|
10413
10582
|
}
|
|
10414
|
-
async function runMcpServer() {
|
|
10415
|
-
|
|
10583
|
+
async function runMcpServer(opts = {}) {
|
|
10584
|
+
const serverInfo = {
|
|
10585
|
+
name: opts.serverInfo?.name ?? "codeindex",
|
|
10586
|
+
version: opts.serverInfo?.version ?? ENGINE_VERSION
|
|
10587
|
+
};
|
|
10416
10588
|
const send = (msg) => {
|
|
10417
10589
|
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", ...msg }) + "\n");
|
|
10418
10590
|
};
|
|
@@ -10439,7 +10611,7 @@ async function runMcpServer() {
|
|
|
10439
10611
|
result: {
|
|
10440
10612
|
protocolVersion: "2024-11-05",
|
|
10441
10613
|
capabilities: { tools: {} },
|
|
10442
|
-
serverInfo
|
|
10614
|
+
serverInfo
|
|
10443
10615
|
}
|
|
10444
10616
|
});
|
|
10445
10617
|
} else if (req.method === "ping") {
|
|
@@ -10467,7 +10639,7 @@ async function runMcpServer() {
|
|
|
10467
10639
|
}
|
|
10468
10640
|
}
|
|
10469
10641
|
}
|
|
10470
|
-
var repoProp, scopeProps, TOOLS, embeddingIndexCache, embedModelCache;
|
|
10642
|
+
var repoProp, scopeProps, TOOLS, embeddingIndexCache, embedModelCache, sessionCache, SCANLESS_TOOLS;
|
|
10471
10643
|
var init_mcp = __esm({
|
|
10472
10644
|
"src/mcp.ts"() {
|
|
10473
10645
|
"use strict";
|
|
@@ -10476,6 +10648,7 @@ var init_mcp = __esm({
|
|
|
10476
10648
|
init_pipeline();
|
|
10477
10649
|
init_graph_json();
|
|
10478
10650
|
init_scan();
|
|
10651
|
+
init_walk();
|
|
10479
10652
|
init_callers();
|
|
10480
10653
|
init_workspaces();
|
|
10481
10654
|
init_git();
|
|
@@ -10746,6 +10919,17 @@ var init_mcp = __esm({
|
|
|
10746
10919
|
}
|
|
10747
10920
|
}
|
|
10748
10921
|
];
|
|
10922
|
+
SCANLESS_TOOLS = /* @__PURE__ */ new Set([
|
|
10923
|
+
"workspaces",
|
|
10924
|
+
"churn",
|
|
10925
|
+
"coupling",
|
|
10926
|
+
"grep",
|
|
10927
|
+
"write_memory",
|
|
10928
|
+
"read_memory",
|
|
10929
|
+
"list_memories",
|
|
10930
|
+
"delete_memory",
|
|
10931
|
+
"embed_status"
|
|
10932
|
+
]);
|
|
10749
10933
|
}
|
|
10750
10934
|
});
|
|
10751
10935
|
|
|
@@ -10885,6 +11069,106 @@ init_code();
|
|
|
10885
11069
|
init_markdown();
|
|
10886
11070
|
init_loader();
|
|
10887
11071
|
init_extract();
|
|
11072
|
+
init_loader();
|
|
11073
|
+
|
|
11074
|
+
// src/ast/grammars-pull.ts
|
|
11075
|
+
init_types();
|
|
11076
|
+
import { createHash as createHash2 } from "crypto";
|
|
11077
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
11078
|
+
import { dirname as dirname2, resolve, sep as sep2 } from "path";
|
|
11079
|
+
import { gunzipSync } from "zlib";
|
|
11080
|
+
var DEFAULT_GRAMMARS_URL = `https://github.com/maxgfr/codeindex/releases/download/v${ENGINE_VERSION}/grammars-${ENGINE_VERSION}.tar.gz`;
|
|
11081
|
+
function resolveGrammarsPullTarget() {
|
|
11082
|
+
const env = process.env.CODEINDEX_GRAMMARS_URL;
|
|
11083
|
+
if (env && env.trim()) return { url: env.trim() };
|
|
11084
|
+
return { url: DEFAULT_GRAMMARS_URL, sha256Url: `${DEFAULT_GRAMMARS_URL}.sha256` };
|
|
11085
|
+
}
|
|
11086
|
+
async function fetchGrammarsTarball(url, expectedSha256) {
|
|
11087
|
+
const res = await fetch(url);
|
|
11088
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
|
|
11089
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
11090
|
+
if (expectedSha256) {
|
|
11091
|
+
const got = createHash2("sha256").update(buf).digest("hex");
|
|
11092
|
+
if (got !== expectedSha256) {
|
|
11093
|
+
throw new Error(`sha256 mismatch: expected ${expectedSha256}, got ${got}`);
|
|
11094
|
+
}
|
|
11095
|
+
}
|
|
11096
|
+
return buf;
|
|
11097
|
+
}
|
|
11098
|
+
function asBuffer(u) {
|
|
11099
|
+
return Buffer.isBuffer(u) ? u : Buffer.from(u.buffer, u.byteOffset, u.byteLength);
|
|
11100
|
+
}
|
|
11101
|
+
async function fetchExpectedSha256(url) {
|
|
11102
|
+
const res = await fetch(url);
|
|
11103
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
|
|
11104
|
+
const text = await res.text();
|
|
11105
|
+
const hex = (text.trim().split(/\s+/)[0] ?? "").toLowerCase();
|
|
11106
|
+
if (!/^[0-9a-f]{64}$/.test(hex)) throw new Error(`invalid sha256 sidecar at ${url}`);
|
|
11107
|
+
return hex;
|
|
11108
|
+
}
|
|
11109
|
+
function cstr(block, start2, len) {
|
|
11110
|
+
const slice = block.subarray(start2, start2 + len);
|
|
11111
|
+
const nul = slice.indexOf(0);
|
|
11112
|
+
return slice.toString("utf8", 0, nul === -1 ? slice.length : nul);
|
|
11113
|
+
}
|
|
11114
|
+
function* readTar(buf) {
|
|
11115
|
+
let off = 0;
|
|
11116
|
+
while (off + 512 <= buf.length) {
|
|
11117
|
+
const block = buf.subarray(off, off + 512);
|
|
11118
|
+
let allZero = true;
|
|
11119
|
+
for (let i2 = 0; i2 < 512; i2++) {
|
|
11120
|
+
if (block[i2] !== 0) {
|
|
11121
|
+
allZero = false;
|
|
11122
|
+
break;
|
|
11123
|
+
}
|
|
11124
|
+
}
|
|
11125
|
+
if (allZero) break;
|
|
11126
|
+
const name2 = cstr(block, 0, 100);
|
|
11127
|
+
const prefix = cstr(block, 345, 155);
|
|
11128
|
+
const sizeStr = cstr(block, 124, 12).trim();
|
|
11129
|
+
const size = sizeStr ? parseInt(sizeStr, 8) : 0;
|
|
11130
|
+
const type = String.fromCharCode(block[156] ?? 0);
|
|
11131
|
+
off += 512;
|
|
11132
|
+
const data = buf.subarray(off, off + size);
|
|
11133
|
+
off += Math.ceil(size / 512) * 512;
|
|
11134
|
+
yield { name: prefix ? `${prefix}/${name2}` : name2, type, data };
|
|
11135
|
+
}
|
|
11136
|
+
}
|
|
11137
|
+
function safeRelPath(name2) {
|
|
11138
|
+
if (!name2 || name2.includes("\0")) return null;
|
|
11139
|
+
if (name2.startsWith("/") || name2.startsWith("\\") || /^[A-Za-z]:/.test(name2)) return null;
|
|
11140
|
+
const out2 = [];
|
|
11141
|
+
for (const part of name2.split(/[/\\]/)) {
|
|
11142
|
+
if (part === "" || part === ".") continue;
|
|
11143
|
+
if (part === "..") return null;
|
|
11144
|
+
out2.push(part);
|
|
11145
|
+
}
|
|
11146
|
+
return out2.length ? out2.join("/") : null;
|
|
11147
|
+
}
|
|
11148
|
+
function extractTarInto(rawTar, destDir) {
|
|
11149
|
+
const root = resolve(destDir);
|
|
11150
|
+
const written = [];
|
|
11151
|
+
for (const entry of readTar(asBuffer(rawTar))) {
|
|
11152
|
+
if (entry.type !== "0" && entry.type !== "\0") continue;
|
|
11153
|
+
const rel = safeRelPath(entry.name);
|
|
11154
|
+
if (rel === null) throw new Error(`refusing unsafe tar entry: ${entry.name}`);
|
|
11155
|
+
const dest = resolve(destDir, rel);
|
|
11156
|
+
if (dest !== root && !dest.startsWith(root + sep2)) {
|
|
11157
|
+
throw new Error(`tar entry escapes destination: ${entry.name}`);
|
|
11158
|
+
}
|
|
11159
|
+
mkdirSync(dirname2(dest), { recursive: true });
|
|
11160
|
+
writeFileSync(dest, entry.data);
|
|
11161
|
+
written.push(rel);
|
|
11162
|
+
}
|
|
11163
|
+
return written;
|
|
11164
|
+
}
|
|
11165
|
+
function extractGrammarsTarball(bytes, destDir) {
|
|
11166
|
+
const b = asBuffer(bytes);
|
|
11167
|
+
const raw = b.length >= 2 && b[0] === 31 && b[1] === 139 ? gunzipSync(b) : b;
|
|
11168
|
+
return extractTarInto(raw, destDir);
|
|
11169
|
+
}
|
|
11170
|
+
|
|
11171
|
+
// src/engine.ts
|
|
10888
11172
|
init_resolve();
|
|
10889
11173
|
init_modules();
|
|
10890
11174
|
init_graph();
|
|
@@ -10905,7 +11189,7 @@ init_graph_json();
|
|
|
10905
11189
|
init_types();
|
|
10906
11190
|
init_walk();
|
|
10907
11191
|
init_sort();
|
|
10908
|
-
import { join as
|
|
11192
|
+
import { join as join12 } from "path";
|
|
10909
11193
|
var utf8 = new TextEncoder();
|
|
10910
11194
|
function pushVarint(out2, n) {
|
|
10911
11195
|
if (n < 0) throw new Error(`pushVarint: negative input ${n} is not a valid unsigned varint`);
|
|
@@ -11075,7 +11359,7 @@ function renderScip(scan2, opts = {}) {
|
|
|
11075
11359
|
};
|
|
11076
11360
|
const documents = [];
|
|
11077
11361
|
for (const f of docs) {
|
|
11078
|
-
const text = readText(
|
|
11362
|
+
const text = readText(join12(scan2.root, f.rel));
|
|
11079
11363
|
const lines = text.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
11080
11364
|
const locate = (lineNo, name2) => {
|
|
11081
11365
|
const line = lines[lineNo - 1];
|
|
@@ -11166,12 +11450,14 @@ init_util();
|
|
|
11166
11450
|
init_types();
|
|
11167
11451
|
init_types();
|
|
11168
11452
|
init_loader();
|
|
11453
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync6, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
11454
|
+
import { dirname as dirname4, join as join15, resolve as resolve2 } from "path";
|
|
11169
11455
|
init_pipeline();
|
|
11456
|
+
init_hash();
|
|
11170
11457
|
init_graph_json();
|
|
11171
11458
|
init_symbols_json();
|
|
11172
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
11173
|
-
import { join as join13, resolve } from "path";
|
|
11174
11459
|
init_scan();
|
|
11460
|
+
init_walk();
|
|
11175
11461
|
init_callers();
|
|
11176
11462
|
init_workspaces();
|
|
11177
11463
|
init_git();
|
|
@@ -11218,6 +11504,14 @@ Commands:
|
|
|
11218
11504
|
the source with CODEINDEX_EMBED_URL
|
|
11219
11505
|
embed serve Print (or --run) the docker command that starts the
|
|
11220
11506
|
containerized embedding server (rich tier)
|
|
11507
|
+
grammars Tree-sitter wasm grammars (optional AST tier; regex without them).
|
|
11508
|
+
Precedence: bundle-adjacent > CODEINDEX_GRAMMARS_DIR > shared cache:
|
|
11509
|
+
grammars status Active tier (adjacent/env/cache/none), resolved
|
|
11510
|
+
dir, pinned ENGINE_VERSION, pull-needed (JSON)
|
|
11511
|
+
grammars pull Fetch the per-release grammars-<version>.tar.gz
|
|
11512
|
+
asset into the shared cache (sha256-verified,
|
|
11513
|
+
atomic). Override the source with
|
|
11514
|
+
CODEINDEX_GRAMMARS_URL
|
|
11221
11515
|
rules Architecture rules (forbidden edges, cycles, orphans) validated
|
|
11222
11516
|
against the link-graph: --config <codeindex.rules.json>; exits 1
|
|
11223
11517
|
on any error-severity violation (a CI gate)
|
|
@@ -11271,10 +11565,10 @@ function parseFlags(args2) {
|
|
|
11271
11565
|
if (!Number.isFinite(n) || n <= 0) throw new Error(`${a} expects a positive number, got "${raw}"`);
|
|
11272
11566
|
return n;
|
|
11273
11567
|
};
|
|
11274
|
-
if (a === "--repo") flags2.repo =
|
|
11568
|
+
if (a === "--repo") flags2.repo = resolve2(next());
|
|
11275
11569
|
else if (a === "--out") {
|
|
11276
11570
|
const v = next();
|
|
11277
|
-
flags2.out = v === "-" ? "-" :
|
|
11571
|
+
flags2.out = v === "-" ? "-" : resolve2(v);
|
|
11278
11572
|
} else if (a === "--project-root") flags2.projectRoot = next();
|
|
11279
11573
|
else if (a === "--include") flags2.include.push(next());
|
|
11280
11574
|
else if (a === "--exclude") flags2.exclude.push(next());
|
|
@@ -11289,7 +11583,7 @@ function parseFlags(args2) {
|
|
|
11289
11583
|
else if (a === "--budget-tokens") flags2.budgetTokens = num();
|
|
11290
11584
|
else if (a === "--no-ast") flags2.noAst = true;
|
|
11291
11585
|
else if (a === "--since") flags2.since = next();
|
|
11292
|
-
else if (a === "--config") flags2.config =
|
|
11586
|
+
else if (a === "--config") flags2.config = resolve2(next());
|
|
11293
11587
|
else if (a === "--limit") flags2.limit = num();
|
|
11294
11588
|
else if (a === "--no-fuzzy") flags2.fuzzy = false;
|
|
11295
11589
|
else if (a === "--semantic") flags2.semantic = true;
|
|
@@ -11301,10 +11595,10 @@ function parseFlags(args2) {
|
|
|
11301
11595
|
return flags2;
|
|
11302
11596
|
}
|
|
11303
11597
|
function emit(content, out2) {
|
|
11304
|
-
if (out2)
|
|
11598
|
+
if (out2) writeFileSync4(out2, content);
|
|
11305
11599
|
else process.stdout.write(content);
|
|
11306
11600
|
}
|
|
11307
|
-
function scanOptions(flags2) {
|
|
11601
|
+
function scanOptions(flags2, precomputedWalk) {
|
|
11308
11602
|
return {
|
|
11309
11603
|
include: flags2.include.length ? flags2.include : void 0,
|
|
11310
11604
|
exclude: flags2.exclude.length ? flags2.exclude : void 0,
|
|
@@ -11313,9 +11607,14 @@ function scanOptions(flags2) {
|
|
|
11313
11607
|
ignoreDirs: flags2.ignoreDirs.length ? flags2.ignoreDirs : void 0,
|
|
11314
11608
|
maxFiles: flags2.maxFiles,
|
|
11315
11609
|
maxBytes: flags2.maxBytes,
|
|
11316
|
-
maxCallsPerFile: flags2.maxCalls
|
|
11610
|
+
maxCallsPerFile: flags2.maxCalls,
|
|
11611
|
+
// The walk performed once in runCli to warm the present-language grammars,
|
|
11612
|
+
// reused here so scanRepo does not traverse the tree a second time. Absent
|
|
11613
|
+
// for --no-ast / scan-less commands: scanRepo walks itself, unchanged.
|
|
11614
|
+
precomputedWalk
|
|
11317
11615
|
};
|
|
11318
11616
|
}
|
|
11617
|
+
var SCANLESS_COMMANDS = /* @__PURE__ */ new Set(["grep", "churn", "coupling", "workspaces", "grammars"]);
|
|
11319
11618
|
async function runCli(argv) {
|
|
11320
11619
|
const [cmd, ...rest] = argv;
|
|
11321
11620
|
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
@@ -11333,46 +11632,102 @@ async function runCli(argv) {
|
|
|
11333
11632
|
}
|
|
11334
11633
|
const flags2 = parseFlags(rest);
|
|
11335
11634
|
if (!existsSync4(flags2.repo)) throw new Error(`--repo path does not exist: ${flags2.repo}`);
|
|
11336
|
-
|
|
11635
|
+
const scans = !SCANLESS_COMMANDS.has(cmd) && !(cmd === "embed" && flags2.positional !== "build");
|
|
11636
|
+
let precomputedWalk;
|
|
11637
|
+
if (scans && !flags2.noAst) {
|
|
11638
|
+
precomputedWalk = walk(flags2.repo, {
|
|
11639
|
+
maxFileBytes: flags2.maxBytes,
|
|
11640
|
+
maxFiles: flags2.maxFiles,
|
|
11641
|
+
gitignore: flags2.gitignore,
|
|
11642
|
+
ignoreDirs: flags2.ignoreDirs.length ? flags2.ignoreDirs : void 0
|
|
11643
|
+
});
|
|
11644
|
+
await ensureGrammars(grammarKeysForExts(precomputedWalk.files.map((f) => f.ext)));
|
|
11645
|
+
}
|
|
11337
11646
|
if (cmd === "index") {
|
|
11338
11647
|
if (!flags2.out) throw new Error("index needs --out <dir>");
|
|
11339
11648
|
const outDir = flags2.out;
|
|
11340
|
-
|
|
11341
|
-
const cachePath =
|
|
11649
|
+
mkdirSync3(outDir, { recursive: true });
|
|
11650
|
+
const cachePath = join15(outDir, "cache.json");
|
|
11342
11651
|
let cache;
|
|
11652
|
+
let meta = {};
|
|
11343
11653
|
try {
|
|
11344
11654
|
const parsed = JSON.parse(readFileSync6(cachePath, "utf8"));
|
|
11345
11655
|
if (parsed.schemaVersion === SCHEMA_VERSION && parsed.extractorVersion === EXTRACTOR_VERSION) {
|
|
11346
11656
|
cache = new Map(Object.entries(parsed.files));
|
|
11657
|
+
meta = {
|
|
11658
|
+
engineVersion: parsed.engineVersion,
|
|
11659
|
+
commit: parsed.commit,
|
|
11660
|
+
graphSha1: parsed.graphSha1,
|
|
11661
|
+
symbolsSha1: parsed.symbolsSha1,
|
|
11662
|
+
embed: parsed.embed
|
|
11663
|
+
};
|
|
11347
11664
|
}
|
|
11348
11665
|
} catch {
|
|
11349
11666
|
}
|
|
11350
|
-
const
|
|
11351
|
-
writeFileSync3(join13(outDir, "graph.json"), renderGraphJson(graph));
|
|
11352
|
-
writeFileSync3(join13(outDir, "symbols.json"), renderSymbolsJson(symbols));
|
|
11353
|
-
const files = {};
|
|
11354
|
-
for (const f of scan2.files) {
|
|
11355
|
-
const entry = { hash: f.hash, record: f, size: f.size };
|
|
11356
|
-
const mtime = scan2.mtimes.get(f.rel);
|
|
11357
|
-
if (mtime !== void 0) entry.mtimeMs = mtime;
|
|
11358
|
-
files[f.rel] = entry;
|
|
11359
|
-
}
|
|
11360
|
-
writeFileSync3(
|
|
11361
|
-
cachePath,
|
|
11362
|
-
JSON.stringify({ schemaVersion: SCHEMA_VERSION, extractorVersion: EXTRACTOR_VERSION, files }) + "\n"
|
|
11363
|
-
);
|
|
11364
|
-
let embedNote = "";
|
|
11667
|
+
const scan2 = scanRepo(flags2.repo, { ...scanOptions(flags2, precomputedWalk), cache, out: outDir });
|
|
11365
11668
|
const modelDir = resolveEmbedModelDir(flags2.repo);
|
|
11366
11669
|
const model = modelDir ? loadEmbedModel(modelDir) : void 0;
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11670
|
+
const graphPath = join15(outDir, "graph.json");
|
|
11671
|
+
const symbolsPath = join15(outDir, "symbols.json");
|
|
11672
|
+
const embedPath = join15(outDir, "embeddings.bin");
|
|
11673
|
+
const artifactSha = (path) => {
|
|
11674
|
+
try {
|
|
11675
|
+
return sha1(readFileSync6(path));
|
|
11676
|
+
} catch {
|
|
11677
|
+
return void 0;
|
|
11678
|
+
}
|
|
11679
|
+
};
|
|
11680
|
+
const writeCache = (out2) => {
|
|
11681
|
+
const files = {};
|
|
11682
|
+
for (const f of scan2.files) {
|
|
11683
|
+
const entry = { hash: f.hash, record: f, size: f.size };
|
|
11684
|
+
const mtime = scan2.mtimes.get(f.rel);
|
|
11685
|
+
if (mtime !== void 0) entry.mtimeMs = mtime;
|
|
11686
|
+
files[f.rel] = entry;
|
|
11687
|
+
}
|
|
11688
|
+
writeFileSync4(
|
|
11689
|
+
cachePath,
|
|
11690
|
+
JSON.stringify({
|
|
11691
|
+
schemaVersion: SCHEMA_VERSION,
|
|
11692
|
+
extractorVersion: EXTRACTOR_VERSION,
|
|
11693
|
+
engineVersion: ENGINE_VERSION,
|
|
11694
|
+
commit: scan2.commit,
|
|
11695
|
+
graphSha1: out2.graphSha1,
|
|
11696
|
+
symbolsSha1: out2.symbolsSha1,
|
|
11697
|
+
embed: out2.embed,
|
|
11698
|
+
files
|
|
11699
|
+
}) + "\n"
|
|
11700
|
+
);
|
|
11701
|
+
};
|
|
11702
|
+
const embedUnchanged = !model || meta.embed !== void 0 && meta.embed.embedVersion === EMBED_VERSION && meta.embed.modelId === model.modelId && meta.embed.sha1 !== void 0 && artifactSha(embedPath) === meta.embed.sha1;
|
|
11703
|
+
const fastpath = scan2.contentUnchanged && meta.engineVersion === ENGINE_VERSION && meta.commit === scan2.commit && meta.graphSha1 !== void 0 && artifactSha(graphPath) === meta.graphSha1 && meta.symbolsSha1 !== void 0 && artifactSha(symbolsPath) === meta.symbolsSha1 && embedUnchanged;
|
|
11704
|
+
if (fastpath) {
|
|
11705
|
+
if (scan2.cacheDirty) writeCache(meta);
|
|
11706
|
+
process.stderr.write(
|
|
11707
|
+
`codeindex: ${scan2.files.length} files \u2192 ${outDir}/graph.json + symbols.json${scan2.capped ? " (capped)" : ""} (unchanged \u2014 artifacts reused)
|
|
11708
|
+
`
|
|
11709
|
+
);
|
|
11710
|
+
} else {
|
|
11711
|
+
const { graph, symbols } = buildArtifactsFromScan(scan2);
|
|
11712
|
+
const graphJson = renderGraphJson(graph);
|
|
11713
|
+
const symbolsJson = renderSymbolsJson(symbols);
|
|
11714
|
+
writeFileSync4(graphPath, graphJson);
|
|
11715
|
+
writeFileSync4(symbolsPath, symbolsJson);
|
|
11716
|
+
let embedNote = "";
|
|
11717
|
+
let embedMeta;
|
|
11718
|
+
if (model) {
|
|
11719
|
+
const index = buildEmbeddingIndex(scan2, model);
|
|
11720
|
+
const bytes = serializeEmbeddings(index);
|
|
11721
|
+
writeFileSync4(embedPath, bytes);
|
|
11722
|
+
embedMeta = { embedVersion: EMBED_VERSION, modelId: model.modelId, sha1: sha1(bytes) };
|
|
11723
|
+
embedNote = ` + embeddings.bin (${index.records.length} records, model ${model.modelId})`;
|
|
11724
|
+
}
|
|
11725
|
+
writeCache({ graphSha1: sha1(graphJson), symbolsSha1: sha1(symbolsJson), embed: embedMeta });
|
|
11726
|
+
process.stderr.write(`codeindex: ${scan2.files.length} files \u2192 ${outDir}/graph.json + symbols.json${embedNote}${scan2.capped ? " (capped)" : ""}
|
|
11373
11727
|
`);
|
|
11728
|
+
}
|
|
11374
11729
|
} else if (cmd === "scan") {
|
|
11375
|
-
const { scan: scan2 } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
11730
|
+
const { scan: scan2 } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11376
11731
|
const summary = {
|
|
11377
11732
|
engineVersion: ENGINE_VERSION,
|
|
11378
11733
|
commit: scan2.commit,
|
|
@@ -11382,30 +11737,30 @@ async function runCli(argv) {
|
|
|
11382
11737
|
};
|
|
11383
11738
|
emit(JSON.stringify(summary, null, 2) + "\n", flags2.out);
|
|
11384
11739
|
} else if (cmd === "graph") {
|
|
11385
|
-
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
11740
|
+
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11386
11741
|
emit(renderGraphJson(graph), flags2.out);
|
|
11387
11742
|
} else if (cmd === "symbols") {
|
|
11388
|
-
const { symbols } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
11743
|
+
const { symbols } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11389
11744
|
emit(renderSymbolsJson(symbols), flags2.out);
|
|
11390
11745
|
} else if (cmd === "scip") {
|
|
11391
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
11746
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11392
11747
|
const bytes = renderScip(scan2, { projectRoot: flags2.projectRoot });
|
|
11393
|
-
const out2 = flags2.out ??
|
|
11748
|
+
const out2 = flags2.out ?? resolve2("index.scip");
|
|
11394
11749
|
if (out2 === "-") process.stdout.write(Buffer.from(bytes));
|
|
11395
11750
|
else {
|
|
11396
|
-
|
|
11751
|
+
writeFileSync4(out2, bytes);
|
|
11397
11752
|
process.stderr.write(`codeindex: SCIP index \u2192 ${out2} (${bytes.length} bytes)
|
|
11398
11753
|
`);
|
|
11399
11754
|
}
|
|
11400
11755
|
} else if (cmd === "callers") {
|
|
11401
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
11756
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11402
11757
|
const index = buildCallerIndex(scan2, void 0, { recall: flags2.recall });
|
|
11403
11758
|
const obj = {};
|
|
11404
11759
|
for (const [name2, entry] of index) obj[name2] = entry;
|
|
11405
11760
|
emit(JSON.stringify(obj, null, 2) + "\n", flags2.out);
|
|
11406
11761
|
} else if (cmd === "search") {
|
|
11407
11762
|
if (!flags2.positional) throw new Error('search needs a query: cli.mjs search "<query>" --repo <dir>');
|
|
11408
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
11763
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11409
11764
|
if (flags2.semantic) {
|
|
11410
11765
|
const endpoint = resolveEmbedEndpoint();
|
|
11411
11766
|
const lexical = () => {
|
|
@@ -11495,17 +11850,17 @@ async function runCli(argv) {
|
|
|
11495
11850
|
return;
|
|
11496
11851
|
}
|
|
11497
11852
|
const model = loadEmbedModel(modelDir);
|
|
11498
|
-
|
|
11499
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
11853
|
+
mkdirSync3(flags2.out, { recursive: true });
|
|
11854
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11500
11855
|
const index = buildEmbeddingIndex(scan2, model);
|
|
11501
|
-
|
|
11856
|
+
writeFileSync4(join15(flags2.out, "embeddings.bin"), serializeEmbeddings(index));
|
|
11502
11857
|
process.stderr.write(`codeindex: ${index.records.length} embedding records \u2192 ${flags2.out}/embeddings.bin (model ${model.modelId})
|
|
11503
11858
|
`);
|
|
11504
11859
|
} else if (sub === "pull") {
|
|
11505
11860
|
const { url, sha256 } = resolveEmbedPullUrl();
|
|
11506
|
-
const destDir = process.env.CODEINDEX_EMBED_DIR ??
|
|
11507
|
-
|
|
11508
|
-
process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${
|
|
11861
|
+
const destDir = process.env.CODEINDEX_EMBED_DIR ?? join15(flags2.repo, ".codeindex", "models");
|
|
11862
|
+
mkdirSync3(destDir, { recursive: true });
|
|
11863
|
+
process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${join15(destDir, "model.json")}
|
|
11509
11864
|
`);
|
|
11510
11865
|
let body2;
|
|
11511
11866
|
try {
|
|
@@ -11526,16 +11881,102 @@ async function runCli(argv) {
|
|
|
11526
11881
|
process.exitCode = 1;
|
|
11527
11882
|
return;
|
|
11528
11883
|
}
|
|
11529
|
-
|
|
11530
|
-
process.stderr.write(`codeindex: model written to ${
|
|
11884
|
+
writeFileSync4(join15(destDir, "model.json"), body2);
|
|
11885
|
+
process.stderr.write(`codeindex: model written to ${join15(destDir, "model.json")}
|
|
11531
11886
|
`);
|
|
11532
11887
|
} else {
|
|
11533
11888
|
throw new Error("embed needs a subcommand: status | build | pull | serve");
|
|
11534
11889
|
}
|
|
11890
|
+
} else if (cmd === "grammars") {
|
|
11891
|
+
const sub = flags2.positional;
|
|
11892
|
+
const cacheDir = sharedGrammarsCacheDir();
|
|
11893
|
+
if (sub === "status") {
|
|
11894
|
+
const info2 = resolveGrammarsTier();
|
|
11895
|
+
const runtimePresent = info2.dir ? existsSync4(join15(info2.dir, "web-tree-sitter.wasm")) : false;
|
|
11896
|
+
const target = resolveGrammarsPullTarget();
|
|
11897
|
+
const status = {
|
|
11898
|
+
engineVersion: ENGINE_VERSION,
|
|
11899
|
+
tier: info2.tier,
|
|
11900
|
+
dir: info2.dir ?? null,
|
|
11901
|
+
cacheDir,
|
|
11902
|
+
runtimePresent,
|
|
11903
|
+
pullNeeded: !runtimePresent,
|
|
11904
|
+
url: target.url
|
|
11905
|
+
};
|
|
11906
|
+
emit(JSON.stringify(status, null, 2) + "\n", flags2.out);
|
|
11907
|
+
} else if (sub === "pull") {
|
|
11908
|
+
const target = resolveGrammarsPullTarget();
|
|
11909
|
+
let expected;
|
|
11910
|
+
if (target.sha256Url) {
|
|
11911
|
+
try {
|
|
11912
|
+
expected = await fetchExpectedSha256(target.sha256Url);
|
|
11913
|
+
} catch (e) {
|
|
11914
|
+
process.stderr.write(
|
|
11915
|
+
`codeindex: could not fetch checksum (${e instanceof Error ? e.message : String(e)}) \u2014 proceeding unverified
|
|
11916
|
+
`
|
|
11917
|
+
);
|
|
11918
|
+
}
|
|
11919
|
+
}
|
|
11920
|
+
const runtime = join15(cacheDir, "web-tree-sitter.wasm");
|
|
11921
|
+
const markerPath = join15(dirname4(cacheDir), `${ENGINE_VERSION}.sha256`);
|
|
11922
|
+
if (existsSync4(runtime) && expected && existsSync4(markerPath)) {
|
|
11923
|
+
let marker = "";
|
|
11924
|
+
try {
|
|
11925
|
+
marker = readFileSync6(markerPath, "utf8").trim();
|
|
11926
|
+
} catch {
|
|
11927
|
+
}
|
|
11928
|
+
if (marker === expected) {
|
|
11929
|
+
process.stderr.write(`codeindex: grammars already present at ${cacheDir} (up to date)
|
|
11930
|
+
`);
|
|
11931
|
+
return;
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
process.stderr.write(`codeindex: fetching grammars from ${target.url} \u2192 ${cacheDir}
|
|
11935
|
+
`);
|
|
11936
|
+
let bytes;
|
|
11937
|
+
try {
|
|
11938
|
+
bytes = await fetchGrammarsTarball(target.url, expected);
|
|
11939
|
+
} catch (e) {
|
|
11940
|
+
process.stderr.write(`codeindex: pull failed \u2014 ${e instanceof Error ? e.message : String(e)} (nothing written)
|
|
11941
|
+
`);
|
|
11942
|
+
process.exitCode = 1;
|
|
11943
|
+
return;
|
|
11944
|
+
}
|
|
11945
|
+
let tmp;
|
|
11946
|
+
try {
|
|
11947
|
+
mkdirSync3(dirname4(cacheDir), { recursive: true });
|
|
11948
|
+
tmp = mkdtempSync(join15(dirname4(cacheDir), ".grammars-tmp-"));
|
|
11949
|
+
extractGrammarsTarball(bytes, tmp);
|
|
11950
|
+
if (!existsSync4(join15(tmp, "web-tree-sitter.wasm"))) {
|
|
11951
|
+
throw new Error("archive is missing web-tree-sitter.wasm");
|
|
11952
|
+
}
|
|
11953
|
+
if (existsSync4(cacheDir)) rmSync2(cacheDir, { recursive: true, force: true });
|
|
11954
|
+
renameSync(tmp, cacheDir);
|
|
11955
|
+
tmp = void 0;
|
|
11956
|
+
if (expected) writeFileSync4(markerPath, expected + "\n");
|
|
11957
|
+
} catch (e) {
|
|
11958
|
+
if (tmp) {
|
|
11959
|
+
try {
|
|
11960
|
+
rmSync2(tmp, { recursive: true, force: true });
|
|
11961
|
+
} catch {
|
|
11962
|
+
}
|
|
11963
|
+
}
|
|
11964
|
+
process.stderr.write(
|
|
11965
|
+
`codeindex: pull failed \u2014 ${e instanceof Error ? e.message : String(e)} (nothing written)
|
|
11966
|
+
`
|
|
11967
|
+
);
|
|
11968
|
+
process.exitCode = 1;
|
|
11969
|
+
return;
|
|
11970
|
+
}
|
|
11971
|
+
process.stderr.write(`codeindex: grammars extracted \u2192 ${cacheDir}
|
|
11972
|
+
`);
|
|
11973
|
+
} else {
|
|
11974
|
+
throw new Error("grammars needs a subcommand: status | pull");
|
|
11975
|
+
}
|
|
11535
11976
|
} else if (cmd === "rules") {
|
|
11536
11977
|
if (!flags2.config) throw new Error("rules needs --config <codeindex.rules.json>");
|
|
11537
11978
|
const rules = parseRules(JSON.parse(readFileSync6(flags2.config, "utf8")));
|
|
11538
|
-
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
11979
|
+
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11539
11980
|
const violations = checkRules(graph, rules);
|
|
11540
11981
|
const errors = violations.filter((v) => v.severity === "error").length;
|
|
11541
11982
|
emit(JSON.stringify({ errors, warnings: violations.length - errors, violations }, null, 2) + "\n", flags2.out);
|
|
@@ -11556,26 +11997,26 @@ async function runCli(argv) {
|
|
|
11556
11997
|
for (const k of [...churn.keys()].sort()) sorted[k] = churn.get(k);
|
|
11557
11998
|
emit(JSON.stringify({ ok, churn: sorted }, null, 2) + "\n", flags2.out);
|
|
11558
11999
|
} else if (cmd === "repomap") {
|
|
11559
|
-
const { scan: scan2, graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
12000
|
+
const { scan: scan2, graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11560
12001
|
emit(renderRepoMap(scan2, graph, { budgetTokens: flags2.budgetTokens }), flags2.out);
|
|
11561
12002
|
} else if (cmd === "hotspots") {
|
|
11562
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
12003
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11563
12004
|
const { churn, ok } = gitChurn(flags2.repo, { since: flags2.since });
|
|
11564
12005
|
emit(JSON.stringify({ churnOk: ok, hotspots: rankHotspots(scan2, churn) }, null, 2) + "\n", flags2.out);
|
|
11565
12006
|
} else if (cmd === "coupling") {
|
|
11566
12007
|
const { ok, couplings } = changeCoupling(flags2.repo, { since: flags2.since });
|
|
11567
12008
|
emit(JSON.stringify({ ok, couplings }, null, 2) + "\n", flags2.out);
|
|
11568
12009
|
} else if (cmd === "deadcode") {
|
|
11569
|
-
emit(JSON.stringify(findDeadCode(scanRepo(flags2.repo, scanOptions(flags2))), null, 2) + "\n", flags2.out);
|
|
12010
|
+
emit(JSON.stringify(findDeadCode(scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk))), null, 2) + "\n", flags2.out);
|
|
11570
12011
|
} else if (cmd === "complexity") {
|
|
11571
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
12012
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11572
12013
|
emit(JSON.stringify(symbolComplexity(scan2, flags2.positional), null, 2) + "\n", flags2.out);
|
|
11573
12014
|
} else if (cmd === "risk") {
|
|
11574
|
-
const scan2 = scanRepo(flags2.repo, scanOptions(flags2));
|
|
12015
|
+
const scan2 = scanRepo(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11575
12016
|
const { churn, ok } = gitChurn(flags2.repo, { since: flags2.since });
|
|
11576
12017
|
emit(JSON.stringify({ churnOk: ok, risks: riskHotspots(scan2, churn) }, null, 2) + "\n", flags2.out);
|
|
11577
12018
|
} else if (cmd === "mermaid") {
|
|
11578
|
-
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2));
|
|
12019
|
+
const { graph } = buildIndexArtifacts(flags2.repo, scanOptions(flags2, precomputedWalk));
|
|
11579
12020
|
emit(renderMermaid(graph, { module: flags2.positional }), flags2.out);
|
|
11580
12021
|
} else if (cmd === "grep") {
|
|
11581
12022
|
if (!flags2.positional) throw new Error("grep needs a pattern: cli.mjs grep <pattern> --repo <dir>");
|
|
@@ -11594,6 +12035,7 @@ ${HELP}`);
|
|
|
11594
12035
|
}
|
|
11595
12036
|
}
|
|
11596
12037
|
export {
|
|
12038
|
+
DEFAULT_GRAMMARS_URL,
|
|
11597
12039
|
DEFAULT_MAX_FILES,
|
|
11598
12040
|
EMBED_VERSION,
|
|
11599
12041
|
ENGINE_VERSION,
|
|
@@ -11604,6 +12046,7 @@ export {
|
|
|
11604
12046
|
applyCentrality,
|
|
11605
12047
|
basicTokenize,
|
|
11606
12048
|
betweennessOf,
|
|
12049
|
+
buildArtifactsFromScan,
|
|
11607
12050
|
buildCallerIndex,
|
|
11608
12051
|
buildEmbeddingIndex,
|
|
11609
12052
|
buildEndpointIndex,
|
|
@@ -11646,14 +12089,19 @@ export {
|
|
|
11646
12089
|
extToLang,
|
|
11647
12090
|
extractAst,
|
|
11648
12091
|
extractCode,
|
|
12092
|
+
extractGrammarsTarball,
|
|
11649
12093
|
extractMarkdown,
|
|
11650
12094
|
extractSymbols,
|
|
12095
|
+
extractTarInto,
|
|
12096
|
+
fetchExpectedSha256,
|
|
12097
|
+
fetchGrammarsTarball,
|
|
11651
12098
|
findDeadCode,
|
|
11652
12099
|
findReferences,
|
|
11653
12100
|
findSymbol,
|
|
11654
12101
|
foldText,
|
|
11655
12102
|
gitChurn,
|
|
11656
12103
|
grammarKeyForExt,
|
|
12104
|
+
grammarKeysForExts,
|
|
11657
12105
|
grammarReady,
|
|
11658
12106
|
grepRepo,
|
|
11659
12107
|
hasEmbedModel,
|
|
@@ -11695,6 +12143,9 @@ export {
|
|
|
11695
12143
|
resolveEmbedEndpoint,
|
|
11696
12144
|
resolveEmbedModelDir,
|
|
11697
12145
|
resolveEmbedPullUrl,
|
|
12146
|
+
resolveGrammarsDir,
|
|
12147
|
+
resolveGrammarsPullTarget,
|
|
12148
|
+
resolveGrammarsTier,
|
|
11698
12149
|
resolveImport,
|
|
11699
12150
|
resolveUniqueSymbol,
|
|
11700
12151
|
riskHotspots,
|
|
@@ -11708,6 +12159,7 @@ export {
|
|
|
11708
12159
|
serializeEmbeddings,
|
|
11709
12160
|
sh,
|
|
11710
12161
|
sha1,
|
|
12162
|
+
sharedGrammarsCacheDir,
|
|
11711
12163
|
shortHash,
|
|
11712
12164
|
slugify,
|
|
11713
12165
|
subtokens,
|