ultra11y 4.2.0 → 4.3.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/package.json +1 -1
- package/scripts/ultra11y.mjs +1471 -527
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +1471 -527
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +1471 -527
- package/src/data/wcag-universe.json +1 -1
- package/src/types.ts +1 -1
- package/src/vendor/codeindex-engine.d.mts +324 -3
- package/src/vendor/codeindex-engine.mjs +1146 -175
- package/src/vendor/engine.meta.json +5 -5
|
@@ -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.28.0";
|
|
18
18
|
SCHEMA_VERSION = 5;
|
|
19
19
|
EXTRACTOR_VERSION = 13;
|
|
20
20
|
}
|
|
@@ -85,6 +85,14 @@ function keywords(question) {
|
|
|
85
85
|
}
|
|
86
86
|
return out2;
|
|
87
87
|
}
|
|
88
|
+
function droppedKeywords(question) {
|
|
89
|
+
const out2 = [];
|
|
90
|
+
for (const raw of foldText(question).split(/[^A-Za-z0-9_]+/)) {
|
|
91
|
+
if (!raw) continue;
|
|
92
|
+
if (raw.length < 2 || STOPWORDS.has(raw.toLowerCase())) out2.push(raw);
|
|
93
|
+
}
|
|
94
|
+
return out2;
|
|
95
|
+
}
|
|
88
96
|
function rankedKeywords(question) {
|
|
89
97
|
const base = keywords(question);
|
|
90
98
|
const score = (raw) => {
|
|
@@ -1759,7 +1767,7 @@ var init_literals = __esm({
|
|
|
1759
1767
|
}
|
|
1760
1768
|
});
|
|
1761
1769
|
|
|
1762
|
-
// node_modules/.pnpm/web-tree-sitter@0.26.
|
|
1770
|
+
// node_modules/.pnpm/web-tree-sitter@0.26.12/node_modules/web-tree-sitter/web-tree-sitter.js
|
|
1763
1771
|
function assertInternal(x) {
|
|
1764
1772
|
if (x !== INTERNAL) throw new Error("Illegal constructor");
|
|
1765
1773
|
}
|
|
@@ -1967,13 +1975,13 @@ async function Module2(moduleArg = {}) {
|
|
|
1967
1975
|
}
|
|
1968
1976
|
readAsync = /* @__PURE__ */ __name(async (url) => {
|
|
1969
1977
|
if (isFileURI(url)) {
|
|
1970
|
-
return new Promise((
|
|
1978
|
+
return new Promise((resolve5, reject) => {
|
|
1971
1979
|
var xhr = new XMLHttpRequest();
|
|
1972
1980
|
xhr.open("GET", url, true);
|
|
1973
1981
|
xhr.responseType = "arraybuffer";
|
|
1974
1982
|
xhr.onload = () => {
|
|
1975
1983
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
1976
|
-
|
|
1984
|
+
resolve5(xhr.response);
|
|
1977
1985
|
return;
|
|
1978
1986
|
}
|
|
1979
1987
|
reject(xhr.status);
|
|
@@ -2169,9 +2177,9 @@ async function Module2(moduleArg = {}) {
|
|
|
2169
2177
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
2170
2178
|
var info2 = getWasmImports();
|
|
2171
2179
|
if (Module["instantiateWasm"]) {
|
|
2172
|
-
return new Promise((
|
|
2180
|
+
return new Promise((resolve5, reject) => {
|
|
2173
2181
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
2174
|
-
|
|
2182
|
+
resolve5(receiveInstance(mod, inst));
|
|
2175
2183
|
});
|
|
2176
2184
|
});
|
|
2177
2185
|
}
|
|
@@ -3502,8 +3510,8 @@ async function Module2(moduleArg = {}) {
|
|
|
3502
3510
|
if (runtimeInitialized) {
|
|
3503
3511
|
moduleRtn = Module;
|
|
3504
3512
|
} else {
|
|
3505
|
-
moduleRtn = new Promise((
|
|
3506
|
-
readyPromiseResolve =
|
|
3513
|
+
moduleRtn = new Promise((resolve5, reject) => {
|
|
3514
|
+
readyPromiseResolve = resolve5;
|
|
3507
3515
|
readyPromiseReject = reject;
|
|
3508
3516
|
});
|
|
3509
3517
|
}
|
|
@@ -3687,7 +3695,7 @@ function parsePattern(index, stepType, stepValueId, captureNames, stringValues,
|
|
|
3687
3695
|
}
|
|
3688
3696
|
var __defProp2, __name, Edit, SIZE_OF_SHORT, SIZE_OF_INT, SIZE_OF_CURSOR, SIZE_OF_NODE, SIZE_OF_POINT, SIZE_OF_RANGE, ZERO_POINT, INTERNAL, C, LookaheadIterator, Tree, TreeCursor, Node, LANGUAGE_FUNCTION_REGEX, Language, web_tree_sitter_default, Module3, TRANSFER_BUFFER, LANGUAGE_VERSION, MIN_COMPATIBLE_VERSION, Parser, PREDICATE_STEP_TYPE_CAPTURE, PREDICATE_STEP_TYPE_STRING, QUERY_WORD_REGEX, CaptureQuantifier, isCaptureStep, isStringStep, QueryErrorKind, QueryError, Query;
|
|
3689
3697
|
var init_web_tree_sitter = __esm({
|
|
3690
|
-
"node_modules/.pnpm/web-tree-sitter@0.26.
|
|
3698
|
+
"node_modules/.pnpm/web-tree-sitter@0.26.12/node_modules/web-tree-sitter/web-tree-sitter.js"() {
|
|
3691
3699
|
"use strict";
|
|
3692
3700
|
__defProp2 = Object.defineProperty;
|
|
3693
3701
|
__name = (target, value) => __defProp2(target, "name", { value, configurable: true });
|
|
@@ -9849,6 +9857,27 @@ function buildTrigramIndex(docs) {
|
|
|
9849
9857
|
return index;
|
|
9850
9858
|
}
|
|
9851
9859
|
function searchIndex(scan2, query, opts = {}) {
|
|
9860
|
+
return runSearch(scan2, query, opts).results;
|
|
9861
|
+
}
|
|
9862
|
+
function explainQuery(scan2, query, opts = {}) {
|
|
9863
|
+
return runSearch(scan2, query, opts);
|
|
9864
|
+
}
|
|
9865
|
+
function emptyExplanation(query, verdict, note) {
|
|
9866
|
+
return {
|
|
9867
|
+
results: [],
|
|
9868
|
+
explain: {
|
|
9869
|
+
query,
|
|
9870
|
+
terms: [],
|
|
9871
|
+
droppedStopwords: droppedKeywords(query),
|
|
9872
|
+
unresolvedTerms: [],
|
|
9873
|
+
verdict,
|
|
9874
|
+
note,
|
|
9875
|
+
bridgedOnlyResults: 0,
|
|
9876
|
+
resultCount: 0
|
|
9877
|
+
}
|
|
9878
|
+
};
|
|
9879
|
+
}
|
|
9880
|
+
function runSearch(scan2, query, opts = {}) {
|
|
9852
9881
|
const terms = [];
|
|
9853
9882
|
const seen = /* @__PURE__ */ new Set();
|
|
9854
9883
|
for (const kw of keywords(query)) {
|
|
@@ -9858,11 +9887,18 @@ function searchIndex(scan2, query, opts = {}) {
|
|
|
9858
9887
|
terms.push(t);
|
|
9859
9888
|
}
|
|
9860
9889
|
}
|
|
9861
|
-
if (!terms.length)
|
|
9890
|
+
if (!terms.length) {
|
|
9891
|
+
const dropped = droppedKeywords(query);
|
|
9892
|
+
return emptyExplanation(
|
|
9893
|
+
query,
|
|
9894
|
+
"none",
|
|
9895
|
+
dropped.length ? `Nothing was searched for: every token in this query (${dropped.join(", ")}) is a stopword or too short to index. Search with the identifiers or domain words you are actually looking for.` : "Nothing was searched for: the query carried no indexable token."
|
|
9896
|
+
);
|
|
9897
|
+
}
|
|
9862
9898
|
const queryWantsTests = terms.some((t) => TEST_INTENT.test(t));
|
|
9863
9899
|
const docs = bm25DocsFor(scan2);
|
|
9864
9900
|
const n = docs.length;
|
|
9865
|
-
if (!n) return
|
|
9901
|
+
if (!n) return emptyExplanation(query, "none", "This index contains no files.");
|
|
9866
9902
|
const avgLen = {};
|
|
9867
9903
|
for (const f of FIELDS) {
|
|
9868
9904
|
let total = 0;
|
|
@@ -9924,7 +9960,7 @@ function searchIndex(scan2, query, opts = {}) {
|
|
|
9924
9960
|
const matchedFields = /* @__PURE__ */ new Set();
|
|
9925
9961
|
const symbolTerms = /* @__PURE__ */ new Set();
|
|
9926
9962
|
const fuzzyHit = /* @__PURE__ */ new Set();
|
|
9927
|
-
let
|
|
9963
|
+
let exactNameHit = false;
|
|
9928
9964
|
const weightedTf = (term) => {
|
|
9929
9965
|
let wtf = 0;
|
|
9930
9966
|
for (const f of FIELDS) {
|
|
@@ -9942,7 +9978,7 @@ function searchIndex(scan2, query, opts = {}) {
|
|
|
9942
9978
|
if (wtf > 0) {
|
|
9943
9979
|
matched.push(t);
|
|
9944
9980
|
symbolTerms.add(t);
|
|
9945
|
-
if (d.exactNames.has(t))
|
|
9981
|
+
if (d.exactNames.has(t)) exactNameHit = true;
|
|
9946
9982
|
score += idfOf(df.get(t)) * (wtf / (K1 + wtf));
|
|
9947
9983
|
continue;
|
|
9948
9984
|
}
|
|
@@ -9955,7 +9991,7 @@ function searchIndex(scan2, query, opts = {}) {
|
|
|
9955
9991
|
}
|
|
9956
9992
|
}
|
|
9957
9993
|
if (!matched.length && !fuzzyHit.size) continue;
|
|
9958
|
-
if (
|
|
9994
|
+
if (exactNameHit) score *= EXACT_NAME_BOOST;
|
|
9959
9995
|
if (d.isTest && !queryWantsTests) score *= TEST_DEMOTION;
|
|
9960
9996
|
if (prior) {
|
|
9961
9997
|
score *= 1 + 0.35 * Math.log1p(prior.get(d.file) ?? 0);
|
|
@@ -9979,10 +10015,60 @@ function searchIndex(scan2, query, opts = {}) {
|
|
|
9979
10015
|
result.line = Math.min(...hits.map((h) => h.line));
|
|
9980
10016
|
}
|
|
9981
10017
|
if (fuzzyHit.size) result.fuzzyTerms = [...fuzzyHit].sort(byStr);
|
|
10018
|
+
if (!matched.length) result.bridgedOnly = true;
|
|
9982
10019
|
results.push(result);
|
|
9983
10020
|
}
|
|
9984
10021
|
results.sort((a, b) => b.score - a.score || byStr(a.file, b.file));
|
|
9985
|
-
|
|
10022
|
+
const kept = (opts.exact ? results.filter((r) => !r.bridgedOnly) : results).slice(0, opts.limit ?? DEFAULT_LIMIT);
|
|
10023
|
+
return { results: kept, explain: explainOf(query, terms, df, fuzzyCandidates, kept) };
|
|
10024
|
+
}
|
|
10025
|
+
function explainOf(query, terms, df, fuzzyCandidates, kept) {
|
|
10026
|
+
const diagnostics = terms.map((term) => {
|
|
10027
|
+
const frequency = df.get(term) ?? 0;
|
|
10028
|
+
const candidates = frequency === 0 ? fuzzyCandidates.get(term) ?? [] : [];
|
|
10029
|
+
if (!candidates.length) return { term, df: frequency };
|
|
10030
|
+
const via = candidates[0].dice === STEM_WEIGHT ? "stem" : "trigram";
|
|
10031
|
+
return { term, df: frequency, bridge: { via, to: candidates.map((c2) => c2.term), dice: candidates[0].dice } };
|
|
10032
|
+
});
|
|
10033
|
+
const unresolvedTerms = diagnostics.filter((d) => d.df === 0 && !d.bridge).map((d) => d.term).sort(byStr);
|
|
10034
|
+
const wholeIdentifier = !/\s/.test(query.trim()) && terms.length ? { term: terms[0], df: df.get(terms[0]) ?? 0 } : void 0;
|
|
10035
|
+
const bridgedOnlyResults = kept.filter((r) => r.bridgedOnly).length;
|
|
10036
|
+
const allBridged = kept.length > 0 && bridgedOnlyResults === kept.length;
|
|
10037
|
+
const missingIdentifier = wholeIdentifier?.df === 0;
|
|
10038
|
+
const verdict = !kept.length ? "none" : missingIdentifier || allBridged ? "weak" : "match";
|
|
10039
|
+
const explain = {
|
|
10040
|
+
query,
|
|
10041
|
+
terms: diagnostics,
|
|
10042
|
+
droppedStopwords: droppedKeywords(query),
|
|
10043
|
+
unresolvedTerms,
|
|
10044
|
+
...wholeIdentifier ? { wholeIdentifier } : {},
|
|
10045
|
+
verdict,
|
|
10046
|
+
bridgedOnlyResults,
|
|
10047
|
+
resultCount: kept.length
|
|
10048
|
+
};
|
|
10049
|
+
const note = noteFor(explain, missingIdentifier, allBridged);
|
|
10050
|
+
if (note) explain.note = note;
|
|
10051
|
+
return explain;
|
|
10052
|
+
}
|
|
10053
|
+
function noteFor(explain, missingIdentifier, allBridged) {
|
|
10054
|
+
const { verdict, wholeIdentifier, resultCount, terms } = explain;
|
|
10055
|
+
if (verdict === "match") return void 0;
|
|
10056
|
+
if (!resultCount) {
|
|
10057
|
+
return explain.unresolvedTerms.length ? `No file matches. ${explain.unresolvedTerms.length === 1 ? "The term" : "The terms"} ${explain.unresolvedTerms.join(", ")} appear nowhere in this index.` : "No file matches this query.";
|
|
10058
|
+
}
|
|
10059
|
+
if (missingIdentifier && wholeIdentifier) {
|
|
10060
|
+
const parts2 = terms.filter((t) => t.term !== wholeIdentifier.term && t.df > 0).map((t) => t.term).join(", ");
|
|
10061
|
+
const near = terms.find((t) => t.term === wholeIdentifier.term)?.bridge?.to ?? [];
|
|
10062
|
+
const because = parts2 ? ` The ${resultCount} result${resultCount === 1 ? "" : "s"} below match only its parts (${parts2}).` : "";
|
|
10063
|
+
const suggestion = near.length ? ` Closest indexed name${near.length === 1 ? "" : "s"}: ${near.join(", ")}.` : "";
|
|
10064
|
+
return `No file in this index defines or mentions "${wholeIdentifier.term}".${because}${suggestion} If you expected it here, check you are indexing the right branch or commit.`;
|
|
10065
|
+
}
|
|
10066
|
+
if (allBridged) {
|
|
10067
|
+
const bridged = terms.filter((t) => t.bridge);
|
|
10068
|
+
const spelled = bridged.map((t) => `"${t.term}" \u2192 ${t.bridge.to.join(", ")}`).join("; ");
|
|
10069
|
+
return `Nothing matched verbatim. Every result below came from a near match: ${spelled}. Re-run with --exact to see only literal matches.`;
|
|
10070
|
+
}
|
|
10071
|
+
return void 0;
|
|
9986
10072
|
}
|
|
9987
10073
|
var K1, DEFAULT_LIMIT, TOP_SYMBOLS, FUZZY_DICE_THRESHOLD, FUZZY_CAP, STEM_WEIGHT, FIELDS, FIELD_WEIGHT, FIELD_B, EXACT_NAME_BOOST, TEST_DEMOTION, TEST_INTENT;
|
|
9988
10074
|
var init_bm25 = __esm({
|
|
@@ -10518,6 +10604,15 @@ function findSymbol(scan2, namePath, opts = {}) {
|
|
|
10518
10604
|
m.body = content.split("\n").slice(m.line - 1, end).join("\n");
|
|
10519
10605
|
}
|
|
10520
10606
|
}
|
|
10607
|
+
if (opts.concise) {
|
|
10608
|
+
return capped.map((m) => ({
|
|
10609
|
+
name: m.name,
|
|
10610
|
+
kind: m.kind,
|
|
10611
|
+
file: m.file,
|
|
10612
|
+
line: m.line,
|
|
10613
|
+
...m.body !== void 0 ? { body: m.body } : {}
|
|
10614
|
+
}));
|
|
10615
|
+
}
|
|
10521
10616
|
return capped;
|
|
10522
10617
|
}
|
|
10523
10618
|
function findReferences(scan2, name2) {
|
|
@@ -12193,6 +12288,722 @@ var init_endpoint = __esm({
|
|
|
12193
12288
|
}
|
|
12194
12289
|
});
|
|
12195
12290
|
|
|
12291
|
+
// src/repomap.ts
|
|
12292
|
+
function renderRepoMap(scan2, graph, opts = {}) {
|
|
12293
|
+
const budgetChars = (opts.budgetTokens ?? 1024) * CHARS_PER_TOKEN;
|
|
12294
|
+
const maxSymbols = opts.maxSymbolsPerFile ?? 8;
|
|
12295
|
+
const ranked = [...graph.files].filter((f) => f.fileKind === "code").sort((a, b) => (b.pagerank ?? 0) - (a.pagerank ?? 0) || b.symbols - a.symbols || byStr(a.rel, b.rel));
|
|
12296
|
+
const records = new Map(scan2.files.map((f) => [f.rel, f]));
|
|
12297
|
+
const header = `# repo map \u2014 ${graph.fileCount} files
|
|
12298
|
+
`;
|
|
12299
|
+
let out2 = header;
|
|
12300
|
+
let files = 0;
|
|
12301
|
+
for (const node of ranked) {
|
|
12302
|
+
const rec = records.get(node.rel);
|
|
12303
|
+
if (!rec) continue;
|
|
12304
|
+
const symbols = [...rec.symbols].filter((s) => s.kind !== "reexport" && s.kind !== "reexport-all").sort((a, b) => Number(b.exported) - Number(a.exported) || a.line - b.line).slice(0, maxSymbols);
|
|
12305
|
+
let block = `
|
|
12306
|
+
${node.rel}:
|
|
12307
|
+
`;
|
|
12308
|
+
for (const s of symbols) {
|
|
12309
|
+
const sig = (s.signature ?? `${s.kind} ${s.name}`).replace(/\s+/g, " ").trim().slice(0, 120);
|
|
12310
|
+
block += ` ${s.line}: ${sig}
|
|
12311
|
+
`;
|
|
12312
|
+
}
|
|
12313
|
+
if (out2.length + block.length > budgetChars) break;
|
|
12314
|
+
out2 += block;
|
|
12315
|
+
files++;
|
|
12316
|
+
}
|
|
12317
|
+
return `${out2}
|
|
12318
|
+
(${files} of ${ranked.length} code files shown, ~${Math.ceil(out2.length / CHARS_PER_TOKEN)} tokens)
|
|
12319
|
+
`;
|
|
12320
|
+
}
|
|
12321
|
+
var CHARS_PER_TOKEN;
|
|
12322
|
+
var init_repomap = __esm({
|
|
12323
|
+
"src/repomap.ts"() {
|
|
12324
|
+
"use strict";
|
|
12325
|
+
init_sort();
|
|
12326
|
+
CHARS_PER_TOKEN = 4;
|
|
12327
|
+
}
|
|
12328
|
+
});
|
|
12329
|
+
|
|
12330
|
+
// src/coupling.ts
|
|
12331
|
+
function changeCoupling(dir, opts = {}) {
|
|
12332
|
+
const maxCommitFiles = opts.maxCommitFiles ?? 30;
|
|
12333
|
+
const minTogether = opts.minTogether ?? 3;
|
|
12334
|
+
const maxPairs = opts.maxPairs ?? 100;
|
|
12335
|
+
const range = opts.since ? [`${opts.since}..HEAD`] : [];
|
|
12336
|
+
const res = sh("git", ["-C", dir, "-c", "core.quotePath=false", "log", ...range, "--pretty=format:%x1e", "--name-only"]);
|
|
12337
|
+
if (!res.ok) return { ok: false, couplings: [] };
|
|
12338
|
+
const totals = /* @__PURE__ */ new Map();
|
|
12339
|
+
const pairs = /* @__PURE__ */ new Map();
|
|
12340
|
+
for (const block of res.stdout.split("")) {
|
|
12341
|
+
const files = block.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
12342
|
+
if (!files.length || files.length > maxCommitFiles) continue;
|
|
12343
|
+
const unique = [...new Set(files)].sort(byStr);
|
|
12344
|
+
for (const f of unique) totals.set(f, (totals.get(f) ?? 0) + 1);
|
|
12345
|
+
for (let i2 = 0; i2 < unique.length; i2++) {
|
|
12346
|
+
for (let j = i2 + 1; j < unique.length; j++) {
|
|
12347
|
+
const key = `${unique[i2]}${SEP4}${unique[j]}`;
|
|
12348
|
+
pairs.set(key, (pairs.get(key) ?? 0) + 1);
|
|
12349
|
+
}
|
|
12350
|
+
}
|
|
12351
|
+
}
|
|
12352
|
+
const out2 = [];
|
|
12353
|
+
for (const [key, together] of pairs) {
|
|
12354
|
+
if (together < minTogether) continue;
|
|
12355
|
+
const [a, b] = key.split(SEP4);
|
|
12356
|
+
const totalA = totals.get(a) ?? together;
|
|
12357
|
+
const totalB = totals.get(b) ?? together;
|
|
12358
|
+
out2.push({ a, b, together, totalA, totalB, strength: Number((together / Math.min(totalA, totalB)).toFixed(3)) });
|
|
12359
|
+
}
|
|
12360
|
+
out2.sort((x, y) => y.strength - x.strength || y.together - x.together || byStr(x.a, y.a) || byStr(x.b, y.b));
|
|
12361
|
+
return { ok: true, couplings: out2.slice(0, maxPairs) };
|
|
12362
|
+
}
|
|
12363
|
+
function rankHotspots(scan2, churn, top = 20) {
|
|
12364
|
+
const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
|
|
12365
|
+
const commits = churn.get(f.rel) ?? 0;
|
|
12366
|
+
return { rel: f.rel, lines: f.lines, commits, score: Number((commits * Math.log2(f.lines + 1)).toFixed(2)) };
|
|
12367
|
+
});
|
|
12368
|
+
out2.sort((a, b) => b.score - a.score || b.lines - a.lines || byStr(a.rel, b.rel));
|
|
12369
|
+
return out2.slice(0, top);
|
|
12370
|
+
}
|
|
12371
|
+
var SEP4;
|
|
12372
|
+
var init_coupling = __esm({
|
|
12373
|
+
"src/coupling.ts"() {
|
|
12374
|
+
"use strict";
|
|
12375
|
+
init_util();
|
|
12376
|
+
init_sort();
|
|
12377
|
+
SEP4 = "\0";
|
|
12378
|
+
}
|
|
12379
|
+
});
|
|
12380
|
+
|
|
12381
|
+
// src/onboard.ts
|
|
12382
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
12383
|
+
import { join as join17, resolve as resolve2 } from "path";
|
|
12384
|
+
function tagline(root) {
|
|
12385
|
+
for (const name2 of README_NAMES) {
|
|
12386
|
+
const path = join17(root, name2);
|
|
12387
|
+
if (!existsSync7(path)) continue;
|
|
12388
|
+
let text;
|
|
12389
|
+
try {
|
|
12390
|
+
text = readFileSync9(path, "utf8");
|
|
12391
|
+
} catch {
|
|
12392
|
+
continue;
|
|
12393
|
+
}
|
|
12394
|
+
for (const raw of text.split(/\n\s*\n/)) {
|
|
12395
|
+
const line = raw.trim();
|
|
12396
|
+
if (!line || line.startsWith("#") || line.startsWith("<")) continue;
|
|
12397
|
+
if (/^(\[!\[|!\[|\[)/.test(line) && !/[.:]\s/.test(line)) continue;
|
|
12398
|
+
const cleaned = line.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_`]/g, "").replace(/\s+/g, " ").trim();
|
|
12399
|
+
if (cleaned.length > 20) return cleaned.slice(0, 400);
|
|
12400
|
+
}
|
|
12401
|
+
break;
|
|
12402
|
+
}
|
|
12403
|
+
return void 0;
|
|
12404
|
+
}
|
|
12405
|
+
function onboardBrief(scan2, graph, opts = {}) {
|
|
12406
|
+
const lines = [];
|
|
12407
|
+
const name2 = resolve2(scan2.root).replace(/\/+$/, "").split("/").pop() || "repository";
|
|
12408
|
+
lines.push(`# ${name2}`, "");
|
|
12409
|
+
const summary = tagline(scan2.root);
|
|
12410
|
+
if (summary) lines.push(summary, "");
|
|
12411
|
+
const languages = Object.entries(graph.languages).sort((a, b) => b[1] - a[1] || byStr(a[0], b[0])).slice(0, 6).map(([lang, count]) => `${lang} ${count}`).join(", ");
|
|
12412
|
+
lines.push(`**${graph.fileCount} indexed files** \xB7 ${languages}`, "");
|
|
12413
|
+
const workspaces = detectWorkspaces(scan2.root);
|
|
12414
|
+
if (workspaces.packages.length > 1) {
|
|
12415
|
+
lines.push(`## Layout \u2014 monorepo, ${workspaces.packages.length} packages`, "");
|
|
12416
|
+
for (const pkg of workspaces.packages.slice(0, 20)) lines.push(`- \`${pkg.dir}\` \u2014 ${pkg.name}`);
|
|
12417
|
+
if (workspaces.packages.length > 20) lines.push(`- \u2026and ${workspaces.packages.length - 20} more`);
|
|
12418
|
+
if (workspaces.cycle?.length) lines.push("", `\u26A0 dependency cycle: ${workspaces.cycle.join(" \u2192 ")}`);
|
|
12419
|
+
lines.push("");
|
|
12420
|
+
}
|
|
12421
|
+
lines.push("## Key files", "", renderRepoMap(scan2, graph, { budgetTokens: opts.budgetTokens ?? 900 }).trim(), "");
|
|
12422
|
+
const { churn, ok: churnOk } = gitChurn(scan2.root);
|
|
12423
|
+
if (churnOk && churn.size) {
|
|
12424
|
+
const hotspots = rankHotspots(scan2, churn, 8);
|
|
12425
|
+
if (hotspots.length) {
|
|
12426
|
+
lines.push("## Where work concentrates", "", "Files ranked by commits \xD7 size \u2014 where changes and defects cluster.", "");
|
|
12427
|
+
for (const spot of hotspots) lines.push(`- \`${spot.rel}\` \u2014 ${spot.commits} commits, ${spot.lines} lines`);
|
|
12428
|
+
lines.push("");
|
|
12429
|
+
}
|
|
12430
|
+
}
|
|
12431
|
+
lines.push(
|
|
12432
|
+
"## Next",
|
|
12433
|
+
"",
|
|
12434
|
+
"- `search <query>` \u2014 BM25 over names, paths, doc comments and prose; `explain_search` says whether it really matched",
|
|
12435
|
+
"- `find_symbol <Name>` / `symbols_overview <file>` \u2014 read structure without reading files",
|
|
12436
|
+
"- `find_references <Name>` \u2014 three labelled tiers; add `lsp: true` when a language server is configured",
|
|
12437
|
+
""
|
|
12438
|
+
);
|
|
12439
|
+
const brief = lines.join("\n");
|
|
12440
|
+
if (opts.remember === false) return { brief };
|
|
12441
|
+
const memoryName = opts.memoryName ?? "onboarding";
|
|
12442
|
+
writeMemory(scan2.root, memoryName, brief);
|
|
12443
|
+
return { brief, memory: memoryName };
|
|
12444
|
+
}
|
|
12445
|
+
var README_NAMES;
|
|
12446
|
+
var init_onboard = __esm({
|
|
12447
|
+
"src/onboard.ts"() {
|
|
12448
|
+
"use strict";
|
|
12449
|
+
init_repomap();
|
|
12450
|
+
init_workspaces();
|
|
12451
|
+
init_coupling();
|
|
12452
|
+
init_git();
|
|
12453
|
+
init_memory();
|
|
12454
|
+
init_sort();
|
|
12455
|
+
README_NAMES = ["README.md", "README.markdown", "README.rst", "README.txt", "README"];
|
|
12456
|
+
}
|
|
12457
|
+
});
|
|
12458
|
+
|
|
12459
|
+
// src/lsp/protocol.ts
|
|
12460
|
+
function encodeMessage(msg) {
|
|
12461
|
+
const body2 = JSON.stringify(msg);
|
|
12462
|
+
return `Content-Length: ${byteLength(body2)}${HEADER_END}${body2}`;
|
|
12463
|
+
}
|
|
12464
|
+
function byteLength(s) {
|
|
12465
|
+
return new TextEncoder().encode(s).byteLength;
|
|
12466
|
+
}
|
|
12467
|
+
function createFramer() {
|
|
12468
|
+
const encoder = new TextEncoder();
|
|
12469
|
+
const decoder = new TextDecoder();
|
|
12470
|
+
let buffer = new Uint8Array(0);
|
|
12471
|
+
const append = (chunk) => {
|
|
12472
|
+
const next = new Uint8Array(buffer.length + chunk.length);
|
|
12473
|
+
next.set(buffer);
|
|
12474
|
+
next.set(chunk, buffer.length);
|
|
12475
|
+
buffer = next;
|
|
12476
|
+
};
|
|
12477
|
+
const headerEnd = () => {
|
|
12478
|
+
for (let i2 = 0; i2 + 3 < buffer.length; i2++) {
|
|
12479
|
+
if (buffer[i2] === 13 && buffer[i2 + 1] === 10 && buffer[i2 + 2] === 13 && buffer[i2 + 3] === 10) return i2;
|
|
12480
|
+
}
|
|
12481
|
+
return -1;
|
|
12482
|
+
};
|
|
12483
|
+
return {
|
|
12484
|
+
push(chunk) {
|
|
12485
|
+
append(typeof chunk === "string" ? encoder.encode(chunk) : chunk);
|
|
12486
|
+
const out2 = [];
|
|
12487
|
+
for (; ; ) {
|
|
12488
|
+
const end = headerEnd();
|
|
12489
|
+
if (end < 0) return out2;
|
|
12490
|
+
const headers = decoder.decode(buffer.subarray(0, end));
|
|
12491
|
+
const match = /content-length:\s*(\d+)/i.exec(headers);
|
|
12492
|
+
if (!match) {
|
|
12493
|
+
buffer = buffer.subarray(end + 4);
|
|
12494
|
+
continue;
|
|
12495
|
+
}
|
|
12496
|
+
const length = Number(match[1]);
|
|
12497
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > MAX_FRAME_BYTES) {
|
|
12498
|
+
buffer = buffer.subarray(end + 4);
|
|
12499
|
+
continue;
|
|
12500
|
+
}
|
|
12501
|
+
const start2 = end + 4;
|
|
12502
|
+
if (buffer.length < start2 + length) return out2;
|
|
12503
|
+
const body2 = decoder.decode(buffer.subarray(start2, start2 + length));
|
|
12504
|
+
buffer = buffer.subarray(start2 + length);
|
|
12505
|
+
try {
|
|
12506
|
+
out2.push(JSON.parse(body2));
|
|
12507
|
+
} catch {
|
|
12508
|
+
}
|
|
12509
|
+
}
|
|
12510
|
+
}
|
|
12511
|
+
};
|
|
12512
|
+
}
|
|
12513
|
+
function fileUri(root, rel2) {
|
|
12514
|
+
const abs = `${root.replace(/\/+$/, "")}/${rel2.replace(/^\/+/, "")}`;
|
|
12515
|
+
const drive = /^([A-Za-z]):/.exec(abs);
|
|
12516
|
+
const path = drive ? `/${abs}` : abs;
|
|
12517
|
+
return "file://" + path.split("/").map((segment, i2) => i2 === 0 ? segment : encodeURIComponent(segment)).join("/");
|
|
12518
|
+
}
|
|
12519
|
+
function relFromUri(root, uri) {
|
|
12520
|
+
if (!uri.startsWith("file://")) return void 0;
|
|
12521
|
+
let path = decodeURIComponent(uri.slice("file://".length));
|
|
12522
|
+
if (/^\/[A-Za-z]:/.test(path)) path = path.slice(1);
|
|
12523
|
+
const base = root.replace(/\/+$/, "");
|
|
12524
|
+
if (path === base) return "";
|
|
12525
|
+
if (!path.startsWith(`${base}/`)) return void 0;
|
|
12526
|
+
return path.slice(base.length + 1);
|
|
12527
|
+
}
|
|
12528
|
+
function locationsToRefs(root, raw) {
|
|
12529
|
+
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" ? [raw] : [];
|
|
12530
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12531
|
+
const out2 = [];
|
|
12532
|
+
for (const item of list) {
|
|
12533
|
+
if (!item || typeof item !== "object") continue;
|
|
12534
|
+
const uri = item.uri ?? item.targetUri;
|
|
12535
|
+
if (!uri) continue;
|
|
12536
|
+
const file = relFromUri(root, uri);
|
|
12537
|
+
if (file === void 0 || file === "") continue;
|
|
12538
|
+
const start2 = (item.range ?? item.targetSelectionRange ?? item.targetRange)?.start;
|
|
12539
|
+
const line = typeof start2?.line === "number" ? start2.line + 1 : 1;
|
|
12540
|
+
const character = typeof start2?.character === "number" ? start2.character : void 0;
|
|
12541
|
+
const key = `${file}:${line}:${character ?? ""}`;
|
|
12542
|
+
if (seen.has(key)) continue;
|
|
12543
|
+
seen.add(key);
|
|
12544
|
+
out2.push(character === void 0 ? { file, line } : { file, line, character });
|
|
12545
|
+
}
|
|
12546
|
+
return out2.sort((a, b) => byStr(a.file, b.file) || a.line - b.line || (a.character ?? 0) - (b.character ?? 0));
|
|
12547
|
+
}
|
|
12548
|
+
var MAX_FRAME_BYTES, HEADER_END;
|
|
12549
|
+
var init_protocol = __esm({
|
|
12550
|
+
"src/lsp/protocol.ts"() {
|
|
12551
|
+
"use strict";
|
|
12552
|
+
init_sort();
|
|
12553
|
+
MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
12554
|
+
HEADER_END = "\r\n\r\n";
|
|
12555
|
+
}
|
|
12556
|
+
});
|
|
12557
|
+
|
|
12558
|
+
// src/lsp/client.ts
|
|
12559
|
+
async function openLspSession(transport, options) {
|
|
12560
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT;
|
|
12561
|
+
const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT;
|
|
12562
|
+
const framer = createFramer();
|
|
12563
|
+
const pending = /* @__PURE__ */ new Map();
|
|
12564
|
+
const open = /* @__PURE__ */ new Set();
|
|
12565
|
+
let nextId = 1;
|
|
12566
|
+
let dead;
|
|
12567
|
+
const failAll = (error) => {
|
|
12568
|
+
dead = error;
|
|
12569
|
+
for (const [, p] of pending) {
|
|
12570
|
+
clearTimeout(p.timer);
|
|
12571
|
+
p.reject(error);
|
|
12572
|
+
}
|
|
12573
|
+
pending.clear();
|
|
12574
|
+
};
|
|
12575
|
+
transport.onData((chunk) => {
|
|
12576
|
+
for (const message of framer.push(chunk)) {
|
|
12577
|
+
if (typeof message.id !== "number") continue;
|
|
12578
|
+
const waiter = pending.get(message.id);
|
|
12579
|
+
if (!waiter) continue;
|
|
12580
|
+
pending.delete(message.id);
|
|
12581
|
+
clearTimeout(waiter.timer);
|
|
12582
|
+
if (message.error) waiter.reject(new Error(`${message.error.message} (code ${message.error.code})`));
|
|
12583
|
+
else waiter.resolve(message.result);
|
|
12584
|
+
}
|
|
12585
|
+
});
|
|
12586
|
+
transport.onExit((code) => failAll(new Error(`language server exited (code ${code ?? "unknown"})`)));
|
|
12587
|
+
const notify = (method, params) => {
|
|
12588
|
+
if (dead) return;
|
|
12589
|
+
transport.write(encodeMessage({ jsonrpc: "2.0", method, params }));
|
|
12590
|
+
};
|
|
12591
|
+
const request = (method, params, budget = timeoutMs) => {
|
|
12592
|
+
if (dead) return Promise.reject(dead);
|
|
12593
|
+
const id = nextId++;
|
|
12594
|
+
return new Promise((resolve5, reject) => {
|
|
12595
|
+
const timer = setTimeout(() => {
|
|
12596
|
+
pending.delete(id);
|
|
12597
|
+
reject(new LspTimeout(method, budget));
|
|
12598
|
+
}, budget);
|
|
12599
|
+
timer.unref?.();
|
|
12600
|
+
pending.set(id, { resolve: resolve5, reject, timer });
|
|
12601
|
+
transport.write(encodeMessage({ jsonrpc: "2.0", id, method, params }));
|
|
12602
|
+
});
|
|
12603
|
+
};
|
|
12604
|
+
const initResult = await request(
|
|
12605
|
+
"initialize",
|
|
12606
|
+
{
|
|
12607
|
+
processId: null,
|
|
12608
|
+
rootUri: fileUri(options.root, ""),
|
|
12609
|
+
workspaceFolders: [{ uri: fileUri(options.root, ""), name: "repo" }],
|
|
12610
|
+
capabilities: {
|
|
12611
|
+
textDocument: {
|
|
12612
|
+
references: { dynamicRegistration: false },
|
|
12613
|
+
definition: { dynamicRegistration: false, linkSupport: true },
|
|
12614
|
+
implementation: { dynamicRegistration: false, linkSupport: true }
|
|
12615
|
+
}
|
|
12616
|
+
},
|
|
12617
|
+
...options.initializationOptions !== void 0 ? { initializationOptions: options.initializationOptions } : {}
|
|
12618
|
+
},
|
|
12619
|
+
startupTimeoutMs
|
|
12620
|
+
);
|
|
12621
|
+
notify("initialized", {});
|
|
12622
|
+
const provides = (key) => {
|
|
12623
|
+
const value = initResult?.capabilities?.[key];
|
|
12624
|
+
return value === true || typeof value === "object" && value !== null;
|
|
12625
|
+
};
|
|
12626
|
+
const capabilities = {
|
|
12627
|
+
references: provides("referencesProvider"),
|
|
12628
|
+
definition: provides("definitionProvider"),
|
|
12629
|
+
implementation: provides("implementationProvider"),
|
|
12630
|
+
typeHierarchy: provides("typeHierarchyProvider")
|
|
12631
|
+
};
|
|
12632
|
+
const positionOf = (rel2, line, character) => ({
|
|
12633
|
+
textDocument: { uri: fileUri(options.root, rel2) },
|
|
12634
|
+
// The engine counts lines from 1; LSP counts from 0.
|
|
12635
|
+
position: { line: Math.max(0, line - 1), character }
|
|
12636
|
+
});
|
|
12637
|
+
return {
|
|
12638
|
+
capabilities,
|
|
12639
|
+
didOpen(rel2, text, languageId) {
|
|
12640
|
+
if (open.has(rel2)) return;
|
|
12641
|
+
open.add(rel2);
|
|
12642
|
+
notify("textDocument/didOpen", {
|
|
12643
|
+
textDocument: { uri: fileUri(options.root, rel2), languageId, version: 1, text }
|
|
12644
|
+
});
|
|
12645
|
+
},
|
|
12646
|
+
async references(rel2, line, character) {
|
|
12647
|
+
if (!capabilities.references) return [];
|
|
12648
|
+
const raw = await request("textDocument/references", {
|
|
12649
|
+
...positionOf(rel2, line, character),
|
|
12650
|
+
context: { includeDeclaration: true }
|
|
12651
|
+
});
|
|
12652
|
+
return locationsToRefs(options.root, raw);
|
|
12653
|
+
},
|
|
12654
|
+
async definition(rel2, line, character) {
|
|
12655
|
+
if (!capabilities.definition) return [];
|
|
12656
|
+
return locationsToRefs(options.root, await request("textDocument/definition", positionOf(rel2, line, character)));
|
|
12657
|
+
},
|
|
12658
|
+
async shutdown() {
|
|
12659
|
+
try {
|
|
12660
|
+
for (const rel2 of open) notify("textDocument/didClose", { textDocument: { uri: fileUri(options.root, rel2) } });
|
|
12661
|
+
open.clear();
|
|
12662
|
+
await request("shutdown", null, Math.min(timeoutMs, 2e3));
|
|
12663
|
+
notify("exit", void 0);
|
|
12664
|
+
} catch {
|
|
12665
|
+
} finally {
|
|
12666
|
+
failAll(new Error("session closed"));
|
|
12667
|
+
transport.close();
|
|
12668
|
+
}
|
|
12669
|
+
}
|
|
12670
|
+
};
|
|
12671
|
+
}
|
|
12672
|
+
var LspTimeout, DEFAULT_TIMEOUT, DEFAULT_STARTUP_TIMEOUT;
|
|
12673
|
+
var init_client = __esm({
|
|
12674
|
+
"src/lsp/client.ts"() {
|
|
12675
|
+
"use strict";
|
|
12676
|
+
init_protocol();
|
|
12677
|
+
LspTimeout = class extends Error {
|
|
12678
|
+
constructor(method, ms) {
|
|
12679
|
+
super(`${method} exceeded ${ms}ms`);
|
|
12680
|
+
this.name = "LspTimeout";
|
|
12681
|
+
}
|
|
12682
|
+
};
|
|
12683
|
+
DEFAULT_TIMEOUT = 5e3;
|
|
12684
|
+
DEFAULT_STARTUP_TIMEOUT = 15e3;
|
|
12685
|
+
}
|
|
12686
|
+
});
|
|
12687
|
+
|
|
12688
|
+
// src/lsp/config.ts
|
|
12689
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
12690
|
+
import { join as join18, resolve as resolve3 } from "path";
|
|
12691
|
+
function resolveLspConfigPath(repo) {
|
|
12692
|
+
const env = process.env.CODEINDEX_LSP_CONFIG;
|
|
12693
|
+
if (env !== void 0) {
|
|
12694
|
+
const trimmed = env.trim();
|
|
12695
|
+
if (!trimmed || trimmed === "0" || trimmed.toLowerCase() === "off") return { path: void 0, source: "none" };
|
|
12696
|
+
return { path: resolve3(trimmed), source: "env" };
|
|
12697
|
+
}
|
|
12698
|
+
const inRepo = join18(repo, LSP_CONFIG_DIR, LSP_CONFIG_NAME);
|
|
12699
|
+
if (existsSync8(inRepo)) return { path: inRepo, source: "repo" };
|
|
12700
|
+
const inCwd = join18(process.cwd(), LSP_CONFIG_DIR, LSP_CONFIG_NAME);
|
|
12701
|
+
if (inCwd !== inRepo && existsSync8(inCwd)) return { path: inCwd, source: "cwd" };
|
|
12702
|
+
return { path: void 0, source: "none" };
|
|
12703
|
+
}
|
|
12704
|
+
function parseLspConfig(payload) {
|
|
12705
|
+
if (!payload || typeof payload !== "object") throw new Error("lsp.json must be a JSON object");
|
|
12706
|
+
const raw = payload;
|
|
12707
|
+
if (raw.version !== 1) throw new Error(`lsp.json: unsupported version ${JSON.stringify(raw.version)} (expected 1)`);
|
|
12708
|
+
if (!Array.isArray(raw.servers)) throw new Error("lsp.json: `servers` must be an array");
|
|
12709
|
+
const ids = /* @__PURE__ */ new Set();
|
|
12710
|
+
const servers = raw.servers.map((entry, i2) => {
|
|
12711
|
+
if (!entry || typeof entry !== "object") throw new Error(`lsp.json: servers[${i2}] must be an object`);
|
|
12712
|
+
const s = entry;
|
|
12713
|
+
const id = typeof s.id === "string" && s.id.trim() ? s.id.trim() : void 0;
|
|
12714
|
+
if (!id) throw new Error(`lsp.json: servers[${i2}].id must be a non-empty string`);
|
|
12715
|
+
if (ids.has(id)) throw new Error(`lsp.json: duplicate server id ${JSON.stringify(id)}`);
|
|
12716
|
+
ids.add(id);
|
|
12717
|
+
if (typeof s.command !== "string" || !s.command.trim()) throw new Error(`lsp.json: servers[${i2}].command must be a non-empty string`);
|
|
12718
|
+
if (!Array.isArray(s.languages) || !s.languages.length || s.languages.some((l) => typeof l !== "string")) {
|
|
12719
|
+
throw new Error(`lsp.json: servers[${i2}].languages must be a non-empty array of strings`);
|
|
12720
|
+
}
|
|
12721
|
+
if (s.args !== void 0 && (!Array.isArray(s.args) || s.args.some((a) => typeof a !== "string"))) {
|
|
12722
|
+
throw new Error(`lsp.json: servers[${i2}].args must be an array of strings`);
|
|
12723
|
+
}
|
|
12724
|
+
return {
|
|
12725
|
+
id,
|
|
12726
|
+
languages: s.languages,
|
|
12727
|
+
...typeof s.languageId === "string" ? { languageId: s.languageId } : {},
|
|
12728
|
+
command: s.command,
|
|
12729
|
+
...Array.isArray(s.args) ? { args: s.args } : {},
|
|
12730
|
+
...s.env && typeof s.env === "object" ? { env: s.env } : {},
|
|
12731
|
+
...s.initializationOptions !== void 0 ? { initializationOptions: s.initializationOptions } : {},
|
|
12732
|
+
...typeof s.timeoutMs === "number" ? { timeoutMs: s.timeoutMs } : {},
|
|
12733
|
+
...typeof s.startupTimeoutMs === "number" ? { startupTimeoutMs: s.startupTimeoutMs } : {}
|
|
12734
|
+
};
|
|
12735
|
+
});
|
|
12736
|
+
return { version: 1, servers };
|
|
12737
|
+
}
|
|
12738
|
+
function loadLspConfig(repo) {
|
|
12739
|
+
const { path } = resolveLspConfigPath(repo);
|
|
12740
|
+
if (!path || !existsSync8(path)) return void 0;
|
|
12741
|
+
let payload;
|
|
12742
|
+
try {
|
|
12743
|
+
payload = JSON.parse(readFileSync10(path, "utf8"));
|
|
12744
|
+
} catch (e) {
|
|
12745
|
+
throw new Error(`${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
12746
|
+
}
|
|
12747
|
+
return parseLspConfig(payload);
|
|
12748
|
+
}
|
|
12749
|
+
function serverForLang(config, lang) {
|
|
12750
|
+
return config.servers.find((s) => s.languages.includes(lang));
|
|
12751
|
+
}
|
|
12752
|
+
function timeoutFor(server) {
|
|
12753
|
+
return positiveEnv("CODEINDEX_LSP_TIMEOUT_MS") ?? server.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
12754
|
+
}
|
|
12755
|
+
function startupTimeoutFor(server) {
|
|
12756
|
+
return positiveEnv("CODEINDEX_LSP_STARTUP_TIMEOUT_MS") ?? server.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
12757
|
+
}
|
|
12758
|
+
function positiveEnv(name2) {
|
|
12759
|
+
const raw = process.env[name2];
|
|
12760
|
+
if (raw === void 0) return void 0;
|
|
12761
|
+
const value = Number(raw);
|
|
12762
|
+
return Number.isFinite(value) && value > 0 ? value : void 0;
|
|
12763
|
+
}
|
|
12764
|
+
var LSP_CONFIG_NAME, LSP_CONFIG_DIR, DEFAULT_TIMEOUT_MS, DEFAULT_STARTUP_TIMEOUT_MS;
|
|
12765
|
+
var init_config2 = __esm({
|
|
12766
|
+
"src/lsp/config.ts"() {
|
|
12767
|
+
"use strict";
|
|
12768
|
+
LSP_CONFIG_NAME = "lsp.json";
|
|
12769
|
+
LSP_CONFIG_DIR = ".codeindex";
|
|
12770
|
+
DEFAULT_TIMEOUT_MS = 5e3;
|
|
12771
|
+
DEFAULT_STARTUP_TIMEOUT_MS = 15e3;
|
|
12772
|
+
}
|
|
12773
|
+
});
|
|
12774
|
+
|
|
12775
|
+
// src/lsp/refs.ts
|
|
12776
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
12777
|
+
import { join as join19 } from "path";
|
|
12778
|
+
function lspUnavailable(server, reason) {
|
|
12779
|
+
return { server, ok: false, reason, refs: [], agreement: { both: [], lspOnly: [], staticOnly: [] } };
|
|
12780
|
+
}
|
|
12781
|
+
function columnOfSymbol(root, rel2, line, name2) {
|
|
12782
|
+
try {
|
|
12783
|
+
const lines = readFileSync11(join19(root, rel2), "utf8").split(/\r?\n/);
|
|
12784
|
+
const index = lines[line - 1]?.indexOf(name2) ?? -1;
|
|
12785
|
+
return index < 0 ? 0 : index;
|
|
12786
|
+
} catch {
|
|
12787
|
+
return 0;
|
|
12788
|
+
}
|
|
12789
|
+
}
|
|
12790
|
+
function agreementOf(refs, statik) {
|
|
12791
|
+
const lspFiles = new Set(refs.map((r) => r.file));
|
|
12792
|
+
const staticFiles = /* @__PURE__ */ new Set([
|
|
12793
|
+
...statik.callSites.map((c2) => c2.file),
|
|
12794
|
+
...statik.referencingFiles,
|
|
12795
|
+
// Declaration sites are references in the LSP sense (includeDeclaration),
|
|
12796
|
+
// so counting them keeps the two sides comparing the same population.
|
|
12797
|
+
...statik.defs.map((d) => d.file)
|
|
12798
|
+
]);
|
|
12799
|
+
const both = [];
|
|
12800
|
+
const lspOnly = [];
|
|
12801
|
+
const staticOnly = [];
|
|
12802
|
+
for (const file of lspFiles) (staticFiles.has(file) ? both : lspOnly).push(file);
|
|
12803
|
+
for (const file of staticFiles) if (!lspFiles.has(file)) staticOnly.push(file);
|
|
12804
|
+
return { both: both.sort(byStr), lspOnly: lspOnly.sort(byStr), staticOnly: staticOnly.sort(byStr) };
|
|
12805
|
+
}
|
|
12806
|
+
async function annotateWithLsp(scan2, name2, statik, session, serverId, languageId) {
|
|
12807
|
+
if (!session.capabilities.references) {
|
|
12808
|
+
return { ...statik, lsp: lspUnavailable(serverId, "server does not provide textDocument/references") };
|
|
12809
|
+
}
|
|
12810
|
+
if (!statik.defs.length) {
|
|
12811
|
+
return { ...statik, lsp: lspUnavailable(serverId, `no declaration of ${name2} to anchor a request on`) };
|
|
12812
|
+
}
|
|
12813
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12814
|
+
const refs = [];
|
|
12815
|
+
try {
|
|
12816
|
+
for (const def of statik.defs) {
|
|
12817
|
+
const text = readTextOrEmpty(scan2.root, def.file);
|
|
12818
|
+
if (!text) continue;
|
|
12819
|
+
session.didOpen(def.file, text, languageId);
|
|
12820
|
+
const character = columnOfSymbol(scan2.root, def.file, def.line, name2);
|
|
12821
|
+
for (const ref of await session.references(def.file, def.line, character)) {
|
|
12822
|
+
const key = `${ref.file}:${ref.line}:${ref.character ?? ""}`;
|
|
12823
|
+
if (seen.has(key)) continue;
|
|
12824
|
+
seen.add(key);
|
|
12825
|
+
refs.push(ref);
|
|
12826
|
+
}
|
|
12827
|
+
}
|
|
12828
|
+
} catch (e) {
|
|
12829
|
+
return {
|
|
12830
|
+
...statik,
|
|
12831
|
+
lsp: {
|
|
12832
|
+
server: serverId,
|
|
12833
|
+
ok: false,
|
|
12834
|
+
reason: e instanceof Error ? e.message : String(e),
|
|
12835
|
+
refs: refs.sort(refOrder),
|
|
12836
|
+
agreement: agreementOf(refs, statik)
|
|
12837
|
+
}
|
|
12838
|
+
};
|
|
12839
|
+
}
|
|
12840
|
+
refs.sort(refOrder);
|
|
12841
|
+
return { ...statik, lsp: { server: serverId, ok: true, refs, agreement: agreementOf(refs, statik) } };
|
|
12842
|
+
}
|
|
12843
|
+
function refOrder(a, b) {
|
|
12844
|
+
return byStr(a.file, b.file) || a.line - b.line || (a.character ?? 0) - (b.character ?? 0);
|
|
12845
|
+
}
|
|
12846
|
+
function readTextOrEmpty(root, rel2) {
|
|
12847
|
+
try {
|
|
12848
|
+
return readFileSync11(join19(root, rel2), "utf8");
|
|
12849
|
+
} catch {
|
|
12850
|
+
return "";
|
|
12851
|
+
}
|
|
12852
|
+
}
|
|
12853
|
+
var init_refs = __esm({
|
|
12854
|
+
"src/lsp/refs.ts"() {
|
|
12855
|
+
"use strict";
|
|
12856
|
+
init_sort();
|
|
12857
|
+
}
|
|
12858
|
+
});
|
|
12859
|
+
|
|
12860
|
+
// src/lsp/spawn.ts
|
|
12861
|
+
import { spawn } from "child_process";
|
|
12862
|
+
function spawnLspTransport(server, cwd) {
|
|
12863
|
+
let child;
|
|
12864
|
+
try {
|
|
12865
|
+
child = spawn(server.command, server.args ?? [], {
|
|
12866
|
+
cwd,
|
|
12867
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
12868
|
+
...server.env ? { env: { ...process.env, ...server.env } } : {}
|
|
12869
|
+
});
|
|
12870
|
+
} catch {
|
|
12871
|
+
return void 0;
|
|
12872
|
+
}
|
|
12873
|
+
let exited = false;
|
|
12874
|
+
const dataListeners = [];
|
|
12875
|
+
const exitListeners = [];
|
|
12876
|
+
const fireExit = (code) => {
|
|
12877
|
+
if (exited) return;
|
|
12878
|
+
exited = true;
|
|
12879
|
+
for (const listener of exitListeners) listener(code);
|
|
12880
|
+
};
|
|
12881
|
+
child.on("error", () => fireExit(null));
|
|
12882
|
+
child.on("close", (code) => fireExit(code));
|
|
12883
|
+
child.stdout?.on("data", (chunk) => {
|
|
12884
|
+
for (const listener of dataListeners) listener(chunk);
|
|
12885
|
+
});
|
|
12886
|
+
child.stderr?.on("data", () => {
|
|
12887
|
+
});
|
|
12888
|
+
return {
|
|
12889
|
+
write(chunk) {
|
|
12890
|
+
if (exited) return;
|
|
12891
|
+
try {
|
|
12892
|
+
child.stdin?.write(chunk);
|
|
12893
|
+
} catch {
|
|
12894
|
+
fireExit(null);
|
|
12895
|
+
}
|
|
12896
|
+
},
|
|
12897
|
+
onData(cb) {
|
|
12898
|
+
dataListeners.push(cb);
|
|
12899
|
+
},
|
|
12900
|
+
onExit(cb) {
|
|
12901
|
+
if (exited) cb(null);
|
|
12902
|
+
else exitListeners.push(cb);
|
|
12903
|
+
},
|
|
12904
|
+
close() {
|
|
12905
|
+
try {
|
|
12906
|
+
child.stdin?.end();
|
|
12907
|
+
} catch {
|
|
12908
|
+
}
|
|
12909
|
+
const timer = setTimeout(() => {
|
|
12910
|
+
try {
|
|
12911
|
+
child.kill("SIGKILL");
|
|
12912
|
+
} catch {
|
|
12913
|
+
}
|
|
12914
|
+
}, 2e3);
|
|
12915
|
+
timer.unref?.();
|
|
12916
|
+
child.on("close", () => clearTimeout(timer));
|
|
12917
|
+
}
|
|
12918
|
+
};
|
|
12919
|
+
}
|
|
12920
|
+
var init_spawn = __esm({
|
|
12921
|
+
"src/lsp/spawn.ts"() {
|
|
12922
|
+
"use strict";
|
|
12923
|
+
}
|
|
12924
|
+
});
|
|
12925
|
+
|
|
12926
|
+
// src/lsp/index.ts
|
|
12927
|
+
async function lspStatus(scan2, repo, probe = false) {
|
|
12928
|
+
const { path, source } = resolveLspConfigPath(repo);
|
|
12929
|
+
const config = loadLspConfig(repo);
|
|
12930
|
+
if (!config) return { lspVersion: 1, mode: "none", configPath: path ?? null, source, servers: [], unmappedLanguages: [] };
|
|
12931
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12932
|
+
for (const file of scan2.files) counts.set(file.lang, (counts.get(file.lang) ?? 0) + 1);
|
|
12933
|
+
const servers = [];
|
|
12934
|
+
for (const server of config.servers) {
|
|
12935
|
+
const status = {
|
|
12936
|
+
id: server.id,
|
|
12937
|
+
languages: server.languages,
|
|
12938
|
+
command: server.command,
|
|
12939
|
+
onPath: have(server.command),
|
|
12940
|
+
filesInRepo: server.languages.reduce((sum, lang) => sum + (counts.get(lang) ?? 0), 0)
|
|
12941
|
+
};
|
|
12942
|
+
if (probe) {
|
|
12943
|
+
const session = await tryOpen(server, scan2.root);
|
|
12944
|
+
if (session.ok) {
|
|
12945
|
+
status.reachable = true;
|
|
12946
|
+
status.capabilities = session.session.capabilities;
|
|
12947
|
+
await session.session.shutdown();
|
|
12948
|
+
} else {
|
|
12949
|
+
status.reachable = false;
|
|
12950
|
+
status.error = session.reason;
|
|
12951
|
+
}
|
|
12952
|
+
}
|
|
12953
|
+
servers.push(status);
|
|
12954
|
+
}
|
|
12955
|
+
const claimed = new Set(config.servers.flatMap((s) => s.languages));
|
|
12956
|
+
const unmappedLanguages = [...counts.keys()].filter((lang) => !claimed.has(lang) && lang !== "other").sort();
|
|
12957
|
+
return { lspVersion: 1, mode: "configured", configPath: path ?? null, source, servers, unmappedLanguages };
|
|
12958
|
+
}
|
|
12959
|
+
async function tryOpen(server, root) {
|
|
12960
|
+
if (!have(server.command)) return { ok: false, reason: `${server.command} is not on PATH` };
|
|
12961
|
+
const transport = spawnLspTransport(server, root);
|
|
12962
|
+
if (!transport) return { ok: false, reason: `could not start ${server.command}` };
|
|
12963
|
+
try {
|
|
12964
|
+
const session = await openLspSession(transport, {
|
|
12965
|
+
root,
|
|
12966
|
+
timeoutMs: timeoutFor(server),
|
|
12967
|
+
startupTimeoutMs: startupTimeoutFor(server),
|
|
12968
|
+
...server.initializationOptions !== void 0 ? { initializationOptions: server.initializationOptions } : {}
|
|
12969
|
+
});
|
|
12970
|
+
return { ok: true, session };
|
|
12971
|
+
} catch (e) {
|
|
12972
|
+
transport.close();
|
|
12973
|
+
return { ok: false, reason: e instanceof Error ? e.message : String(e) };
|
|
12974
|
+
}
|
|
12975
|
+
}
|
|
12976
|
+
async function referencesWithLsp(scan2, repo, name2, statik) {
|
|
12977
|
+
let config;
|
|
12978
|
+
try {
|
|
12979
|
+
config = loadLspConfig(repo);
|
|
12980
|
+
} catch (e) {
|
|
12981
|
+
return { ...statik, lsp: lspUnavailable("(config)", e instanceof Error ? e.message : String(e)) };
|
|
12982
|
+
}
|
|
12983
|
+
if (!config) return statik;
|
|
12984
|
+
const lang = statik.defs[0]?.lang;
|
|
12985
|
+
if (!lang) return { ...statik, lsp: lspUnavailable("(none)", `no declaration of ${name2} to anchor a request on`) };
|
|
12986
|
+
const server = serverForLang(config, lang);
|
|
12987
|
+
if (!server) return { ...statik, lsp: lspUnavailable("(none)", `no server configured for ${lang}`) };
|
|
12988
|
+
const opened = await tryOpen(server, scan2.root);
|
|
12989
|
+
if (!opened.ok) return { ...statik, lsp: lspUnavailable(server.id, opened.reason) };
|
|
12990
|
+
try {
|
|
12991
|
+
return await annotateWithLsp(scan2, name2, statik, opened.session, server.id, server.languageId ?? server.languages[0]);
|
|
12992
|
+
} finally {
|
|
12993
|
+
await opened.session.shutdown();
|
|
12994
|
+
}
|
|
12995
|
+
}
|
|
12996
|
+
var init_lsp = __esm({
|
|
12997
|
+
"src/lsp/index.ts"() {
|
|
12998
|
+
"use strict";
|
|
12999
|
+
init_util();
|
|
13000
|
+
init_client();
|
|
13001
|
+
init_config2();
|
|
13002
|
+
init_refs();
|
|
13003
|
+
init_spawn();
|
|
13004
|
+
}
|
|
13005
|
+
});
|
|
13006
|
+
|
|
12196
13007
|
// src/rules.ts
|
|
12197
13008
|
function isEntrypointLike(rel2) {
|
|
12198
13009
|
const base = rel2.split("/").pop();
|
|
@@ -12395,96 +13206,6 @@ var init_rules = __esm({
|
|
|
12395
13206
|
}
|
|
12396
13207
|
});
|
|
12397
13208
|
|
|
12398
|
-
// src/coupling.ts
|
|
12399
|
-
function changeCoupling(dir, opts = {}) {
|
|
12400
|
-
const maxCommitFiles = opts.maxCommitFiles ?? 30;
|
|
12401
|
-
const minTogether = opts.minTogether ?? 3;
|
|
12402
|
-
const maxPairs = opts.maxPairs ?? 100;
|
|
12403
|
-
const range = opts.since ? [`${opts.since}..HEAD`] : [];
|
|
12404
|
-
const res = sh("git", ["-C", dir, "-c", "core.quotePath=false", "log", ...range, "--pretty=format:%x1e", "--name-only"]);
|
|
12405
|
-
if (!res.ok) return { ok: false, couplings: [] };
|
|
12406
|
-
const totals = /* @__PURE__ */ new Map();
|
|
12407
|
-
const pairs = /* @__PURE__ */ new Map();
|
|
12408
|
-
for (const block of res.stdout.split("")) {
|
|
12409
|
-
const files = block.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
12410
|
-
if (!files.length || files.length > maxCommitFiles) continue;
|
|
12411
|
-
const unique = [...new Set(files)].sort(byStr);
|
|
12412
|
-
for (const f of unique) totals.set(f, (totals.get(f) ?? 0) + 1);
|
|
12413
|
-
for (let i2 = 0; i2 < unique.length; i2++) {
|
|
12414
|
-
for (let j = i2 + 1; j < unique.length; j++) {
|
|
12415
|
-
const key = `${unique[i2]}${SEP4}${unique[j]}`;
|
|
12416
|
-
pairs.set(key, (pairs.get(key) ?? 0) + 1);
|
|
12417
|
-
}
|
|
12418
|
-
}
|
|
12419
|
-
}
|
|
12420
|
-
const out2 = [];
|
|
12421
|
-
for (const [key, together] of pairs) {
|
|
12422
|
-
if (together < minTogether) continue;
|
|
12423
|
-
const [a, b] = key.split(SEP4);
|
|
12424
|
-
const totalA = totals.get(a) ?? together;
|
|
12425
|
-
const totalB = totals.get(b) ?? together;
|
|
12426
|
-
out2.push({ a, b, together, totalA, totalB, strength: Number((together / Math.min(totalA, totalB)).toFixed(3)) });
|
|
12427
|
-
}
|
|
12428
|
-
out2.sort((x, y) => y.strength - x.strength || y.together - x.together || byStr(x.a, y.a) || byStr(x.b, y.b));
|
|
12429
|
-
return { ok: true, couplings: out2.slice(0, maxPairs) };
|
|
12430
|
-
}
|
|
12431
|
-
function rankHotspots(scan2, churn, top = 20) {
|
|
12432
|
-
const out2 = scan2.files.filter((f) => f.kind === "code").map((f) => {
|
|
12433
|
-
const commits = churn.get(f.rel) ?? 0;
|
|
12434
|
-
return { rel: f.rel, lines: f.lines, commits, score: Number((commits * Math.log2(f.lines + 1)).toFixed(2)) };
|
|
12435
|
-
});
|
|
12436
|
-
out2.sort((a, b) => b.score - a.score || b.lines - a.lines || byStr(a.rel, b.rel));
|
|
12437
|
-
return out2.slice(0, top);
|
|
12438
|
-
}
|
|
12439
|
-
var SEP4;
|
|
12440
|
-
var init_coupling = __esm({
|
|
12441
|
-
"src/coupling.ts"() {
|
|
12442
|
-
"use strict";
|
|
12443
|
-
init_util();
|
|
12444
|
-
init_sort();
|
|
12445
|
-
SEP4 = "\0";
|
|
12446
|
-
}
|
|
12447
|
-
});
|
|
12448
|
-
|
|
12449
|
-
// src/repomap.ts
|
|
12450
|
-
function renderRepoMap(scan2, graph, opts = {}) {
|
|
12451
|
-
const budgetChars = (opts.budgetTokens ?? 1024) * CHARS_PER_TOKEN;
|
|
12452
|
-
const maxSymbols = opts.maxSymbolsPerFile ?? 8;
|
|
12453
|
-
const ranked = [...graph.files].filter((f) => f.fileKind === "code").sort((a, b) => (b.pagerank ?? 0) - (a.pagerank ?? 0) || b.symbols - a.symbols || byStr(a.rel, b.rel));
|
|
12454
|
-
const records = new Map(scan2.files.map((f) => [f.rel, f]));
|
|
12455
|
-
const header = `# repo map \u2014 ${graph.fileCount} files
|
|
12456
|
-
`;
|
|
12457
|
-
let out2 = header;
|
|
12458
|
-
let files = 0;
|
|
12459
|
-
for (const node of ranked) {
|
|
12460
|
-
const rec = records.get(node.rel);
|
|
12461
|
-
if (!rec) continue;
|
|
12462
|
-
const symbols = [...rec.symbols].filter((s) => s.kind !== "reexport" && s.kind !== "reexport-all").sort((a, b) => Number(b.exported) - Number(a.exported) || a.line - b.line).slice(0, maxSymbols);
|
|
12463
|
-
let block = `
|
|
12464
|
-
${node.rel}:
|
|
12465
|
-
`;
|
|
12466
|
-
for (const s of symbols) {
|
|
12467
|
-
const sig = (s.signature ?? `${s.kind} ${s.name}`).replace(/\s+/g, " ").trim().slice(0, 120);
|
|
12468
|
-
block += ` ${s.line}: ${sig}
|
|
12469
|
-
`;
|
|
12470
|
-
}
|
|
12471
|
-
if (out2.length + block.length > budgetChars) break;
|
|
12472
|
-
out2 += block;
|
|
12473
|
-
files++;
|
|
12474
|
-
}
|
|
12475
|
-
return `${out2}
|
|
12476
|
-
(${files} of ${ranked.length} code files shown, ~${Math.ceil(out2.length / CHARS_PER_TOKEN)} tokens)
|
|
12477
|
-
`;
|
|
12478
|
-
}
|
|
12479
|
-
var CHARS_PER_TOKEN;
|
|
12480
|
-
var init_repomap = __esm({
|
|
12481
|
-
"src/repomap.ts"() {
|
|
12482
|
-
"use strict";
|
|
12483
|
-
init_sort();
|
|
12484
|
-
CHARS_PER_TOKEN = 4;
|
|
12485
|
-
}
|
|
12486
|
-
});
|
|
12487
|
-
|
|
12488
13209
|
// src/deadcode.ts
|
|
12489
13210
|
function findDeadCode(scan2) {
|
|
12490
13211
|
const callers = callerIndexFor(scan2);
|
|
@@ -12594,8 +13315,8 @@ var init_viz = __esm({
|
|
|
12594
13315
|
});
|
|
12595
13316
|
|
|
12596
13317
|
// src/mcp/protocol.ts
|
|
12597
|
-
import { existsSync as
|
|
12598
|
-
import { join as
|
|
13318
|
+
import { existsSync as existsSync9 } from "fs";
|
|
13319
|
+
import { join as join20 } from "path";
|
|
12599
13320
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
12600
13321
|
function validateArgs(schema, args2) {
|
|
12601
13322
|
const props = schema.properties ?? {};
|
|
@@ -12637,7 +13358,7 @@ function negotiateProtocol(requested) {
|
|
|
12637
13358
|
function capResponse(text, tool, repo, maxBytes) {
|
|
12638
13359
|
const bytes = Buffer.byteLength(text, "utf8");
|
|
12639
13360
|
if (bytes <= maxBytes) return text;
|
|
12640
|
-
const artifact = ARTIFACT_FOR[tool] ?
|
|
13361
|
+
const artifact = ARTIFACT_FOR[tool] ? join20(repo, INDEX_DIR, ARTIFACT_FOR[tool]) : void 0;
|
|
12641
13362
|
return JSON.stringify(
|
|
12642
13363
|
{
|
|
12643
13364
|
truncated: true,
|
|
@@ -12646,7 +13367,7 @@ function capResponse(text, tool, repo, maxBytes) {
|
|
|
12646
13367
|
maxBytes,
|
|
12647
13368
|
reason: "This response exceeds the configured limit and was withheld rather than sent as an unusable partial payload.",
|
|
12648
13369
|
narrower: NARROWER[tool] ?? "narrow the request with `scope`, `include`/`exclude`, or a `limit`",
|
|
12649
|
-
...artifact &&
|
|
13370
|
+
...artifact && existsSync9(artifact) ? { artifact, artifactNote: "The full result is already on disk here \u2014 read it directly if you need all of it." } : artifact ? { artifactNote: `Run \`codeindex index --repo ${repo} --out ${join20(repo, INDEX_DIR)}\` to get this as a file.` } : {}
|
|
12650
13371
|
},
|
|
12651
13372
|
null,
|
|
12652
13373
|
2
|
|
@@ -12671,7 +13392,7 @@ function resourceLinkFor(text, tool) {
|
|
|
12671
13392
|
};
|
|
12672
13393
|
}
|
|
12673
13394
|
var PROTOCOL_VERSIONS, LATEST_PROTOCOL, ANNOTATIONS_SINCE, RICH_TOOLS_SINCE, DEFAULT_MAX_RESPONSE_BYTES, NARROWER, ARTIFACT_FOR;
|
|
12674
|
-
var
|
|
13395
|
+
var init_protocol2 = __esm({
|
|
12675
13396
|
"src/mcp/protocol.ts"() {
|
|
12676
13397
|
"use strict";
|
|
12677
13398
|
init_preload();
|
|
@@ -12702,11 +13423,27 @@ function annotationsFor(name2) {
|
|
|
12702
13423
|
openWorldHint: meta.openWorld === true
|
|
12703
13424
|
};
|
|
12704
13425
|
}
|
|
12705
|
-
function
|
|
13426
|
+
function profileNames() {
|
|
13427
|
+
return ["all", ...Object.keys(TOOL_PROFILES).sort()];
|
|
13428
|
+
}
|
|
13429
|
+
function toolsInProfiles(spec) {
|
|
13430
|
+
const names = spec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
13431
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
13432
|
+
for (const name2 of names) {
|
|
13433
|
+
if (name2 === "all") return new Set(TOOLS.map((t) => t.name));
|
|
13434
|
+
const profile = TOOL_PROFILES[name2];
|
|
13435
|
+
if (!profile) throw new Error(`unknown tool profile "${name2}" \u2014 one of: ${profileNames().join(", ")}`);
|
|
13436
|
+
for (const tool of profile) out2.add(tool);
|
|
13437
|
+
}
|
|
13438
|
+
return out2;
|
|
13439
|
+
}
|
|
13440
|
+
function toolsFor(defaultRepo, protocolVersion = PROTOCOL_VERSIONS[0], profile) {
|
|
12706
13441
|
const withAnnotations = protocolVersion >= ANNOTATIONS_SINCE;
|
|
12707
13442
|
const withRich = protocolVersion >= RICH_TOOLS_SINCE;
|
|
12708
|
-
|
|
12709
|
-
|
|
13443
|
+
const allowed = profile ? toolsInProfiles(profile) : void 0;
|
|
13444
|
+
const TOOLS_ = allowed ? TOOLS.filter((t) => allowed.has(t.name)) : TOOLS;
|
|
13445
|
+
if (!defaultRepo && !withAnnotations && !withRich) return TOOLS_;
|
|
13446
|
+
return TOOLS_.map((t) => ({
|
|
12710
13447
|
...t,
|
|
12711
13448
|
...withRich && TOOL_META[t.name] ? { title: TOOL_META[t.name].title } : {},
|
|
12712
13449
|
...withRich && OUTPUT_SCHEMAS[t.name] ? { outputSchema: OUTPUT_SCHEMAS[t.name] } : {},
|
|
@@ -12724,11 +13461,11 @@ function toolsFor(defaultRepo, protocolVersion = PROTOCOL_VERSIONS[0]) {
|
|
|
12724
13461
|
}
|
|
12725
13462
|
}));
|
|
12726
13463
|
}
|
|
12727
|
-
var repoProp, scopeProps, TOOLS, strArr, anyObj, OUTPUT_SCHEMAS, TOOL_META;
|
|
13464
|
+
var repoProp, scopeProps, TOOLS, strArr, anyObj, OUTPUT_SCHEMAS, TOOL_META, TOOL_PROFILES;
|
|
12728
13465
|
var init_tools = __esm({
|
|
12729
13466
|
"src/mcp/tools.ts"() {
|
|
12730
13467
|
"use strict";
|
|
12731
|
-
|
|
13468
|
+
init_protocol2();
|
|
12732
13469
|
repoProp = { repo: { type: "string", description: "Absolute path to the repository root" } };
|
|
12733
13470
|
scopeProps = {
|
|
12734
13471
|
scope: { type: "string", description: "Restrict to one directory (repo-relative)" },
|
|
@@ -12796,7 +13533,7 @@ var init_tools = __esm({
|
|
|
12796
13533
|
},
|
|
12797
13534
|
{
|
|
12798
13535
|
name: "find_symbol",
|
|
12799
|
-
description:
|
|
13536
|
+
description: `Find symbol declarations by name or name path ('Class/method' matches a method inside Class). Each match carries its COMPLETE SIGNATURE (parameters and return type) by default, because "what shape is it" is the question that follows "where is it" almost every time and one round trip beats two. Options: substring matching, includeBody for the declaration's source, concise to drop everything but name/kind/file/line when you genuinely only want a location. Exact-name matches rank first.`,
|
|
12800
13537
|
inputSchema: {
|
|
12801
13538
|
type: "object",
|
|
12802
13539
|
properties: {
|
|
@@ -12804,6 +13541,10 @@ var init_tools = __esm({
|
|
|
12804
13541
|
namePath: { type: "string", description: "Symbol name or Parent/child path" },
|
|
12805
13542
|
substring: { type: "boolean" },
|
|
12806
13543
|
includeBody: { type: "boolean" },
|
|
13544
|
+
concise: {
|
|
13545
|
+
type: "boolean",
|
|
13546
|
+
description: "Return only name/kind/file/line \u2014 drop the signature, line span, visibility and language. Roughly 2.5x smaller; use it when you are resolving a path and nothing more (default false)."
|
|
13547
|
+
},
|
|
12807
13548
|
maxResults: { type: "number", description: "Cap matches (default 50)" }
|
|
12808
13549
|
},
|
|
12809
13550
|
required: ["repo", "namePath"]
|
|
@@ -12814,10 +13555,42 @@ var init_tools = __esm({
|
|
|
12814
13555
|
description: "Who references a symbol? Three labeled tiers: defs (declarations), callSites (line-precise, import-corroborated call bindings), referencingFiles (file-level identifier/doc mentions \u2014 may include homonyms). Confidence decreases across tiers; the labels let you decide what to trust.",
|
|
12815
13556
|
inputSchema: {
|
|
12816
13557
|
type: "object",
|
|
12817
|
-
properties: {
|
|
13558
|
+
properties: {
|
|
13559
|
+
...repoProp,
|
|
13560
|
+
name: { type: "string", description: "Symbol name" },
|
|
13561
|
+
lsp: {
|
|
13562
|
+
type: "boolean",
|
|
13563
|
+
description: "Also ask a configured language server (see lsp_status) and append an `lsp` block: its references plus an `agreement` matrix (both / lspOnly / staticOnly). The three static tiers are unchanged either way. `staticOnly` is where the homonyms are. No config, no binary, a crash or a timeout all degrade to the static answer with a stated reason (default false)."
|
|
13564
|
+
}
|
|
13565
|
+
},
|
|
12818
13566
|
required: ["repo", "name"]
|
|
12819
13567
|
}
|
|
12820
13568
|
},
|
|
13569
|
+
{
|
|
13570
|
+
name: "lsp_status",
|
|
13571
|
+
description: "Is the optional LSP tier configured, and would it answer? Reports the config path and its source, each server with whether its command is on PATH and how many files in this repo it claims, and the languages no server covers. Opt-in by asset: the tier is active only when <repo>/.codeindex/lsp.json exists (or CODEINDEX_LSP_CONFIG points at one). `probe: true` additionally starts each server to read the capabilities it really advertises. The tier never touches graph.json/symbols.json \u2014 it annotates query answers only.",
|
|
13572
|
+
inputSchema: {
|
|
13573
|
+
type: "object",
|
|
13574
|
+
properties: {
|
|
13575
|
+
...repoProp,
|
|
13576
|
+
probe: { type: "boolean", description: "Start each server and read its real capabilities (default false: no spawn)" }
|
|
13577
|
+
},
|
|
13578
|
+
required: ["repo"]
|
|
13579
|
+
}
|
|
13580
|
+
},
|
|
13581
|
+
{
|
|
13582
|
+
name: "onboard",
|
|
13583
|
+
description: "One call that says what this repository IS: its own tagline, size and language mix, monorepo layout when there is one, a token-budgeted map of the highest-PageRank files with their key signatures, and where git says work concentrates. Composes scan_summary + workspaces + repo_map + hotspots so the first four round trips of a session become one, and persists the result as the `onboarding` memory (read_memory) so the next session does not repeat them. Set remember:false to skip the write.",
|
|
13584
|
+
inputSchema: {
|
|
13585
|
+
type: "object",
|
|
13586
|
+
properties: {
|
|
13587
|
+
...repoProp,
|
|
13588
|
+
budgetTokens: { type: "number", description: "Token budget for the key-files section (default 900)" },
|
|
13589
|
+
remember: { type: "boolean", description: "Persist the brief as the `onboarding` memory (default true)" }
|
|
13590
|
+
},
|
|
13591
|
+
required: ["repo"]
|
|
13592
|
+
}
|
|
13593
|
+
},
|
|
12821
13594
|
{
|
|
12822
13595
|
name: "repo_map",
|
|
12823
13596
|
description: "Token-budgeted map of the repository: the highest-PageRank files with their key exported signatures, deterministically rendered to fit `budgetTokens` (default 1024). The densest single read to understand an unfamiliar codebase.",
|
|
@@ -12990,6 +13763,14 @@ var init_tools = __esm({
|
|
|
12990
13763
|
type: "string",
|
|
12991
13764
|
description: `Structural prior: "graph" multiplies the lexical score by the file's PageRank over the resolved import graph; "lexical" (default) scores on text alone. Unproven on the judged corpus \u2014 see SearchOptions.rank.`
|
|
12992
13765
|
},
|
|
13766
|
+
exact: {
|
|
13767
|
+
type: "boolean",
|
|
13768
|
+
description: "Drop results that carry no verbatim query-term match \u2014 the ones the stem/trigram bridge produced (default false)."
|
|
13769
|
+
},
|
|
13770
|
+
explain: {
|
|
13771
|
+
type: "boolean",
|
|
13772
|
+
description: "Wrap the response as `{ results, explain }` with the query verdict (default false = bare array). See explain_search."
|
|
13773
|
+
},
|
|
12993
13774
|
semantic: {
|
|
12994
13775
|
type: "boolean",
|
|
12995
13776
|
description: 'RRF-fuse an embedding tier with lexical (default false). Precedence: the HTTP endpoint (CODEINDEX_EMBED_ENDPOINT) if set, else a local static model. The response reports the effective tier as a top-level `tier` field ("endpoint"/"static" on success, "lexical" plus `degradedReason` when neither is available/reachable) instead of degrading silently \u2014 see embed_status.'
|
|
@@ -12998,6 +13779,22 @@ var init_tools = __esm({
|
|
|
12998
13779
|
required: ["repo", "query"]
|
|
12999
13780
|
}
|
|
13000
13781
|
},
|
|
13782
|
+
{
|
|
13783
|
+
name: "explain_search",
|
|
13784
|
+
description: 'Search, and say whether the query actually found anything. Returns `{ results, explain }` where explain.verdict is "match" (a verbatim term matched), "weak" (results exist but rest on a near match, or the identifier you asked for has document frequency 0) or "none". Use this instead of `search` whenever an empty-feeling or surprising result matters: a query for an identifier that is NOT in the indexed tree still returns confident-looking rows built from its subtokens \u2014 searching "nullGipStep7" in a repo that only has "nullGipStep2" ranks files matching "null" and "gip" \u2014 and only the verdict distinguishes that from a real hit. Also names the terms dropped as stopwords, the terms that exist nowhere, and what each near match bridged to.',
|
|
13785
|
+
inputSchema: {
|
|
13786
|
+
type: "object",
|
|
13787
|
+
properties: {
|
|
13788
|
+
...repoProp,
|
|
13789
|
+
...scopeProps,
|
|
13790
|
+
query: { type: "string", description: "Natural-language or identifier query" },
|
|
13791
|
+
limit: { type: "number", description: "Max results (default 20)" },
|
|
13792
|
+
fuzzy: { type: "boolean", description: "Stem/trigram fallback for zero-document-frequency terms (default true)" },
|
|
13793
|
+
exact: { type: "boolean", description: "Drop results carrying no verbatim term match (default false)" }
|
|
13794
|
+
},
|
|
13795
|
+
required: ["repo", "query"]
|
|
13796
|
+
}
|
|
13797
|
+
},
|
|
13001
13798
|
{
|
|
13002
13799
|
name: "embed_status",
|
|
13003
13800
|
description: "Report the embedding tier: the effective mode (none/static/endpoint; endpoint > static model), the resolved model (opt-in, never shipped in the package) with its modelId/dim, EMBED_VERSION, and the configured HTTP endpoint with its reachability. Use to check whether `search` with semantic:true will fuse embeddings or degrade to lexical.",
|
|
@@ -13137,6 +13934,45 @@ var init_tools = __esm({
|
|
|
13137
13934
|
},
|
|
13138
13935
|
required: ["defs", "callSites", "referencingFiles"]
|
|
13139
13936
|
},
|
|
13937
|
+
lsp_status: {
|
|
13938
|
+
type: "object",
|
|
13939
|
+
properties: {
|
|
13940
|
+
lspVersion: { type: "number" },
|
|
13941
|
+
mode: { type: "string", enum: ["none", "configured"] },
|
|
13942
|
+
configPath: { type: ["string", "null"] },
|
|
13943
|
+
source: { type: "string", enum: ["env", "repo", "cwd", "none"] },
|
|
13944
|
+
servers: { type: "array", items: anyObj },
|
|
13945
|
+
unmappedLanguages: strArr
|
|
13946
|
+
},
|
|
13947
|
+
required: ["lspVersion", "mode", "source", "servers", "unmappedLanguages"]
|
|
13948
|
+
},
|
|
13949
|
+
onboard: {
|
|
13950
|
+
type: "object",
|
|
13951
|
+
properties: { brief: { type: "string" }, memory: { type: "string" } },
|
|
13952
|
+
required: ["brief"]
|
|
13953
|
+
},
|
|
13954
|
+
explain_search: {
|
|
13955
|
+
type: "object",
|
|
13956
|
+
properties: {
|
|
13957
|
+
results: { type: "array", items: anyObj },
|
|
13958
|
+
explain: {
|
|
13959
|
+
type: "object",
|
|
13960
|
+
properties: {
|
|
13961
|
+
query: { type: "string" },
|
|
13962
|
+
terms: { type: "array", items: anyObj },
|
|
13963
|
+
droppedStopwords: strArr,
|
|
13964
|
+
unresolvedTerms: strArr,
|
|
13965
|
+
wholeIdentifier: anyObj,
|
|
13966
|
+
verdict: { type: "string", enum: ["match", "weak", "none"] },
|
|
13967
|
+
note: { type: "string" },
|
|
13968
|
+
bridgedOnlyResults: { type: "number" },
|
|
13969
|
+
resultCount: { type: "number" }
|
|
13970
|
+
},
|
|
13971
|
+
required: ["query", "terms", "droppedStopwords", "unresolvedTerms", "verdict", "bridgedOnlyResults", "resultCount"]
|
|
13972
|
+
}
|
|
13973
|
+
},
|
|
13974
|
+
required: ["results", "explain"]
|
|
13975
|
+
},
|
|
13140
13976
|
hotspots: {
|
|
13141
13977
|
type: "object",
|
|
13142
13978
|
properties: { churnOk: { type: "boolean" }, hotspots: { type: "array", items: anyObj } },
|
|
@@ -13200,6 +14036,7 @@ var init_tools = __esm({
|
|
|
13200
14036
|
find_symbol: { title: "Find symbol" },
|
|
13201
14037
|
find_references: { title: "Find references" },
|
|
13202
14038
|
repo_map: { title: "Repository map" },
|
|
14039
|
+
onboard: { title: "Project brief", write: true, destructive: false, idempotent: true },
|
|
13203
14040
|
hotspots: { title: "Hotspots" },
|
|
13204
14041
|
coupling: { title: "Change coupling" },
|
|
13205
14042
|
replace_symbol_body: { title: "Replace symbol body", write: true, destructive: true, idempotent: true },
|
|
@@ -13215,18 +14052,35 @@ var init_tools = __esm({
|
|
|
13215
14052
|
mermaid: { title: "Mermaid module diagram" },
|
|
13216
14053
|
grep: { title: "Grep file contents" },
|
|
13217
14054
|
search: { title: "Lexical search", openWorld: true },
|
|
14055
|
+
explain_search: { title: "Search with a verdict", openWorld: true },
|
|
14056
|
+
// openWorld: it spawns a process the user configured, whose answer this
|
|
14057
|
+
// engine does not determine — the same honesty `search` and `embed_status`
|
|
14058
|
+
// already carry for reaching outside their own artifacts.
|
|
14059
|
+
lsp_status: { title: "LSP tier status", openWorld: true },
|
|
13218
14060
|
embed_status: { title: "Embedding tier status", openWorld: true },
|
|
13219
14061
|
type_hierarchy: { title: "Type hierarchy" },
|
|
13220
14062
|
implementations: { title: "Implementations" },
|
|
13221
14063
|
call_graph: { title: "Call graph neighborhood" },
|
|
13222
14064
|
check_rules: { title: "Check architecture rules" }
|
|
13223
14065
|
};
|
|
14066
|
+
TOOL_PROFILES = {
|
|
14067
|
+
// Land in an unfamiliar repository and get your bearings.
|
|
14068
|
+
orient: ["scan_summary", "repo_map", "onboard", "workspaces", "mermaid", "read_memory", "list_memories"],
|
|
14069
|
+
// Locate a thing.
|
|
14070
|
+
find: ["search", "explain_search", "grep", "find_symbol", "symbols", "symbols_overview"],
|
|
14071
|
+
// Decide whether changing it is safe.
|
|
14072
|
+
impact: ["find_references", "callers", "call_graph", "dead_code", "type_hierarchy", "implementations", "lsp_status"],
|
|
14073
|
+
// Change it.
|
|
14074
|
+
edit: ["find_symbol", "symbols_overview", "replace_symbol_body", "insert_after_symbol", "insert_before_symbol"],
|
|
14075
|
+
// Where the work and the risk concentrate.
|
|
14076
|
+
risk: ["hotspots", "churn", "coupling", "complexity", "check_rules", "duplicated_literals", "dead_code"]
|
|
14077
|
+
};
|
|
13224
14078
|
}
|
|
13225
14079
|
});
|
|
13226
14080
|
|
|
13227
14081
|
// src/mcp/session.ts
|
|
13228
14082
|
import { statSync as statSync5 } from "fs";
|
|
13229
|
-
import { join as
|
|
14083
|
+
import { join as join21 } from "path";
|
|
13230
14084
|
function scanFingerprint(scan2) {
|
|
13231
14085
|
return sha1(scan2.files.map((f) => `${f.rel}:${f.hash}`).join("\n"));
|
|
13232
14086
|
}
|
|
@@ -13240,7 +14094,7 @@ async function memoizedEmbeddingIndex(key, build) {
|
|
|
13240
14094
|
function memoizedEmbedModel(modelDir) {
|
|
13241
14095
|
let stat;
|
|
13242
14096
|
try {
|
|
13243
|
-
stat = statSync5(
|
|
14097
|
+
stat = statSync5(join21(modelDir, "model.json"));
|
|
13244
14098
|
} catch {
|
|
13245
14099
|
return void 0;
|
|
13246
14100
|
}
|
|
@@ -13354,6 +14208,7 @@ __export(mcp_exports, {
|
|
|
13354
14208
|
PROTOCOL_VERSIONS: () => PROTOCOL_VERSIONS,
|
|
13355
14209
|
TOOLS: () => TOOLS,
|
|
13356
14210
|
TOOL_META: () => TOOL_META,
|
|
14211
|
+
TOOL_PROFILES: () => TOOL_PROFILES,
|
|
13357
14212
|
annotationsFor: () => annotationsFor,
|
|
13358
14213
|
capResponse: () => capResponse,
|
|
13359
14214
|
getArtifacts: () => getArtifacts,
|
|
@@ -13362,18 +14217,20 @@ __export(mcp_exports, {
|
|
|
13362
14217
|
memoizedEmbedModel: () => memoizedEmbedModel,
|
|
13363
14218
|
memoizedEmbeddingIndex: () => memoizedEmbeddingIndex,
|
|
13364
14219
|
negotiateProtocol: () => negotiateProtocol,
|
|
14220
|
+
profileNames: () => profileNames,
|
|
13365
14221
|
resourceLinkFor: () => resourceLinkFor,
|
|
13366
14222
|
runMcpServer: () => runMcpServer,
|
|
13367
14223
|
scanFingerprint: () => scanFingerprint,
|
|
13368
14224
|
structuredContentFor: () => structuredContentFor,
|
|
13369
14225
|
toCacheMap: () => toCacheMap,
|
|
13370
14226
|
toolsFor: () => toolsFor,
|
|
14227
|
+
toolsInProfiles: () => toolsInProfiles,
|
|
13371
14228
|
validateArgs: () => validateArgs,
|
|
13372
14229
|
warmGrammarsForRepo: () => warmGrammarsForRepo,
|
|
13373
14230
|
warmGrammarsForWalk: () => warmGrammarsForWalk
|
|
13374
14231
|
});
|
|
13375
|
-
import { readFileSync as
|
|
13376
|
-
import { isAbsolute, join as
|
|
14232
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
14233
|
+
import { isAbsolute, join as join22 } from "path";
|
|
13377
14234
|
import { createInterface } from "readline";
|
|
13378
14235
|
function str(v) {
|
|
13379
14236
|
return typeof v === "string" && v ? v : void 0;
|
|
@@ -13451,6 +14308,7 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13451
14308
|
const matches = findSymbol(getScan(repo, scanOpts, walked), namePath, {
|
|
13452
14309
|
substring: args2.substring === true,
|
|
13453
14310
|
includeBody: args2.includeBody === true,
|
|
14311
|
+
concise: args2.concise === true,
|
|
13454
14312
|
maxResults: num(args2.maxResults)
|
|
13455
14313
|
});
|
|
13456
14314
|
return JSON.stringify(matches, null, 2);
|
|
@@ -13458,7 +14316,13 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13458
14316
|
if (name2 === "find_references") {
|
|
13459
14317
|
const symName = str(args2.name);
|
|
13460
14318
|
if (!symName) throw new Error("`name` is required");
|
|
13461
|
-
|
|
14319
|
+
const scan2 = getScan(repo, scanOpts, walked);
|
|
14320
|
+
const statik = findReferences(scan2, symName);
|
|
14321
|
+
if (args2.lsp === true) return JSON.stringify(await referencesWithLsp(scan2, repo, symName, statik), null, 2);
|
|
14322
|
+
return JSON.stringify(statik, null, 2);
|
|
14323
|
+
}
|
|
14324
|
+
if (name2 === "lsp_status") {
|
|
14325
|
+
return JSON.stringify(await lspStatus(getScan(repo, scanOpts, walked), repo, args2.probe === true), null, 2);
|
|
13462
14326
|
}
|
|
13463
14327
|
if (name2 === "replace_symbol_body" || name2 === "insert_after_symbol" || name2 === "insert_before_symbol") {
|
|
13464
14328
|
const namePath = str(args2.namePath);
|
|
@@ -13529,6 +14393,18 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13529
14393
|
const { graph } = getArtifacts(repo, scanOpts, walked);
|
|
13530
14394
|
return renderMermaid(graph, { module: str(args2.module), maxEdges: num(args2.maxEdges) });
|
|
13531
14395
|
}
|
|
14396
|
+
if (name2 === "onboard") {
|
|
14397
|
+
const scan2 = getScan(repo, scanOpts, walked);
|
|
14398
|
+
const { graph } = getArtifacts(repo, scanOpts, walked);
|
|
14399
|
+
return JSON.stringify(
|
|
14400
|
+
onboardBrief(scan2, graph, {
|
|
14401
|
+
...typeof args2.budgetTokens === "number" ? { budgetTokens: args2.budgetTokens } : {},
|
|
14402
|
+
...args2.remember === false ? { remember: false } : {}
|
|
14403
|
+
}),
|
|
14404
|
+
null,
|
|
14405
|
+
2
|
|
14406
|
+
);
|
|
14407
|
+
}
|
|
13532
14408
|
if (name2 === "repo_map") {
|
|
13533
14409
|
const { scan: scan2, graph } = getArtifacts(repo, scanOpts, walked);
|
|
13534
14410
|
return renderRepoMap(scan2, graph, { budgetTokens: typeof args2.budgetTokens === "number" ? args2.budgetTokens : void 0 });
|
|
@@ -13560,18 +14436,19 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13560
14436
|
const scan2 = getScan(repo, scanOpts, walked);
|
|
13561
14437
|
const limit = typeof args2.limit === "number" ? args2.limit : void 0;
|
|
13562
14438
|
const fuzzy = typeof args2.fuzzy === "boolean" ? args2.fuzzy : void 0;
|
|
14439
|
+
const exactOpt = args2.exact === true ? { exact: true } : {};
|
|
13563
14440
|
if (args2.semantic === true) {
|
|
13564
14441
|
const endpoint = resolveEmbedEndpoint();
|
|
13565
14442
|
if (endpoint) {
|
|
13566
14443
|
try {
|
|
13567
14444
|
const index = await memoizedEmbeddingIndex({ mode: "endpoint", identity: endpoint, scan: scan2 }, () => buildEndpointIndex(scan2));
|
|
13568
14445
|
const queryVec = await encodeQueryViaEndpoint(query);
|
|
13569
|
-
const
|
|
13570
|
-
return JSON.stringify({ results:
|
|
14446
|
+
const results3 = searchSemantic(scan2, query, index, { queryVec, limit, fuzzy });
|
|
14447
|
+
return JSON.stringify({ results: results3, tier: "endpoint" }, null, 2);
|
|
13571
14448
|
} catch (e) {
|
|
13572
|
-
const
|
|
14449
|
+
const results3 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
|
|
13573
14450
|
return JSON.stringify(
|
|
13574
|
-
{ results:
|
|
14451
|
+
{ results: results3, tier: "lexical", degradedReason: `embedding endpoint failed: ${errMessage(e)}` },
|
|
13575
14452
|
null,
|
|
13576
14453
|
2
|
|
13577
14454
|
);
|
|
@@ -13584,17 +14461,32 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13584
14461
|
{ mode: "static", identity: `${modelDir}#${model.modelId}`, scan: scan2 },
|
|
13585
14462
|
() => buildEmbeddingIndex(scan2, model)
|
|
13586
14463
|
);
|
|
13587
|
-
const
|
|
13588
|
-
return JSON.stringify({ results:
|
|
14464
|
+
const results3 = searchSemantic(scan2, query, index, { model, limit, fuzzy });
|
|
14465
|
+
return JSON.stringify({ results: results3, tier: "static" }, null, 2);
|
|
13589
14466
|
}
|
|
13590
|
-
const
|
|
14467
|
+
const results2 = searchIndex(scan2, query, { limit, fuzzy, ...rankOpt });
|
|
13591
14468
|
return JSON.stringify(
|
|
13592
|
-
{ results, tier: "lexical", degradedReason: "no embedding endpoint or static model configured \u2014 see embed_status" },
|
|
14469
|
+
{ results: results2, tier: "lexical", degradedReason: "no embedding endpoint or static model configured \u2014 see embed_status" },
|
|
13593
14470
|
null,
|
|
13594
14471
|
2
|
|
13595
14472
|
);
|
|
13596
14473
|
}
|
|
13597
|
-
|
|
14474
|
+
const { results, explain } = explainQuery(scan2, query, { limit, fuzzy, ...exactOpt, ...rankOpt });
|
|
14475
|
+
return JSON.stringify(args2.explain === true ? { results, explain } : results, null, 2);
|
|
14476
|
+
}
|
|
14477
|
+
if (name2 === "explain_search") {
|
|
14478
|
+
const query = str(args2.query);
|
|
14479
|
+
if (!query) throw new Error("`query` is required");
|
|
14480
|
+
const scan2 = getScan(repo, scanOpts, walked);
|
|
14481
|
+
const limit = typeof args2.limit === "number" ? args2.limit : void 0;
|
|
14482
|
+
const fuzzy = typeof args2.fuzzy === "boolean" ? args2.fuzzy : void 0;
|
|
14483
|
+
const { results, explain } = explainQuery(scan2, query, {
|
|
14484
|
+
limit,
|
|
14485
|
+
fuzzy,
|
|
14486
|
+
...args2.exact === true ? { exact: true } : {},
|
|
14487
|
+
...rankOpt
|
|
14488
|
+
});
|
|
14489
|
+
return JSON.stringify({ results, explain }, null, 2);
|
|
13598
14490
|
}
|
|
13599
14491
|
if (name2 === "embed_status") {
|
|
13600
14492
|
const modelDir = resolveEmbedModelDir(repo);
|
|
@@ -13645,9 +14537,9 @@ async function callTool(name2, args2, defaultRepo) {
|
|
|
13645
14537
|
const configPath = str(args2.configPath);
|
|
13646
14538
|
let payload = args2.rules;
|
|
13647
14539
|
if (payload === void 0 && configPath) {
|
|
13648
|
-
const abs = isAbsolute(configPath) ? configPath :
|
|
14540
|
+
const abs = isAbsolute(configPath) ? configPath : join22(repo, configPath);
|
|
13649
14541
|
try {
|
|
13650
|
-
payload = JSON.parse(
|
|
14542
|
+
payload = JSON.parse(readFileSync12(abs, "utf8"));
|
|
13651
14543
|
} catch (e) {
|
|
13652
14544
|
throw new Error(`cannot read rules from ${abs}: ${errMessage(e)}`);
|
|
13653
14545
|
}
|
|
@@ -13665,7 +14557,7 @@ async function runMcpServer(opts = {}) {
|
|
|
13665
14557
|
version: opts.serverInfo?.version ?? ENGINE_VERSION
|
|
13666
14558
|
};
|
|
13667
14559
|
let protocolVersion = PROTOCOL_VERSIONS[0];
|
|
13668
|
-
let tools = toolsFor(opts.defaultRepo, protocolVersion);
|
|
14560
|
+
let tools = toolsFor(opts.defaultRepo, protocolVersion, opts.profile);
|
|
13669
14561
|
const send = (msg) => {
|
|
13670
14562
|
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", ...msg }) + "\n");
|
|
13671
14563
|
};
|
|
@@ -13688,7 +14580,7 @@ async function runMcpServer(opts = {}) {
|
|
|
13688
14580
|
try {
|
|
13689
14581
|
if (req.method === "initialize") {
|
|
13690
14582
|
protocolVersion = negotiateProtocol(req.params?.protocolVersion);
|
|
13691
|
-
tools = toolsFor(opts.defaultRepo, protocolVersion);
|
|
14583
|
+
tools = toolsFor(opts.defaultRepo, protocolVersion, opts.profile);
|
|
13692
14584
|
send({
|
|
13693
14585
|
id: req.id,
|
|
13694
14586
|
result: {
|
|
@@ -13758,6 +14650,8 @@ var init_mcp = __esm({
|
|
|
13758
14650
|
init_complexity();
|
|
13759
14651
|
init_viz();
|
|
13760
14652
|
init_query();
|
|
14653
|
+
init_lsp();
|
|
14654
|
+
init_onboard();
|
|
13761
14655
|
init_edit();
|
|
13762
14656
|
init_memory();
|
|
13763
14657
|
init_bm25();
|
|
@@ -13768,10 +14662,10 @@ var init_mcp = __esm({
|
|
|
13768
14662
|
init_endpoint();
|
|
13769
14663
|
init_walk();
|
|
13770
14664
|
init_tools();
|
|
13771
|
-
|
|
14665
|
+
init_protocol2();
|
|
13772
14666
|
init_session();
|
|
13773
14667
|
init_tools();
|
|
13774
|
-
|
|
14668
|
+
init_protocol2();
|
|
13775
14669
|
init_session();
|
|
13776
14670
|
SCANLESS_TOOLS = /* @__PURE__ */ new Set([
|
|
13777
14671
|
"workspaces",
|
|
@@ -13984,7 +14878,7 @@ runExtractWorker(workerData.input, (o) => parentPort.postMessage(o)).catch((e) =
|
|
|
13984
14878
|
try {
|
|
13985
14879
|
const outputs = await Promise.all(
|
|
13986
14880
|
shards.map(
|
|
13987
|
-
(jobsForShard) => new Promise((
|
|
14881
|
+
(jobsForShard) => new Promise((resolve5, reject) => {
|
|
13988
14882
|
const w = new Worker(bootstrap, {
|
|
13989
14883
|
eval: true,
|
|
13990
14884
|
workerData: { input: { jobs: jobsForShard, grammarKeys: wanted, maxCallsPerFile: opts.maxCallsPerFile } }
|
|
@@ -13998,7 +14892,7 @@ runExtractWorker(workerData.input, (o) => parentPort.postMessage(o)).catch((e) =
|
|
|
13998
14892
|
fn();
|
|
13999
14893
|
};
|
|
14000
14894
|
w.once("message", (m) => {
|
|
14001
|
-
settle(() =>
|
|
14895
|
+
settle(() => resolve5(m));
|
|
14002
14896
|
void w.terminate();
|
|
14003
14897
|
});
|
|
14004
14898
|
w.once("error", (e) => settle(() => reject(e)));
|
|
@@ -14778,6 +15672,13 @@ init_encode();
|
|
|
14778
15672
|
init_embed();
|
|
14779
15673
|
init_search();
|
|
14780
15674
|
init_endpoint();
|
|
15675
|
+
init_onboard();
|
|
15676
|
+
init_lsp();
|
|
15677
|
+
init_config2();
|
|
15678
|
+
init_client();
|
|
15679
|
+
init_spawn();
|
|
15680
|
+
init_protocol();
|
|
15681
|
+
init_refs();
|
|
14781
15682
|
init_rules();
|
|
14782
15683
|
init_coupling();
|
|
14783
15684
|
init_repomap();
|
|
@@ -15177,8 +16078,8 @@ init_util();
|
|
|
15177
16078
|
init_types();
|
|
15178
16079
|
init_types();
|
|
15179
16080
|
init_loader();
|
|
15180
|
-
import { existsSync as
|
|
15181
|
-
import { join as
|
|
16081
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
16082
|
+
import { join as join23, resolve as resolve4 } from "path";
|
|
15182
16083
|
init_pipeline();
|
|
15183
16084
|
init_hash();
|
|
15184
16085
|
init_graph_json();
|
|
@@ -15206,6 +16107,8 @@ init_embed();
|
|
|
15206
16107
|
init_search();
|
|
15207
16108
|
init_endpoint();
|
|
15208
16109
|
init_util();
|
|
16110
|
+
init_lsp();
|
|
16111
|
+
init_tools();
|
|
15209
16112
|
var HELP = `codeindex engine v${ENGINE_VERSION} \u2014 deterministic repo indexing
|
|
15210
16113
|
|
|
15211
16114
|
Usage: engine.mjs <command> [flags]
|
|
@@ -15239,6 +16142,15 @@ Commands:
|
|
|
15239
16142
|
the source with CODEINDEX_EMBED_URL
|
|
15240
16143
|
embed serve Print (or --run) the docker command that starts the
|
|
15241
16144
|
containerized embedding server (rich tier)
|
|
16145
|
+
lsp Optional LSP tier (opt-in by asset \u2014 the tier is active only when
|
|
16146
|
+
<repo>/.codeindex/lsp.json exists, or CODEINDEX_LSP_CONFIG points
|
|
16147
|
+
at one). It annotates QUERY answers only and never touches
|
|
16148
|
+
graph.json/symbols.json:
|
|
16149
|
+
lsp status Config path and source, each server with whether
|
|
16150
|
+
its command is on PATH and how many files it
|
|
16151
|
+
claims, and the languages nothing covers (JSON).
|
|
16152
|
+
--probe also starts each server to read the
|
|
16153
|
+
capabilities it really advertises
|
|
15242
16154
|
grammars Tree-sitter wasm grammars (optional AST tier; regex without them).
|
|
15243
16155
|
Two tiers: CORE ships with the bundle; EXTENDED (kotlin, elixir,
|
|
15244
16156
|
zig, solidity, hcl/terraform) arrives only via \`grammars pull\`.
|
|
@@ -15284,17 +16196,22 @@ Commands:
|
|
|
15284
16196
|
and exits 0, or exits 1 when it has no opinion (run the original).
|
|
15285
16197
|
Deliberately conservative \u2014 any shell metacharacter or unknown
|
|
15286
16198
|
flag refuses the rewrite
|
|
15287
|
-
mcp Run as an MCP server over stdio (
|
|
16199
|
+
mcp Run as an MCP server over stdio (33 tools: scan_summary, graph,
|
|
15288
16200
|
symbols, callers, workspaces, churn, symbols_overview,
|
|
15289
|
-
find_symbol, find_references,
|
|
15290
|
-
dead_code, complexity, mermaid, grep, search,
|
|
15291
|
-
check_rules, the memory quartet and
|
|
15292
|
-
writes). Flags: --repo <dir> pins ONE
|
|
15293
|
-
repo argument becomes optional (an
|
|
15294
|
-
wins); --server-name <name> overrides
|
|
15295
|
-
--max-response-bytes <n> caps a single
|
|
15296
|
-
a response under the cap is
|
|
15297
|
-
|
|
16201
|
+
find_symbol, find_references, lsp_status, onboard, repo_map,
|
|
16202
|
+
hotspots, coupling, dead_code, complexity, mermaid, grep, search,
|
|
16203
|
+
explain_search, embed_status, check_rules, the memory quartet and
|
|
16204
|
+
the three symbolic-edit writes). Flags: --repo <dir> pins ONE
|
|
16205
|
+
repository so the per-tool repo argument becomes optional (an
|
|
16206
|
+
explicit per-call repo still wins); --server-name <name> overrides
|
|
16207
|
+
the announced serverInfo; --max-response-bytes <n> caps a single
|
|
16208
|
+
tool response (default 1e6; a response under the cap is
|
|
16209
|
+
byte-identical, one over it is replaced by an actionable notice
|
|
16210
|
+
instead of an unusable blob); --tools <profile[,profile]>
|
|
16211
|
+
advertises a named subset (all | orient | find | impact | edit |
|
|
16212
|
+
risk, default all) \u2014 every advertised tool's schema costs an agent
|
|
16213
|
+
context on EVERY turn, and a tool left out is still answerable
|
|
16214
|
+
when called by name
|
|
15298
16215
|
version Print the engine version
|
|
15299
16216
|
|
|
15300
16217
|
Flags (accepted before OR after the subcommand: '--repo X scan' and
|
|
@@ -15329,10 +16246,16 @@ Flags (accepted before OR after the subcommand: '--repo X scan' and
|
|
|
15329
16246
|
--limit <n> Max results for \`search\` (default 20)
|
|
15330
16247
|
--no-fuzzy \`search\`: disable trigram fuzzy fallback for query terms
|
|
15331
16248
|
with zero document frequency (default: enabled)
|
|
16249
|
+
--exact \`search\`: drop results that carry no verbatim term match
|
|
16250
|
+
(the ones the stem/trigram bridge produced)
|
|
16251
|
+
--explain \`search\`: emit { results, explain } \u2014 which terms matched,
|
|
16252
|
+
which bridged, and whether the query really found anything
|
|
15332
16253
|
--semantic \`search\`: RRF-fuse an embedding tier with lexical \u2014 the
|
|
15333
16254
|
HTTP endpoint if CODEINDEX_EMBED_ENDPOINT is set, else a
|
|
15334
16255
|
local static model (lexical-only when neither is available)
|
|
15335
16256
|
--run \`embed serve\`: run the docker command instead of printing it
|
|
16257
|
+
--probe \`lsp status\`: start each server and read the capabilities
|
|
16258
|
+
it really advertises (default: no spawn)
|
|
15336
16259
|
--recall \`callers\`: recall-oriented binding (issue #7) \u2014 relaxes
|
|
15337
16260
|
the JS/TS import gate to unique repo-wide names and labels
|
|
15338
16261
|
each site corroborated|unique-name
|
|
@@ -15358,10 +16281,10 @@ function parseFlags(args2) {
|
|
|
15358
16281
|
if (!Number.isFinite(n) || n <= 0) throw new Error(`${a} expects a positive number, got "${raw}"`);
|
|
15359
16282
|
return n;
|
|
15360
16283
|
};
|
|
15361
|
-
if (a === "--repo") flags2.repo =
|
|
16284
|
+
if (a === "--repo") flags2.repo = resolve4(next());
|
|
15362
16285
|
else if (a === "--out") {
|
|
15363
16286
|
const v = next();
|
|
15364
|
-
flags2.out = v === "-" ? "-" :
|
|
16287
|
+
flags2.out = v === "-" ? "-" : resolve4(v);
|
|
15365
16288
|
} else if (a === "--project-root") flags2.projectRoot = next();
|
|
15366
16289
|
else if (a === "--include") flags2.include.push(next());
|
|
15367
16290
|
else if (a === "--exclude") flags2.exclude.push(next());
|
|
@@ -15386,12 +16309,15 @@ function parseFlags(args2) {
|
|
|
15386
16309
|
if (!Number.isInteger(n) || n < 0) throw new Error(`--workers expects a non-negative integer, got "${raw}"`);
|
|
15387
16310
|
flags2.workers = n;
|
|
15388
16311
|
} else if (a === "--since") flags2.since = next();
|
|
15389
|
-
else if (a === "--config") flags2.config =
|
|
16312
|
+
else if (a === "--config") flags2.config = resolve4(next());
|
|
15390
16313
|
else if (a === "--limit") flags2.limit = num2();
|
|
15391
16314
|
else if (a === "--no-fuzzy") flags2.fuzzy = false;
|
|
16315
|
+
else if (a === "--exact") flags2.exact = true;
|
|
16316
|
+
else if (a === "--explain") flags2.explain = true;
|
|
15392
16317
|
else if (a === "--semantic") flags2.semantic = true;
|
|
15393
16318
|
else if (a === "--recall") flags2.recall = true;
|
|
15394
16319
|
else if (a === "--run") flags2.run = true;
|
|
16320
|
+
else if (a === "--probe") flags2.probe = true;
|
|
15395
16321
|
else if (a === "--base") flags2.base = next();
|
|
15396
16322
|
else if (a === "--staged") flags2.staged = true;
|
|
15397
16323
|
else if (a === "--depth") flags2.depth = num2();
|
|
@@ -15435,12 +16361,13 @@ function parseMcpFlags(argv) {
|
|
|
15435
16361
|
let defaultRepo;
|
|
15436
16362
|
let name2;
|
|
15437
16363
|
let maxResponseBytes;
|
|
16364
|
+
let profile;
|
|
15438
16365
|
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
15439
16366
|
const a = argv[i2];
|
|
15440
16367
|
if (a === "--repo") {
|
|
15441
16368
|
const v = argv[++i2];
|
|
15442
16369
|
if (!v) throw new Error("--repo requires a directory");
|
|
15443
|
-
defaultRepo =
|
|
16370
|
+
defaultRepo = resolve4(v);
|
|
15444
16371
|
} else if (a === "--server-name") {
|
|
15445
16372
|
const v = argv[++i2];
|
|
15446
16373
|
if (!v) throw new Error("--server-name requires a value");
|
|
@@ -15450,12 +16377,17 @@ function parseMcpFlags(argv) {
|
|
|
15450
16377
|
const n = Number(v);
|
|
15451
16378
|
if (!v || !Number.isFinite(n) || n <= 0) throw new Error("--max-response-bytes requires a positive number");
|
|
15452
16379
|
maxResponseBytes = n;
|
|
16380
|
+
} else if (a === "--tools") {
|
|
16381
|
+
const v = argv[++i2];
|
|
16382
|
+
if (!v) throw new Error(`--tools requires a profile: ${profileNames().join(", ")}`);
|
|
16383
|
+
toolsInProfiles(v);
|
|
16384
|
+
profile = v === "all" ? void 0 : v;
|
|
15453
16385
|
} else {
|
|
15454
16386
|
throw new Error(`unknown flag for \`mcp\`: ${a}`);
|
|
15455
16387
|
}
|
|
15456
16388
|
}
|
|
15457
|
-
if (defaultRepo && !
|
|
15458
|
-
return { defaultRepo, serverInfo: name2 ? { name: name2 } : void 0, maxResponseBytes };
|
|
16389
|
+
if (defaultRepo && !existsSync10(defaultRepo)) throw new Error(`--repo path does not exist: ${defaultRepo}`);
|
|
16390
|
+
return { defaultRepo, serverInfo: name2 ? { name: name2 } : void 0, maxResponseBytes, profile };
|
|
15459
16391
|
}
|
|
15460
16392
|
var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
15461
16393
|
"--repo",
|
|
@@ -15476,6 +16408,7 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
15476
16408
|
"--config",
|
|
15477
16409
|
"--limit",
|
|
15478
16410
|
"--server-name",
|
|
16411
|
+
"--tools",
|
|
15479
16412
|
"--workers",
|
|
15480
16413
|
"--index",
|
|
15481
16414
|
"--max-response-bytes"
|
|
@@ -15523,7 +16456,7 @@ async function runCli(rawArgv) {
|
|
|
15523
16456
|
return;
|
|
15524
16457
|
}
|
|
15525
16458
|
const flags2 = parseFlags(rest);
|
|
15526
|
-
if (!
|
|
16459
|
+
if (!existsSync10(flags2.repo)) throw new Error(`--repo path does not exist: ${flags2.repo}`);
|
|
15527
16460
|
const scans = !SCANLESS_COMMANDS.has(cmd) && !(cmd === "embed" && flags2.positional !== "build");
|
|
15528
16461
|
let precomputedWalk;
|
|
15529
16462
|
if (scans && !flags2.noAst) {
|
|
@@ -15557,11 +16490,11 @@ async function runCli(rawArgv) {
|
|
|
15557
16490
|
if (!flags2.out) throw new Error("index needs --out <dir>");
|
|
15558
16491
|
const outDir = flags2.out;
|
|
15559
16492
|
mkdirSync3(outDir, { recursive: true });
|
|
15560
|
-
const cachePath =
|
|
16493
|
+
const cachePath = join23(outDir, "cache.json");
|
|
15561
16494
|
let cache;
|
|
15562
16495
|
let meta = {};
|
|
15563
16496
|
try {
|
|
15564
|
-
const parsed = JSON.parse(
|
|
16497
|
+
const parsed = JSON.parse(readFileSync13(cachePath, "utf8"));
|
|
15565
16498
|
if (parsed.schemaVersion === SCHEMA_VERSION && parsed.extractorVersion === EXTRACTOR_VERSION) {
|
|
15566
16499
|
cache = new Map(Object.entries(parsed.files));
|
|
15567
16500
|
meta = {
|
|
@@ -15582,12 +16515,12 @@ async function runCli(rawArgv) {
|
|
|
15582
16515
|
});
|
|
15583
16516
|
const modelDir = resolveEmbedModelDir(flags2.repo);
|
|
15584
16517
|
const model = modelDir ? loadEmbedModel(modelDir) : void 0;
|
|
15585
|
-
const graphPath =
|
|
15586
|
-
const symbolsPath =
|
|
15587
|
-
const embedPath =
|
|
16518
|
+
const graphPath = join23(outDir, "graph.json");
|
|
16519
|
+
const symbolsPath = join23(outDir, "symbols.json");
|
|
16520
|
+
const embedPath = join23(outDir, "embeddings.bin");
|
|
15588
16521
|
const artifactSha = (path) => {
|
|
15589
16522
|
try {
|
|
15590
|
-
return sha1(
|
|
16523
|
+
return sha1(readFileSync13(path));
|
|
15591
16524
|
} catch {
|
|
15592
16525
|
return void 0;
|
|
15593
16526
|
}
|
|
@@ -15660,7 +16593,7 @@ async function runCli(rawArgv) {
|
|
|
15660
16593
|
} else if (cmd === "scip") {
|
|
15661
16594
|
const scan2 = readScan();
|
|
15662
16595
|
const bytes = renderScip(scan2, { projectRoot: flags2.projectRoot });
|
|
15663
|
-
const out2 = flags2.out ??
|
|
16596
|
+
const out2 = flags2.out ?? resolve4("index.scip");
|
|
15664
16597
|
if (out2 === "-") process.stdout.write(Buffer.from(bytes));
|
|
15665
16598
|
else {
|
|
15666
16599
|
writeFileSync4(out2, bytes);
|
|
@@ -15707,10 +16640,21 @@ async function runCli(rawArgv) {
|
|
|
15707
16640
|
} else if (cmd === "search") {
|
|
15708
16641
|
if (!flags2.positional) throw new Error('search needs a query: cli.mjs search "<query>" --repo <dir>');
|
|
15709
16642
|
const scan2 = readScan();
|
|
16643
|
+
const searchOpts = {
|
|
16644
|
+
limit: flags2.limit,
|
|
16645
|
+
fuzzy: flags2.fuzzy,
|
|
16646
|
+
...flags2.exact ? { exact: true } : {},
|
|
16647
|
+
...flags2.rank ? { rank: flags2.rank } : {}
|
|
16648
|
+
};
|
|
16649
|
+
const warnIfWeak = () => {
|
|
16650
|
+
const { explain } = explainQuery(scan2, flags2.positional, searchOpts);
|
|
16651
|
+
if (explain.note) process.stderr.write(`codeindex: ${explain.note}
|
|
16652
|
+
`);
|
|
16653
|
+
};
|
|
15710
16654
|
if (flags2.semantic) {
|
|
15711
16655
|
const endpoint = resolveEmbedEndpoint();
|
|
15712
16656
|
const lexical = () => {
|
|
15713
|
-
const results = searchIndex(scan2, flags2.positional,
|
|
16657
|
+
const results = searchIndex(scan2, flags2.positional, searchOpts);
|
|
15714
16658
|
emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
|
|
15715
16659
|
};
|
|
15716
16660
|
if (endpoint) {
|
|
@@ -15740,9 +16684,12 @@ async function runCli(rawArgv) {
|
|
|
15740
16684
|
emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
|
|
15741
16685
|
}
|
|
15742
16686
|
}
|
|
16687
|
+
warnIfWeak();
|
|
15743
16688
|
} else {
|
|
15744
|
-
const results =
|
|
15745
|
-
emit(JSON.stringify(results, null, 2) + "\n", flags2.out);
|
|
16689
|
+
const { results, explain } = explainQuery(scan2, flags2.positional, searchOpts);
|
|
16690
|
+
emit(JSON.stringify(flags2.explain ? { results, explain } : results, null, 2) + "\n", flags2.out);
|
|
16691
|
+
if (explain.note) process.stderr.write(`codeindex: ${explain.note}
|
|
16692
|
+
`);
|
|
15746
16693
|
}
|
|
15747
16694
|
} else if (cmd === "embed") {
|
|
15748
16695
|
const sub = flags2.positional;
|
|
@@ -15799,14 +16746,14 @@ async function runCli(rawArgv) {
|
|
|
15799
16746
|
mkdirSync3(flags2.out, { recursive: true });
|
|
15800
16747
|
const scan2 = readScan();
|
|
15801
16748
|
const index = buildEmbeddingIndex(scan2, model);
|
|
15802
|
-
writeFileSync4(
|
|
16749
|
+
writeFileSync4(join23(flags2.out, "embeddings.bin"), serializeEmbeddings(index));
|
|
15803
16750
|
process.stderr.write(`codeindex: ${index.records.length} embedding records \u2192 ${flags2.out}/embeddings.bin (model ${model.modelId})
|
|
15804
16751
|
`);
|
|
15805
16752
|
} else if (sub === "pull") {
|
|
15806
16753
|
const { url, sha256 } = resolveEmbedPullUrl();
|
|
15807
|
-
const destDir = process.env.CODEINDEX_EMBED_DIR ??
|
|
16754
|
+
const destDir = process.env.CODEINDEX_EMBED_DIR ?? join23(flags2.repo, ".codeindex", "models");
|
|
15808
16755
|
mkdirSync3(destDir, { recursive: true });
|
|
15809
|
-
process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${
|
|
16756
|
+
process.stderr.write(`codeindex: fetching model from ${url} \u2192 ${join23(destDir, "model.json")}
|
|
15810
16757
|
`);
|
|
15811
16758
|
let body2;
|
|
15812
16759
|
try {
|
|
@@ -15827,18 +16774,22 @@ async function runCli(rawArgv) {
|
|
|
15827
16774
|
process.exitCode = 1;
|
|
15828
16775
|
return;
|
|
15829
16776
|
}
|
|
15830
|
-
writeFileSync4(
|
|
15831
|
-
process.stderr.write(`codeindex: model written to ${
|
|
16777
|
+
writeFileSync4(join23(destDir, "model.json"), body2);
|
|
16778
|
+
process.stderr.write(`codeindex: model written to ${join23(destDir, "model.json")}
|
|
15832
16779
|
`);
|
|
15833
16780
|
} else {
|
|
15834
16781
|
throw new Error("embed needs a subcommand: status | build | pull | serve");
|
|
15835
16782
|
}
|
|
16783
|
+
} else if (cmd === "lsp") {
|
|
16784
|
+
const sub = flags2.positional;
|
|
16785
|
+
if (sub !== "status") throw new Error("lsp needs a subcommand: status");
|
|
16786
|
+
emit(JSON.stringify(await lspStatus(readScan(), flags2.repo, flags2.probe === true), null, 2) + "\n", flags2.out);
|
|
15836
16787
|
} else if (cmd === "grammars") {
|
|
15837
16788
|
const sub = flags2.positional;
|
|
15838
16789
|
const cacheDir = sharedGrammarsCacheDir();
|
|
15839
16790
|
if (sub === "status") {
|
|
15840
16791
|
const info2 = resolveGrammarsTier();
|
|
15841
|
-
const present = (name2) => info2.dirs.some((d) =>
|
|
16792
|
+
const present = (name2) => info2.dirs.some((d) => existsSync10(join23(d, name2)));
|
|
15842
16793
|
const runtimePresent = present("web-tree-sitter.wasm");
|
|
15843
16794
|
const target = resolveGrammarsPullTarget();
|
|
15844
16795
|
const resolvedIn = (keys) => [...keys].filter((k) => present(`${k}.wasm`)).sort();
|
|
@@ -15870,7 +16821,7 @@ async function runCli(rawArgv) {
|
|
|
15870
16821
|
}
|
|
15871
16822
|
} else if (cmd === "rules") {
|
|
15872
16823
|
if (!flags2.config) throw new Error("rules needs --config <codeindex.rules.json>");
|
|
15873
|
-
const rules = parseRules(JSON.parse(
|
|
16824
|
+
const rules = parseRules(JSON.parse(readFileSync13(flags2.config, "utf8")));
|
|
15874
16825
|
const { graph } = readArtifacts();
|
|
15875
16826
|
const violations = checkRules(graph, rules);
|
|
15876
16827
|
const errors = violations.filter((v) => v.severity === "error").length;
|
|
@@ -15970,9 +16921,12 @@ export {
|
|
|
15970
16921
|
EXTRACTOR_VERSION,
|
|
15971
16922
|
EXT_GRAMMAR,
|
|
15972
16923
|
INDEX_DIR,
|
|
16924
|
+
LspTimeout,
|
|
15973
16925
|
MARKDOWN_EXT,
|
|
16926
|
+
MAX_FRAME_BYTES,
|
|
15974
16927
|
RISK_WEIGHTS,
|
|
15975
16928
|
SCHEMA_VERSION,
|
|
16929
|
+
agreementOf,
|
|
15976
16930
|
allGrammarKeys,
|
|
15977
16931
|
applyCentrality,
|
|
15978
16932
|
basicTokenize,
|
|
@@ -15999,6 +16953,7 @@ export {
|
|
|
15999
16953
|
classify,
|
|
16000
16954
|
clip,
|
|
16001
16955
|
clipInline,
|
|
16956
|
+
columnOfSymbol,
|
|
16002
16957
|
communityOf,
|
|
16003
16958
|
compileGlobs,
|
|
16004
16959
|
complexityOfSource,
|
|
@@ -16007,6 +16962,7 @@ export {
|
|
|
16007
16962
|
computeSurprises,
|
|
16008
16963
|
computeSymbolRefs,
|
|
16009
16964
|
computeTestMap,
|
|
16965
|
+
createFramer,
|
|
16010
16966
|
deleteMemory,
|
|
16011
16967
|
deltaFor,
|
|
16012
16968
|
deserializeEmbeddings,
|
|
@@ -16019,9 +16975,11 @@ export {
|
|
|
16019
16975
|
embeddingUnits,
|
|
16020
16976
|
enclosingSymbol,
|
|
16021
16977
|
encode,
|
|
16978
|
+
encodeMessage,
|
|
16022
16979
|
encodeQueryViaEndpoint,
|
|
16023
16980
|
ensureGrammars,
|
|
16024
16981
|
escapeRegExp,
|
|
16982
|
+
explainQuery,
|
|
16025
16983
|
extToLang,
|
|
16026
16984
|
extractAst,
|
|
16027
16985
|
extractCode,
|
|
@@ -16033,6 +16991,7 @@ export {
|
|
|
16033
16991
|
extractTarInto,
|
|
16034
16992
|
fetchExpectedSha256,
|
|
16035
16993
|
fetchGrammarsTarball,
|
|
16994
|
+
fileUri,
|
|
16036
16995
|
findDeadCode,
|
|
16037
16996
|
findLiteralDuplications,
|
|
16038
16997
|
findReferences,
|
|
@@ -16066,10 +17025,17 @@ export {
|
|
|
16066
17025
|
languageOf,
|
|
16067
17026
|
listMemories,
|
|
16068
17027
|
loadEmbedModel,
|
|
17028
|
+
loadLspConfig,
|
|
17029
|
+
locationsToRefs,
|
|
17030
|
+
lspStatus,
|
|
17031
|
+
lspUnavailable,
|
|
16069
17032
|
neighborhood,
|
|
16070
17033
|
neighborsOf,
|
|
17034
|
+
onboardBrief,
|
|
17035
|
+
openLspSession,
|
|
16071
17036
|
pagerankOf,
|
|
16072
17037
|
parseGitignore,
|
|
17038
|
+
parseLspConfig,
|
|
16073
17039
|
parseRules,
|
|
16074
17040
|
preloadArtifacts,
|
|
16075
17041
|
preloadSession,
|
|
@@ -16081,6 +17047,8 @@ export {
|
|
|
16081
17047
|
readMemory,
|
|
16082
17048
|
readPersistedIndex,
|
|
16083
17049
|
readText,
|
|
17050
|
+
referencesWithLsp,
|
|
17051
|
+
relFromUri,
|
|
16084
17052
|
renderGraphJson,
|
|
16085
17053
|
renderMermaid,
|
|
16086
17054
|
renderMermaidClustered,
|
|
@@ -16098,6 +17066,7 @@ export {
|
|
|
16098
17066
|
resolveGrammarsPullTarget,
|
|
16099
17067
|
resolveGrammarsTier,
|
|
16100
17068
|
resolveImport,
|
|
17069
|
+
resolveLspConfigPath,
|
|
16101
17070
|
resolveRelationEdges,
|
|
16102
17071
|
resolveRelations,
|
|
16103
17072
|
resolveUniqueSymbol,
|
|
@@ -16115,11 +17084,13 @@ export {
|
|
|
16115
17084
|
searchIndex,
|
|
16116
17085
|
searchSemantic,
|
|
16117
17086
|
serializeEmbeddings,
|
|
17087
|
+
serverForLang,
|
|
16118
17088
|
sh,
|
|
16119
17089
|
sha1,
|
|
16120
17090
|
sharedGrammarsCacheDir,
|
|
16121
17091
|
shortHash,
|
|
16122
17092
|
slugify,
|
|
17093
|
+
spawnLspTransport,
|
|
16123
17094
|
subtokens,
|
|
16124
17095
|
symbolComplexity,
|
|
16125
17096
|
symbolId,
|