@vohongtho.infotech/code-intel 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli/main.js +883 -324
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +705 -529
- package/dist/index.js.map +1 -1
- package/dist/web/assets/{es-DIfCC5I3.js → es-Bu8iwdFw.js} +1 -1
- package/dist/web/assets/index-C9M6YLlS.css +2 -0
- package/dist/web/assets/index-CKc3HEpe.js +354 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-QSOOiRQm.js +0 -352
- package/dist/web/assets/index-XjZQJMiV.css +0 -2
package/dist/cli/main.js
CHANGED
|
@@ -7,7 +7,7 @@ import { SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, SEMRESATTRS_SERVICE_NAME } from '@o
|
|
|
7
7
|
import { SpanStatusCode, trace, context } from '@opentelemetry/api';
|
|
8
8
|
import winston from 'winston';
|
|
9
9
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
10
|
-
import
|
|
10
|
+
import fs39, { readFileSync, existsSync } from 'fs';
|
|
11
11
|
import path39, { dirname, join } from 'path';
|
|
12
12
|
import os13 from 'os';
|
|
13
13
|
import { Registry, collectDefaultMetrics, Counter, Histogram, Gauge } from 'prom-client';
|
|
@@ -336,8 +336,8 @@ var init_logger = __esm({
|
|
|
336
336
|
transports.push(new winston.transports.Console());
|
|
337
337
|
if (!isProduction) {
|
|
338
338
|
try {
|
|
339
|
-
if (!
|
|
340
|
-
|
|
339
|
+
if (!fs39.existsSync(_Logger.LOG_DIR)) {
|
|
340
|
+
fs39.mkdirSync(_Logger.LOG_DIR, { recursive: true });
|
|
341
341
|
}
|
|
342
342
|
transports.push(
|
|
343
343
|
new DailyRotateFile({
|
|
@@ -394,6 +394,10 @@ var init_logger = __esm({
|
|
|
394
394
|
});
|
|
395
395
|
|
|
396
396
|
// src/graph/knowledge-graph.ts
|
|
397
|
+
var knowledge_graph_exports = {};
|
|
398
|
+
__export(knowledge_graph_exports, {
|
|
399
|
+
createKnowledgeGraph: () => createKnowledgeGraph
|
|
400
|
+
});
|
|
397
401
|
function createKnowledgeGraph() {
|
|
398
402
|
const nodes = /* @__PURE__ */ new Map();
|
|
399
403
|
const edges = /* @__PURE__ */ new Map();
|
|
@@ -889,7 +893,7 @@ var init_id_generator = __esm({
|
|
|
889
893
|
});
|
|
890
894
|
function loadIgnorePatterns(workspaceRoot) {
|
|
891
895
|
try {
|
|
892
|
-
const raw =
|
|
896
|
+
const raw = fs39.readFileSync(path39.join(workspaceRoot, ".codeintelignore"), "utf-8");
|
|
893
897
|
const extras = /* @__PURE__ */ new Set();
|
|
894
898
|
for (const line of raw.split("\n")) {
|
|
895
899
|
const trimmed = line.trim();
|
|
@@ -949,7 +953,7 @@ var init_scan_phase = __esm({
|
|
|
949
953
|
function walk2(dir) {
|
|
950
954
|
let entries;
|
|
951
955
|
try {
|
|
952
|
-
entries =
|
|
956
|
+
entries = fs39.readdirSync(dir, { withFileTypes: true });
|
|
953
957
|
} catch {
|
|
954
958
|
return;
|
|
955
959
|
}
|
|
@@ -966,7 +970,7 @@ var init_scan_phase = __esm({
|
|
|
966
970
|
if (!extensions.has(ext)) continue;
|
|
967
971
|
const fullPath = path39.join(dir, name);
|
|
968
972
|
try {
|
|
969
|
-
const stat =
|
|
973
|
+
const stat = fs39.statSync(fullPath);
|
|
970
974
|
if (stat.size > MAX_FILE_SIZE_BYTES) continue;
|
|
971
975
|
} catch {
|
|
972
976
|
continue;
|
|
@@ -2519,7 +2523,7 @@ var init_parse_phase = __esm({
|
|
|
2519
2523
|
const batch = filePaths.slice(i, i + CONCURRENCY);
|
|
2520
2524
|
await Promise.all(batch.map(async (filePath) => {
|
|
2521
2525
|
try {
|
|
2522
|
-
const source = await
|
|
2526
|
+
const source = await fs39.promises.readFile(filePath, "utf-8");
|
|
2523
2527
|
context2.fileCache.set(filePath, source);
|
|
2524
2528
|
} catch {
|
|
2525
2529
|
}
|
|
@@ -3127,8 +3131,8 @@ var init_llm_governance = __esm({
|
|
|
3127
3131
|
...entry
|
|
3128
3132
|
};
|
|
3129
3133
|
const logPath = this.getLogPath();
|
|
3130
|
-
|
|
3131
|
-
|
|
3134
|
+
fs39.mkdirSync(path39.dirname(logPath), { recursive: true });
|
|
3135
|
+
fs39.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
|
|
3132
3136
|
} catch {
|
|
3133
3137
|
}
|
|
3134
3138
|
}
|
|
@@ -3138,7 +3142,7 @@ var init_llm_governance = __esm({
|
|
|
3138
3142
|
*/
|
|
3139
3143
|
readLog(limit = 100) {
|
|
3140
3144
|
try {
|
|
3141
|
-
const raw =
|
|
3145
|
+
const raw = fs39.readFileSync(this.getLogPath(), "utf-8");
|
|
3142
3146
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
|
|
3143
3147
|
return lines.map((l) => JSON.parse(l));
|
|
3144
3148
|
} catch {
|
|
@@ -3612,14 +3616,14 @@ var init_parse_phase_parallel = __esm({
|
|
|
3612
3616
|
const batch = filePaths.slice(i, i + CONCURRENCY);
|
|
3613
3617
|
await Promise.all(batch.map(async (filePath) => {
|
|
3614
3618
|
try {
|
|
3615
|
-
const source = await
|
|
3619
|
+
const source = await fs39.promises.readFile(filePath, "utf-8");
|
|
3616
3620
|
context2.fileCache.set(filePath, source);
|
|
3617
3621
|
} catch {
|
|
3618
3622
|
}
|
|
3619
3623
|
}));
|
|
3620
3624
|
}
|
|
3621
3625
|
const workerScript = workerScriptPath();
|
|
3622
|
-
const workerScriptExists =
|
|
3626
|
+
const workerScriptExists = fs39.existsSync(workerScript);
|
|
3623
3627
|
if (!workerScriptExists || workerCount === 1) {
|
|
3624
3628
|
logger_default.info(`[parse-parallel] falling back to sequential (workerCount=${workerCount}, scriptExists=${workerScriptExists})`);
|
|
3625
3629
|
const { parsePhase: parsePhase2 } = await Promise.resolve().then(() => (init_parse_phase(), parse_phase_exports));
|
|
@@ -3734,7 +3738,7 @@ var init_resolve_phase_parallel = __esm({
|
|
|
3734
3738
|
}
|
|
3735
3739
|
const snapshot = { symbolIndex, fileSymbolIndex, fileIndex, workspaceRoot };
|
|
3736
3740
|
const workerScript = workerScriptPath2();
|
|
3737
|
-
const workerScriptExists =
|
|
3741
|
+
const workerScriptExists = fs39.existsSync(workerScript);
|
|
3738
3742
|
const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1, os13.cpus().length - 1);
|
|
3739
3743
|
if (!workerScriptExists || workerCount === 1) {
|
|
3740
3744
|
logger_default.info(`[resolve-parallel] falling back to sequential`);
|
|
@@ -4071,6 +4075,41 @@ function nodeToDoc(node) {
|
|
|
4071
4075
|
(node.content ?? "").slice(0, 1e3)
|
|
4072
4076
|
].join(" ");
|
|
4073
4077
|
}
|
|
4078
|
+
function heapTopK(scores, k) {
|
|
4079
|
+
if (k <= 0) return [];
|
|
4080
|
+
const heap = [];
|
|
4081
|
+
function heapifyUp(i) {
|
|
4082
|
+
while (i > 0) {
|
|
4083
|
+
const parent = i - 1 >> 1;
|
|
4084
|
+
if (heap[parent][1] > heap[i][1]) {
|
|
4085
|
+
[heap[parent], heap[i]] = [heap[i], heap[parent]];
|
|
4086
|
+
i = parent;
|
|
4087
|
+
} else break;
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
function heapifyDown(i) {
|
|
4091
|
+
const n = heap.length;
|
|
4092
|
+
while (true) {
|
|
4093
|
+
let smallest = i;
|
|
4094
|
+
const l = 2 * i + 1, r = 2 * i + 2;
|
|
4095
|
+
if (l < n && heap[l][1] < heap[smallest][1]) smallest = l;
|
|
4096
|
+
if (r < n && heap[r][1] < heap[smallest][1]) smallest = r;
|
|
4097
|
+
if (smallest === i) break;
|
|
4098
|
+
[heap[smallest], heap[i]] = [heap[i], heap[smallest]];
|
|
4099
|
+
i = smallest;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
for (const [nodeId, score] of scores) {
|
|
4103
|
+
if (heap.length < k) {
|
|
4104
|
+
heap.push([nodeId, score]);
|
|
4105
|
+
heapifyUp(heap.length - 1);
|
|
4106
|
+
} else if (score > heap[0][1]) {
|
|
4107
|
+
heap[0] = [nodeId, score];
|
|
4108
|
+
heapifyDown(0);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
return heap.sort((a, b) => b[1] - a[1]);
|
|
4112
|
+
}
|
|
4074
4113
|
function getBm25DbPath(workspaceRoot) {
|
|
4075
4114
|
return path39.join(workspaceRoot, ".code-intel", "bm25.db");
|
|
4076
4115
|
}
|
|
@@ -4132,10 +4171,10 @@ var init_bm25_index = __esm({
|
|
|
4132
4171
|
postings.push({ nodeId, tf: count });
|
|
4133
4172
|
}
|
|
4134
4173
|
}
|
|
4135
|
-
|
|
4174
|
+
fs39.mkdirSync(path39.dirname(this.dbPath), { recursive: true });
|
|
4136
4175
|
for (const f of [this.dbPath, `${this.dbPath}-shm`, `${this.dbPath}-wal`]) {
|
|
4137
4176
|
try {
|
|
4138
|
-
if (
|
|
4177
|
+
if (fs39.existsSync(f)) fs39.unlinkSync(f);
|
|
4139
4178
|
} catch {
|
|
4140
4179
|
}
|
|
4141
4180
|
}
|
|
@@ -4173,7 +4212,7 @@ var init_bm25_index = __esm({
|
|
|
4173
4212
|
* Called once on `serve` startup.
|
|
4174
4213
|
*/
|
|
4175
4214
|
load() {
|
|
4176
|
-
if (!
|
|
4215
|
+
if (!fs39.existsSync(this.dbPath)) return;
|
|
4177
4216
|
const db = new Database2(this.dbPath, { readonly: true });
|
|
4178
4217
|
try {
|
|
4179
4218
|
const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
|
|
@@ -4207,8 +4246,13 @@ var init_bm25_index = __esm({
|
|
|
4207
4246
|
}
|
|
4208
4247
|
// ── Search ──────────────────────────────────────────────────────────────────
|
|
4209
4248
|
/**
|
|
4210
|
-
* BM25 search.
|
|
4211
|
-
*
|
|
4249
|
+
* BM25 search.
|
|
4250
|
+
*
|
|
4251
|
+
* Performance strategy:
|
|
4252
|
+
* 1. Skip ultra-high-df terms (df/N > 0.6) — near-zero IDF, dominate posting
|
|
4253
|
+
* lists for common words like "function", "return", "export" in large repos.
|
|
4254
|
+
* 2. Min-heap top-K selection — O(n log k) instead of full O(n log n) sort.
|
|
4255
|
+
* For k=10 and n=30,000 candidates this is ~10× faster than Array.sort.
|
|
4212
4256
|
*/
|
|
4213
4257
|
search(query, limit) {
|
|
4214
4258
|
if (!this._loaded || this.invertedIndex.size === 0) return [];
|
|
@@ -4221,6 +4265,7 @@ var init_bm25_index = __esm({
|
|
|
4221
4265
|
const postings = this.invertedIndex.get(term);
|
|
4222
4266
|
if (!postings) continue;
|
|
4223
4267
|
const df = postings.length;
|
|
4268
|
+
if (N > 100 && df / N > 0.6) continue;
|
|
4224
4269
|
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
4225
4270
|
for (const { nodeId, tf } of postings) {
|
|
4226
4271
|
const dl = this.docLengths.get(nodeId) ?? avgdl;
|
|
@@ -4228,10 +4273,9 @@ var init_bm25_index = __esm({
|
|
|
4228
4273
|
scores.set(nodeId, (scores.get(nodeId) ?? 0) + score);
|
|
4229
4274
|
}
|
|
4230
4275
|
}
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
return topK.map(([nodeId, score]) => {
|
|
4276
|
+
if (scores.size === 0) return [];
|
|
4277
|
+
const topEntries = heapTopK(scores, limit);
|
|
4278
|
+
return topEntries.map(([nodeId, score]) => {
|
|
4235
4279
|
const meta = this.nodeMeta.get(nodeId);
|
|
4236
4280
|
return {
|
|
4237
4281
|
nodeId,
|
|
@@ -4250,7 +4294,7 @@ var init_bm25_index = __esm({
|
|
|
4250
4294
|
* Works even if `load()` was not called (reads affected terms directly from DB).
|
|
4251
4295
|
*/
|
|
4252
4296
|
updateNodes(nodes) {
|
|
4253
|
-
if (!
|
|
4297
|
+
if (!fs39.existsSync(this.dbPath)) return;
|
|
4254
4298
|
if (nodes.length === 0) return;
|
|
4255
4299
|
const changedIds = new Set(nodes.map((n) => n.id));
|
|
4256
4300
|
const newTermFreqs = /* @__PURE__ */ new Map();
|
|
@@ -4325,6 +4369,12 @@ var init_bm25_index = __esm({
|
|
|
4325
4369
|
};
|
|
4326
4370
|
}
|
|
4327
4371
|
});
|
|
4372
|
+
|
|
4373
|
+
// src/storage/db-manager.ts
|
|
4374
|
+
var db_manager_exports = {};
|
|
4375
|
+
__export(db_manager_exports, {
|
|
4376
|
+
DbManager: () => DbManager
|
|
4377
|
+
});
|
|
4328
4378
|
var DbManager;
|
|
4329
4379
|
var init_db_manager = __esm({
|
|
4330
4380
|
"src/storage/db-manager.ts"() {
|
|
@@ -4339,7 +4389,7 @@ var init_db_manager = __esm({
|
|
|
4339
4389
|
}
|
|
4340
4390
|
async init() {
|
|
4341
4391
|
if (!this.readOnly) {
|
|
4342
|
-
|
|
4392
|
+
fs39.mkdirSync(path39.dirname(this.dbPath), { recursive: true });
|
|
4343
4393
|
}
|
|
4344
4394
|
this.db = new Database(this.dbPath, 0, true, this.readOnly);
|
|
4345
4395
|
await this.db.init();
|
|
@@ -4379,7 +4429,7 @@ var init_db_manager = __esm({
|
|
|
4379
4429
|
}
|
|
4380
4430
|
});
|
|
4381
4431
|
function writeNodeCSVs(graph, outputDir) {
|
|
4382
|
-
|
|
4432
|
+
fs39.mkdirSync(outputDir, { recursive: true });
|
|
4383
4433
|
const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
|
|
4384
4434
|
const tableBuffers = /* @__PURE__ */ new Map();
|
|
4385
4435
|
const tableFilePaths = /* @__PURE__ */ new Map();
|
|
@@ -4407,12 +4457,12 @@ function writeNodeCSVs(graph, outputDir) {
|
|
|
4407
4457
|
);
|
|
4408
4458
|
}
|
|
4409
4459
|
for (const [table, lines] of tableBuffers) {
|
|
4410
|
-
|
|
4460
|
+
fs39.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
|
|
4411
4461
|
}
|
|
4412
4462
|
return tableFilePaths;
|
|
4413
4463
|
}
|
|
4414
4464
|
function writeEdgeCSV(graph, outputDir) {
|
|
4415
|
-
|
|
4465
|
+
fs39.mkdirSync(outputDir, { recursive: true });
|
|
4416
4466
|
const header = "from_id,to_id,kind,weight,label\n";
|
|
4417
4467
|
const groups = /* @__PURE__ */ new Map();
|
|
4418
4468
|
for (const edge of graph.allEdges()) {
|
|
@@ -4438,7 +4488,7 @@ function writeEdgeCSV(graph, outputDir) {
|
|
|
4438
4488
|
}
|
|
4439
4489
|
const result = [];
|
|
4440
4490
|
for (const group of groups.values()) {
|
|
4441
|
-
|
|
4491
|
+
fs39.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
|
|
4442
4492
|
result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
|
|
4443
4493
|
}
|
|
4444
4494
|
return result;
|
|
@@ -4481,7 +4531,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
4481
4531
|
} catch {
|
|
4482
4532
|
}
|
|
4483
4533
|
}
|
|
4484
|
-
const tmpDir =
|
|
4534
|
+
const tmpDir = fs39.mkdtempSync(path39.join(os13.tmpdir(), "code-intel-csv-"));
|
|
4485
4535
|
try {
|
|
4486
4536
|
const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
|
|
4487
4537
|
const edgeGroups = writeEdgeCSV(graph, tmpDir);
|
|
@@ -4500,8 +4550,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
4500
4550
|
}
|
|
4501
4551
|
let nodeCount = 0;
|
|
4502
4552
|
for (const [table, csvPath] of nodeTableFiles) {
|
|
4503
|
-
if (!
|
|
4504
|
-
const stat =
|
|
4553
|
+
if (!fs39.existsSync(csvPath)) continue;
|
|
4554
|
+
const stat = fs39.statSync(csvPath);
|
|
4505
4555
|
if (stat.size < 50) continue;
|
|
4506
4556
|
try {
|
|
4507
4557
|
await dbManager.execute(
|
|
@@ -4514,8 +4564,8 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
4514
4564
|
}
|
|
4515
4565
|
let edgeCount = 0;
|
|
4516
4566
|
for (const group of edgeGroups) {
|
|
4517
|
-
if (!
|
|
4518
|
-
const stat =
|
|
4567
|
+
if (!fs39.existsSync(group.filePath)) continue;
|
|
4568
|
+
const stat = fs39.statSync(group.filePath);
|
|
4519
4569
|
if (stat.size < 50) continue;
|
|
4520
4570
|
try {
|
|
4521
4571
|
await dbManager.execute(
|
|
@@ -4529,7 +4579,7 @@ async function loadGraphToDB(graph, dbManager) {
|
|
|
4529
4579
|
return { nodeCount, edgeCount };
|
|
4530
4580
|
} finally {
|
|
4531
4581
|
try {
|
|
4532
|
-
|
|
4582
|
+
fs39.rmSync(tmpDir, { recursive: true, force: true });
|
|
4533
4583
|
} catch {
|
|
4534
4584
|
}
|
|
4535
4585
|
}
|
|
@@ -4640,15 +4690,15 @@ __export(repo_registry_exports, {
|
|
|
4640
4690
|
});
|
|
4641
4691
|
function loadRegistry() {
|
|
4642
4692
|
try {
|
|
4643
|
-
const data =
|
|
4693
|
+
const data = fs39.readFileSync(REPOS_FILE, "utf-8");
|
|
4644
4694
|
return JSON.parse(data);
|
|
4645
4695
|
} catch {
|
|
4646
4696
|
return [];
|
|
4647
4697
|
}
|
|
4648
4698
|
}
|
|
4649
4699
|
function saveRegistry(entries) {
|
|
4650
|
-
|
|
4651
|
-
|
|
4700
|
+
fs39.mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
4701
|
+
fs39.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
|
|
4652
4702
|
}
|
|
4653
4703
|
function upsertRepo(entry) {
|
|
4654
4704
|
const entries = loadRegistry();
|
|
@@ -4682,12 +4732,12 @@ __export(metadata_exports, {
|
|
|
4682
4732
|
});
|
|
4683
4733
|
function saveMetadata(repoDir, metadata) {
|
|
4684
4734
|
const metaDir = path39.join(repoDir, ".code-intel");
|
|
4685
|
-
|
|
4686
|
-
|
|
4735
|
+
fs39.mkdirSync(metaDir, { recursive: true });
|
|
4736
|
+
fs39.writeFileSync(path39.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
|
|
4687
4737
|
}
|
|
4688
4738
|
function loadMetadata(repoDir) {
|
|
4689
4739
|
try {
|
|
4690
|
-
const data =
|
|
4740
|
+
const data = fs39.readFileSync(path39.join(repoDir, ".code-intel", "meta.json"), "utf-8");
|
|
4691
4741
|
return JSON.parse(data);
|
|
4692
4742
|
} catch {
|
|
4693
4743
|
return null;
|
|
@@ -4757,23 +4807,23 @@ function groupFile(name) {
|
|
|
4757
4807
|
}
|
|
4758
4808
|
function loadGroup(name) {
|
|
4759
4809
|
try {
|
|
4760
|
-
return JSON.parse(
|
|
4810
|
+
return JSON.parse(fs39.readFileSync(groupFile(name), "utf-8"));
|
|
4761
4811
|
} catch {
|
|
4762
4812
|
return null;
|
|
4763
4813
|
}
|
|
4764
4814
|
}
|
|
4765
4815
|
function saveGroup(group) {
|
|
4766
|
-
|
|
4767
|
-
|
|
4816
|
+
fs39.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
4817
|
+
fs39.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
|
|
4768
4818
|
}
|
|
4769
4819
|
function listGroups() {
|
|
4770
4820
|
const groups = [];
|
|
4771
4821
|
try {
|
|
4772
|
-
for (const file of
|
|
4822
|
+
for (const file of fs39.readdirSync(GROUPS_DIR)) {
|
|
4773
4823
|
if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
|
|
4774
4824
|
try {
|
|
4775
4825
|
const g = JSON.parse(
|
|
4776
|
-
|
|
4826
|
+
fs39.readFileSync(path39.join(GROUPS_DIR, file), "utf-8")
|
|
4777
4827
|
);
|
|
4778
4828
|
groups.push(g);
|
|
4779
4829
|
} catch {
|
|
@@ -4785,16 +4835,16 @@ function listGroups() {
|
|
|
4785
4835
|
}
|
|
4786
4836
|
function deleteGroup(name) {
|
|
4787
4837
|
try {
|
|
4788
|
-
|
|
4838
|
+
fs39.unlinkSync(groupFile(name));
|
|
4789
4839
|
} catch {
|
|
4790
4840
|
}
|
|
4791
4841
|
try {
|
|
4792
|
-
|
|
4842
|
+
fs39.unlinkSync(path39.join(GROUPS_DIR, `${name}.sync.json`));
|
|
4793
4843
|
} catch {
|
|
4794
4844
|
}
|
|
4795
4845
|
}
|
|
4796
4846
|
function groupExists(name) {
|
|
4797
|
-
return
|
|
4847
|
+
return fs39.existsSync(groupFile(name));
|
|
4798
4848
|
}
|
|
4799
4849
|
function addMember(groupName, member) {
|
|
4800
4850
|
const group = loadGroup(groupName);
|
|
@@ -4820,8 +4870,8 @@ function removeMember(groupName, groupPath) {
|
|
|
4820
4870
|
return group;
|
|
4821
4871
|
}
|
|
4822
4872
|
function saveSyncResult(result) {
|
|
4823
|
-
|
|
4824
|
-
|
|
4873
|
+
fs39.mkdirSync(GROUPS_DIR, { recursive: true });
|
|
4874
|
+
fs39.writeFileSync(
|
|
4825
4875
|
path39.join(GROUPS_DIR, `${result.groupName}.sync.json`),
|
|
4826
4876
|
JSON.stringify(result, null, 2) + "\n"
|
|
4827
4877
|
);
|
|
@@ -4829,7 +4879,7 @@ function saveSyncResult(result) {
|
|
|
4829
4879
|
function loadSyncResult(groupName) {
|
|
4830
4880
|
try {
|
|
4831
4881
|
return JSON.parse(
|
|
4832
|
-
|
|
4882
|
+
fs39.readFileSync(path39.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
|
|
4833
4883
|
);
|
|
4834
4884
|
} catch {
|
|
4835
4885
|
return null;
|
|
@@ -4843,6 +4893,10 @@ var init_group_registry = __esm({
|
|
|
4843
4893
|
});
|
|
4844
4894
|
|
|
4845
4895
|
// src/multi-repo/graph-from-db.ts
|
|
4896
|
+
var graph_from_db_exports = {};
|
|
4897
|
+
__export(graph_from_db_exports, {
|
|
4898
|
+
loadGraphFromDB: () => loadGraphFromDB
|
|
4899
|
+
});
|
|
4846
4900
|
function parseRow(row, kind) {
|
|
4847
4901
|
return {
|
|
4848
4902
|
id: String(row["id"] ?? ""),
|
|
@@ -4923,7 +4977,7 @@ function scanForFiles(root, matcher, maxDepth = 2) {
|
|
|
4923
4977
|
if (depth > maxDepth) return;
|
|
4924
4978
|
let entries;
|
|
4925
4979
|
try {
|
|
4926
|
-
entries =
|
|
4980
|
+
entries = fs39.readdirSync(dir, { withFileTypes: true });
|
|
4927
4981
|
} catch {
|
|
4928
4982
|
return;
|
|
4929
4983
|
}
|
|
@@ -4945,7 +4999,7 @@ var init_file_scanner = __esm({
|
|
|
4945
4999
|
});
|
|
4946
5000
|
function tryParseFile(filePath) {
|
|
4947
5001
|
const ext = path39.extname(filePath).toLowerCase();
|
|
4948
|
-
const content =
|
|
5002
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
4949
5003
|
if (ext === ".json") {
|
|
4950
5004
|
try {
|
|
4951
5005
|
return JSON.parse(content);
|
|
@@ -5014,7 +5068,7 @@ async function parseGraphQLContracts(repoRoot) {
|
|
|
5014
5068
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
|
|
5015
5069
|
const contracts = [];
|
|
5016
5070
|
for (const filePath of files) {
|
|
5017
|
-
const content =
|
|
5071
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
5018
5072
|
const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
|
|
5019
5073
|
let match;
|
|
5020
5074
|
while ((match = typeRegex.exec(content)) !== null) {
|
|
@@ -5050,7 +5104,7 @@ async function parseProtoContracts(repoRoot) {
|
|
|
5050
5104
|
const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
|
|
5051
5105
|
const contracts = [];
|
|
5052
5106
|
for (const filePath of files) {
|
|
5053
|
-
const content =
|
|
5107
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
5054
5108
|
const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
|
|
5055
5109
|
let serviceMatch;
|
|
5056
5110
|
while ((serviceMatch = serviceRegex.exec(content)) !== null) {
|
|
@@ -5269,7 +5323,7 @@ async function syncGroup(group) {
|
|
|
5269
5323
|
continue;
|
|
5270
5324
|
}
|
|
5271
5325
|
const dbPath = path39.join(regEntry.path, ".code-intel", "graph.db");
|
|
5272
|
-
if (!
|
|
5326
|
+
if (!fs39.existsSync(dbPath)) {
|
|
5273
5327
|
logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
|
|
5274
5328
|
continue;
|
|
5275
5329
|
}
|
|
@@ -5450,10 +5504,10 @@ var init_codes = __esm({
|
|
|
5450
5504
|
}
|
|
5451
5505
|
});
|
|
5452
5506
|
function secureMkdir(dir) {
|
|
5453
|
-
|
|
5507
|
+
fs39.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
|
5454
5508
|
if (process.platform !== "win32") {
|
|
5455
5509
|
try {
|
|
5456
|
-
|
|
5510
|
+
fs39.chmodSync(dir, SECURE_DIR_MODE);
|
|
5457
5511
|
} catch {
|
|
5458
5512
|
}
|
|
5459
5513
|
}
|
|
@@ -5461,22 +5515,22 @@ function secureMkdir(dir) {
|
|
|
5461
5515
|
function secureChmodFile(file) {
|
|
5462
5516
|
if (process.platform === "win32") return;
|
|
5463
5517
|
try {
|
|
5464
|
-
|
|
5518
|
+
fs39.chmodSync(file, SECURE_FILE_MODE);
|
|
5465
5519
|
} catch {
|
|
5466
5520
|
}
|
|
5467
5521
|
}
|
|
5468
5522
|
function secureWriteFile(file, data) {
|
|
5469
5523
|
secureMkdir(path39.dirname(file));
|
|
5470
|
-
|
|
5524
|
+
fs39.writeFileSync(file, data, { mode: SECURE_FILE_MODE });
|
|
5471
5525
|
secureChmodFile(file);
|
|
5472
5526
|
}
|
|
5473
5527
|
function tightenDbFiles(dir) {
|
|
5474
5528
|
if (process.platform === "win32") return;
|
|
5475
|
-
if (!
|
|
5476
|
-
for (const name of
|
|
5529
|
+
if (!fs39.existsSync(dir)) return;
|
|
5530
|
+
for (const name of fs39.readdirSync(dir)) {
|
|
5477
5531
|
if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
|
|
5478
5532
|
try {
|
|
5479
|
-
|
|
5533
|
+
fs39.chmodSync(path39.join(dir, name), SECURE_FILE_MODE);
|
|
5480
5534
|
} catch {
|
|
5481
5535
|
}
|
|
5482
5536
|
}
|
|
@@ -5825,8 +5879,8 @@ function decryptSecrets(encrypted) {
|
|
|
5825
5879
|
return JSON.parse(plaintext.toString("utf8"));
|
|
5826
5880
|
}
|
|
5827
5881
|
function loadSecrets(secretsPath = getSecretsPath()) {
|
|
5828
|
-
if (!
|
|
5829
|
-
const blob =
|
|
5882
|
+
if (!fs39.existsSync(secretsPath)) return {};
|
|
5883
|
+
const blob = fs39.readFileSync(secretsPath);
|
|
5830
5884
|
return decryptSecrets(blob);
|
|
5831
5885
|
}
|
|
5832
5886
|
function saveSecrets(blob, secretsPath = getSecretsPath()) {
|
|
@@ -5866,11 +5920,18 @@ function getSessionTtlMs() {
|
|
|
5866
5920
|
const hours = parseInt(process.env["CODE_INTEL_SESSION_TTL_HOURS"] ?? "8", 10);
|
|
5867
5921
|
return (isNaN(hours) ? 8 : hours) * 60 * 60 * 1e3;
|
|
5868
5922
|
}
|
|
5869
|
-
function createSession(user) {
|
|
5923
|
+
function createSession(user, rememberMe = false) {
|
|
5870
5924
|
const sessionId = v4();
|
|
5871
|
-
const
|
|
5872
|
-
|
|
5873
|
-
|
|
5925
|
+
const ttlMs = rememberMe ? REMEMBER_ME_TTL_MS : getSessionTtlMs();
|
|
5926
|
+
const expiresAt = Date.now() + ttlMs;
|
|
5927
|
+
sessionStore.set(sessionId, {
|
|
5928
|
+
userId: user.id,
|
|
5929
|
+
username: user.username,
|
|
5930
|
+
role: user.role,
|
|
5931
|
+
expiresAt,
|
|
5932
|
+
ttlMs
|
|
5933
|
+
});
|
|
5934
|
+
return { sessionId, ttlMs };
|
|
5874
5935
|
}
|
|
5875
5936
|
function getSession(sessionId) {
|
|
5876
5937
|
const entry = sessionStore.get(sessionId);
|
|
@@ -5879,10 +5940,9 @@ function getSession(sessionId) {
|
|
|
5879
5940
|
sessionStore.delete(sessionId);
|
|
5880
5941
|
return null;
|
|
5881
5942
|
}
|
|
5882
|
-
const ttlMs = getSessionTtlMs();
|
|
5883
5943
|
const remaining = entry.expiresAt - Date.now();
|
|
5884
|
-
if (remaining < ttlMs * 0.75) {
|
|
5885
|
-
entry.expiresAt = Date.now() + ttlMs;
|
|
5944
|
+
if (remaining < entry.ttlMs * 0.75) {
|
|
5945
|
+
entry.expiresAt = Date.now() + entry.ttlMs;
|
|
5886
5946
|
}
|
|
5887
5947
|
return entry;
|
|
5888
5948
|
}
|
|
@@ -5902,7 +5962,7 @@ function authMiddleware(req, res, next) {
|
|
|
5902
5962
|
const session = getSession(sessionId);
|
|
5903
5963
|
if (session) {
|
|
5904
5964
|
req.user = { id: session.userId, username: session.username, role: session.role, authMethod: "session" };
|
|
5905
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
5965
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, session.ttlMs));
|
|
5906
5966
|
next();
|
|
5907
5967
|
return;
|
|
5908
5968
|
}
|
|
@@ -6072,9 +6132,9 @@ function parseCookies(cookieHeader) {
|
|
|
6072
6132
|
}
|
|
6073
6133
|
return result;
|
|
6074
6134
|
}
|
|
6075
|
-
function buildSessionCookie(sessionId) {
|
|
6135
|
+
function buildSessionCookie(sessionId, ttlMs) {
|
|
6076
6136
|
const isProduction = process.env["NODE_ENV"] === "production";
|
|
6077
|
-
const maxAge = Math.floor(getSessionTtlMs() / 1e3);
|
|
6137
|
+
const maxAge = Math.floor((ttlMs ?? getSessionTtlMs()) / 1e3);
|
|
6078
6138
|
const parts = [
|
|
6079
6139
|
`${SESSION_COOKIE_NAME}=${encodeURIComponent(sessionId)}`,
|
|
6080
6140
|
`HttpOnly`,
|
|
@@ -6091,7 +6151,7 @@ function clearSessionCookie() {
|
|
|
6091
6151
|
async function verifyPassword(plain, hash) {
|
|
6092
6152
|
return bcrypt.compare(plain, hash);
|
|
6093
6153
|
}
|
|
6094
|
-
var sessionStore, SESSION_COOKIE_NAME, ROLE_RANK;
|
|
6154
|
+
var sessionStore, SESSION_COOKIE_NAME, REMEMBER_ME_TTL_MS, ROLE_RANK;
|
|
6095
6155
|
var init_middleware = __esm({
|
|
6096
6156
|
"src/auth/middleware.ts"() {
|
|
6097
6157
|
init_users_db();
|
|
@@ -6099,6 +6159,7 @@ var init_middleware = __esm({
|
|
|
6099
6159
|
init_secret_store();
|
|
6100
6160
|
sessionStore = /* @__PURE__ */ new Map();
|
|
6101
6161
|
SESSION_COOKIE_NAME = "code_intel_session";
|
|
6162
|
+
REMEMBER_ME_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
6102
6163
|
ROLE_RANK = {
|
|
6103
6164
|
viewer: 1,
|
|
6104
6165
|
"repo-owner": 2,
|
|
@@ -7871,7 +7932,7 @@ var init_secret_scanner = __esm({
|
|
|
7871
7932
|
const ignorePatterns = [...options?.ignorePatterns ?? []];
|
|
7872
7933
|
if (options?.workspaceRoot) {
|
|
7873
7934
|
try {
|
|
7874
|
-
const raw =
|
|
7935
|
+
const raw = fs39.readFileSync(path39.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
|
|
7875
7936
|
for (const line of raw.split("\n")) {
|
|
7876
7937
|
const trimmed = line.trim();
|
|
7877
7938
|
if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
|
|
@@ -8129,22 +8190,22 @@ __export(init_wizard_exports, {
|
|
|
8129
8190
|
wipeConfig: () => wipeConfig
|
|
8130
8191
|
});
|
|
8131
8192
|
function configExists() {
|
|
8132
|
-
return
|
|
8193
|
+
return fs39.existsSync(CONFIG_PATH);
|
|
8133
8194
|
}
|
|
8134
8195
|
function loadConfig() {
|
|
8135
8196
|
if (!configExists()) return null;
|
|
8136
8197
|
try {
|
|
8137
|
-
return JSON.parse(
|
|
8198
|
+
return JSON.parse(fs39.readFileSync(CONFIG_PATH, "utf-8"));
|
|
8138
8199
|
} catch {
|
|
8139
8200
|
return null;
|
|
8140
8201
|
}
|
|
8141
8202
|
}
|
|
8142
8203
|
function saveConfig(cfg) {
|
|
8143
|
-
|
|
8144
|
-
|
|
8204
|
+
fs39.mkdirSync(GLOBAL_DIR2, { recursive: true });
|
|
8205
|
+
fs39.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
|
|
8145
8206
|
}
|
|
8146
8207
|
function wipeConfig() {
|
|
8147
|
-
if (
|
|
8208
|
+
if (fs39.existsSync(CONFIG_PATH)) fs39.unlinkSync(CONFIG_PATH);
|
|
8148
8209
|
}
|
|
8149
8210
|
function commandExists(bin) {
|
|
8150
8211
|
try {
|
|
@@ -8224,8 +8285,8 @@ async function runInitWizard(opts = {}) {
|
|
|
8224
8285
|
const mcpFile = path39.resolve(editor.mcpConfigKey);
|
|
8225
8286
|
try {
|
|
8226
8287
|
let existing = {};
|
|
8227
|
-
if (
|
|
8228
|
-
existing = JSON.parse(
|
|
8288
|
+
if (fs39.existsSync(mcpFile)) {
|
|
8289
|
+
existing = JSON.parse(fs39.readFileSync(mcpFile, "utf-8"));
|
|
8229
8290
|
}
|
|
8230
8291
|
const merged = {
|
|
8231
8292
|
...existing,
|
|
@@ -8234,8 +8295,8 @@ async function runInitWizard(opts = {}) {
|
|
|
8234
8295
|
...mcpConfig.servers
|
|
8235
8296
|
}
|
|
8236
8297
|
};
|
|
8237
|
-
|
|
8238
|
-
|
|
8298
|
+
fs39.mkdirSync(path39.dirname(mcpFile), { recursive: true });
|
|
8299
|
+
fs39.writeFileSync(mcpFile, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
8239
8300
|
console.log(` \u2705 MCP registered for ${name} \u2192 ${editor.mcpConfigKey}`);
|
|
8240
8301
|
} catch {
|
|
8241
8302
|
console.log(` \u26A0 Could not write MCP config for ${name} \u2014 do it manually.`);
|
|
@@ -9005,8 +9066,8 @@ var init_file_watcher = __esm({
|
|
|
9005
9066
|
readCodeIntelIgnore() {
|
|
9006
9067
|
const ignoreFile = path39.join(this.workspaceRoot, ".codeintelignore");
|
|
9007
9068
|
try {
|
|
9008
|
-
if (!
|
|
9009
|
-
return
|
|
9069
|
+
if (!fs39.existsSync(ignoreFile)) return [];
|
|
9070
|
+
return fs39.readFileSync(ignoreFile, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
9010
9071
|
} catch {
|
|
9011
9072
|
return [];
|
|
9012
9073
|
}
|
|
@@ -9067,7 +9128,7 @@ var init_incremental_indexer = __esm({
|
|
|
9067
9128
|
graph.removeNodeCascade(id);
|
|
9068
9129
|
nodesRemoved++;
|
|
9069
9130
|
}
|
|
9070
|
-
if (
|
|
9131
|
+
if (fs39.existsSync(dbPath)) {
|
|
9071
9132
|
try {
|
|
9072
9133
|
const db = new DbManager(dbPath);
|
|
9073
9134
|
await db.init();
|
|
@@ -9082,7 +9143,7 @@ var init_incremental_indexer = __esm({
|
|
|
9082
9143
|
}
|
|
9083
9144
|
const existingFiles = changedFiles.filter((f) => {
|
|
9084
9145
|
try {
|
|
9085
|
-
return
|
|
9146
|
+
return fs39.statSync(f).isFile();
|
|
9086
9147
|
} catch {
|
|
9087
9148
|
return false;
|
|
9088
9149
|
}
|
|
@@ -9104,7 +9165,7 @@ var init_incremental_indexer = __esm({
|
|
|
9104
9165
|
await runPipeline([noopScan, parsePhase, resolvePhase], context2);
|
|
9105
9166
|
}
|
|
9106
9167
|
const nodesAdded = Math.max(0, graph.size.nodes - (nodesBeforeParse - nodesRemoved));
|
|
9107
|
-
if (
|
|
9168
|
+
if (fs39.existsSync(dbPath) && existingFiles.length > 0) {
|
|
9108
9169
|
try {
|
|
9109
9170
|
const db = new DbManager(dbPath);
|
|
9110
9171
|
await db.init();
|
|
@@ -9116,7 +9177,7 @@ var init_incremental_indexer = __esm({
|
|
|
9116
9177
|
db.close();
|
|
9117
9178
|
try {
|
|
9118
9179
|
const bm25DbPath = getBm25DbPath(workspaceRoot);
|
|
9119
|
-
if (
|
|
9180
|
+
if (fs39.existsSync(bm25DbPath)) {
|
|
9120
9181
|
const bm25 = new Bm25Index(bm25DbPath);
|
|
9121
9182
|
bm25.updateNodes(nodesToUpsert);
|
|
9122
9183
|
logger_default.info(`[incremental] BM25 index updated: ${nodesToUpsert.length} nodes`);
|
|
@@ -9155,31 +9216,31 @@ function getQueriesDir(workspaceRoot) {
|
|
|
9155
9216
|
}
|
|
9156
9217
|
function ensureQueriesDir(workspaceRoot) {
|
|
9157
9218
|
const dir = getQueriesDir(workspaceRoot);
|
|
9158
|
-
if (!
|
|
9159
|
-
|
|
9219
|
+
if (!fs39.existsSync(dir)) {
|
|
9220
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
9160
9221
|
}
|
|
9161
9222
|
return dir;
|
|
9162
9223
|
}
|
|
9163
9224
|
function saveQuery(workspaceRoot, name, gql) {
|
|
9164
9225
|
const dir = ensureQueriesDir(workspaceRoot);
|
|
9165
9226
|
const filePath = path39.join(dir, `${name}.gql`);
|
|
9166
|
-
|
|
9227
|
+
fs39.writeFileSync(filePath, gql, "utf-8");
|
|
9167
9228
|
}
|
|
9168
9229
|
function loadQuery(workspaceRoot, name) {
|
|
9169
9230
|
const dir = getQueriesDir(workspaceRoot);
|
|
9170
9231
|
const filePath = path39.join(dir, `${name}.gql`);
|
|
9171
|
-
if (!
|
|
9172
|
-
return
|
|
9232
|
+
if (!fs39.existsSync(filePath)) return null;
|
|
9233
|
+
return fs39.readFileSync(filePath, "utf-8");
|
|
9173
9234
|
}
|
|
9174
9235
|
function listQueries(workspaceRoot) {
|
|
9175
9236
|
const dir = getQueriesDir(workspaceRoot);
|
|
9176
|
-
if (!
|
|
9177
|
-
const files =
|
|
9237
|
+
if (!fs39.existsSync(dir)) return [];
|
|
9238
|
+
const files = fs39.readdirSync(dir).filter((f) => f.endsWith(".gql"));
|
|
9178
9239
|
return files.map((f) => {
|
|
9179
9240
|
const filePath = path39.join(dir, f);
|
|
9180
9241
|
const name = f.replace(/\.gql$/, "");
|
|
9181
|
-
const content =
|
|
9182
|
-
const stat =
|
|
9242
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
9243
|
+
const stat = fs39.statSync(filePath);
|
|
9183
9244
|
return {
|
|
9184
9245
|
name,
|
|
9185
9246
|
content,
|
|
@@ -9191,14 +9252,14 @@ function listQueries(workspaceRoot) {
|
|
|
9191
9252
|
function deleteQuery(workspaceRoot, name) {
|
|
9192
9253
|
const dir = getQueriesDir(workspaceRoot);
|
|
9193
9254
|
const filePath = path39.join(dir, `${name}.gql`);
|
|
9194
|
-
if (!
|
|
9195
|
-
|
|
9255
|
+
if (!fs39.existsSync(filePath)) return false;
|
|
9256
|
+
fs39.unlinkSync(filePath);
|
|
9196
9257
|
return true;
|
|
9197
9258
|
}
|
|
9198
9259
|
function queryExists(workspaceRoot, name) {
|
|
9199
9260
|
const dir = getQueriesDir(workspaceRoot);
|
|
9200
9261
|
const filePath = path39.join(dir, `${name}.gql`);
|
|
9201
|
-
return
|
|
9262
|
+
return fs39.existsSync(filePath);
|
|
9202
9263
|
}
|
|
9203
9264
|
var init_saved_queries = __esm({
|
|
9204
9265
|
"src/query/saved-queries.ts"() {
|
|
@@ -9266,6 +9327,324 @@ var init_sarif_builder = __esm({
|
|
|
9266
9327
|
}
|
|
9267
9328
|
});
|
|
9268
9329
|
|
|
9330
|
+
// src/context/token-counter.ts
|
|
9331
|
+
var token_counter_exports = {};
|
|
9332
|
+
__export(token_counter_exports, {
|
|
9333
|
+
estimateTokens: () => estimateTokens,
|
|
9334
|
+
measureBlocks: () => measureBlocks
|
|
9335
|
+
});
|
|
9336
|
+
function estimateTokens(text) {
|
|
9337
|
+
if (!text) return 0;
|
|
9338
|
+
const words = text.split(/\s+/).filter(Boolean).length;
|
|
9339
|
+
const chars = text.length;
|
|
9340
|
+
return Math.ceil((words * 1.3 + chars * 0.25) / 2);
|
|
9341
|
+
}
|
|
9342
|
+
function measureBlocks(doc) {
|
|
9343
|
+
const summary = estimateTokens(doc.summary);
|
|
9344
|
+
const logic = estimateTokens(doc.logic);
|
|
9345
|
+
const relation = estimateTokens(doc.relation);
|
|
9346
|
+
const focusCode = estimateTokens(doc.focusCode);
|
|
9347
|
+
return { summary, logic, relation, focusCode, total: summary + logic + relation + focusCode };
|
|
9348
|
+
}
|
|
9349
|
+
var init_token_counter = __esm({
|
|
9350
|
+
"src/context/token-counter.ts"() {
|
|
9351
|
+
}
|
|
9352
|
+
});
|
|
9353
|
+
|
|
9354
|
+
// src/context/builder.ts
|
|
9355
|
+
var builder_exports = {};
|
|
9356
|
+
__export(builder_exports, {
|
|
9357
|
+
build: () => build,
|
|
9358
|
+
detectQueryIntent: () => detectQueryIntent
|
|
9359
|
+
});
|
|
9360
|
+
function detectQueryIntent(question) {
|
|
9361
|
+
const q = question.toLowerCase();
|
|
9362
|
+
if (/\b(show|code|implement|source|how is written|function body|method body)\b/.test(q)) return "code";
|
|
9363
|
+
if (/\b(who calls|callers?|depends on|blast radius|impact|upstream)\b/.test(q)) return "callers";
|
|
9364
|
+
if (/\b(architecture|overview|structure|design|how is built|system)\b/.test(q)) return "architecture";
|
|
9365
|
+
return "auto";
|
|
9366
|
+
}
|
|
9367
|
+
function last2Segments(filePath) {
|
|
9368
|
+
const parts = filePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
9369
|
+
return parts.slice(-2).join("/");
|
|
9370
|
+
}
|
|
9371
|
+
function firstSentence(text) {
|
|
9372
|
+
if (!text) return "";
|
|
9373
|
+
const sentence = text.split(/[.!?]/)[0]?.trim() ?? "";
|
|
9374
|
+
const words = sentence.split(/\s+/);
|
|
9375
|
+
return words.slice(0, 15).join(" ");
|
|
9376
|
+
}
|
|
9377
|
+
function getCluster(graph, nodeId) {
|
|
9378
|
+
for (const edge of graph.findEdgesFrom(nodeId)) {
|
|
9379
|
+
if (edge.kind === "belongs_to") return graph.getNode(edge.target)?.name;
|
|
9380
|
+
}
|
|
9381
|
+
return void 0;
|
|
9382
|
+
}
|
|
9383
|
+
function dirOf(filePath) {
|
|
9384
|
+
const parts = filePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
9385
|
+
return parts.slice(0, -1).join("/") || ".";
|
|
9386
|
+
}
|
|
9387
|
+
function meaningfulLines(content) {
|
|
9388
|
+
return content.split("\n").filter((l) => {
|
|
9389
|
+
const t = l.trim();
|
|
9390
|
+
return t.length > 0 && !t.startsWith("//") && !t.startsWith("*") && !t.startsWith("#");
|
|
9391
|
+
});
|
|
9392
|
+
}
|
|
9393
|
+
function adaptiveSnippet(content) {
|
|
9394
|
+
if (!content) return { lines: "", truncated: false };
|
|
9395
|
+
const stripped = content.replace(/^\n+|\n+$/g, "");
|
|
9396
|
+
const rawLines = stripped.split("\n");
|
|
9397
|
+
const ml = meaningfulLines(stripped).length;
|
|
9398
|
+
if (ml <= 10) return { lines: stripped, truncated: false };
|
|
9399
|
+
if (ml <= 25) {
|
|
9400
|
+
const out2 = rawLines.slice(0, 25);
|
|
9401
|
+
const truncated = rawLines.length > 25;
|
|
9402
|
+
return { lines: out2.join("\n") + (truncated ? "\n// ..." : ""), truncated };
|
|
9403
|
+
}
|
|
9404
|
+
const out = rawLines.slice(0, 40);
|
|
9405
|
+
const remaining = rawLines.length - 40;
|
|
9406
|
+
return {
|
|
9407
|
+
lines: out.join("\n") + (remaining > 0 ? `
|
|
9408
|
+
// ... (${remaining} more lines)` : ""),
|
|
9409
|
+
truncated: remaining > 0
|
|
9410
|
+
};
|
|
9411
|
+
}
|
|
9412
|
+
function buildSummaryBlock(nodes, graph, dedup) {
|
|
9413
|
+
if (nodes.length === 0) return "";
|
|
9414
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
9415
|
+
for (const node of nodes) {
|
|
9416
|
+
const dir = dirOf(node.filePath);
|
|
9417
|
+
if (!byDir.has(dir)) byDir.set(dir, []);
|
|
9418
|
+
byDir.get(dir).push(node);
|
|
9419
|
+
}
|
|
9420
|
+
const lines = ["[SUMMARY]"];
|
|
9421
|
+
for (const [dir, group] of byDir) {
|
|
9422
|
+
const useHeader = group.length >= 3;
|
|
9423
|
+
if (useHeader) lines.push(`${dir}/:`);
|
|
9424
|
+
for (const node of group) {
|
|
9425
|
+
const summary = firstSentence(node.metadata?.["summary"]);
|
|
9426
|
+
const callerCount = [...graph.findEdgesTo(node.id)].filter((e) => e.kind === "calls").length;
|
|
9427
|
+
getCluster(graph, node.id);
|
|
9428
|
+
const badges = [];
|
|
9429
|
+
if (callerCount >= 10) badges.push("\u26A0");
|
|
9430
|
+
if (callerCount === 0) badges.push("\u{1F47B}");
|
|
9431
|
+
const badgeStr = badges.join("");
|
|
9432
|
+
const path210 = last2Segments(node.filePath);
|
|
9433
|
+
const line = node.startLine ? `:${node.startLine}` : "";
|
|
9434
|
+
const fullFmt = `${node.name} [${node.kind}] ${path210}${line}${badgeStr ? " " + badgeStr : ""}${summary ? " \u2014 " + summary : ""}`;
|
|
9435
|
+
const formatted = dedup.formatSymbol(node.name, node.filePath, fullFmt);
|
|
9436
|
+
lines.push(useHeader ? ` ${formatted}` : formatted);
|
|
9437
|
+
}
|
|
9438
|
+
}
|
|
9439
|
+
return lines.join("\n");
|
|
9440
|
+
}
|
|
9441
|
+
function buildLogicBlock(nodes, graph, dedup) {
|
|
9442
|
+
if (nodes.length === 0) return "";
|
|
9443
|
+
const lines = ["[LOGIC]"];
|
|
9444
|
+
const nodeCallees = /* @__PURE__ */ new Map();
|
|
9445
|
+
const calleeUsage = /* @__PURE__ */ new Map();
|
|
9446
|
+
for (const node of nodes) {
|
|
9447
|
+
const callees = [];
|
|
9448
|
+
for (const edge of graph.findEdgesFrom(node.id)) {
|
|
9449
|
+
if (edge.kind === "calls") {
|
|
9450
|
+
const callee = graph.getNode(edge.target);
|
|
9451
|
+
if (callee && callee.name !== node.name) {
|
|
9452
|
+
callees.push(callee.name);
|
|
9453
|
+
calleeUsage.set(callee.name, (calleeUsage.get(callee.name) ?? 0) + 1);
|
|
9454
|
+
}
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
nodeCallees.set(node.id, [...new Set(callees)]);
|
|
9458
|
+
}
|
|
9459
|
+
const sharedCallees = new Set(
|
|
9460
|
+
[...calleeUsage.entries()].filter(([, cnt]) => cnt >= 3).map(([name]) => name)
|
|
9461
|
+
);
|
|
9462
|
+
if (sharedCallees.size > 0) {
|
|
9463
|
+
lines.push(`(all above \u2192 ${[...sharedCallees].join(", ")})`);
|
|
9464
|
+
}
|
|
9465
|
+
for (const node of nodes) {
|
|
9466
|
+
const callees = (nodeCallees.get(node.id) ?? []).filter((c) => !sharedCallees.has(c));
|
|
9467
|
+
for (const callee of callees) {
|
|
9468
|
+
dedup.markCallPair(node.name, callee);
|
|
9469
|
+
}
|
|
9470
|
+
if (callees.length === 0) continue;
|
|
9471
|
+
if (callees.length <= 5) {
|
|
9472
|
+
for (const callee of callees) dedup.markInLogic(callee);
|
|
9473
|
+
lines.push(`${node.name} \u2192 ${callees.join(", ")}`);
|
|
9474
|
+
} else {
|
|
9475
|
+
lines.push(`${node.name} \u2192`);
|
|
9476
|
+
for (const callee of callees) {
|
|
9477
|
+
dedup.markInLogic(callee);
|
|
9478
|
+
if (dedup.hasSymbol(callee)) {
|
|
9479
|
+
lines.push(` ${callee}`);
|
|
9480
|
+
} else {
|
|
9481
|
+
const calleeNode = [...graph.allNodes()].find((n) => n.name === callee);
|
|
9482
|
+
const path40 = calleeNode ? ` (${last2Segments(calleeNode.filePath)})` : "";
|
|
9483
|
+
lines.push(` ${callee}${path40}`);
|
|
9484
|
+
}
|
|
9485
|
+
}
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9488
|
+
return lines.length > 1 ? lines.join("\n") : "";
|
|
9489
|
+
}
|
|
9490
|
+
function buildRelationBlock(nodes, graph, dedup) {
|
|
9491
|
+
if (nodes.length === 0) return "";
|
|
9492
|
+
const lines = ["[RELATION]"];
|
|
9493
|
+
for (const node of nodes) {
|
|
9494
|
+
const callers = [...graph.findEdgesTo(node.id)].filter((e) => e.kind === "calls").map((e) => graph.getNode(e.source)?.name).filter((n) => Boolean(n));
|
|
9495
|
+
const extendsNodes = [...graph.findEdgesFrom(node.id)].filter((e) => e.kind === "extends").map((e) => graph.getNode(e.target)?.name).filter((n) => Boolean(n));
|
|
9496
|
+
const implementsNodes = [...graph.findEdgesFrom(node.id)].filter((e) => e.kind === "implements").map((e) => graph.getNode(e.target)?.name).filter((n) => Boolean(n));
|
|
9497
|
+
const highBlast = callers.length >= 5;
|
|
9498
|
+
const prefix = highBlast ? "\u26A1 " : "";
|
|
9499
|
+
if (callers.length > 0) {
|
|
9500
|
+
const nonDupCallers = callers.filter(
|
|
9501
|
+
(c) => highBlast || !dedup.hasCallPair(c, node.name)
|
|
9502
|
+
);
|
|
9503
|
+
if (nonDupCallers.length > 0) {
|
|
9504
|
+
const top3 = nonDupCallers.slice(0, 3);
|
|
9505
|
+
const rest = nonDupCallers.length - 3;
|
|
9506
|
+
const callerStr = top3.join(", ") + (rest > 0 ? ` (+${rest} more \u2014 use blast_radius for full list)` : "");
|
|
9507
|
+
lines.push(`${prefix}${node.name} \u2190 ${callerStr}`);
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
const heritage = [];
|
|
9511
|
+
if (extendsNodes.length > 0) heritage.push(`extends ${extendsNodes.join(", ")}`);
|
|
9512
|
+
if (implementsNodes.length > 0) heritage.push(`implements ${implementsNodes.join(" \xB7 ")}`);
|
|
9513
|
+
if (heritage.length > 0) lines.push(`${node.name}: ${heritage.join(" \xB7 ")}`);
|
|
9514
|
+
}
|
|
9515
|
+
return lines.length > 1 ? lines.join("\n") : "";
|
|
9516
|
+
}
|
|
9517
|
+
function buildFocusCodeBlock(seeds, nodes, dedup, signatureOnlyThreshold, tokenBudget) {
|
|
9518
|
+
if (nodes.length === 0) return { text: "", truncated: false };
|
|
9519
|
+
const lines = ["[FOCUS CODE]"];
|
|
9520
|
+
let usedTokens = estimateTokens("[FOCUS CODE]");
|
|
9521
|
+
let truncated = false;
|
|
9522
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
9523
|
+
const node = nodes[i];
|
|
9524
|
+
const seed = seeds.find((s) => s.nodeId === node.id);
|
|
9525
|
+
const score = seed?.refinedScore ?? 1;
|
|
9526
|
+
const content = node.content;
|
|
9527
|
+
const ml = content ? meaningfulLines(content).length : 0;
|
|
9528
|
+
if (ml <= 5 && dedup.isInLogic(node.name)) continue;
|
|
9529
|
+
const header = `// ${node.name} \u2014 ${last2Segments(node.filePath)}${node.startLine ? ":" + node.startLine : ""}`;
|
|
9530
|
+
if (score < signatureOnlyThreshold) {
|
|
9531
|
+
const sig = content?.split("\n").find((l) => l.trim().length > 0) ?? "";
|
|
9532
|
+
const sigLine = sig ? sig.trimEnd() + (sig.includes("{") ? " ... }" : "") : "";
|
|
9533
|
+
const entry2 = `${header}
|
|
9534
|
+
// (low relevance)
|
|
9535
|
+
${sigLine}`;
|
|
9536
|
+
const toks2 = estimateTokens(entry2);
|
|
9537
|
+
if (usedTokens + toks2 > tokenBudget) {
|
|
9538
|
+
truncated = true;
|
|
9539
|
+
break;
|
|
9540
|
+
}
|
|
9541
|
+
lines.push(entry2);
|
|
9542
|
+
usedTokens += toks2;
|
|
9543
|
+
continue;
|
|
9544
|
+
}
|
|
9545
|
+
const { lines: snippet, truncated: snipTruncated } = adaptiveSnippet(content);
|
|
9546
|
+
const entry = `${header}
|
|
9547
|
+
\`\`\`
|
|
9548
|
+
${snippet}
|
|
9549
|
+
\`\`\``;
|
|
9550
|
+
const toks = estimateTokens(entry);
|
|
9551
|
+
if (usedTokens + toks > tokenBudget) {
|
|
9552
|
+
truncated = true;
|
|
9553
|
+
break;
|
|
9554
|
+
}
|
|
9555
|
+
lines.push(entry);
|
|
9556
|
+
usedTokens += toks;
|
|
9557
|
+
if (snipTruncated) truncated = true;
|
|
9558
|
+
}
|
|
9559
|
+
return { text: lines.length > 1 ? lines.join("\n\n") : "", truncated };
|
|
9560
|
+
}
|
|
9561
|
+
function build(seeds, graph, options = {}) {
|
|
9562
|
+
const maxTokens = options.maxTokens ?? 6e3;
|
|
9563
|
+
const signatureOnlyThreshold = options.signatureOnlyThreshold ?? 0.3;
|
|
9564
|
+
const intent = options.queryIntent ?? "auto";
|
|
9565
|
+
const budgets = BUDGET_PRESETS[intent];
|
|
9566
|
+
const nodes = seeds.map((s) => graph.getNode(s.nodeId)).filter((n) => n !== void 0);
|
|
9567
|
+
const dedup = new DedupeRegistry();
|
|
9568
|
+
let available = maxTokens;
|
|
9569
|
+
const summaryText = buildSummaryBlock(nodes, graph, dedup);
|
|
9570
|
+
const summaryToks = estimateTokens(summaryText);
|
|
9571
|
+
const summaryUsed = Math.min(summaryToks, Math.min(budgets.summary, available));
|
|
9572
|
+
available -= summaryUsed;
|
|
9573
|
+
const logicText = buildLogicBlock(nodes, graph, dedup);
|
|
9574
|
+
const logicToks = estimateTokens(logicText);
|
|
9575
|
+
const logicBudget = Math.min(budgets.logic, Math.floor(available * 0.35));
|
|
9576
|
+
const logicUsed = Math.min(logicToks, logicBudget);
|
|
9577
|
+
available -= logicUsed;
|
|
9578
|
+
const relationText = buildRelationBlock(nodes, graph, dedup);
|
|
9579
|
+
const relationToks = estimateTokens(relationText);
|
|
9580
|
+
const relationBudget = Math.min(budgets.relation, Math.floor(available * 0.35));
|
|
9581
|
+
const relationUsed = Math.min(relationToks, relationBudget);
|
|
9582
|
+
available -= relationUsed;
|
|
9583
|
+
const focusBudget = available;
|
|
9584
|
+
const { text: focusText, truncated } = buildFocusCodeBlock(
|
|
9585
|
+
seeds,
|
|
9586
|
+
nodes,
|
|
9587
|
+
dedup,
|
|
9588
|
+
signatureOnlyThreshold,
|
|
9589
|
+
focusBudget
|
|
9590
|
+
);
|
|
9591
|
+
return {
|
|
9592
|
+
summary: summaryText,
|
|
9593
|
+
logic: logicText,
|
|
9594
|
+
relation: relationText,
|
|
9595
|
+
focusCode: focusText,
|
|
9596
|
+
truncated,
|
|
9597
|
+
intent
|
|
9598
|
+
};
|
|
9599
|
+
}
|
|
9600
|
+
var BUDGET_PRESETS, DedupeRegistry;
|
|
9601
|
+
var init_builder = __esm({
|
|
9602
|
+
"src/context/builder.ts"() {
|
|
9603
|
+
init_token_counter();
|
|
9604
|
+
BUDGET_PRESETS = {
|
|
9605
|
+
code: { summary: 300, logic: 400, relation: 300, focusCode: 5e3 },
|
|
9606
|
+
callers: { summary: 500, logic: 300, relation: 2500, focusCode: 700 },
|
|
9607
|
+
architecture: { summary: 1200, logic: 800, relation: 800, focusCode: 1200 },
|
|
9608
|
+
auto: { summary: 800, logic: 600, relation: 500, focusCode: 1500 }
|
|
9609
|
+
};
|
|
9610
|
+
DedupeRegistry = class {
|
|
9611
|
+
seenSymbols = /* @__PURE__ */ new Set();
|
|
9612
|
+
seenFilePaths = /* @__PURE__ */ new Set();
|
|
9613
|
+
seenCallPairs = /* @__PURE__ */ new Set();
|
|
9614
|
+
logicSymbols = /* @__PURE__ */ new Set();
|
|
9615
|
+
// B.4.2: symbols referenced in LOGIC
|
|
9616
|
+
/** Returns full format on first mention, name-only on repeats. */
|
|
9617
|
+
formatSymbol(name, filePath, extra) {
|
|
9618
|
+
const key = name;
|
|
9619
|
+
if (this.seenSymbols.has(key)) return name;
|
|
9620
|
+
this.seenSymbols.add(key);
|
|
9621
|
+
this.seenFilePaths.add(filePath);
|
|
9622
|
+
return extra;
|
|
9623
|
+
}
|
|
9624
|
+
hasSymbol(name) {
|
|
9625
|
+
return this.seenSymbols.has(name);
|
|
9626
|
+
}
|
|
9627
|
+
markCallPair(caller, callee) {
|
|
9628
|
+
this.seenCallPairs.add(`${caller}\u2192${callee}`);
|
|
9629
|
+
}
|
|
9630
|
+
hasCallPair(caller, callee) {
|
|
9631
|
+
return this.seenCallPairs.has(`${caller}\u2192${callee}`);
|
|
9632
|
+
}
|
|
9633
|
+
hasFilePath(fp) {
|
|
9634
|
+
return this.seenFilePaths.has(fp);
|
|
9635
|
+
}
|
|
9636
|
+
/** Mark a symbol as referenced in the LOGIC block (B.4.2). */
|
|
9637
|
+
markInLogic(name) {
|
|
9638
|
+
this.logicSymbols.add(name);
|
|
9639
|
+
}
|
|
9640
|
+
/** Returns true only if symbol was referenced in LOGIC (B.4.2). */
|
|
9641
|
+
isInLogic(name) {
|
|
9642
|
+
return this.logicSymbols.has(name);
|
|
9643
|
+
}
|
|
9644
|
+
};
|
|
9645
|
+
}
|
|
9646
|
+
});
|
|
9647
|
+
|
|
9269
9648
|
// src/cli/main.ts
|
|
9270
9649
|
init_logger();
|
|
9271
9650
|
init_knowledge_graph();
|
|
@@ -9763,7 +10142,7 @@ init_embedder();
|
|
|
9763
10142
|
async function hybridSearch(graph, query, limit, options = {}) {
|
|
9764
10143
|
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
|
|
9765
10144
|
const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
9766
|
-
const hasVectorDb = Boolean(vectorDbPath &&
|
|
10145
|
+
const hasVectorDb = Boolean(vectorDbPath && fs39.existsSync(vectorDbPath));
|
|
9767
10146
|
if (!hasVectorDb) {
|
|
9768
10147
|
const bm25Results2 = await bm25Promise;
|
|
9769
10148
|
return {
|
|
@@ -9831,7 +10210,7 @@ async function queryGroup(group, query, limit = 20) {
|
|
|
9831
10210
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
9832
10211
|
if (!regEntry) continue;
|
|
9833
10212
|
const dbPath = path39.join(regEntry.path, ".code-intel", "graph.db");
|
|
9834
|
-
if (!
|
|
10213
|
+
if (!fs39.existsSync(dbPath)) continue;
|
|
9835
10214
|
const graph = createKnowledgeGraph();
|
|
9836
10215
|
const db = new DbManager(dbPath, true);
|
|
9837
10216
|
try {
|
|
@@ -9873,7 +10252,7 @@ var STUCK_THRESHOLD_MINUTES = 30;
|
|
|
9873
10252
|
var JobsDB = class {
|
|
9874
10253
|
db;
|
|
9875
10254
|
constructor(dbPath) {
|
|
9876
|
-
|
|
10255
|
+
fs39.mkdirSync(path39.dirname(dbPath), { recursive: true });
|
|
9877
10256
|
this.db = new Database2(dbPath);
|
|
9878
10257
|
this.db.pragma("journal_mode = WAL");
|
|
9879
10258
|
this.db.pragma("foreign_keys = ON");
|
|
@@ -10141,7 +10520,7 @@ var BackupService = class {
|
|
|
10141
10520
|
constructor(backupDir) {
|
|
10142
10521
|
this.backupDir = backupDir ?? getBackupDir();
|
|
10143
10522
|
this.key = getBackupKey();
|
|
10144
|
-
|
|
10523
|
+
fs39.mkdirSync(this.backupDir, { recursive: true });
|
|
10145
10524
|
}
|
|
10146
10525
|
/**
|
|
10147
10526
|
* Create a backup for a repository.
|
|
@@ -10155,16 +10534,16 @@ var BackupService = class {
|
|
|
10155
10534
|
const candidates = ["graph.db", "vector.db", "meta.json"];
|
|
10156
10535
|
for (const f of candidates) {
|
|
10157
10536
|
const fp = path39.join(codeIntelDir, f);
|
|
10158
|
-
if (
|
|
10537
|
+
if (fs39.existsSync(fp)) {
|
|
10159
10538
|
filesToBackup.push({ name: f, localPath: fp });
|
|
10160
10539
|
}
|
|
10161
10540
|
}
|
|
10162
10541
|
const registryPath = path39.join(os13.homedir(), ".code-intel", "registry.json");
|
|
10163
|
-
if (
|
|
10542
|
+
if (fs39.existsSync(registryPath)) {
|
|
10164
10543
|
filesToBackup.push({ name: "registry.json", localPath: registryPath });
|
|
10165
10544
|
}
|
|
10166
10545
|
const usersDbPath = path39.join(os13.homedir(), ".code-intel", "users.db");
|
|
10167
|
-
if (
|
|
10546
|
+
if (fs39.existsSync(usersDbPath)) {
|
|
10168
10547
|
filesToBackup.push({ name: "users.db", localPath: usersDbPath });
|
|
10169
10548
|
}
|
|
10170
10549
|
if (filesToBackup.length === 0) {
|
|
@@ -10175,7 +10554,7 @@ var BackupService = class {
|
|
|
10175
10554
|
createdAt,
|
|
10176
10555
|
version: BACKUP_VERSION,
|
|
10177
10556
|
files: filesToBackup.map((f) => {
|
|
10178
|
-
const data =
|
|
10557
|
+
const data = fs39.readFileSync(f.localPath);
|
|
10179
10558
|
return {
|
|
10180
10559
|
name: f.name,
|
|
10181
10560
|
sha256: crypto5.createHash("sha256").update(data).digest("hex"),
|
|
@@ -10189,7 +10568,7 @@ var BackupService = class {
|
|
|
10189
10568
|
manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
|
|
10190
10569
|
parts.push(manifestLenBuf, manifestBuf);
|
|
10191
10570
|
for (const f of filesToBackup) {
|
|
10192
|
-
const data =
|
|
10571
|
+
const data = fs39.readFileSync(f.localPath);
|
|
10193
10572
|
const nameBuf = Buffer.from(f.name, "utf-8");
|
|
10194
10573
|
const nameLenBuf = Buffer.alloc(2);
|
|
10195
10574
|
nameLenBuf.writeUInt16BE(nameBuf.length, 0);
|
|
@@ -10201,7 +10580,7 @@ var BackupService = class {
|
|
|
10201
10580
|
const encrypted = encryptBuffer(plaintext, this.key);
|
|
10202
10581
|
const backupFileName = `backup-${id}.cib`;
|
|
10203
10582
|
const backupPath = path39.join(this.backupDir, backupFileName);
|
|
10204
|
-
|
|
10583
|
+
fs39.writeFileSync(backupPath, encrypted);
|
|
10205
10584
|
const entry = {
|
|
10206
10585
|
id,
|
|
10207
10586
|
createdAt,
|
|
@@ -10230,7 +10609,7 @@ var BackupService = class {
|
|
|
10230
10609
|
if (!cfg) throw new Error("S3 not configured. Set CODE_INTEL_BACKUP_S3_BUCKET, CODE_INTEL_BACKUP_S3_ACCESS_KEY_ID, CODE_INTEL_BACKUP_S3_SECRET_ACCESS_KEY.");
|
|
10231
10610
|
const fileName = path39.basename(entry.path);
|
|
10232
10611
|
const s3Key = `${cfg.prefix}${fileName}`;
|
|
10233
|
-
const body =
|
|
10612
|
+
const body = fs39.readFileSync(entry.path);
|
|
10234
10613
|
const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
|
|
10235
10614
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
10236
10615
|
throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
@@ -10247,8 +10626,8 @@ var BackupService = class {
|
|
|
10247
10626
|
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
10248
10627
|
throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
|
|
10249
10628
|
}
|
|
10250
|
-
|
|
10251
|
-
|
|
10629
|
+
fs39.mkdirSync(path39.dirname(destPath), { recursive: true });
|
|
10630
|
+
fs39.writeFileSync(destPath, Buffer.from(result.body, "binary"));
|
|
10252
10631
|
}
|
|
10253
10632
|
/**
|
|
10254
10633
|
* List backup objects in S3 with the configured prefix.
|
|
@@ -10294,10 +10673,10 @@ var BackupService = class {
|
|
|
10294
10673
|
if (!entry) {
|
|
10295
10674
|
throw new Error(`Backup "${backupId}" not found.`);
|
|
10296
10675
|
}
|
|
10297
|
-
if (!
|
|
10676
|
+
if (!fs39.existsSync(entry.path)) {
|
|
10298
10677
|
throw new Error(`Backup file not found at: ${entry.path}`);
|
|
10299
10678
|
}
|
|
10300
|
-
const encrypted =
|
|
10679
|
+
const encrypted = fs39.readFileSync(entry.path);
|
|
10301
10680
|
let plaintext;
|
|
10302
10681
|
try {
|
|
10303
10682
|
plaintext = decryptBuffer(encrypted, this.key);
|
|
@@ -10312,7 +10691,7 @@ var BackupService = class {
|
|
|
10312
10691
|
const manifest = JSON.parse(manifestStr);
|
|
10313
10692
|
const restoreBase = targetRepoPath ?? entry.repoPath;
|
|
10314
10693
|
const codeIntelDir = path39.join(restoreBase, ".code-intel");
|
|
10315
|
-
|
|
10694
|
+
fs39.mkdirSync(codeIntelDir, { recursive: true });
|
|
10316
10695
|
for (const fileEntry of manifest.files) {
|
|
10317
10696
|
const nameLen = plaintext.readUInt16BE(offset);
|
|
10318
10697
|
offset += 2;
|
|
@@ -10333,14 +10712,14 @@ var BackupService = class {
|
|
|
10333
10712
|
} else {
|
|
10334
10713
|
destPath = path39.join(codeIntelDir, name);
|
|
10335
10714
|
}
|
|
10336
|
-
|
|
10715
|
+
fs39.writeFileSync(destPath, data);
|
|
10337
10716
|
}
|
|
10338
10717
|
}
|
|
10339
10718
|
/**
|
|
10340
10719
|
* Apply retention policy: keep N daily, M weekly, L monthly backups.
|
|
10341
10720
|
*/
|
|
10342
10721
|
applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
|
|
10343
|
-
const entries = this._loadIndex().filter((e) =>
|
|
10722
|
+
const entries = this._loadIndex().filter((e) => fs39.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
10344
10723
|
const keep = /* @__PURE__ */ new Set();
|
|
10345
10724
|
const now = /* @__PURE__ */ new Date();
|
|
10346
10725
|
const dailyCutoff = new Date(now);
|
|
@@ -10370,7 +10749,7 @@ var BackupService = class {
|
|
|
10370
10749
|
for (const e of entries) {
|
|
10371
10750
|
if (!keep.has(e.id)) {
|
|
10372
10751
|
try {
|
|
10373
|
-
|
|
10752
|
+
fs39.unlinkSync(e.path);
|
|
10374
10753
|
deleted++;
|
|
10375
10754
|
} catch {
|
|
10376
10755
|
}
|
|
@@ -10386,13 +10765,13 @@ var BackupService = class {
|
|
|
10386
10765
|
}
|
|
10387
10766
|
_loadIndex() {
|
|
10388
10767
|
try {
|
|
10389
|
-
return JSON.parse(
|
|
10768
|
+
return JSON.parse(fs39.readFileSync(this._indexPath(), "utf-8"));
|
|
10390
10769
|
} catch {
|
|
10391
10770
|
return [];
|
|
10392
10771
|
}
|
|
10393
10772
|
}
|
|
10394
10773
|
_saveIndex(entries) {
|
|
10395
|
-
|
|
10774
|
+
fs39.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
|
|
10396
10775
|
}
|
|
10397
10776
|
_appendIndex(entry) {
|
|
10398
10777
|
const entries = this._loadIndex();
|
|
@@ -11036,7 +11415,7 @@ var openApiSpec = {
|
|
|
11036
11415
|
var __dirname$1 = path39.dirname(fileURLToPath(import.meta.url));
|
|
11037
11416
|
var WEB_DIST = (() => {
|
|
11038
11417
|
const bundled = path39.resolve(__dirname$1, "..", "web");
|
|
11039
|
-
if (
|
|
11418
|
+
if (fs39.existsSync(bundled)) return bundled;
|
|
11040
11419
|
return path39.resolve(__dirname$1, "..", "..", "..", "web", "dist");
|
|
11041
11420
|
})();
|
|
11042
11421
|
function getAllowedOrigins() {
|
|
@@ -11121,8 +11500,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11121
11500
|
const metaFilePath = path39.join(workspaceRoot, ".code-intel", "meta.json");
|
|
11122
11501
|
let metaOk = false;
|
|
11123
11502
|
try {
|
|
11124
|
-
if (
|
|
11125
|
-
const raw =
|
|
11503
|
+
if (fs39.existsSync(metaFilePath)) {
|
|
11504
|
+
const raw = fs39.readFileSync(metaFilePath, "utf-8");
|
|
11126
11505
|
const meta = JSON.parse(raw);
|
|
11127
11506
|
if (meta?.indexVersion) res.setHeader("X-Index-Version", meta.indexVersion);
|
|
11128
11507
|
}
|
|
@@ -11313,12 +11692,12 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11313
11692
|
return;
|
|
11314
11693
|
}
|
|
11315
11694
|
const user = db.createUser(username, password, "admin");
|
|
11316
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11317
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11695
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11696
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11318
11697
|
res.status(201).json({ user: { id: user.id, username: user.username, role: user.role } });
|
|
11319
11698
|
});
|
|
11320
11699
|
app.post("/auth/login", async (req, res) => {
|
|
11321
|
-
const { username, password } = req.body;
|
|
11700
|
+
const { username, password, rememberMe } = req.body;
|
|
11322
11701
|
if (!username || !password) {
|
|
11323
11702
|
res.status(400).json({
|
|
11324
11703
|
error: {
|
|
@@ -11362,10 +11741,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11362
11741
|
});
|
|
11363
11742
|
return;
|
|
11364
11743
|
}
|
|
11365
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11744
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role }, rememberMe === true);
|
|
11366
11745
|
db.logAccess(user.id, "/auth/login", "login", "allow", req.ip ?? "unknown");
|
|
11367
11746
|
authAttemptsTotal.inc({ method: "local", outcome: "success" });
|
|
11368
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11747
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11369
11748
|
res.json({ user: { id: user.id, username: user.username, role: user.role } });
|
|
11370
11749
|
});
|
|
11371
11750
|
app.post("/auth/logout", (req, res) => {
|
|
@@ -11487,9 +11866,9 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11487
11866
|
authAttemptsTotal.inc({ method: "oidc", outcome: "success" });
|
|
11488
11867
|
logger_default.info(`[oidc] Auto-provisioned new user: ${finalUsername} (${cfg.defaultRole})`);
|
|
11489
11868
|
}
|
|
11490
|
-
const sessionId = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11869
|
+
const { sessionId, ttlMs } = createSession({ id: user.id, username: user.username, role: user.role });
|
|
11491
11870
|
db.logAccess(user.id, "/auth/callback", "oidc-login", "allow", req.ip ?? "unknown");
|
|
11492
|
-
res.setHeader("Set-Cookie", buildSessionCookie(sessionId));
|
|
11871
|
+
res.setHeader("Set-Cookie", buildSessionCookie(sessionId, ttlMs));
|
|
11493
11872
|
res.redirect(302, "/");
|
|
11494
11873
|
} catch (err) {
|
|
11495
11874
|
logger_default.warn("[oidc] Callback failed:", err instanceof Error ? err.message : err);
|
|
@@ -11607,7 +11986,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11607
11986
|
const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
|
|
11608
11987
|
if (!entry) return null;
|
|
11609
11988
|
const dbPath = path39.join(entry.path, ".code-intel", "graph.db");
|
|
11610
|
-
if (!
|
|
11989
|
+
if (!fs39.existsSync(dbPath)) return null;
|
|
11611
11990
|
const repoGraph = createKnowledgeGraph();
|
|
11612
11991
|
const db = new DbManager(dbPath, true);
|
|
11613
11992
|
try {
|
|
@@ -11629,7 +12008,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11629
12008
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
11630
12009
|
if (!regEntry) continue;
|
|
11631
12010
|
const dbPath = path39.join(regEntry.path, ".code-intel", "graph.db");
|
|
11632
|
-
if (!
|
|
12011
|
+
if (!fs39.existsSync(dbPath)) continue;
|
|
11633
12012
|
const db = new DbManager(dbPath, true);
|
|
11634
12013
|
try {
|
|
11635
12014
|
await db.init();
|
|
@@ -11711,7 +12090,21 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11711
12090
|
});
|
|
11712
12091
|
});
|
|
11713
12092
|
app.post("/api/v1/search", requireToolScope("search"), async (req, res) => {
|
|
11714
|
-
const { query, limit, repo } = req.body;
|
|
12093
|
+
const { query, limit, repo, group } = req.body;
|
|
12094
|
+
if (group) {
|
|
12095
|
+
const grp = loadGroup(group);
|
|
12096
|
+
if (!grp) {
|
|
12097
|
+
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: `Group '${group}' not found`, hint: "Use /api/v1/groups to list available groups" } });
|
|
12098
|
+
return;
|
|
12099
|
+
}
|
|
12100
|
+
try {
|
|
12101
|
+
const { perRepo, merged } = await queryGroup(grp, query ?? "", limit ?? 20);
|
|
12102
|
+
res.json({ results: merged, perRepo, searchMode: "bm25", group });
|
|
12103
|
+
} catch (err) {
|
|
12104
|
+
res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err) } });
|
|
12105
|
+
}
|
|
12106
|
+
return;
|
|
12107
|
+
}
|
|
11715
12108
|
const g = await getGraphForRepo(repo);
|
|
11716
12109
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
11717
12110
|
const bm25 = !repo || repo === repoName ? ensureBm25Index() : null;
|
|
@@ -11720,7 +12113,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11720
12113
|
vectorDbPath: vdbPath,
|
|
11721
12114
|
bm25Results: bm25Results ?? void 0
|
|
11722
12115
|
});
|
|
11723
|
-
res.json({ results, searchMode });
|
|
12116
|
+
res.json({ results, searchMode, repo: repo ?? repoName });
|
|
11724
12117
|
});
|
|
11725
12118
|
app.post("/api/v1/vector-search", async (req, res) => {
|
|
11726
12119
|
const { query, limit = 10 } = req.body;
|
|
@@ -11762,7 +12155,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
11762
12155
|
return;
|
|
11763
12156
|
}
|
|
11764
12157
|
try {
|
|
11765
|
-
const content =
|
|
12158
|
+
const content = fs39.readFileSync(file_path, "utf-8");
|
|
11766
12159
|
res.json({ content });
|
|
11767
12160
|
} catch {
|
|
11768
12161
|
res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
|
|
@@ -12041,7 +12434,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12041
12434
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
12042
12435
|
if (!regEntry) continue;
|
|
12043
12436
|
const dbPath = path39.join(regEntry.path, ".code-intel", "graph.db");
|
|
12044
|
-
if (!
|
|
12437
|
+
if (!fs39.existsSync(dbPath)) continue;
|
|
12045
12438
|
const db = new DbManager(dbPath, true);
|
|
12046
12439
|
try {
|
|
12047
12440
|
await db.init();
|
|
@@ -12068,7 +12461,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12068
12461
|
let edgeCount = 0;
|
|
12069
12462
|
if (regEntry) {
|
|
12070
12463
|
const dbPath = path39.join(regEntry.path, ".code-intel", "graph.db");
|
|
12071
|
-
if (
|
|
12464
|
+
if (fs39.existsSync(dbPath)) {
|
|
12072
12465
|
try {
|
|
12073
12466
|
const db = new DbManager(dbPath, true);
|
|
12074
12467
|
await db.init();
|
|
@@ -12131,7 +12524,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12131
12524
|
const regEntry = registry.find((r) => r.name === member.registryName);
|
|
12132
12525
|
if (!regEntry) continue;
|
|
12133
12526
|
const candidate = path39.resolve(path39.join(regEntry.path, normalizedFile));
|
|
12134
|
-
if (
|
|
12527
|
+
if (fs39.existsSync(candidate)) {
|
|
12135
12528
|
baseDir = regEntry.path;
|
|
12136
12529
|
break;
|
|
12137
12530
|
}
|
|
@@ -12183,7 +12576,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12183
12576
|
}
|
|
12184
12577
|
let fileContent;
|
|
12185
12578
|
try {
|
|
12186
|
-
fileContent =
|
|
12579
|
+
fileContent = fs39.readFileSync(resolvedFile, "utf-8");
|
|
12187
12580
|
} catch {
|
|
12188
12581
|
res.status(404).json({
|
|
12189
12582
|
error: {
|
|
@@ -12349,7 +12742,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
|
|
|
12349
12742
|
res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err), requestId: req.requestId, timestamp: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
12350
12743
|
}
|
|
12351
12744
|
});
|
|
12352
|
-
if (
|
|
12745
|
+
if (fs39.existsSync(WEB_DIST)) {
|
|
12353
12746
|
app.use(express.static(WEB_DIST));
|
|
12354
12747
|
app.get("/{*path}", (_req, res) => {
|
|
12355
12748
|
res.sendFile(path39.join(WEB_DIST, "index.html"));
|
|
@@ -12503,6 +12896,7 @@ async function startHttpServer(graph, repoName, port = 4747, workspaceRoot, watc
|
|
|
12503
12896
|
});
|
|
12504
12897
|
});
|
|
12505
12898
|
}
|
|
12899
|
+
init_bm25_index();
|
|
12506
12900
|
init_storage();
|
|
12507
12901
|
init_repo_registry();
|
|
12508
12902
|
init_metadata();
|
|
@@ -13029,11 +13423,30 @@ function summarizeCluster(graph, cluster) {
|
|
|
13029
13423
|
}
|
|
13030
13424
|
|
|
13031
13425
|
// src/mcp-server/server.ts
|
|
13426
|
+
function compact(obj) {
|
|
13427
|
+
return JSON.stringify(obj, (_key, value) => value === null || value === void 0 ? void 0 : value);
|
|
13428
|
+
}
|
|
13032
13429
|
function createMcpServer(graph, repoName, workspaceRoot) {
|
|
13033
13430
|
const server = new Server(
|
|
13034
13431
|
{ name: "code-intel", version: "0.1.0" },
|
|
13035
13432
|
{ capabilities: { tools: {}, resources: {} } }
|
|
13036
13433
|
);
|
|
13434
|
+
let bm25Index = null;
|
|
13435
|
+
function ensureBm25Index() {
|
|
13436
|
+
if (bm25Index) return bm25Index;
|
|
13437
|
+
if (!workspaceRoot) return null;
|
|
13438
|
+
try {
|
|
13439
|
+
const idx = new Bm25Index(getBm25DbPath(workspaceRoot));
|
|
13440
|
+
idx.load();
|
|
13441
|
+
bm25Index = idx;
|
|
13442
|
+
return bm25Index;
|
|
13443
|
+
} catch {
|
|
13444
|
+
return null;
|
|
13445
|
+
}
|
|
13446
|
+
}
|
|
13447
|
+
if (workspaceRoot) {
|
|
13448
|
+
setImmediate(() => ensureBm25Index());
|
|
13449
|
+
}
|
|
13037
13450
|
const _tokenProp = {
|
|
13038
13451
|
_token: { type: "string", description: "Required if CODE_INTEL_TOKEN is configured" }
|
|
13039
13452
|
};
|
|
@@ -13053,13 +13466,15 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13053
13466
|
// ── Search & inspect ─────────────────────────────────────────────────
|
|
13054
13467
|
{
|
|
13055
13468
|
name: "search",
|
|
13056
|
-
description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc.",
|
|
13469
|
+
description: "BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc. Optionally scope to a specific repo or group.",
|
|
13057
13470
|
inputSchema: {
|
|
13058
13471
|
type: "object",
|
|
13059
13472
|
properties: {
|
|
13060
13473
|
query: { type: "string", description: "Search query (symbol name, keyword, or partial match)" },
|
|
13061
13474
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
13062
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
13475
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
13476
|
+
repo: { type: "string", description: "Scope search to a specific indexed repo name (optional; defaults to current repo)" },
|
|
13477
|
+
group: { type: "string", description: "Scope search across all repos in a group via cross-repo RRF merge (optional; overrides repo)" },
|
|
13063
13478
|
..._tokenProp
|
|
13064
13479
|
},
|
|
13065
13480
|
required: ["query"]
|
|
@@ -13089,7 +13504,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13089
13504
|
enum: ["callers", "callees", "both"],
|
|
13090
13505
|
description: "Which direction to trace \u2014 callers (who depends on it), callees (what it depends on), or both (default: both)"
|
|
13091
13506
|
},
|
|
13092
|
-
max_hops: { type: "number", description: "Maximum traversal depth (default:
|
|
13507
|
+
max_hops: { type: "number", description: "Maximum traversal depth (default: 2, max: 10)" },
|
|
13093
13508
|
..._tokenProp
|
|
13094
13509
|
},
|
|
13095
13510
|
required: ["target"]
|
|
@@ -13103,7 +13518,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13103
13518
|
properties: {
|
|
13104
13519
|
file_path: { type: "string", description: 'File path (partial match is supported, e.g. "auth/login.ts")' },
|
|
13105
13520
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
13106
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
13521
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
13107
13522
|
..._tokenProp
|
|
13108
13523
|
},
|
|
13109
13524
|
required: ["file_path"]
|
|
@@ -13134,7 +13549,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13134
13549
|
description: "Filter by node kind: function | class | interface | method | type_alias | constant | enum (optional)"
|
|
13135
13550
|
},
|
|
13136
13551
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
13137
|
-
limit: { type: "number", description: "Max results per page (default:
|
|
13552
|
+
limit: { type: "number", description: "Max results per page (default: 10, max: 500)" },
|
|
13138
13553
|
..._tokenProp
|
|
13139
13554
|
}
|
|
13140
13555
|
}
|
|
@@ -13152,7 +13567,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13152
13567
|
type: "object",
|
|
13153
13568
|
properties: {
|
|
13154
13569
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
13155
|
-
limit: { type: "number", description: "Max clusters per page (default:
|
|
13570
|
+
limit: { type: "number", description: "Max clusters per page (default: 10, max: 500)" },
|
|
13156
13571
|
..._tokenProp
|
|
13157
13572
|
}
|
|
13158
13573
|
}
|
|
@@ -13164,7 +13579,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13164
13579
|
type: "object",
|
|
13165
13580
|
properties: {
|
|
13166
13581
|
offset: { type: "number", description: "Number of results to skip for pagination (default: 0)" },
|
|
13167
|
-
limit: { type: "number", description: "Max flows per page (default:
|
|
13582
|
+
limit: { type: "number", description: "Max flows per page (default: 10, max: 500)" },
|
|
13168
13583
|
..._tokenProp
|
|
13169
13584
|
}
|
|
13170
13585
|
}
|
|
@@ -13318,7 +13733,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13318
13733
|
},
|
|
13319
13734
|
maxHops: {
|
|
13320
13735
|
type: "number",
|
|
13321
|
-
description: "Maximum BFS depth for blast radius (default:
|
|
13736
|
+
description: "Maximum BFS depth for blast radius (default: 2, max: 10)"
|
|
13322
13737
|
},
|
|
13323
13738
|
..._tokenProp
|
|
13324
13739
|
}
|
|
@@ -13446,13 +13861,13 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13446
13861
|
const providedToken = a._token;
|
|
13447
13862
|
if (providedToken !== expectedToken) {
|
|
13448
13863
|
return {
|
|
13449
|
-
content: [{ type: "text", text:
|
|
13864
|
+
content: [{ type: "text", text: compact({ error: "Unauthorized: invalid or missing CODE_INTEL_TOKEN" }) }],
|
|
13450
13865
|
isError: true
|
|
13451
13866
|
};
|
|
13452
13867
|
}
|
|
13453
13868
|
}
|
|
13454
13869
|
const startMs = Date.now();
|
|
13455
|
-
const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot);
|
|
13870
|
+
const dispatch = () => dispatchTool(name, a, graph, repoName, workspaceRoot, ensureBm25Index);
|
|
13456
13871
|
const MCP_TIMEOUT_MS = parseInt(process.env["CODE_INTEL_MCP_TIMEOUT_MS"] ?? "30000", 10);
|
|
13457
13872
|
let timeoutHandle = null;
|
|
13458
13873
|
let timedOut = false;
|
|
@@ -13484,7 +13899,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13484
13899
|
mcpToolDurationSeconds.observe({ tool: name }, (Date.now() - startMs) / 1e3);
|
|
13485
13900
|
if (timedOut) {
|
|
13486
13901
|
return {
|
|
13487
|
-
content: [{ type: "text", text:
|
|
13902
|
+
content: [{ type: "text", text: compact({ truncated: true, reason: `Tool '${name}' timed out after ${MCP_TIMEOUT_MS}ms`, partialResults: [] }) }],
|
|
13488
13903
|
isError: false
|
|
13489
13904
|
};
|
|
13490
13905
|
}
|
|
@@ -13499,7 +13914,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
13499
13914
|
registerResources(server, graph, repoName);
|
|
13500
13915
|
return server;
|
|
13501
13916
|
}
|
|
13502
|
-
async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
13917
|
+
async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolver) {
|
|
13503
13918
|
switch (name) {
|
|
13504
13919
|
// ── repos ──────────────────────────────────────────────────────────────
|
|
13505
13920
|
case "repos": {
|
|
@@ -13507,10 +13922,8 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13507
13922
|
return {
|
|
13508
13923
|
content: [{
|
|
13509
13924
|
type: "text",
|
|
13510
|
-
text:
|
|
13511
|
-
registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats }))
|
|
13512
|
-
null,
|
|
13513
|
-
2
|
|
13925
|
+
text: compact(
|
|
13926
|
+
registry.map((r) => ({ name: r.name, path: r.path, indexedAt: r.indexedAt, stats: r.stats }))
|
|
13514
13927
|
)
|
|
13515
13928
|
}]
|
|
13516
13929
|
};
|
|
@@ -13538,13 +13951,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13538
13951
|
return {
|
|
13539
13952
|
content: [{
|
|
13540
13953
|
type: "text",
|
|
13541
|
-
text:
|
|
13954
|
+
text: compact({
|
|
13542
13955
|
repo: repoName,
|
|
13543
13956
|
stats: graph.size,
|
|
13544
13957
|
nodeCounts: kindCounts,
|
|
13545
13958
|
edgeCounts,
|
|
13546
13959
|
health
|
|
13547
|
-
}
|
|
13960
|
+
})
|
|
13548
13961
|
}]
|
|
13549
13962
|
};
|
|
13550
13963
|
}
|
|
@@ -13552,15 +13965,59 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13552
13965
|
case "search": {
|
|
13553
13966
|
const query = a.query;
|
|
13554
13967
|
const offset = a.offset ?? 0;
|
|
13555
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
13968
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
13969
|
+
if (a.group) {
|
|
13970
|
+
const grp = loadGroup(a.group);
|
|
13971
|
+
if (!grp) {
|
|
13972
|
+
return { content: [{ type: "text", text: `Group "${a.group}" not found. Use list_groups to see available groups.` }] };
|
|
13973
|
+
}
|
|
13974
|
+
const { perRepo, merged } = await queryGroup(grp, query, effectiveLimit + offset);
|
|
13975
|
+
const paged = merged.slice(offset, offset + effectiveLimit);
|
|
13976
|
+
return {
|
|
13977
|
+
content: [{
|
|
13978
|
+
type: "text",
|
|
13979
|
+
text: compact({
|
|
13980
|
+
results: paged,
|
|
13981
|
+
perRepo,
|
|
13982
|
+
searchMode: "bm25-cross-repo",
|
|
13983
|
+
group: a.group,
|
|
13984
|
+
total: merged.length,
|
|
13985
|
+
offset,
|
|
13986
|
+
limit: effectiveLimit,
|
|
13987
|
+
hasMore: offset + effectiveLimit < merged.length
|
|
13988
|
+
})
|
|
13989
|
+
}]
|
|
13990
|
+
};
|
|
13991
|
+
}
|
|
13992
|
+
const repoGraph = a.repo ? await (async () => {
|
|
13993
|
+
const registry = loadRegistry();
|
|
13994
|
+
const entry = registry.find((r) => r.name === a.repo || r.path === a.repo);
|
|
13995
|
+
if (!entry) return graph;
|
|
13996
|
+
const { DbManager: DbMgr } = await Promise.resolve().then(() => (init_db_manager(), db_manager_exports));
|
|
13997
|
+
const { loadGraphFromDB: loadG } = await Promise.resolve().then(() => (init_graph_from_db(), graph_from_db_exports));
|
|
13998
|
+
const { createKnowledgeGraph: createG } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
13999
|
+
const dbPath = path39.join(entry.path, ".code-intel", "graph.db");
|
|
14000
|
+
if (!fs39.existsSync(dbPath)) return graph;
|
|
14001
|
+
const db = new DbMgr(dbPath, true);
|
|
14002
|
+
await db.init();
|
|
14003
|
+
const g = createG();
|
|
14004
|
+
await loadG(g, db);
|
|
14005
|
+
db.close();
|
|
14006
|
+
return g;
|
|
14007
|
+
})() : graph;
|
|
13556
14008
|
const vdbPath = workspaceRoot ? getVectorDbPath(workspaceRoot) : void 0;
|
|
13557
14009
|
const fetchLimit = Math.min(offset + effectiveLimit, 500);
|
|
13558
|
-
const
|
|
14010
|
+
const bm25 = !a.repo || a.repo === repoName ? bm25Resolver ? bm25Resolver() : null : null;
|
|
14011
|
+
const bm25Results = bm25 ? bm25.search(query, fetchLimit * 3) : void 0;
|
|
14012
|
+
const { results: allResults, searchMode } = await hybridSearch(repoGraph, query, fetchLimit, {
|
|
14013
|
+
vectorDbPath: vdbPath,
|
|
14014
|
+
bm25Results: bm25Results ?? void 0
|
|
14015
|
+
});
|
|
13559
14016
|
const total = allResults.length;
|
|
13560
14017
|
const results = allResults.slice(offset, offset + effectiveLimit);
|
|
13561
14018
|
const hasMore = offset + effectiveLimit < total;
|
|
13562
14019
|
const suggestNextTools = [];
|
|
13563
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
14020
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
13564
14021
|
if (suggestEnabled && results.length > 0) {
|
|
13565
14022
|
const topName = results[0].name;
|
|
13566
14023
|
suggestNextTools.push(
|
|
@@ -13571,15 +14028,16 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13571
14028
|
return {
|
|
13572
14029
|
content: [{
|
|
13573
14030
|
type: "text",
|
|
13574
|
-
text:
|
|
14031
|
+
text: compact({
|
|
13575
14032
|
results,
|
|
13576
14033
|
searchMode,
|
|
14034
|
+
repo: a.repo ?? repoName,
|
|
13577
14035
|
total,
|
|
13578
14036
|
offset,
|
|
13579
14037
|
limit: effectiveLimit,
|
|
13580
14038
|
hasMore,
|
|
13581
14039
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
13582
|
-
}
|
|
14040
|
+
})
|
|
13583
14041
|
}]
|
|
13584
14042
|
};
|
|
13585
14043
|
}
|
|
@@ -13601,7 +14059,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13601
14059
|
file: graph.getNode(e.target)?.filePath
|
|
13602
14060
|
}));
|
|
13603
14061
|
const cluster = incoming.filter((e) => e.kind === "belongs_to").map((e) => graph.getNode(e.target)?.name)[0];
|
|
13604
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
14062
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
13605
14063
|
const suggestNextTools = [];
|
|
13606
14064
|
if (suggestEnabled) {
|
|
13607
14065
|
const topCallerName = callers[0]?.name;
|
|
@@ -13613,7 +14071,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13613
14071
|
return {
|
|
13614
14072
|
content: [{
|
|
13615
14073
|
type: "text",
|
|
13616
|
-
text:
|
|
14074
|
+
text: compact({
|
|
13617
14075
|
node: {
|
|
13618
14076
|
id: node.id,
|
|
13619
14077
|
kind: node.kind,
|
|
@@ -13636,7 +14094,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13636
14094
|
cluster,
|
|
13637
14095
|
content: node.content?.slice(0, 500),
|
|
13638
14096
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
13639
|
-
}
|
|
14097
|
+
})
|
|
13640
14098
|
}]
|
|
13641
14099
|
};
|
|
13642
14100
|
}
|
|
@@ -13644,7 +14102,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13644
14102
|
case "blast_radius": {
|
|
13645
14103
|
const target = a.target;
|
|
13646
14104
|
const direction = a.direction ?? "both";
|
|
13647
|
-
const maxHops = a.max_hops ??
|
|
14105
|
+
const maxHops = a.max_hops ?? 2;
|
|
13648
14106
|
const node = findNodeByName(graph, target);
|
|
13649
14107
|
if (!node) return { content: [{ type: "text", text: `Symbol "${target}" not found.` }] };
|
|
13650
14108
|
const affected = /* @__PURE__ */ new Set();
|
|
@@ -13671,7 +14129,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13671
14129
|
return n ? { id, name: n.name, kind: n.kind, filePath: n.filePath } : { id };
|
|
13672
14130
|
});
|
|
13673
14131
|
const risk = affected.size > 10 ? "HIGH" : affected.size > 5 ? "MEDIUM" : "LOW";
|
|
13674
|
-
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"]
|
|
14132
|
+
const suggestEnabled = process.env["CODE_INTEL_SUGGEST_NEXT_TOOLS"] === "true";
|
|
13675
14133
|
const suggestNextTools = [];
|
|
13676
14134
|
if (suggestEnabled) {
|
|
13677
14135
|
const highestRiskSymbol = node.name;
|
|
@@ -13684,13 +14142,13 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13684
14142
|
return {
|
|
13685
14143
|
content: [{
|
|
13686
14144
|
type: "text",
|
|
13687
|
-
text:
|
|
14145
|
+
text: compact({
|
|
13688
14146
|
target: node.name,
|
|
13689
14147
|
affectedCount: affected.size,
|
|
13690
14148
|
riskLevel: risk,
|
|
13691
14149
|
affected: affectedDetails,
|
|
13692
14150
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
13693
|
-
}
|
|
14151
|
+
})
|
|
13694
14152
|
}]
|
|
13695
14153
|
};
|
|
13696
14154
|
}
|
|
@@ -13698,7 +14156,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13698
14156
|
case "file_symbols": {
|
|
13699
14157
|
const filePath = a.file_path;
|
|
13700
14158
|
const offset = a.offset ?? 0;
|
|
13701
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
14159
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
13702
14160
|
const allMatches = [];
|
|
13703
14161
|
for (const node of graph.allNodes()) {
|
|
13704
14162
|
if (node.filePath && node.filePath.includes(filePath)) {
|
|
@@ -13715,7 +14173,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13715
14173
|
return {
|
|
13716
14174
|
content: [{
|
|
13717
14175
|
type: "text",
|
|
13718
|
-
text:
|
|
14176
|
+
text: compact({ symbols: matches, total, offset, limit: effectiveLimit, hasMore })
|
|
13719
14177
|
}]
|
|
13720
14178
|
};
|
|
13721
14179
|
}
|
|
@@ -13756,7 +14214,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13756
14214
|
return {
|
|
13757
14215
|
content: [{
|
|
13758
14216
|
type: "text",
|
|
13759
|
-
text:
|
|
14217
|
+
text: compact({ from: fromName, to: toName, hops: foundPath.length - 1, path: pathDetails })
|
|
13760
14218
|
}]
|
|
13761
14219
|
};
|
|
13762
14220
|
}
|
|
@@ -13764,7 +14222,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13764
14222
|
case "list_exports": {
|
|
13765
14223
|
const kindFilter = a.kind;
|
|
13766
14224
|
const offset = a.offset ?? 0;
|
|
13767
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
14225
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
13768
14226
|
const allExports = [];
|
|
13769
14227
|
for (const node of graph.allNodes()) {
|
|
13770
14228
|
if (!node.exported) continue;
|
|
@@ -13777,7 +14235,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13777
14235
|
return {
|
|
13778
14236
|
content: [{
|
|
13779
14237
|
type: "text",
|
|
13780
|
-
text:
|
|
14238
|
+
text: compact({ exports: exports$1, total, offset, limit: effectiveLimit, hasMore })
|
|
13781
14239
|
}]
|
|
13782
14240
|
};
|
|
13783
14241
|
}
|
|
@@ -13789,12 +14247,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13789
14247
|
routes.push({ name: node.name, filePath: node.filePath, startLine: node.startLine });
|
|
13790
14248
|
}
|
|
13791
14249
|
}
|
|
13792
|
-
return { content: [{ type: "text", text:
|
|
14250
|
+
return { content: [{ type: "text", text: compact(routes) }] };
|
|
13793
14251
|
}
|
|
13794
14252
|
// ── clusters ───────────────────────────────────────────────────────────
|
|
13795
14253
|
case "clusters": {
|
|
13796
14254
|
const offset = a.offset ?? 0;
|
|
13797
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
14255
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
13798
14256
|
const allClusters = [];
|
|
13799
14257
|
for (const node of graph.allNodes()) {
|
|
13800
14258
|
if (node.kind === "cluster") {
|
|
@@ -13821,14 +14279,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13821
14279
|
return {
|
|
13822
14280
|
content: [{
|
|
13823
14281
|
type: "text",
|
|
13824
|
-
text:
|
|
14282
|
+
text: compact({ clusters, total, offset, limit: effectiveLimit, hasMore })
|
|
13825
14283
|
}]
|
|
13826
14284
|
};
|
|
13827
14285
|
}
|
|
13828
14286
|
// ── flows ──────────────────────────────────────────────────────────────
|
|
13829
14287
|
case "flows": {
|
|
13830
14288
|
const offset = a.offset ?? 0;
|
|
13831
|
-
const effectiveLimit = Math.min(a.limit ??
|
|
14289
|
+
const effectiveLimit = Math.min(a.limit ?? 10, 500);
|
|
13832
14290
|
const allFlows = [];
|
|
13833
14291
|
for (const node of graph.allNodes()) {
|
|
13834
14292
|
if (node.kind === "flow") {
|
|
@@ -13848,7 +14306,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13848
14306
|
return {
|
|
13849
14307
|
content: [{
|
|
13850
14308
|
type: "text",
|
|
13851
|
-
text:
|
|
14309
|
+
text: compact({ flows, total, offset, limit: effectiveLimit, hasMore })
|
|
13852
14310
|
}]
|
|
13853
14311
|
};
|
|
13854
14312
|
}
|
|
@@ -13916,14 +14374,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13916
14374
|
return {
|
|
13917
14375
|
content: [{
|
|
13918
14376
|
type: "text",
|
|
13919
|
-
text:
|
|
14377
|
+
text: compact({
|
|
13920
14378
|
baseRef,
|
|
13921
14379
|
changedFiles: changedFiles.map((f) => f.filePath),
|
|
13922
14380
|
directlyChangedSymbols: changedSymbols,
|
|
13923
14381
|
transitivelyAffectedSymbols: affectedSymbols,
|
|
13924
14382
|
totalAffected: allAffected.size,
|
|
13925
14383
|
riskLevel: risk
|
|
13926
|
-
}
|
|
14384
|
+
})
|
|
13927
14385
|
}]
|
|
13928
14386
|
};
|
|
13929
14387
|
}
|
|
@@ -13931,14 +14389,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13931
14389
|
case "query": {
|
|
13932
14390
|
const gqlInput = a.gql;
|
|
13933
14391
|
if (!gqlInput) {
|
|
13934
|
-
return { content: [{ type: "text", text:
|
|
14392
|
+
return { content: [{ type: "text", text: compact({ error: "Missing required parameter: gql" }) }], isError: true };
|
|
13935
14393
|
}
|
|
13936
14394
|
const { parseGQL: parseGQL2, isGQLParseError: isGQLParseError2 } = await Promise.resolve().then(() => (init_gql_parser(), gql_parser_exports));
|
|
13937
14395
|
const { executeGQL: executeGQL2 } = await Promise.resolve().then(() => (init_gql_executor(), gql_executor_exports));
|
|
13938
14396
|
const ast = parseGQL2(gqlInput);
|
|
13939
14397
|
if (isGQLParseError2(ast)) {
|
|
13940
14398
|
return {
|
|
13941
|
-
content: [{ type: "text", text:
|
|
14399
|
+
content: [{ type: "text", text: compact({ error: `GQL parse error: ${ast.message}`, pos: ast.pos, expected: ast.expected, got: ast.got }) }],
|
|
13942
14400
|
isError: true
|
|
13943
14401
|
};
|
|
13944
14402
|
}
|
|
@@ -13949,7 +14407,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13949
14407
|
return {
|
|
13950
14408
|
content: [{
|
|
13951
14409
|
type: "text",
|
|
13952
|
-
text:
|
|
14410
|
+
text: compact({
|
|
13953
14411
|
nodes: result.nodes,
|
|
13954
14412
|
edges: result.edges,
|
|
13955
14413
|
groups: result.groups,
|
|
@@ -13957,7 +14415,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13957
14415
|
executionTimeMs: result.executionTimeMs,
|
|
13958
14416
|
truncated: result.truncated,
|
|
13959
14417
|
totalCount: result.totalCount
|
|
13960
|
-
}
|
|
14418
|
+
})
|
|
13961
14419
|
}]
|
|
13962
14420
|
};
|
|
13963
14421
|
}
|
|
@@ -13971,7 +14429,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13971
14429
|
for (const node of graph.allNodes()) {
|
|
13972
14430
|
if (node.name === nameMatch[1]) results.push(node);
|
|
13973
14431
|
}
|
|
13974
|
-
return { content: [{ type: "text", text:
|
|
14432
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
|
|
13975
14433
|
}
|
|
13976
14434
|
const kindMatch = q?.match(/:\s*(\w+)/);
|
|
13977
14435
|
if (kindMatch) {
|
|
@@ -13980,9 +14438,9 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13980
14438
|
if (node.kind === kindMatch[1]) results.push(node);
|
|
13981
14439
|
if (results.length >= 50) break;
|
|
13982
14440
|
}
|
|
13983
|
-
return { content: [{ type: "text", text:
|
|
14441
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, results }) }] };
|
|
13984
14442
|
}
|
|
13985
|
-
return { content: [{ type: "text", text:
|
|
14443
|
+
return { content: [{ type: "text", text: compact({ deprecation: deprecationWarning, error: "Query not recognized. Use name='X' or :kind syntax. Or use the query tool with GQL instead." }) }] };
|
|
13986
14444
|
}
|
|
13987
14445
|
// ── group_list ─────────────────────────────────────────────────────────
|
|
13988
14446
|
case "group_list": {
|
|
@@ -13990,16 +14448,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
13990
14448
|
if (groupName) {
|
|
13991
14449
|
const group = loadGroup(groupName);
|
|
13992
14450
|
if (!group) return { content: [{ type: "text", text: `Group "${groupName}" not found.` }] };
|
|
13993
|
-
return { content: [{ type: "text", text:
|
|
14451
|
+
return { content: [{ type: "text", text: compact(group) }] };
|
|
13994
14452
|
}
|
|
13995
14453
|
const groups = listGroups();
|
|
13996
14454
|
return {
|
|
13997
14455
|
content: [{
|
|
13998
14456
|
type: "text",
|
|
13999
|
-
text:
|
|
14000
|
-
groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members }))
|
|
14001
|
-
null,
|
|
14002
|
-
2
|
|
14457
|
+
text: compact(
|
|
14458
|
+
groups.map((g) => ({ name: g.name, createdAt: g.createdAt, lastSync: g.lastSync, memberCount: g.members.length, members: g.members }))
|
|
14003
14459
|
)
|
|
14004
14460
|
}]
|
|
14005
14461
|
};
|
|
@@ -14017,14 +14473,14 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14017
14473
|
return {
|
|
14018
14474
|
content: [{
|
|
14019
14475
|
type: "text",
|
|
14020
|
-
text:
|
|
14476
|
+
text: compact({
|
|
14021
14477
|
groupName: result.groupName,
|
|
14022
14478
|
syncedAt: result.syncedAt,
|
|
14023
14479
|
memberCount: result.memberCount,
|
|
14024
14480
|
contractCount: result.contracts.length,
|
|
14025
14481
|
linkCount: result.links.length,
|
|
14026
14482
|
topLinks: result.links.slice(0, 20)
|
|
14027
|
-
}
|
|
14483
|
+
})
|
|
14028
14484
|
}]
|
|
14029
14485
|
};
|
|
14030
14486
|
}
|
|
@@ -14044,7 +14500,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14044
14500
|
return {
|
|
14045
14501
|
content: [{
|
|
14046
14502
|
type: "text",
|
|
14047
|
-
text:
|
|
14503
|
+
text: compact({ syncedAt: result.syncedAt, contracts, links })
|
|
14048
14504
|
}]
|
|
14049
14505
|
};
|
|
14050
14506
|
}
|
|
@@ -14059,7 +14515,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14059
14515
|
return {
|
|
14060
14516
|
content: [{
|
|
14061
14517
|
type: "text",
|
|
14062
|
-
text:
|
|
14518
|
+
text: compact({ query, merged, perRepo })
|
|
14063
14519
|
}]
|
|
14064
14520
|
};
|
|
14065
14521
|
}
|
|
@@ -14091,12 +14547,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14091
14547
|
return {
|
|
14092
14548
|
content: [{
|
|
14093
14549
|
type: "text",
|
|
14094
|
-
text:
|
|
14550
|
+
text: compact({
|
|
14095
14551
|
group: groupName,
|
|
14096
14552
|
lastSync: group.lastSync ?? null,
|
|
14097
14553
|
syncAgeMinutes: syncAge,
|
|
14098
14554
|
members: memberStatus
|
|
14099
|
-
}
|
|
14555
|
+
})
|
|
14100
14556
|
}]
|
|
14101
14557
|
};
|
|
14102
14558
|
}
|
|
@@ -14105,11 +14561,11 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14105
14561
|
const fromName = a.from;
|
|
14106
14562
|
const toName = a.to;
|
|
14107
14563
|
const result = explainRelationship(graph, fromName, toName);
|
|
14108
|
-
return { content: [{ type: "text", text:
|
|
14564
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14109
14565
|
}
|
|
14110
14566
|
// ── pr_impact ──────────────────────────────────────────────────────────
|
|
14111
14567
|
case "pr_impact": {
|
|
14112
|
-
const maxHops = a.maxHops ??
|
|
14568
|
+
const maxHops = a.maxHops ?? 2;
|
|
14113
14569
|
let changedFiles = a.changedFiles ?? [];
|
|
14114
14570
|
if (a.diff && typeof a.diff === "string") {
|
|
14115
14571
|
const diffFiles = parseDiffFiles(a.diff);
|
|
@@ -14119,37 +14575,37 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14119
14575
|
return {
|
|
14120
14576
|
content: [{
|
|
14121
14577
|
type: "text",
|
|
14122
|
-
text:
|
|
14578
|
+
text: compact({ error: 'No changed files provided. Supply "changedFiles" or "diff".' })
|
|
14123
14579
|
}]
|
|
14124
14580
|
};
|
|
14125
14581
|
}
|
|
14126
14582
|
const result = computePRImpact(graph, changedFiles, maxHops);
|
|
14127
|
-
return { content: [{ type: "text", text:
|
|
14583
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14128
14584
|
}
|
|
14129
14585
|
// ── similar_symbols ────────────────────────────────────────────────────
|
|
14130
14586
|
case "similar_symbols": {
|
|
14131
14587
|
const symbolName = a.symbol;
|
|
14132
14588
|
const limit = a.limit ?? 10;
|
|
14133
14589
|
const result = findSimilarSymbols(graph, symbolName, limit);
|
|
14134
|
-
return { content: [{ type: "text", text:
|
|
14590
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14135
14591
|
}
|
|
14136
14592
|
// ── health_report ──────────────────────────────────────────────────────
|
|
14137
14593
|
case "health_report": {
|
|
14138
14594
|
const scope = a.scope ?? ".";
|
|
14139
14595
|
const result = computeHealthReport(graph, scope);
|
|
14140
|
-
return { content: [{ type: "text", text:
|
|
14596
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14141
14597
|
}
|
|
14142
14598
|
// ── suggest_tests ──────────────────────────────────────────────────────
|
|
14143
14599
|
case "suggest_tests": {
|
|
14144
14600
|
const sym = a.symbol;
|
|
14145
14601
|
const result = suggestTests(graph, sym);
|
|
14146
|
-
return { content: [{ type: "text", text:
|
|
14602
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14147
14603
|
}
|
|
14148
14604
|
// ── cluster_summary ────────────────────────────────────────────────────
|
|
14149
14605
|
case "cluster_summary": {
|
|
14150
14606
|
const cluster = a.cluster;
|
|
14151
14607
|
const result = summarizeCluster(graph, cluster);
|
|
14152
|
-
return { content: [{ type: "text", text:
|
|
14608
|
+
return { content: [{ type: "text", text: compact(result) }] };
|
|
14153
14609
|
}
|
|
14154
14610
|
case "deprecated_usage": {
|
|
14155
14611
|
const scope = a.scope;
|
|
@@ -14157,7 +14613,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14157
14613
|
const detector = new DeprecatedDetector2();
|
|
14158
14614
|
detector.tagDeprecated(graph);
|
|
14159
14615
|
const findings = detector.detect(graph, scope);
|
|
14160
|
-
return { content: [{ type: "text", text:
|
|
14616
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
14161
14617
|
}
|
|
14162
14618
|
// ── complexity_hotspots ────────────────────────────────────────────────
|
|
14163
14619
|
case "complexity_hotspots": {
|
|
@@ -14165,7 +14621,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14165
14621
|
const scope = a.scope;
|
|
14166
14622
|
const limit = typeof a.limit === "number" ? a.limit : 20;
|
|
14167
14623
|
const hotspots = computeComplexity2(graph, scope).slice(0, limit);
|
|
14168
|
-
return { content: [{ type: "text", text:
|
|
14624
|
+
return { content: [{ type: "text", text: compact({ hotspots, total: hotspots.length }) }] };
|
|
14169
14625
|
}
|
|
14170
14626
|
// ── coverage_gaps ──────────────────────────────────────────────────────
|
|
14171
14627
|
case "coverage_gaps": {
|
|
@@ -14177,12 +14633,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14177
14633
|
return {
|
|
14178
14634
|
content: [{
|
|
14179
14635
|
type: "text",
|
|
14180
|
-
text:
|
|
14636
|
+
text: compact({
|
|
14181
14637
|
untestedByRisk,
|
|
14182
14638
|
coveragePct: summary.coveragePct,
|
|
14183
14639
|
totalExported: summary.totalExported,
|
|
14184
14640
|
testedExported: summary.testedExported
|
|
14185
|
-
}
|
|
14641
|
+
})
|
|
14186
14642
|
}]
|
|
14187
14643
|
};
|
|
14188
14644
|
}
|
|
@@ -14193,7 +14649,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14193
14649
|
const scope = a.scope;
|
|
14194
14650
|
const includeTestFiles = a.includeTestFiles ?? false;
|
|
14195
14651
|
const findings = scanner.scan(graph, { scope, includeTestFiles });
|
|
14196
|
-
return { content: [{ type: "text", text:
|
|
14652
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
14197
14653
|
}
|
|
14198
14654
|
// ── vulnerability_scan ─────────────────────────────────────────────────
|
|
14199
14655
|
case "vulnerability_scan": {
|
|
@@ -14206,7 +14662,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
|
|
|
14206
14662
|
const minRank = sevRank[minSev] ?? 1;
|
|
14207
14663
|
let findings = detector.detect(graph, { scope, types });
|
|
14208
14664
|
findings = findings.filter((f) => (sevRank[f.severity] ?? 1) >= minRank);
|
|
14209
|
-
return { content: [{ type: "text", text:
|
|
14665
|
+
return { content: [{ type: "text", text: compact({ findings, total: findings.length }) }] };
|
|
14210
14666
|
}
|
|
14211
14667
|
default:
|
|
14212
14668
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
|
@@ -14227,21 +14683,21 @@ function registerResources(server, graph, repoName) {
|
|
|
14227
14683
|
for (const node of graph.allNodes()) {
|
|
14228
14684
|
kindCounts[node.kind] = (kindCounts[node.kind] ?? 0) + 1;
|
|
14229
14685
|
}
|
|
14230
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
14686
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact({ repo: repoName, stats: graph.size, nodeCounts: kindCounts }) }] };
|
|
14231
14687
|
}
|
|
14232
14688
|
if (uri.endsWith("/clusters")) {
|
|
14233
14689
|
const clusters = [];
|
|
14234
14690
|
for (const node of graph.allNodes()) {
|
|
14235
14691
|
if (node.kind === "cluster") clusters.push({ id: node.id, name: node.name, memberCount: node.metadata?.memberCount });
|
|
14236
14692
|
}
|
|
14237
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
14693
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact(clusters) }] };
|
|
14238
14694
|
}
|
|
14239
14695
|
if (uri.endsWith("/flows")) {
|
|
14240
14696
|
const flows = [];
|
|
14241
14697
|
for (const node of graph.allNodes()) {
|
|
14242
14698
|
if (node.kind === "flow") flows.push({ id: node.id, name: node.name, steps: node.metadata?.steps, entryPoint: node.metadata?.entryPoint });
|
|
14243
14699
|
}
|
|
14244
|
-
return { contents: [{ uri, mimeType: "application/json", text:
|
|
14700
|
+
return { contents: [{ uri, mimeType: "application/json", text: compact(flows) }] };
|
|
14245
14701
|
}
|
|
14246
14702
|
throw new Error(`Unknown resource: ${uri}`);
|
|
14247
14703
|
});
|
|
@@ -14297,8 +14753,8 @@ async function writeSkillFiles(graph, workspaceRoot, projectName) {
|
|
|
14297
14753
|
const outputDir = path39.join(workspaceRoot, ".claude", "skills", "code-intel");
|
|
14298
14754
|
const areas = buildAreaMap(graph, workspaceRoot);
|
|
14299
14755
|
if (areas.length === 0) return { skills: [], outputDir };
|
|
14300
|
-
|
|
14301
|
-
|
|
14756
|
+
fs39.rmSync(outputDir, { recursive: true, force: true });
|
|
14757
|
+
fs39.mkdirSync(outputDir, { recursive: true });
|
|
14302
14758
|
const skills = [];
|
|
14303
14759
|
const usedNames = /* @__PURE__ */ new Set();
|
|
14304
14760
|
for (const area of areas) {
|
|
@@ -14306,8 +14762,8 @@ async function writeSkillFiles(graph, workspaceRoot, projectName) {
|
|
|
14306
14762
|
usedNames.add(kebab);
|
|
14307
14763
|
const content = renderSkill(area, projectName, kebab);
|
|
14308
14764
|
const dir = path39.join(outputDir, kebab);
|
|
14309
|
-
|
|
14310
|
-
|
|
14765
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
14766
|
+
fs39.writeFileSync(path39.join(dir, "SKILL.md"), content, "utf-8");
|
|
14311
14767
|
skills.push({ name: kebab, label: area.label, symbolCount: area.nodes.length, fileCount: area.files.size });
|
|
14312
14768
|
}
|
|
14313
14769
|
return { skills, outputDir };
|
|
@@ -14490,13 +14946,13 @@ function writeContextFiles(workspaceRoot, projectName, stats, skills) {
|
|
|
14490
14946
|
upsertFile(path39.join(workspaceRoot, "AGENTS.md"), block, "AGENTS.md");
|
|
14491
14947
|
upsertFile(path39.join(workspaceRoot, "CLAUDE.md"), block, "CLAUDE.md");
|
|
14492
14948
|
const githubDir = path39.join(workspaceRoot, ".github");
|
|
14493
|
-
if (!
|
|
14949
|
+
if (!fs39.existsSync(githubDir)) fs39.mkdirSync(githubDir, { recursive: true });
|
|
14494
14950
|
upsertFile(path39.join(githubDir, "copilot-instructions.md"), block, "copilot-instructions.md");
|
|
14495
14951
|
const cursorDir = path39.join(workspaceRoot, ".cursor", "rules");
|
|
14496
|
-
if (!
|
|
14952
|
+
if (!fs39.existsSync(cursorDir)) fs39.mkdirSync(cursorDir, { recursive: true });
|
|
14497
14953
|
upsertFile(path39.join(cursorDir, "code-intel.mdc"), block, "code-intel.mdc");
|
|
14498
14954
|
const kiroDir = path39.join(workspaceRoot, ".kiro", "steering");
|
|
14499
|
-
if (!
|
|
14955
|
+
if (!fs39.existsSync(kiroDir)) fs39.mkdirSync(kiroDir, { recursive: true });
|
|
14500
14956
|
upsertFile(path39.join(kiroDir, "code-intel.md"), block, "code-intel.md");
|
|
14501
14957
|
}
|
|
14502
14958
|
function buildBlock(projectName, stats, skills) {
|
|
@@ -14523,6 +14979,14 @@ ${skills.map((s) => `- Working in **${s.label}**? \u2192 Load \`.claude/skills/c
|
|
|
14523
14979
|
> \u26A0 This section is auto-managed by \`code-intel analyze\`. Do **not** edit between the markers \u2014 your changes will be overwritten.
|
|
14524
14980
|
> Add your own notes below the \`${BLOCK_END}\` marker.
|
|
14525
14981
|
|
|
14982
|
+
<!-- TOOL POLICY: ENFORCED -->
|
|
14983
|
+
FORBIDDEN: grep, rg, find, cat, sed, ls (for symbol/code discovery)
|
|
14984
|
+
REQUIRED BEFORE ANY CODE ACTION:
|
|
14985
|
+
1. code-intel search "<concept>" \u2190 replaces grep/rg/find
|
|
14986
|
+
2. code-intel inspect <symbol> \u2190 replaces cat + manual file reading
|
|
14987
|
+
3. code-intel impact <symbol> \u2190 replaces manual caller tracing
|
|
14988
|
+
<!-- /TOOL POLICY -->
|
|
14989
|
+
|
|
14526
14990
|
Indexed: **${stats.nodes.toLocaleString()} nodes** | **${stats.edges.toLocaleString()} edges** | **${stats.files} files** | analyzed in ${(stats.duration / 1e3).toFixed(1)}s
|
|
14527
14991
|
|
|
14528
14992
|
> Index stale? Re-run: \`code-intel analyze\`
|
|
@@ -14558,7 +15022,9 @@ These rules apply to **every coding agent or AI assistant** working in this repo
|
|
|
14558
15022
|
## Never Do
|
|
14559
15023
|
|
|
14560
15024
|
- NEVER ignore impact warnings \u2014 always report blast radius to the user.
|
|
14561
|
-
-
|
|
15025
|
+
- **STOP** \u2014 do not call grep, rg, find, cat, sed, or read a file cold.
|
|
15026
|
+
Always run \`code-intel search "<concept>"\` first.
|
|
15027
|
+
Violating this wastes ~3,000 tokens per lookup and degrades session quality.
|
|
14562
15028
|
- NEVER make changes to a symbol with \u2265 5 callers without running \`code-intel impact\` first.
|
|
14563
15029
|
- NEVER use find-and-replace for symbol renames.
|
|
14564
15030
|
|
|
@@ -14634,7 +15100,7 @@ ${skillTable}
|
|
|
14634
15100
|
${BLOCK_END}`;
|
|
14635
15101
|
}
|
|
14636
15102
|
function upsertFile(filePath, block, fileName) {
|
|
14637
|
-
if (!
|
|
15103
|
+
if (!fs39.existsSync(filePath)) {
|
|
14638
15104
|
const newContent = [
|
|
14639
15105
|
`# ${fileName}`,
|
|
14640
15106
|
"",
|
|
@@ -14645,17 +15111,17 @@ function upsertFile(filePath, block, fileName) {
|
|
|
14645
15111
|
"<!-- Add your own custom notes below this line. They will never be overwritten by code-intel. -->",
|
|
14646
15112
|
""
|
|
14647
15113
|
].join("\n");
|
|
14648
|
-
|
|
15114
|
+
fs39.writeFileSync(filePath, newContent, "utf-8");
|
|
14649
15115
|
return;
|
|
14650
15116
|
}
|
|
14651
|
-
const existing =
|
|
15117
|
+
const existing = fs39.readFileSync(filePath, "utf-8");
|
|
14652
15118
|
const startIdx = findLineMarker(existing, BLOCK_START);
|
|
14653
15119
|
const endIdx = findLineMarker(existing, BLOCK_END, startIdx === -1 ? 0 : startIdx);
|
|
14654
15120
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
14655
15121
|
const before = existing.slice(0, startIdx);
|
|
14656
15122
|
const after = existing.slice(endIdx + BLOCK_END.length);
|
|
14657
15123
|
const updated = (before + block + after).trimEnd() + "\n";
|
|
14658
|
-
|
|
15124
|
+
fs39.writeFileSync(filePath, updated, "utf-8");
|
|
14659
15125
|
return;
|
|
14660
15126
|
}
|
|
14661
15127
|
const appended = [
|
|
@@ -14668,7 +15134,7 @@ function upsertFile(filePath, block, fileName) {
|
|
|
14668
15134
|
block,
|
|
14669
15135
|
""
|
|
14670
15136
|
].join("\n");
|
|
14671
|
-
|
|
15137
|
+
fs39.writeFileSync(filePath, appended, "utf-8");
|
|
14672
15138
|
}
|
|
14673
15139
|
function findLineMarker(content, marker, startFrom = 0) {
|
|
14674
15140
|
let idx = content.indexOf(marker, startFrom);
|
|
@@ -14717,7 +15183,7 @@ function filterChangedByMtime(allFilePaths, workspaceRoot, storedMtimes) {
|
|
|
14717
15183
|
continue;
|
|
14718
15184
|
}
|
|
14719
15185
|
try {
|
|
14720
|
-
const { mtimeMs } =
|
|
15186
|
+
const { mtimeMs } = fs39.statSync(absPath);
|
|
14721
15187
|
if (mtimeMs > stored) changed.push(absPath);
|
|
14722
15188
|
} catch {
|
|
14723
15189
|
changed.push(absPath);
|
|
@@ -14729,7 +15195,7 @@ function buildMtimeSnapshot(filePaths, workspaceRoot) {
|
|
|
14729
15195
|
const snap = {};
|
|
14730
15196
|
for (const absPath of filePaths) {
|
|
14731
15197
|
try {
|
|
14732
|
-
const { mtimeMs } =
|
|
15198
|
+
const { mtimeMs } = fs39.statSync(absPath);
|
|
14733
15199
|
snap[path39.relative(workspaceRoot, absPath)] = mtimeMs;
|
|
14734
15200
|
} catch {
|
|
14735
15201
|
}
|
|
@@ -14770,10 +15236,10 @@ function expandGlob(root, pattern) {
|
|
|
14770
15236
|
const parts = pattern.replace(/\/\*\*?$/, "").split("/").filter(Boolean);
|
|
14771
15237
|
if (parts.length === 0) return [];
|
|
14772
15238
|
const dir = path39.join(root, ...parts);
|
|
14773
|
-
if (!
|
|
14774
|
-
return
|
|
15239
|
+
if (!fs39.existsSync(dir)) return [];
|
|
15240
|
+
return fs39.readdirSync(dir).map((entry) => path39.join(dir, entry)).filter((p) => {
|
|
14775
15241
|
try {
|
|
14776
|
-
return
|
|
15242
|
+
return fs39.statSync(p).isDirectory();
|
|
14777
15243
|
} catch {
|
|
14778
15244
|
return false;
|
|
14779
15245
|
}
|
|
@@ -14785,9 +15251,9 @@ function resolvePackages(root, patterns) {
|
|
|
14785
15251
|
const dirs = expandGlob(root, pattern);
|
|
14786
15252
|
for (const dir of dirs) {
|
|
14787
15253
|
const pkgJsonPath = path39.join(dir, "package.json");
|
|
14788
|
-
if (!
|
|
15254
|
+
if (!fs39.existsSync(pkgJsonPath)) continue;
|
|
14789
15255
|
try {
|
|
14790
|
-
const pkgJson = JSON.parse(
|
|
15256
|
+
const pkgJson = JSON.parse(fs39.readFileSync(pkgJsonPath, "utf-8"));
|
|
14791
15257
|
const name = pkgJson.name ?? path39.basename(dir);
|
|
14792
15258
|
packages.push({ name, path: dir });
|
|
14793
15259
|
} catch {
|
|
@@ -14798,12 +15264,12 @@ function resolvePackages(root, patterns) {
|
|
|
14798
15264
|
}
|
|
14799
15265
|
async function detectWorkspace(root) {
|
|
14800
15266
|
const turboJsonPath = path39.join(root, "turbo.json");
|
|
14801
|
-
if (
|
|
15267
|
+
if (fs39.existsSync(turboJsonPath)) {
|
|
14802
15268
|
let patterns = [];
|
|
14803
15269
|
const pkgJsonPath = path39.join(root, "package.json");
|
|
14804
|
-
if (
|
|
15270
|
+
if (fs39.existsSync(pkgJsonPath)) {
|
|
14805
15271
|
try {
|
|
14806
|
-
const pkgJson = JSON.parse(
|
|
15272
|
+
const pkgJson = JSON.parse(fs39.readFileSync(pkgJsonPath, "utf-8"));
|
|
14807
15273
|
if (pkgJson.workspaces) {
|
|
14808
15274
|
patterns = Array.isArray(pkgJson.workspaces) ? pkgJson.workspaces : pkgJson.workspaces.packages;
|
|
14809
15275
|
}
|
|
@@ -14812,15 +15278,15 @@ async function detectWorkspace(root) {
|
|
|
14812
15278
|
}
|
|
14813
15279
|
if (patterns.length === 0) {
|
|
14814
15280
|
const fallbackDir = path39.join(root, "packages");
|
|
14815
|
-
if (
|
|
15281
|
+
if (fs39.existsSync(fallbackDir)) patterns = ["packages/*"];
|
|
14816
15282
|
}
|
|
14817
15283
|
const packages = resolvePackages(root, patterns);
|
|
14818
15284
|
return { type: "turborepo", root, packages };
|
|
14819
15285
|
}
|
|
14820
15286
|
const npmPkgJsonPath = path39.join(root, "package.json");
|
|
14821
|
-
if (
|
|
15287
|
+
if (fs39.existsSync(npmPkgJsonPath)) {
|
|
14822
15288
|
try {
|
|
14823
|
-
const pkgJson = JSON.parse(
|
|
15289
|
+
const pkgJson = JSON.parse(fs39.readFileSync(npmPkgJsonPath, "utf-8"));
|
|
14824
15290
|
if (pkgJson.workspaces) {
|
|
14825
15291
|
const patterns = Array.isArray(pkgJson.workspaces) ? pkgJson.workspaces : pkgJson.workspaces.packages;
|
|
14826
15292
|
const packages = resolvePackages(root, patterns);
|
|
@@ -14830,10 +15296,10 @@ async function detectWorkspace(root) {
|
|
|
14830
15296
|
}
|
|
14831
15297
|
}
|
|
14832
15298
|
const pnpmYamlPath = path39.join(root, "pnpm-workspace.yaml");
|
|
14833
|
-
if (
|
|
15299
|
+
if (fs39.existsSync(pnpmYamlPath)) {
|
|
14834
15300
|
const patterns = [];
|
|
14835
15301
|
try {
|
|
14836
|
-
const content =
|
|
15302
|
+
const content = fs39.readFileSync(pnpmYamlPath, "utf-8");
|
|
14837
15303
|
let inPackages = false;
|
|
14838
15304
|
for (const line of content.split("\n")) {
|
|
14839
15305
|
if (/^packages\s*:/.test(line)) {
|
|
@@ -14854,13 +15320,13 @@ async function detectWorkspace(root) {
|
|
|
14854
15320
|
return { type: "pnpm", root, packages };
|
|
14855
15321
|
}
|
|
14856
15322
|
const nxJsonPath = path39.join(root, "nx.json");
|
|
14857
|
-
if (
|
|
15323
|
+
if (fs39.existsSync(nxJsonPath)) {
|
|
14858
15324
|
const packages = [];
|
|
14859
15325
|
const scanForProjects = (dir, depth) => {
|
|
14860
15326
|
if (depth > 2) return;
|
|
14861
15327
|
let entries;
|
|
14862
15328
|
try {
|
|
14863
|
-
entries =
|
|
15329
|
+
entries = fs39.readdirSync(dir);
|
|
14864
15330
|
} catch {
|
|
14865
15331
|
return;
|
|
14866
15332
|
}
|
|
@@ -14868,14 +15334,14 @@ async function detectWorkspace(root) {
|
|
|
14868
15334
|
if (entry === "node_modules" || entry.startsWith(".")) continue;
|
|
14869
15335
|
const fullPath = path39.join(dir, entry);
|
|
14870
15336
|
try {
|
|
14871
|
-
if (!
|
|
15337
|
+
if (!fs39.statSync(fullPath).isDirectory()) continue;
|
|
14872
15338
|
} catch {
|
|
14873
15339
|
continue;
|
|
14874
15340
|
}
|
|
14875
15341
|
const projectJsonPath = path39.join(fullPath, "project.json");
|
|
14876
|
-
if (
|
|
15342
|
+
if (fs39.existsSync(projectJsonPath)) {
|
|
14877
15343
|
try {
|
|
14878
|
-
const proj = JSON.parse(
|
|
15344
|
+
const proj = JSON.parse(fs39.readFileSync(projectJsonPath, "utf-8"));
|
|
14879
15345
|
const name = proj.name ?? path39.basename(fullPath);
|
|
14880
15346
|
packages.push({ name, path: fullPath });
|
|
14881
15347
|
} catch {
|
|
@@ -14984,9 +15450,9 @@ var MigrationRunner = class {
|
|
|
14984
15450
|
autoBackupBeforeMigration() {
|
|
14985
15451
|
try {
|
|
14986
15452
|
const dbFile = this.db.name;
|
|
14987
|
-
if (!dbFile || !
|
|
15453
|
+
if (!dbFile || !fs39.existsSync(dbFile)) return;
|
|
14988
15454
|
const backupDir = path39.join(os13.homedir(), ".code-intel", "backups", "pre-migration");
|
|
14989
|
-
|
|
15455
|
+
fs39.mkdirSync(backupDir, { recursive: true });
|
|
14990
15456
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
14991
15457
|
const baseName = path39.basename(dbFile, ".db");
|
|
14992
15458
|
const backupPath = path39.join(backupDir, `${baseName}-pre-migration-${ts}.db`);
|
|
@@ -14994,7 +15460,7 @@ var MigrationRunner = class {
|
|
|
14994
15460
|
this.db.pragma("wal_checkpoint(TRUNCATE)");
|
|
14995
15461
|
} catch {
|
|
14996
15462
|
}
|
|
14997
|
-
|
|
15463
|
+
fs39.copyFileSync(dbFile, backupPath);
|
|
14998
15464
|
} catch {
|
|
14999
15465
|
}
|
|
15000
15466
|
}
|
|
@@ -15144,7 +15610,7 @@ init_codes();
|
|
|
15144
15610
|
var GLOBAL_DIR3 = path39.join(os13.homedir(), ".code-intel");
|
|
15145
15611
|
function loadRepoPaths() {
|
|
15146
15612
|
try {
|
|
15147
|
-
const data =
|
|
15613
|
+
const data = fs39.readFileSync(path39.join(GLOBAL_DIR3, "repos.json"), "utf-8");
|
|
15148
15614
|
const repos = JSON.parse(data);
|
|
15149
15615
|
return repos.map((r) => r.path);
|
|
15150
15616
|
} catch {
|
|
@@ -15154,7 +15620,7 @@ function loadRepoPaths() {
|
|
|
15154
15620
|
function loadGroupNames() {
|
|
15155
15621
|
const groupsDir = path39.join(GLOBAL_DIR3, "groups");
|
|
15156
15622
|
try {
|
|
15157
|
-
return
|
|
15623
|
+
return fs39.readdirSync(groupsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
15158
15624
|
} catch {
|
|
15159
15625
|
return [];
|
|
15160
15626
|
}
|
|
@@ -15417,8 +15883,8 @@ function autoInstallCompletion() {
|
|
|
15417
15883
|
if (shell === "fish") {
|
|
15418
15884
|
const dir = path39.join(os13.homedir(), ".config", "fish", "completions");
|
|
15419
15885
|
const dest = path39.join(dir, "code-intel.fish");
|
|
15420
|
-
|
|
15421
|
-
|
|
15886
|
+
fs39.mkdirSync(dir, { recursive: true });
|
|
15887
|
+
fs39.writeFileSync(dest, fishCompletion(), "utf-8");
|
|
15422
15888
|
console.log(` \u2705 Fish completion installed \u2192 ${dest}
|
|
15423
15889
|
`);
|
|
15424
15890
|
return;
|
|
@@ -15430,13 +15896,13 @@ source <(code-intel completion bash)
|
|
|
15430
15896
|
`;
|
|
15431
15897
|
const rcFile = shell === "zsh" ? path39.join(os13.homedir(), ".zshrc") : path39.join(os13.homedir(), ".bashrc");
|
|
15432
15898
|
try {
|
|
15433
|
-
const existing =
|
|
15899
|
+
const existing = fs39.existsSync(rcFile) ? fs39.readFileSync(rcFile, "utf-8") : "";
|
|
15434
15900
|
if (existing.includes("code-intel completion")) {
|
|
15435
15901
|
console.log(` \u2139 Completion already configured in ${rcFile}
|
|
15436
15902
|
`);
|
|
15437
15903
|
return;
|
|
15438
15904
|
}
|
|
15439
|
-
|
|
15905
|
+
fs39.appendFileSync(rcFile, script, "utf-8");
|
|
15440
15906
|
console.log(` \u2705 ${shell} completion added to ${rcFile}`);
|
|
15441
15907
|
console.log(` Restart your shell or run: source ${rcFile}
|
|
15442
15908
|
`);
|
|
@@ -15461,14 +15927,14 @@ var PACKAGE_NAME = "code-intel";
|
|
|
15461
15927
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
15462
15928
|
function loadMeta() {
|
|
15463
15929
|
try {
|
|
15464
|
-
return JSON.parse(
|
|
15930
|
+
return JSON.parse(fs39.readFileSync(META_PATH, "utf-8"));
|
|
15465
15931
|
} catch {
|
|
15466
15932
|
return null;
|
|
15467
15933
|
}
|
|
15468
15934
|
}
|
|
15469
15935
|
function saveMeta(meta) {
|
|
15470
|
-
|
|
15471
|
-
|
|
15936
|
+
fs39.mkdirSync(GLOBAL_DIR4, { recursive: true });
|
|
15937
|
+
fs39.writeFileSync(META_PATH, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
15472
15938
|
}
|
|
15473
15939
|
function isNewer(current, candidate) {
|
|
15474
15940
|
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
@@ -15688,14 +16154,37 @@ program.name("code-intel").description("Code Intelligence Platform \u2014 Static
|
|
|
15688
16154
|
|
|
15689
16155
|
Docs: https://github.com/vohongtho/code-intel-platform
|
|
15690
16156
|
`);
|
|
16157
|
+
function ensureGitignore(workspaceRoot, silent) {
|
|
16158
|
+
const gitignorePath = path39.join(workspaceRoot, ".gitignore");
|
|
16159
|
+
const entry = ".code-intel/";
|
|
16160
|
+
try {
|
|
16161
|
+
let existing = "";
|
|
16162
|
+
if (fs39.existsSync(gitignorePath)) {
|
|
16163
|
+
existing = fs39.readFileSync(gitignorePath, "utf-8");
|
|
16164
|
+
}
|
|
16165
|
+
const lines = existing.split("\n").map((l) => l.trim());
|
|
16166
|
+
if (lines.includes(".code-intel/") || lines.includes(".code-intel")) {
|
|
16167
|
+
return;
|
|
16168
|
+
}
|
|
16169
|
+
const suffix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
16170
|
+
fs39.appendFileSync(gitignorePath, `${suffix}${entry}
|
|
16171
|
+
`, "utf-8");
|
|
16172
|
+
logger_default.info(".gitignore updated: added .code-intel/");
|
|
16173
|
+
if (!silent) {
|
|
16174
|
+
console.log(` \u2713 .gitignore: added .code-intel/`);
|
|
16175
|
+
}
|
|
16176
|
+
} catch (err) {
|
|
16177
|
+
logger_default.warn(`.gitignore update failed: ${err instanceof Error ? err.message : err}`);
|
|
16178
|
+
}
|
|
16179
|
+
}
|
|
15691
16180
|
async function analyzeWorkspace(targetPath, options) {
|
|
15692
16181
|
const workspaceRoot = path39.resolve(targetPath);
|
|
15693
|
-
if (!
|
|
16182
|
+
if (!fs39.existsSync(workspaceRoot)) {
|
|
15694
16183
|
logger_default.error(`Path does not exist: ${workspaceRoot}`);
|
|
15695
16184
|
console.error(` \u2717 Path does not exist: ${workspaceRoot}`);
|
|
15696
16185
|
process.exit(1);
|
|
15697
16186
|
}
|
|
15698
|
-
if (!
|
|
16187
|
+
if (!fs39.statSync(workspaceRoot).isDirectory()) {
|
|
15699
16188
|
logger_default.error(`Path is not a directory: ${workspaceRoot}`);
|
|
15700
16189
|
console.error(` \u2717 Path is not a directory: ${workspaceRoot}`);
|
|
15701
16190
|
process.exit(1);
|
|
@@ -15720,14 +16209,14 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
15720
16209
|
];
|
|
15721
16210
|
for (const f of wipeFiles) {
|
|
15722
16211
|
try {
|
|
15723
|
-
if (
|
|
16212
|
+
if (fs39.existsSync(f)) fs39.unlinkSync(f);
|
|
15724
16213
|
} catch {
|
|
15725
16214
|
}
|
|
15726
16215
|
}
|
|
15727
16216
|
}
|
|
15728
16217
|
if (!options?.skipGit) {
|
|
15729
16218
|
const gitDir = path39.join(workspaceRoot, ".git");
|
|
15730
|
-
if (!
|
|
16219
|
+
if (!fs39.existsSync(gitDir)) {
|
|
15731
16220
|
logger_default.warn(`${workspaceRoot} is not a Git repository`);
|
|
15732
16221
|
}
|
|
15733
16222
|
}
|
|
@@ -15866,8 +16355,8 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
15866
16355
|
};
|
|
15867
16356
|
const profilePath = path39.join(workspaceRoot, ".code-intel", "profile.json");
|
|
15868
16357
|
try {
|
|
15869
|
-
|
|
15870
|
-
|
|
16358
|
+
fs39.mkdirSync(path39.join(workspaceRoot, ".code-intel"), { recursive: true });
|
|
16359
|
+
fs39.writeFileSync(profilePath, JSON.stringify(profileJson, null, 2));
|
|
15871
16360
|
if (!options?.silent) console.log(` \u2713 Profile written: ${profilePath}`);
|
|
15872
16361
|
} catch (err) {
|
|
15873
16362
|
logger_default.warn(`Failed to write profile.json: ${err instanceof Error ? err.message : err}`);
|
|
@@ -15894,7 +16383,7 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
15894
16383
|
}
|
|
15895
16384
|
if (isIncremental && incrementalChangedFiles && incrementalChangedFiles.length > 0) {
|
|
15896
16385
|
const dbPath = getDbPath(workspaceRoot);
|
|
15897
|
-
if (
|
|
16386
|
+
if (fs39.existsSync(dbPath)) {
|
|
15898
16387
|
try {
|
|
15899
16388
|
const db = new DbManager(dbPath);
|
|
15900
16389
|
await db.init();
|
|
@@ -15967,7 +16456,7 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
15967
16456
|
];
|
|
15968
16457
|
for (const f of newStaleFiles) {
|
|
15969
16458
|
try {
|
|
15970
|
-
if (
|
|
16459
|
+
if (fs39.existsSync(f)) fs39.unlinkSync(f);
|
|
15971
16460
|
} catch {
|
|
15972
16461
|
}
|
|
15973
16462
|
}
|
|
@@ -15984,21 +16473,21 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
15984
16473
|
];
|
|
15985
16474
|
for (const f of staleFiles) {
|
|
15986
16475
|
try {
|
|
15987
|
-
if (
|
|
16476
|
+
if (fs39.existsSync(f)) fs39.unlinkSync(f);
|
|
15988
16477
|
} catch {
|
|
15989
16478
|
}
|
|
15990
16479
|
}
|
|
15991
16480
|
for (const f of newStaleFiles) {
|
|
15992
16481
|
if (f === dbPathNew) continue;
|
|
15993
|
-
if (
|
|
16482
|
+
if (fs39.existsSync(f)) {
|
|
15994
16483
|
const dest = f.replace(dbPathNew, dbPath);
|
|
15995
16484
|
try {
|
|
15996
|
-
|
|
16485
|
+
fs39.renameSync(f, dest);
|
|
15997
16486
|
} catch {
|
|
15998
16487
|
}
|
|
15999
16488
|
}
|
|
16000
16489
|
}
|
|
16001
|
-
|
|
16490
|
+
fs39.renameSync(dbPathNew, dbPath);
|
|
16002
16491
|
stopSpinner();
|
|
16003
16492
|
logger_default.info(`DB persisted: ${nodeCount} nodes, ${edgeCount} edges`);
|
|
16004
16493
|
if (!options?.silent) {
|
|
@@ -16030,7 +16519,7 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
16030
16519
|
const staleVdb = [vdbPath, `${vdbPath}-shm`, `${vdbPath}-wal`];
|
|
16031
16520
|
for (const f of staleVdb) {
|
|
16032
16521
|
try {
|
|
16033
|
-
if (
|
|
16522
|
+
if (fs39.existsSync(f)) fs39.unlinkSync(f);
|
|
16034
16523
|
} catch {
|
|
16035
16524
|
}
|
|
16036
16525
|
}
|
|
@@ -16093,6 +16582,7 @@ async function analyzeWorkspace(targetPath, options) {
|
|
|
16093
16582
|
logger_default.warn(`Context file write failed: ${err instanceof Error ? err.message : err}`);
|
|
16094
16583
|
}
|
|
16095
16584
|
}
|
|
16585
|
+
ensureGitignore(workspaceRoot, options?.silent ?? false);
|
|
16096
16586
|
if (!options?.silent) {
|
|
16097
16587
|
const dur = result.totalDuration;
|
|
16098
16588
|
const durStr = dur >= 1e3 ? `${(dur / 1e3).toFixed(1)}s` : `${dur}ms`;
|
|
@@ -16247,8 +16737,8 @@ program.command("setup").description("Configure MCP server for your editors (one
|
|
|
16247
16737
|
const configFile = `${configDir}/claude_desktop_config.json`;
|
|
16248
16738
|
try {
|
|
16249
16739
|
let existing = {};
|
|
16250
|
-
if (
|
|
16251
|
-
existing = JSON.parse(
|
|
16740
|
+
if (fs39.existsSync(configFile)) {
|
|
16741
|
+
existing = JSON.parse(fs39.readFileSync(configFile, "utf-8"));
|
|
16252
16742
|
}
|
|
16253
16743
|
const merged = {
|
|
16254
16744
|
...existing,
|
|
@@ -16257,8 +16747,8 @@ program.command("setup").description("Configure MCP server for your editors (one
|
|
|
16257
16747
|
...mcpConfig.mcpServers
|
|
16258
16748
|
}
|
|
16259
16749
|
};
|
|
16260
|
-
|
|
16261
|
-
|
|
16750
|
+
fs39.mkdirSync(configDir, { recursive: true });
|
|
16751
|
+
fs39.writeFileSync(configFile, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
16262
16752
|
console.log(`
|
|
16263
16753
|
\u2705 Written to ${configFile}`);
|
|
16264
16754
|
} catch (err) {
|
|
@@ -16360,11 +16850,11 @@ program.command("mcp").description("Start MCP server over stdio \u2014 exposes a
|
|
|
16360
16850
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16361
16851
|
const repoName = path39.basename(workspaceRoot);
|
|
16362
16852
|
const dbPath = getDbPath(workspaceRoot);
|
|
16363
|
-
if (!
|
|
16853
|
+
if (!fs39.existsSync(workspaceRoot) || !fs39.statSync(workspaceRoot).isDirectory()) {
|
|
16364
16854
|
console.error(` \u2717 Path does not exist: ${workspaceRoot}`);
|
|
16365
16855
|
process.exit(1);
|
|
16366
16856
|
}
|
|
16367
|
-
const existingIndex =
|
|
16857
|
+
const existingIndex = fs39.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
|
|
16368
16858
|
if (existingIndex) {
|
|
16369
16859
|
const graph = createKnowledgeGraph();
|
|
16370
16860
|
const db = new DbManager(dbPath, true);
|
|
@@ -16398,11 +16888,11 @@ program.command("serve").description("Start the local HTTP server + web UI for g
|
|
|
16398
16888
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16399
16889
|
const repoName = path39.basename(workspaceRoot);
|
|
16400
16890
|
const dbPath = getDbPath(workspaceRoot);
|
|
16401
|
-
if (!
|
|
16891
|
+
if (!fs39.existsSync(workspaceRoot) || !fs39.statSync(workspaceRoot).isDirectory()) {
|
|
16402
16892
|
console.error(` \u2717 Path does not exist: ${workspaceRoot}`);
|
|
16403
16893
|
process.exit(1);
|
|
16404
16894
|
}
|
|
16405
|
-
const existingIndex = !options.force &&
|
|
16895
|
+
const existingIndex = !options.force && fs39.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
|
|
16406
16896
|
if (existingIndex) {
|
|
16407
16897
|
const meta = loadMetadata(workspaceRoot);
|
|
16408
16898
|
if (meta.parser === "regex" || meta.parser === void 0) {
|
|
@@ -16440,11 +16930,11 @@ program.command("watch").description("Start HTTP server + file watcher (auto-rei
|
|
|
16440
16930
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16441
16931
|
const repoName = path39.basename(workspaceRoot);
|
|
16442
16932
|
const dbPath = getDbPath(workspaceRoot);
|
|
16443
|
-
if (!
|
|
16933
|
+
if (!fs39.existsSync(workspaceRoot) || !fs39.statSync(workspaceRoot).isDirectory()) {
|
|
16444
16934
|
console.error(` \u2717 Path does not exist: ${workspaceRoot}`);
|
|
16445
16935
|
process.exit(1);
|
|
16446
16936
|
}
|
|
16447
|
-
const existingIndex = !options.force &&
|
|
16937
|
+
const existingIndex = !options.force && fs39.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
|
|
16448
16938
|
let graph;
|
|
16449
16939
|
if (existingIndex) {
|
|
16450
16940
|
const meta = loadMetadata(workspaceRoot);
|
|
@@ -16564,16 +17054,16 @@ function trashDirName(repoPath) {
|
|
|
16564
17054
|
}
|
|
16565
17055
|
function softDeleteCodeIntel(repoPath) {
|
|
16566
17056
|
const codeIntelDir = path39.join(repoPath, ".code-intel");
|
|
16567
|
-
if (!
|
|
17057
|
+
if (!fs39.existsSync(codeIntelDir)) return;
|
|
16568
17058
|
const trashName = trashDirName();
|
|
16569
17059
|
const trashDir = path39.join(repoPath, trashName);
|
|
16570
17060
|
let dest = trashDir;
|
|
16571
17061
|
let counter = 1;
|
|
16572
|
-
while (
|
|
17062
|
+
while (fs39.existsSync(dest)) {
|
|
16573
17063
|
dest = `${trashDir}-${counter++}`;
|
|
16574
17064
|
}
|
|
16575
|
-
|
|
16576
|
-
|
|
17065
|
+
fs39.renameSync(codeIntelDir, dest);
|
|
17066
|
+
fs39.writeFileSync(
|
|
16577
17067
|
path39.join(dest, "TRASH_META.json"),
|
|
16578
17068
|
JSON.stringify({ deletedAt: (/* @__PURE__ */ new Date()).toISOString(), repoPath, permanent: false }, null, 2)
|
|
16579
17069
|
);
|
|
@@ -16583,15 +17073,15 @@ function softDeleteCodeIntel(repoPath) {
|
|
|
16583
17073
|
function purgeStaleTrashes(repoPath) {
|
|
16584
17074
|
const cutoff = Date.now() - TRASH_TTL_DAYS * 24 * 60 * 60 * 1e3;
|
|
16585
17075
|
try {
|
|
16586
|
-
for (const entry of
|
|
17076
|
+
for (const entry of fs39.readdirSync(repoPath)) {
|
|
16587
17077
|
if (!entry.startsWith(".code-intel-trash-")) continue;
|
|
16588
17078
|
const fullPath = path39.join(repoPath, entry);
|
|
16589
17079
|
const metaPath = path39.join(fullPath, "TRASH_META.json");
|
|
16590
|
-
if (
|
|
17080
|
+
if (fs39.existsSync(metaPath)) {
|
|
16591
17081
|
try {
|
|
16592
|
-
const meta = JSON.parse(
|
|
17082
|
+
const meta = JSON.parse(fs39.readFileSync(metaPath, "utf-8"));
|
|
16593
17083
|
if (new Date(meta.deletedAt).getTime() < cutoff) {
|
|
16594
|
-
|
|
17084
|
+
fs39.rmSync(fullPath, { recursive: true, force: true });
|
|
16595
17085
|
console.log(` \u2713 Auto-purged stale trash: ${fullPath}`);
|
|
16596
17086
|
}
|
|
16597
17087
|
} catch {
|
|
@@ -16618,18 +17108,18 @@ program.command("clean").description("Soft-delete the knowledge graph index for
|
|
|
16618
17108
|
if (opts.dryRun) {
|
|
16619
17109
|
const showDryRun = (repoPath) => {
|
|
16620
17110
|
const codeIntelDir = path39.join(path39.resolve(repoPath), ".code-intel");
|
|
16621
|
-
if (!
|
|
17111
|
+
if (!fs39.existsSync(codeIntelDir)) {
|
|
16622
17112
|
console.log(` (no .code-intel/ found at ${repoPath})`);
|
|
16623
17113
|
return;
|
|
16624
17114
|
}
|
|
16625
17115
|
let totalBytes = 0;
|
|
16626
17116
|
const countDir = (dir) => {
|
|
16627
17117
|
try {
|
|
16628
|
-
for (const entry of
|
|
17118
|
+
for (const entry of fs39.readdirSync(dir, { withFileTypes: true })) {
|
|
16629
17119
|
const full = path39.join(dir, entry.name);
|
|
16630
17120
|
if (entry.isDirectory()) countDir(full);
|
|
16631
17121
|
else try {
|
|
16632
|
-
totalBytes +=
|
|
17122
|
+
totalBytes += fs39.statSync(full).size;
|
|
16633
17123
|
} catch {
|
|
16634
17124
|
}
|
|
16635
17125
|
}
|
|
@@ -16666,14 +17156,14 @@ program.command("clean").description("Soft-delete the knowledge graph index for
|
|
|
16666
17156
|
let found = 0;
|
|
16667
17157
|
for (const root of roots) {
|
|
16668
17158
|
try {
|
|
16669
|
-
for (const entry of
|
|
17159
|
+
for (const entry of fs39.readdirSync(root)) {
|
|
16670
17160
|
if (!entry.startsWith(".code-intel-trash-")) continue;
|
|
16671
17161
|
const fullPath = path39.join(root, entry);
|
|
16672
17162
|
const metaPath = path39.join(fullPath, "TRASH_META.json");
|
|
16673
17163
|
let deletedAt = "unknown";
|
|
16674
|
-
if (
|
|
17164
|
+
if (fs39.existsSync(metaPath)) {
|
|
16675
17165
|
try {
|
|
16676
|
-
deletedAt = JSON.parse(
|
|
17166
|
+
deletedAt = JSON.parse(fs39.readFileSync(metaPath, "utf-8")).deletedAt;
|
|
16677
17167
|
} catch {
|
|
16678
17168
|
}
|
|
16679
17169
|
}
|
|
@@ -16702,8 +17192,8 @@ program.command("clean").description("Soft-delete the knowledge graph index for
|
|
|
16702
17192
|
for (const r of repos) {
|
|
16703
17193
|
if (opts.purge) {
|
|
16704
17194
|
const codeIntelDir = path39.join(r.path, ".code-intel");
|
|
16705
|
-
if (
|
|
16706
|
-
|
|
17195
|
+
if (fs39.existsSync(codeIntelDir)) {
|
|
17196
|
+
fs39.rmSync(codeIntelDir, { recursive: true, force: true });
|
|
16707
17197
|
console.log(` \u2713 Hard-deleted ${codeIntelDir}`);
|
|
16708
17198
|
}
|
|
16709
17199
|
} else {
|
|
@@ -16720,8 +17210,8 @@ program.command("clean").description("Soft-delete the knowledge graph index for
|
|
|
16720
17210
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16721
17211
|
if (opts.purge) {
|
|
16722
17212
|
const codeIntelDir = path39.join(workspaceRoot, ".code-intel");
|
|
16723
|
-
if (
|
|
16724
|
-
|
|
17213
|
+
if (fs39.existsSync(codeIntelDir)) {
|
|
17214
|
+
fs39.rmSync(codeIntelDir, { recursive: true, force: true });
|
|
16725
17215
|
console.log(`
|
|
16726
17216
|
\u2713 Hard-deleted ${codeIntelDir}`);
|
|
16727
17217
|
}
|
|
@@ -16735,7 +17225,7 @@ program.command("clean").description("Soft-delete the knowledge graph index for
|
|
|
16735
17225
|
async function loadOrAnalyzeWorkspace(targetPath) {
|
|
16736
17226
|
const workspaceRoot = path39.resolve(targetPath);
|
|
16737
17227
|
const dbPath = getDbPath(workspaceRoot);
|
|
16738
|
-
const existingIndex =
|
|
17228
|
+
const existingIndex = fs39.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
|
|
16739
17229
|
if (existingIndex) {
|
|
16740
17230
|
const graph = createKnowledgeGraph();
|
|
16741
17231
|
const db = new DbManager(dbPath, true);
|
|
@@ -17223,7 +17713,7 @@ groupCmd.command("status <name>").description("Check index freshness and sync st
|
|
|
17223
17713
|
}
|
|
17224
17714
|
const metaPath = path39.join(regEntry.path, ".code-intel", "meta.json");
|
|
17225
17715
|
try {
|
|
17226
|
-
const meta = JSON.parse(
|
|
17716
|
+
const meta = JSON.parse(fs39.readFileSync(metaPath, "utf-8"));
|
|
17227
17717
|
const indexedAt = meta.indexedAt;
|
|
17228
17718
|
const ageMin = Math.round((now - new Date(indexedAt).getTime()) / 6e4);
|
|
17229
17719
|
const stale = ageMin > 1440 ? " \u26A0 STALE (>24h)" : "";
|
|
@@ -17557,7 +18047,7 @@ backupCmd.command("list").description("List all available backups").action(() =>
|
|
|
17557
18047
|
Backups (${entries.length}):
|
|
17558
18048
|
`);
|
|
17559
18049
|
for (const e of entries) {
|
|
17560
|
-
const exists =
|
|
18050
|
+
const exists = fs39.existsSync(e.path);
|
|
17561
18051
|
const status = exists ? "\u2713" : "\u2717 (missing)";
|
|
17562
18052
|
console.log(` ${status} ${e.id.slice(0, 8)} ${e.createdAt} ${(e.size / 1024).toFixed(1)} KB \u2192 ${e.repoPath}`);
|
|
17563
18053
|
}
|
|
@@ -17590,7 +18080,7 @@ program.command("migrate").description("Manage database schema migrations").opti
|
|
|
17590
18080
|
$ code-intel migrate --rollback
|
|
17591
18081
|
`).action((opts) => {
|
|
17592
18082
|
const dbPath = opts.db ?? path39.join(os13.homedir(), ".code-intel", "users.db");
|
|
17593
|
-
if (!
|
|
18083
|
+
if (!fs39.existsSync(dbPath)) {
|
|
17594
18084
|
console.error(`
|
|
17595
18085
|
\u2717 Database not found: ${dbPath}
|
|
17596
18086
|
Run \`code-intel serve\` or \`code-intel user create\` first.
|
|
@@ -17712,8 +18202,8 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
|
|
|
17712
18202
|
server: serverUrl,
|
|
17713
18203
|
storedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
17714
18204
|
};
|
|
17715
|
-
|
|
17716
|
-
|
|
18205
|
+
fs39.mkdirSync(path39.dirname(tokenPath), { recursive: true });
|
|
18206
|
+
fs39.writeFileSync(tokenPath, JSON.stringify(tokenData, null, 2), { mode: 384 });
|
|
17717
18207
|
console.log(` \u2705 Authenticated successfully!`);
|
|
17718
18208
|
console.log(` Token stored at: ${tokenPath}`);
|
|
17719
18209
|
console.log(` Use CODE_INTEL_TOKEN env var or --token flag to use it with CLI/MCP.
|
|
@@ -17727,12 +18217,12 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
|
|
|
17727
18217
|
});
|
|
17728
18218
|
authCmd.command("status").description("Show the current OIDC authentication status").action(() => {
|
|
17729
18219
|
const tokenPath = path39.join(os13.homedir(), ".code-intel", "oidc-token.json");
|
|
17730
|
-
if (!
|
|
18220
|
+
if (!fs39.existsSync(tokenPath)) {
|
|
17731
18221
|
console.log("\n Not authenticated via OIDC. Run: code-intel auth login\n");
|
|
17732
18222
|
return;
|
|
17733
18223
|
}
|
|
17734
18224
|
try {
|
|
17735
|
-
const data = JSON.parse(
|
|
18225
|
+
const data = JSON.parse(fs39.readFileSync(tokenPath, "utf-8"));
|
|
17736
18226
|
console.log(`
|
|
17737
18227
|
\u2705 OIDC token stored`);
|
|
17738
18228
|
console.log(` Server : ${data.server ?? "unknown"}`);
|
|
@@ -17745,8 +18235,8 @@ authCmd.command("status").description("Show the current OIDC authentication stat
|
|
|
17745
18235
|
});
|
|
17746
18236
|
authCmd.command("logout").description("Remove locally stored OIDC token").action(() => {
|
|
17747
18237
|
const tokenPath = path39.join(os13.homedir(), ".code-intel", "oidc-token.json");
|
|
17748
|
-
if (
|
|
17749
|
-
|
|
18238
|
+
if (fs39.existsSync(tokenPath)) {
|
|
18239
|
+
fs39.unlinkSync(tokenPath);
|
|
17750
18240
|
console.log("\n \u2705 OIDC token removed. You are now logged out.\n");
|
|
17751
18241
|
} else {
|
|
17752
18242
|
console.log("\n No stored token found.\n");
|
|
@@ -17871,7 +18361,7 @@ program.command("config-validate <file>").description("Validate a JSON config fi
|
|
|
17871
18361
|
$ code-intel config-validate ~/.code-intel/config.json
|
|
17872
18362
|
`).action((file) => {
|
|
17873
18363
|
const filePath = path39.resolve(file);
|
|
17874
|
-
if (!
|
|
18364
|
+
if (!fs39.existsSync(filePath)) {
|
|
17875
18365
|
console.error(`
|
|
17876
18366
|
\u2717 File not found: ${filePath}
|
|
17877
18367
|
`);
|
|
@@ -17879,7 +18369,7 @@ program.command("config-validate <file>").description("Validate a JSON config fi
|
|
|
17879
18369
|
}
|
|
17880
18370
|
let cfg;
|
|
17881
18371
|
try {
|
|
17882
|
-
cfg = JSON.parse(
|
|
18372
|
+
cfg = JSON.parse(fs39.readFileSync(filePath, "utf-8"));
|
|
17883
18373
|
} catch (err) {
|
|
17884
18374
|
console.error(`
|
|
17885
18375
|
\u2717 Could not parse JSON: ${err instanceof Error ? err.message : err}
|
|
@@ -17920,7 +18410,7 @@ program.command("health").description("Run code health checks: dead code, circul
|
|
|
17920
18410
|
const workspaceRoot = path39.resolve(targetPath);
|
|
17921
18411
|
const dbPath = getDbPath(workspaceRoot);
|
|
17922
18412
|
const meta = loadMetadata(workspaceRoot);
|
|
17923
|
-
if (!meta || !
|
|
18413
|
+
if (!meta || !fs39.existsSync(dbPath)) {
|
|
17924
18414
|
console.error(`
|
|
17925
18415
|
\u2717 ${workspaceRoot} is not indexed.`);
|
|
17926
18416
|
console.error(" Run `code-intel analyze` first to build the index.\n");
|
|
@@ -18065,13 +18555,13 @@ program.command("query").description("Execute a GQL (Graph Query Language) query
|
|
|
18065
18555
|
gqlInput = content;
|
|
18066
18556
|
} else if (opts.file) {
|
|
18067
18557
|
const filePath = path39.resolve(opts.file);
|
|
18068
|
-
if (!
|
|
18558
|
+
if (!fs39.existsSync(filePath)) {
|
|
18069
18559
|
console.error(`
|
|
18070
18560
|
\u2717 File not found: ${filePath}
|
|
18071
18561
|
`);
|
|
18072
18562
|
process.exit(1);
|
|
18073
18563
|
}
|
|
18074
|
-
gqlInput =
|
|
18564
|
+
gqlInput = fs39.readFileSync(filePath, "utf-8");
|
|
18075
18565
|
} else if (gqlArg) {
|
|
18076
18566
|
gqlInput = gqlArg;
|
|
18077
18567
|
} else {
|
|
@@ -18599,7 +19089,7 @@ program.command("doctor").description("Run diagnostics \u2014 check Node.js, git
|
|
|
18599
19089
|
continue;
|
|
18600
19090
|
}
|
|
18601
19091
|
const vdbPath = getVectorDbPath2(repo.path);
|
|
18602
|
-
if (
|
|
19092
|
+
if (fs39.existsSync(vdbPath)) {
|
|
18603
19093
|
try {
|
|
18604
19094
|
const Database22 = (await import('better-sqlite3')).default;
|
|
18605
19095
|
const vdb = new Database22(vdbPath, { readonly: true, fileMustExist: true });
|
|
@@ -18636,6 +19126,75 @@ program.command("doctor").description("Run diagnostics \u2014 check Node.js, git
|
|
|
18636
19126
|
console.log(" \u2705 All checks passed.\n");
|
|
18637
19127
|
}
|
|
18638
19128
|
});
|
|
19129
|
+
program.command("context").description("Build a token-efficient context document for a set of seed symbols").argument("<symbols...>", "One or more symbol names to use as seeds").option("-p, --path <path>", "Path to the repository (default: current directory)", ".").option("-l, --limit <n>", "Max seeds to resolve (default: 10)", "10").option("--intent <intent>", "Query intent: code | callers | architecture | auto (default: auto)", "auto").option("--max-tokens <n>", "Max total tokens for output (default: 6000)", "6000").option("--show-context", "Print per-block token breakdown after output").addHelpText("after", `
|
|
19130
|
+
Builds a structured [SUMMARY] / [LOGIC] / [RELATION] / [FOCUS CODE] context
|
|
19131
|
+
document for one or more symbols. Designed to give AI assistants dense,
|
|
19132
|
+
token-efficient context instead of raw file reads.
|
|
19133
|
+
|
|
19134
|
+
Examples:
|
|
19135
|
+
$ code-intel context UserService
|
|
19136
|
+
$ code-intel context createUser login --intent callers
|
|
19137
|
+
$ code-intel context AuthService --show-context
|
|
19138
|
+
$ code-intel context handlePayment --max-tokens 3000 --intent code
|
|
19139
|
+
`).action(async (symbols, opts) => {
|
|
19140
|
+
const { graph } = await loadOrAnalyzeWorkspace(opts.path);
|
|
19141
|
+
const { build: build2, detectQueryIntent: detectQueryIntent2 } = await Promise.resolve().then(() => (init_builder(), builder_exports));
|
|
19142
|
+
const { measureBlocks: measureBlocks2 } = await Promise.resolve().then(() => (init_token_counter(), token_counter_exports));
|
|
19143
|
+
const maxSeeds = parseInt(opts.limit, 10);
|
|
19144
|
+
const maxTokens = parseInt(opts.maxTokens, 10);
|
|
19145
|
+
const intent = ["code", "callers", "architecture", "auto"].includes(opts.intent) ? opts.intent : detectQueryIntent2(opts.intent);
|
|
19146
|
+
const seeds = [];
|
|
19147
|
+
for (const symbol of symbols.slice(0, maxSeeds)) {
|
|
19148
|
+
for (const node of graph.allNodes()) {
|
|
19149
|
+
if (node.name === symbol) {
|
|
19150
|
+
seeds.push({ nodeId: node.id, refinedScore: 1 });
|
|
19151
|
+
break;
|
|
19152
|
+
}
|
|
19153
|
+
}
|
|
19154
|
+
}
|
|
19155
|
+
if (seeds.length === 0) {
|
|
19156
|
+
console.log(`
|
|
19157
|
+
No symbols found for: ${symbols.join(", ")}
|
|
19158
|
+
`);
|
|
19159
|
+
console.log(' Try: code-intel search "<name>" to find symbol names.\n');
|
|
19160
|
+
return;
|
|
19161
|
+
}
|
|
19162
|
+
const doc = build2(seeds, graph, { maxTokens, queryIntent: intent });
|
|
19163
|
+
console.log("");
|
|
19164
|
+
if (doc.summary) {
|
|
19165
|
+
console.log(doc.summary);
|
|
19166
|
+
console.log("");
|
|
19167
|
+
}
|
|
19168
|
+
if (doc.logic) {
|
|
19169
|
+
console.log(doc.logic);
|
|
19170
|
+
console.log("");
|
|
19171
|
+
}
|
|
19172
|
+
if (doc.relation) {
|
|
19173
|
+
console.log(doc.relation);
|
|
19174
|
+
console.log("");
|
|
19175
|
+
}
|
|
19176
|
+
if (doc.focusCode) {
|
|
19177
|
+
console.log(doc.focusCode);
|
|
19178
|
+
console.log("");
|
|
19179
|
+
}
|
|
19180
|
+
if (doc.truncated) {
|
|
19181
|
+
console.log(" \u26A0 Output was truncated due to token budget. Use --max-tokens to increase.");
|
|
19182
|
+
console.log("");
|
|
19183
|
+
}
|
|
19184
|
+
if (opts.showContext) {
|
|
19185
|
+
const counts = measureBlocks2(doc);
|
|
19186
|
+
console.log(" \u2500\u2500 Token breakdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
19187
|
+
console.log(` [SUMMARY] ${String(counts.summary).padStart(5)} tokens`);
|
|
19188
|
+
console.log(` [LOGIC] ${String(counts.logic).padStart(5)} tokens`);
|
|
19189
|
+
console.log(` [RELATION] ${String(counts.relation).padStart(5)} tokens`);
|
|
19190
|
+
console.log(` [FOCUS CODE] ${String(counts.focusCode).padStart(5)} tokens`);
|
|
19191
|
+
console.log(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
19192
|
+
console.log(` Total ${String(counts.total).padStart(5)} tokens (budget: ${maxTokens})`);
|
|
19193
|
+
console.log(` Intent ${doc.intent}`);
|
|
19194
|
+
console.log(` Truncated ${doc.truncated}`);
|
|
19195
|
+
console.log("");
|
|
19196
|
+
}
|
|
19197
|
+
});
|
|
18639
19198
|
program.parse();
|
|
18640
19199
|
//# sourceMappingURL=main.js.map
|
|
18641
19200
|
//# sourceMappingURL=main.js.map
|