@vohongtho.infotech/code-intel 1.0.2 → 1.0.3
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 +11 -2
- package/dist/cli/hook.js +117 -14
- package/dist/cli/hook.js.map +1 -1
- package/dist/cli/main.js +2361 -1004
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/router.js +35 -0
- package/dist/cli/router.js.map +1 -0
- package/dist/cli/search.js +244 -0
- package/dist/cli/search.js.map +1 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +452 -126
- package/dist/index.js.map +1 -1
- package/dist/web/assets/es-DOPp2DTx.js +10 -0
- package/dist/web/assets/index-BPmJG_ti.css +2 -0
- package/dist/web/assets/{index-CzWucUxe.js → index-gqp6A4LM.js} +8 -8
- package/dist/web/index.html +2 -2
- package/package.json +7 -6
- package/dist/web/assets/es-DiINqj58.js +0 -10
- package/dist/web/assets/index-D3zJQH9-.css +0 -2
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { fileURLToPath } from 'url';
|
|
|
14
14
|
import { Parser, Language, Query } from 'web-tree-sitter';
|
|
15
15
|
import { Database, Connection } from '@ladybugdb/core';
|
|
16
16
|
import { execSync } from 'child_process';
|
|
17
|
-
import
|
|
17
|
+
import Database3 from 'better-sqlite3';
|
|
18
18
|
import bcrypt from 'bcrypt';
|
|
19
19
|
import crypto5 from 'crypto';
|
|
20
20
|
import { v4 } from 'uuid';
|
|
@@ -198,6 +198,13 @@ var init_id_generator = __esm({
|
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
// src/storage/schema.ts
|
|
201
|
+
var schema_exports = {};
|
|
202
|
+
__export(schema_exports, {
|
|
203
|
+
ALL_NODE_TABLES: () => ALL_NODE_TABLES,
|
|
204
|
+
NODE_TABLE_MAP: () => NODE_TABLE_MAP,
|
|
205
|
+
getCreateEdgeTableDDL: () => getCreateEdgeTableDDL,
|
|
206
|
+
getCreateNodeTableDDL: () => getCreateNodeTableDDL
|
|
207
|
+
});
|
|
201
208
|
function getCreateNodeTableDDL(tableName) {
|
|
202
209
|
return `CREATE NODE TABLE IF NOT EXISTS ${tableName} (
|
|
203
210
|
id STRING,
|
|
@@ -2580,6 +2587,65 @@ var init_resolve_phase = __esm({
|
|
|
2580
2587
|
}
|
|
2581
2588
|
});
|
|
2582
2589
|
|
|
2590
|
+
// src/storage/db-manager.ts
|
|
2591
|
+
var db_manager_exports = {};
|
|
2592
|
+
__export(db_manager_exports, {
|
|
2593
|
+
DbManager: () => DbManager
|
|
2594
|
+
});
|
|
2595
|
+
var DbManager;
|
|
2596
|
+
var init_db_manager = __esm({
|
|
2597
|
+
"src/storage/db-manager.ts"() {
|
|
2598
|
+
DbManager = class {
|
|
2599
|
+
db = null;
|
|
2600
|
+
conn = null;
|
|
2601
|
+
dbPath;
|
|
2602
|
+
readOnly;
|
|
2603
|
+
constructor(dbPath, readOnly = false) {
|
|
2604
|
+
this.dbPath = dbPath;
|
|
2605
|
+
this.readOnly = readOnly;
|
|
2606
|
+
}
|
|
2607
|
+
async init() {
|
|
2608
|
+
if (!this.readOnly) {
|
|
2609
|
+
fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
2610
|
+
}
|
|
2611
|
+
this.db = new Database(this.dbPath, 0, true, this.readOnly);
|
|
2612
|
+
await this.db.init();
|
|
2613
|
+
this.conn = new Connection(this.db);
|
|
2614
|
+
await this.conn.init();
|
|
2615
|
+
}
|
|
2616
|
+
async query(cypher) {
|
|
2617
|
+
if (!this.conn) throw new Error("Database not initialized");
|
|
2618
|
+
const result = await this.conn.query(cypher);
|
|
2619
|
+
const qr = Array.isArray(result) ? result[0] : result;
|
|
2620
|
+
const rows = await qr.getAll();
|
|
2621
|
+
qr.close();
|
|
2622
|
+
return rows;
|
|
2623
|
+
}
|
|
2624
|
+
async execute(cypher) {
|
|
2625
|
+
if (!this.conn) throw new Error("Database not initialized");
|
|
2626
|
+
const result = await this.conn.query(cypher);
|
|
2627
|
+
const qr = Array.isArray(result) ? result[0] : result;
|
|
2628
|
+
qr.close();
|
|
2629
|
+
}
|
|
2630
|
+
close() {
|
|
2631
|
+
try {
|
|
2632
|
+
this.conn?.closeSync();
|
|
2633
|
+
} catch {
|
|
2634
|
+
}
|
|
2635
|
+
try {
|
|
2636
|
+
this.db?.closeSync();
|
|
2637
|
+
} catch {
|
|
2638
|
+
}
|
|
2639
|
+
this.conn = null;
|
|
2640
|
+
this.db = null;
|
|
2641
|
+
}
|
|
2642
|
+
get isOpen() {
|
|
2643
|
+
return this.conn !== null;
|
|
2644
|
+
}
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
});
|
|
2648
|
+
|
|
2583
2649
|
// src/llm/providers/openai.ts
|
|
2584
2650
|
var openai_exports = {};
|
|
2585
2651
|
__export(openai_exports, {
|
|
@@ -2590,18 +2656,42 @@ var init_openai = __esm({
|
|
|
2590
2656
|
"src/llm/providers/openai.ts"() {
|
|
2591
2657
|
OpenAIProvider = class {
|
|
2592
2658
|
modelName;
|
|
2593
|
-
|
|
2659
|
+
endpoint;
|
|
2660
|
+
apiKey;
|
|
2661
|
+
constructor(model, baseUrl, apiKey) {
|
|
2594
2662
|
this.modelName = model ?? "gpt-4o-mini";
|
|
2663
|
+
this.endpoint = (baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
2664
|
+
this.apiKey = apiKey ?? "";
|
|
2665
|
+
}
|
|
2666
|
+
resolvedKey() {
|
|
2667
|
+
return this.apiKey || process.env["OPENAI_API_KEY"] || "";
|
|
2668
|
+
}
|
|
2669
|
+
async getContextWindow() {
|
|
2670
|
+
try {
|
|
2671
|
+
const res = await fetch(`${this.endpoint}/models/${encodeURIComponent(this.modelName)}`, {
|
|
2672
|
+
headers: { "Authorization": `Bearer ${this.resolvedKey()}` },
|
|
2673
|
+
signal: AbortSignal.timeout(5e3)
|
|
2674
|
+
});
|
|
2675
|
+
if (!res.ok) return void 0;
|
|
2676
|
+
const json = await res.json();
|
|
2677
|
+
return json.context_window;
|
|
2678
|
+
} catch {
|
|
2679
|
+
return void 0;
|
|
2680
|
+
}
|
|
2595
2681
|
}
|
|
2596
2682
|
async summarize(prompt) {
|
|
2597
2683
|
const { default: OpenAI } = await import('openai');
|
|
2598
|
-
const client = new OpenAI({ apiKey:
|
|
2684
|
+
const client = new OpenAI({ apiKey: this.resolvedKey(), baseURL: this.endpoint });
|
|
2599
2685
|
const res = await client.chat.completions.create({
|
|
2600
2686
|
model: this.modelName,
|
|
2601
2687
|
messages: [{ role: "user", content: prompt }],
|
|
2602
|
-
max_tokens:
|
|
2688
|
+
max_tokens: 500
|
|
2603
2689
|
});
|
|
2604
|
-
return
|
|
2690
|
+
return {
|
|
2691
|
+
text: res.choices?.[0]?.message?.content?.trim() ?? "",
|
|
2692
|
+
promptTokens: res.usage?.prompt_tokens ?? 0,
|
|
2693
|
+
completionTokens: res.usage?.completion_tokens ?? 0
|
|
2694
|
+
};
|
|
2605
2695
|
}
|
|
2606
2696
|
};
|
|
2607
2697
|
}
|
|
@@ -2612,24 +2702,51 @@ var anthropic_exports = {};
|
|
|
2612
2702
|
__export(anthropic_exports, {
|
|
2613
2703
|
AnthropicProvider: () => AnthropicProvider
|
|
2614
2704
|
});
|
|
2615
|
-
var AnthropicProvider;
|
|
2705
|
+
var ANTHROPIC_CONTEXT, AnthropicProvider;
|
|
2616
2706
|
var init_anthropic = __esm({
|
|
2617
2707
|
"src/llm/providers/anthropic.ts"() {
|
|
2708
|
+
ANTHROPIC_CONTEXT = {
|
|
2709
|
+
"claude-3-5-sonnet-20241022": 2e5,
|
|
2710
|
+
"claude-3-5-haiku-20241022": 2e5,
|
|
2711
|
+
"claude-3-opus-20240229": 2e5,
|
|
2712
|
+
"claude-3-sonnet-20240229": 2e5,
|
|
2713
|
+
"claude-3-haiku-20240307": 2e5,
|
|
2714
|
+
"claude-haiku-4-5": 2e5,
|
|
2715
|
+
"claude-sonnet-4-5": 2e5,
|
|
2716
|
+
"claude-opus-4-5": 2e5
|
|
2717
|
+
};
|
|
2618
2718
|
AnthropicProvider = class {
|
|
2619
2719
|
modelName;
|
|
2620
|
-
|
|
2720
|
+
endpoint;
|
|
2721
|
+
apiKey;
|
|
2722
|
+
constructor(model, baseUrl, apiKey) {
|
|
2621
2723
|
this.modelName = model ?? "claude-haiku-4-5";
|
|
2724
|
+
this.endpoint = (baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
2725
|
+
this.apiKey = apiKey ?? "";
|
|
2726
|
+
}
|
|
2727
|
+
resolvedKey() {
|
|
2728
|
+
return this.apiKey || process.env["ANTHROPIC_API_KEY"] || "";
|
|
2729
|
+
}
|
|
2730
|
+
async getContextWindow() {
|
|
2731
|
+
return ANTHROPIC_CONTEXT[this.modelName] ?? 2e5;
|
|
2622
2732
|
}
|
|
2623
2733
|
async summarize(prompt) {
|
|
2624
2734
|
const Anthropic = (await import('@anthropic-ai/sdk')).default;
|
|
2625
|
-
const client = new Anthropic({
|
|
2735
|
+
const client = new Anthropic({
|
|
2736
|
+
apiKey: this.resolvedKey(),
|
|
2737
|
+
baseURL: this.endpoint
|
|
2738
|
+
});
|
|
2626
2739
|
const res = await client.messages.create({
|
|
2627
2740
|
model: this.modelName,
|
|
2628
|
-
max_tokens:
|
|
2741
|
+
max_tokens: 500,
|
|
2629
2742
|
messages: [{ role: "user", content: prompt }]
|
|
2630
2743
|
});
|
|
2631
2744
|
const block = res.content?.[0];
|
|
2632
|
-
return
|
|
2745
|
+
return {
|
|
2746
|
+
text: block && block.type === "text" ? block.text.trim() : "",
|
|
2747
|
+
promptTokens: res.usage?.input_tokens ?? 0,
|
|
2748
|
+
completionTokens: res.usage?.output_tokens ?? 0
|
|
2749
|
+
};
|
|
2633
2750
|
}
|
|
2634
2751
|
};
|
|
2635
2752
|
}
|
|
@@ -2640,20 +2757,58 @@ var custom_exports = {};
|
|
|
2640
2757
|
__export(custom_exports, {
|
|
2641
2758
|
CustomProvider: () => CustomProvider
|
|
2642
2759
|
});
|
|
2643
|
-
var CustomProvider;
|
|
2760
|
+
var KNOWN_CONTEXT_WINDOWS, CustomProvider;
|
|
2644
2761
|
var init_custom = __esm({
|
|
2645
2762
|
"src/llm/providers/custom.ts"() {
|
|
2763
|
+
KNOWN_CONTEXT_WINDOWS = {
|
|
2764
|
+
// DeepSeek
|
|
2765
|
+
"deepseek-v4-flash": 64e3,
|
|
2766
|
+
"deepseek-v4-pro": 64e3,
|
|
2767
|
+
"deepseek-chat": 64e3,
|
|
2768
|
+
"deepseek-coder": 16384,
|
|
2769
|
+
"deepseek-v2": 128e3,
|
|
2770
|
+
"deepseek-v3": 64e3,
|
|
2771
|
+
// Groq
|
|
2772
|
+
"llama3-8b-8192": 8192,
|
|
2773
|
+
"llama3-70b-8192": 8192,
|
|
2774
|
+
"mixtral-8x7b-32768": 32768,
|
|
2775
|
+
"gemma-7b-it": 8192,
|
|
2776
|
+
// Together AI
|
|
2777
|
+
"mistralai/Mistral-7B-v0.1": 8192,
|
|
2778
|
+
"meta-llama/Llama-3-8b-chat": 8192,
|
|
2779
|
+
// LM Studio / vLLM defaults
|
|
2780
|
+
"default": 4096
|
|
2781
|
+
};
|
|
2646
2782
|
CustomProvider = class {
|
|
2647
2783
|
modelName;
|
|
2784
|
+
endpoint;
|
|
2648
2785
|
baseUrl;
|
|
2649
2786
|
apiKey;
|
|
2650
2787
|
constructor(baseUrl, model, apiKey = "") {
|
|
2651
2788
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
2652
2789
|
this.modelName = model;
|
|
2653
2790
|
this.apiKey = apiKey;
|
|
2791
|
+
this.endpoint = this.baseUrl;
|
|
2792
|
+
}
|
|
2793
|
+
async getContextWindow() {
|
|
2794
|
+
const modelsUrl = this.baseUrl.endsWith("/v1") ? `${this.baseUrl}/models` : `${this.baseUrl}/v1/models`;
|
|
2795
|
+
try {
|
|
2796
|
+
const res = await fetch(modelsUrl, {
|
|
2797
|
+
headers: this.apiKey ? { "Authorization": `Bearer ${this.apiKey}` } : {},
|
|
2798
|
+
signal: AbortSignal.timeout(5e3)
|
|
2799
|
+
});
|
|
2800
|
+
if (res.ok) {
|
|
2801
|
+
const json = await res.json();
|
|
2802
|
+
const model = json.data?.find((m) => m.id === this.modelName);
|
|
2803
|
+
if (model?.context_window) return model.context_window;
|
|
2804
|
+
}
|
|
2805
|
+
} catch {
|
|
2806
|
+
}
|
|
2807
|
+
return KNOWN_CONTEXT_WINDOWS[this.modelName];
|
|
2654
2808
|
}
|
|
2655
2809
|
async summarize(prompt) {
|
|
2656
|
-
const
|
|
2810
|
+
const base = this.baseUrl.endsWith("/v1") ? this.baseUrl : `${this.baseUrl}/v1`;
|
|
2811
|
+
const url = `${base}/chat/completions`;
|
|
2657
2812
|
const headers = {
|
|
2658
2813
|
"Content-Type": "application/json"
|
|
2659
2814
|
};
|
|
@@ -2663,16 +2818,22 @@ var init_custom = __esm({
|
|
|
2663
2818
|
const body = JSON.stringify({
|
|
2664
2819
|
model: this.modelName,
|
|
2665
2820
|
messages: [{ role: "user", content: prompt }],
|
|
2666
|
-
max_tokens:
|
|
2821
|
+
max_tokens: 500,
|
|
2667
2822
|
temperature: 0.3
|
|
2668
2823
|
});
|
|
2669
2824
|
const res = await fetch(url, { method: "POST", headers, body });
|
|
2670
2825
|
if (!res.ok) {
|
|
2671
|
-
const
|
|
2672
|
-
throw new Error(`Custom LLM API error ${res.status}: ${
|
|
2826
|
+
const text2 = await res.text().catch(() => res.statusText);
|
|
2827
|
+
throw new Error(`Custom LLM API error ${res.status}: ${text2}`);
|
|
2673
2828
|
}
|
|
2674
2829
|
const data = await res.json();
|
|
2675
|
-
|
|
2830
|
+
const msg = data.choices?.[0]?.message;
|
|
2831
|
+
const text = (msg?.content?.trim() || msg?.reasoning_content?.trim()) ?? "";
|
|
2832
|
+
return {
|
|
2833
|
+
text,
|
|
2834
|
+
promptTokens: data.usage?.prompt_tokens ?? 0,
|
|
2835
|
+
completionTokens: data.usage?.completion_tokens ?? 0
|
|
2836
|
+
};
|
|
2676
2837
|
}
|
|
2677
2838
|
};
|
|
2678
2839
|
}
|
|
@@ -2688,10 +2849,31 @@ var init_ollama = __esm({
|
|
|
2688
2849
|
"src/llm/providers/ollama.ts"() {
|
|
2689
2850
|
OllamaProvider = class {
|
|
2690
2851
|
modelName;
|
|
2852
|
+
endpoint;
|
|
2691
2853
|
baseUrl;
|
|
2692
2854
|
constructor(model, baseUrl) {
|
|
2693
2855
|
this.modelName = model ?? "llama3";
|
|
2694
2856
|
this.baseUrl = baseUrl ?? "http://localhost:11434";
|
|
2857
|
+
this.endpoint = this.baseUrl;
|
|
2858
|
+
}
|
|
2859
|
+
async getContextWindow() {
|
|
2860
|
+
try {
|
|
2861
|
+
const res = await fetch(`${this.baseUrl}/api/show`, {
|
|
2862
|
+
method: "POST",
|
|
2863
|
+
headers: { "Content-Type": "application/json" },
|
|
2864
|
+
body: JSON.stringify({ model: this.modelName }),
|
|
2865
|
+
signal: AbortSignal.timeout(5e3)
|
|
2866
|
+
});
|
|
2867
|
+
if (!res.ok) return void 0;
|
|
2868
|
+
const json = await res.json();
|
|
2869
|
+
const info = json.model_info ?? {};
|
|
2870
|
+
for (const [k, v] of Object.entries(info)) {
|
|
2871
|
+
if (k.endsWith(".context_length") && typeof v === "number") return v;
|
|
2872
|
+
}
|
|
2873
|
+
return void 0;
|
|
2874
|
+
} catch {
|
|
2875
|
+
return void 0;
|
|
2876
|
+
}
|
|
2695
2877
|
}
|
|
2696
2878
|
async summarize(prompt) {
|
|
2697
2879
|
const url = `${this.baseUrl}/api/generate`;
|
|
@@ -2709,7 +2891,11 @@ var init_ollama = __esm({
|
|
|
2709
2891
|
throw new Error(`Ollama request failed: ${res.status} ${res.statusText}`);
|
|
2710
2892
|
}
|
|
2711
2893
|
const json = await res.json();
|
|
2712
|
-
return
|
|
2894
|
+
return {
|
|
2895
|
+
text: (json.response ?? "").trim(),
|
|
2896
|
+
promptTokens: json.prompt_eval_count ?? 0,
|
|
2897
|
+
completionTokens: json.eval_count ?? 0
|
|
2898
|
+
};
|
|
2713
2899
|
}
|
|
2714
2900
|
};
|
|
2715
2901
|
}
|
|
@@ -2725,11 +2911,11 @@ async function createLLMProvider(config = {}) {
|
|
|
2725
2911
|
switch (provider) {
|
|
2726
2912
|
case "openai": {
|
|
2727
2913
|
const { OpenAIProvider: OpenAIProvider2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
2728
|
-
return new OpenAIProvider2(model);
|
|
2914
|
+
return new OpenAIProvider2(model, baseUrl, apiKey);
|
|
2729
2915
|
}
|
|
2730
2916
|
case "anthropic": {
|
|
2731
2917
|
const { AnthropicProvider: AnthropicProvider2 } = await Promise.resolve().then(() => (init_anthropic(), anthropic_exports));
|
|
2732
|
-
return new AnthropicProvider2(model);
|
|
2918
|
+
return new AnthropicProvider2(model, baseUrl, apiKey);
|
|
2733
2919
|
}
|
|
2734
2920
|
case "custom": {
|
|
2735
2921
|
const { CustomProvider: CustomProvider2 } = await Promise.resolve().then(() => (init_custom(), custom_exports));
|
|
@@ -2741,7 +2927,7 @@ async function createLLMProvider(config = {}) {
|
|
|
2741
2927
|
case "ollama":
|
|
2742
2928
|
default: {
|
|
2743
2929
|
const { OllamaProvider: OllamaProvider2 } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
2744
|
-
return new OllamaProvider2(model);
|
|
2930
|
+
return new OllamaProvider2(model, baseUrl);
|
|
2745
2931
|
}
|
|
2746
2932
|
}
|
|
2747
2933
|
}
|
|
@@ -2812,65 +2998,6 @@ var init_embedder = __esm({
|
|
|
2812
2998
|
}
|
|
2813
2999
|
});
|
|
2814
3000
|
|
|
2815
|
-
// src/storage/db-manager.ts
|
|
2816
|
-
var db_manager_exports = {};
|
|
2817
|
-
__export(db_manager_exports, {
|
|
2818
|
-
DbManager: () => DbManager
|
|
2819
|
-
});
|
|
2820
|
-
var DbManager;
|
|
2821
|
-
var init_db_manager = __esm({
|
|
2822
|
-
"src/storage/db-manager.ts"() {
|
|
2823
|
-
DbManager = class {
|
|
2824
|
-
db = null;
|
|
2825
|
-
conn = null;
|
|
2826
|
-
dbPath;
|
|
2827
|
-
readOnly;
|
|
2828
|
-
constructor(dbPath, readOnly = false) {
|
|
2829
|
-
this.dbPath = dbPath;
|
|
2830
|
-
this.readOnly = readOnly;
|
|
2831
|
-
}
|
|
2832
|
-
async init() {
|
|
2833
|
-
if (!this.readOnly) {
|
|
2834
|
-
fs26.mkdirSync(path32.dirname(this.dbPath), { recursive: true });
|
|
2835
|
-
}
|
|
2836
|
-
this.db = new Database(this.dbPath, 0, true, this.readOnly);
|
|
2837
|
-
await this.db.init();
|
|
2838
|
-
this.conn = new Connection(this.db);
|
|
2839
|
-
await this.conn.init();
|
|
2840
|
-
}
|
|
2841
|
-
async query(cypher) {
|
|
2842
|
-
if (!this.conn) throw new Error("Database not initialized");
|
|
2843
|
-
const result = await this.conn.query(cypher);
|
|
2844
|
-
const qr = Array.isArray(result) ? result[0] : result;
|
|
2845
|
-
const rows = await qr.getAll();
|
|
2846
|
-
qr.close();
|
|
2847
|
-
return rows;
|
|
2848
|
-
}
|
|
2849
|
-
async execute(cypher) {
|
|
2850
|
-
if (!this.conn) throw new Error("Database not initialized");
|
|
2851
|
-
const result = await this.conn.query(cypher);
|
|
2852
|
-
const qr = Array.isArray(result) ? result[0] : result;
|
|
2853
|
-
qr.close();
|
|
2854
|
-
}
|
|
2855
|
-
close() {
|
|
2856
|
-
try {
|
|
2857
|
-
this.conn?.closeSync();
|
|
2858
|
-
} catch {
|
|
2859
|
-
}
|
|
2860
|
-
try {
|
|
2861
|
-
this.db?.closeSync();
|
|
2862
|
-
} catch {
|
|
2863
|
-
}
|
|
2864
|
-
this.conn = null;
|
|
2865
|
-
this.db = null;
|
|
2866
|
-
}
|
|
2867
|
-
get isOpen() {
|
|
2868
|
-
return this.conn !== null;
|
|
2869
|
-
}
|
|
2870
|
-
};
|
|
2871
|
-
}
|
|
2872
|
-
});
|
|
2873
|
-
|
|
2874
3001
|
// src/multi-repo/graph-from-db.ts
|
|
2875
3002
|
var graph_from_db_exports = {};
|
|
2876
3003
|
__export(graph_from_db_exports, {
|
|
@@ -3760,19 +3887,26 @@ function executeFIND(stmt, graph) {
|
|
|
3760
3887
|
totalCount
|
|
3761
3888
|
};
|
|
3762
3889
|
}
|
|
3890
|
+
function findBestNode(graph, name) {
|
|
3891
|
+
const matches = [];
|
|
3892
|
+
for (const node of graph.allNodes()) {
|
|
3893
|
+
if (node.name === name) matches.push(node);
|
|
3894
|
+
}
|
|
3895
|
+
if (matches.length === 0) return void 0;
|
|
3896
|
+
if (matches.length === 1) return matches[0];
|
|
3897
|
+
return matches.sort((a, b) => {
|
|
3898
|
+
const aEdges = [...graph.findEdgesFrom(a.id)].length + [...graph.findEdgesTo(a.id)].length;
|
|
3899
|
+
const bEdges = [...graph.findEdgesFrom(b.id)].length + [...graph.findEdgesTo(b.id)].length;
|
|
3900
|
+
return bEdges - aEdges;
|
|
3901
|
+
})[0];
|
|
3902
|
+
}
|
|
3763
3903
|
function executeTRAVERSE(stmt, graph) {
|
|
3764
3904
|
const start = Date.now();
|
|
3765
3905
|
const maxDepth = stmt.depth ?? 5;
|
|
3766
3906
|
const edgeKind = stmt.edgeKind;
|
|
3767
3907
|
const direction = stmt.direction ?? "OUTGOING";
|
|
3768
3908
|
const deadline = start + EXECUTION_TIMEOUT_MS;
|
|
3769
|
-
|
|
3770
|
-
for (const node of graph.allNodes()) {
|
|
3771
|
-
if (node.name === stmt.from) {
|
|
3772
|
-
startNode = node;
|
|
3773
|
-
break;
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3909
|
+
const startNode = findBestNode(graph, stmt.from);
|
|
3776
3910
|
if (!startNode) {
|
|
3777
3911
|
return {
|
|
3778
3912
|
nodes: [],
|
|
@@ -3836,13 +3970,8 @@ function executeTRAVERSE(stmt, graph) {
|
|
|
3836
3970
|
function executePATH(stmt, graph) {
|
|
3837
3971
|
const start = Date.now();
|
|
3838
3972
|
const deadline = start + EXECUTION_TIMEOUT_MS;
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
for (const node of graph.allNodes()) {
|
|
3842
|
-
if (node.name === stmt.from) startNode = node;
|
|
3843
|
-
if (node.name === stmt.to) endNode = node;
|
|
3844
|
-
if (startNode && endNode) break;
|
|
3845
|
-
}
|
|
3973
|
+
const startNode = findBestNode(graph, stmt.from);
|
|
3974
|
+
const endNode = findBestNode(graph, stmt.to);
|
|
3846
3975
|
if (!startNode || !endNode) {
|
|
3847
3976
|
return {
|
|
3848
3977
|
path: null,
|
|
@@ -4615,7 +4744,7 @@ var init_users_db = __esm({
|
|
|
4615
4744
|
constructor(dbPath) {
|
|
4616
4745
|
const dir = path32.dirname(dbPath);
|
|
4617
4746
|
secureMkdir(dir);
|
|
4618
|
-
this.db = new
|
|
4747
|
+
this.db = new Database3(dbPath);
|
|
4619
4748
|
this.db.pragma("journal_mode = WAL");
|
|
4620
4749
|
this.db.pragma("foreign_keys = ON");
|
|
4621
4750
|
this.createTables();
|
|
@@ -6770,12 +6899,16 @@ function textSearch(graph, query, limit = 20) {
|
|
|
6770
6899
|
let score = 0;
|
|
6771
6900
|
const nameLC = node.name.toLowerCase();
|
|
6772
6901
|
const pathLC = node.filePath.toLowerCase();
|
|
6902
|
+
const fileBaseName = (node.filePath.split(/[/\\]/).pop()?.replace(/\.[^.]+$/, "") ?? "").toLowerCase();
|
|
6903
|
+
const contentLC = node.content?.toLowerCase() ?? "";
|
|
6773
6904
|
for (const term of terms) {
|
|
6774
6905
|
if (nameLC === term) score += 10;
|
|
6775
6906
|
else if (nameLC.startsWith(term)) score += 7;
|
|
6776
6907
|
else if (nameLC.includes(term)) score += 5;
|
|
6777
|
-
if (
|
|
6778
|
-
if (
|
|
6908
|
+
if (fileBaseName === term) score += 8;
|
|
6909
|
+
else if (fileBaseName.startsWith(term)) score += 5;
|
|
6910
|
+
else if (pathLC.includes(term)) score += 2;
|
|
6911
|
+
if (contentLC.includes(term)) score += 3;
|
|
6779
6912
|
}
|
|
6780
6913
|
if (score > 0) {
|
|
6781
6914
|
if (isDistPath(node.filePath)) score -= 8;
|
|
@@ -6830,7 +6963,7 @@ var VectorIndex = class {
|
|
|
6830
6963
|
this.sqlitePath = sqlitePath;
|
|
6831
6964
|
}
|
|
6832
6965
|
async init() {
|
|
6833
|
-
this.db = new
|
|
6966
|
+
this.db = new Database3(this.sqlitePath);
|
|
6834
6967
|
this.db.pragma("journal_mode = WAL");
|
|
6835
6968
|
this.db.exec(`
|
|
6836
6969
|
CREATE TABLE IF NOT EXISTS ${EMBED_TABLE} (
|
|
@@ -6960,24 +7093,92 @@ function siftDown(arr, i, score) {
|
|
|
6960
7093
|
}
|
|
6961
7094
|
}
|
|
6962
7095
|
init_embedder();
|
|
7096
|
+
|
|
7097
|
+
// src/search/reranker.ts
|
|
7098
|
+
function tokenizeForRerank(text) {
|
|
7099
|
+
return text.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[\s\-_./:(){}[\]<>,"'`~!@#$%^&*+=|;?\\]+/).filter((t) => t.length >= 2);
|
|
7100
|
+
}
|
|
7101
|
+
var DEFAULT_KIND_WEIGHTS = {
|
|
7102
|
+
class: 1.2,
|
|
7103
|
+
interface: 1.15,
|
|
7104
|
+
function: 1.1,
|
|
7105
|
+
method: 1.08,
|
|
7106
|
+
type_alias: 1.03,
|
|
7107
|
+
enum: 1.02,
|
|
7108
|
+
constant: 0.98,
|
|
7109
|
+
variable: 0.9,
|
|
7110
|
+
file: 0.85
|
|
7111
|
+
};
|
|
7112
|
+
var PATH_MULTIPLIER_TEST = 0.4;
|
|
7113
|
+
var PATH_MULTIPLIER_DIST = 0.25;
|
|
7114
|
+
function rerank(query, results, options = {}) {
|
|
7115
|
+
if (results.length === 0) return results;
|
|
7116
|
+
const {
|
|
7117
|
+
nameWeight = 0.4,
|
|
7118
|
+
snippetWeight = 0.25,
|
|
7119
|
+
kindWeights = {}
|
|
7120
|
+
} = options;
|
|
7121
|
+
const effectiveKindWeights = {
|
|
7122
|
+
...DEFAULT_KIND_WEIGHTS,
|
|
7123
|
+
...kindWeights
|
|
7124
|
+
};
|
|
7125
|
+
const queryTerms = [...new Set(tokenizeForRerank(query))];
|
|
7126
|
+
if (queryTerms.length === 0) return results.slice();
|
|
7127
|
+
const queryLower = query.toLowerCase();
|
|
7128
|
+
const scored = results.map((r) => {
|
|
7129
|
+
let bonus = 0;
|
|
7130
|
+
const nameLower = r.name.toLowerCase();
|
|
7131
|
+
const nameTerms = tokenizeForRerank(r.name);
|
|
7132
|
+
if (nameLower === queryLower) {
|
|
7133
|
+
bonus += nameWeight;
|
|
7134
|
+
} else if (nameLower.startsWith(queryLower)) {
|
|
7135
|
+
bonus += nameWeight * 0.75;
|
|
7136
|
+
} else if (queryLower.includes(nameLower) && nameLower.length >= 3) {
|
|
7137
|
+
bonus += nameWeight * 0.45;
|
|
7138
|
+
} else {
|
|
7139
|
+
const matchCount = queryTerms.filter((t) => nameTerms.includes(t)).length;
|
|
7140
|
+
if (matchCount > 0) {
|
|
7141
|
+
const overlap = matchCount / queryTerms.length;
|
|
7142
|
+
bonus += nameWeight * overlap * 0.6;
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
7145
|
+
if (r.snippet && r.snippet.length > 0) {
|
|
7146
|
+
const snippetLower = r.snippet.toLowerCase();
|
|
7147
|
+
const hitCount = queryTerms.filter((t) => snippetLower.includes(t)).length;
|
|
7148
|
+
bonus += snippetWeight * (hitCount / queryTerms.length);
|
|
7149
|
+
}
|
|
7150
|
+
const kw = effectiveKindWeights[r.kind] ?? 1;
|
|
7151
|
+
const fp = r.filePath;
|
|
7152
|
+
const fpNorm = "/" + fp;
|
|
7153
|
+
const isTestPath = fpNorm.includes("/test/") || fpNorm.includes("/tests/") || fpNorm.includes("/spec/") || fpNorm.includes("/__tests__/") || fp.includes(".test.") || fp.includes(".spec.");
|
|
7154
|
+
const isDistPath = fpNorm.includes("/dist/") || fpNorm.includes("/build/") || fp.endsWith(".d.ts") || fpNorm.includes("/node_modules/");
|
|
7155
|
+
const pathMul = isDistPath ? PATH_MULTIPLIER_DIST : isTestPath ? PATH_MULTIPLIER_TEST : 1;
|
|
7156
|
+
const clampedBonus = Math.max(0, Math.min(nameWeight + snippetWeight, bonus));
|
|
7157
|
+
const finalScore = r.score * (1 + clampedBonus) * kw * pathMul;
|
|
7158
|
+
return { result: r, finalScore };
|
|
7159
|
+
});
|
|
7160
|
+
return scored.sort((a, b) => b.finalScore - a.finalScore).map(({ result, finalScore }) => ({ ...result, score: finalScore }));
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7163
|
+
// src/search/hybrid-search.ts
|
|
6963
7164
|
async function hybridSearch(graph, query, limit, options = {}) {
|
|
6964
7165
|
const { vectorDbPath, bm25Limit = 50, vectorLimit = 50, bm25Results: precomputedBm25 } = options;
|
|
7166
|
+
const rerankEnabled = options.rerank?.enabled !== false;
|
|
7167
|
+
const rerankOptions = rerankEnabled && options.rerank && options.rerank.enabled !== false ? options.rerank : {};
|
|
6965
7168
|
const bm25Promise = precomputedBm25 ? Promise.resolve(precomputedBm25) : Promise.resolve(textSearch(graph, query, bm25Limit));
|
|
6966
7169
|
const hasVectorDb = Boolean(vectorDbPath && fs26.existsSync(vectorDbPath));
|
|
6967
7170
|
if (!hasVectorDb) {
|
|
6968
7171
|
const bm25Results2 = await bm25Promise;
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
};
|
|
7172
|
+
const candidates2 = bm25Results2.slice(0, limit).map((r) => ({ ...r, searchMode: "bm25" }));
|
|
7173
|
+
const final2 = rerankEnabled ? rerank(query, candidates2, rerankOptions) : candidates2;
|
|
7174
|
+
return { results: final2, searchMode: "bm25" };
|
|
6973
7175
|
}
|
|
6974
7176
|
const vectorPromise = runVectorSearch(vectorDbPath, query, vectorLimit);
|
|
6975
7177
|
const [bm25Results, vectorResults] = await Promise.all([bm25Promise, vectorPromise]);
|
|
6976
7178
|
if (vectorResults === null || vectorResults.length === 0) {
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
};
|
|
7179
|
+
const candidates2 = bm25Results.slice(0, limit).map((r) => ({ ...r, searchMode: "bm25" }));
|
|
7180
|
+
const final2 = rerankEnabled ? rerank(query, candidates2, rerankOptions) : candidates2;
|
|
7181
|
+
return { results: final2, searchMode: "bm25" };
|
|
6981
7182
|
}
|
|
6982
7183
|
const vectorAsSearchResults = vectorResults.map((h) => ({
|
|
6983
7184
|
nodeId: h.nodeId,
|
|
@@ -6988,10 +7189,9 @@ async function hybridSearch(graph, query, limit, options = {}) {
|
|
|
6988
7189
|
snippet: graph.getNode(h.nodeId)?.content?.slice(0, 200)
|
|
6989
7190
|
}));
|
|
6990
7191
|
const merged = reciprocalRankFusion(bm25Results, vectorAsSearchResults);
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
};
|
|
7192
|
+
const candidates = merged.slice(0, limit).map((r) => ({ ...r, searchMode: "hybrid" }));
|
|
7193
|
+
const final = rerankEnabled ? rerank(query, candidates, rerankOptions) : candidates;
|
|
7194
|
+
return { results: final, searchMode: "hybrid" };
|
|
6995
7195
|
}
|
|
6996
7196
|
async function runVectorSearch(vectorDbPath, query, topK) {
|
|
6997
7197
|
try {
|
|
@@ -7021,11 +7221,19 @@ function tokenize(text) {
|
|
|
7021
7221
|
return text.toLowerCase().split(/[\s\-_./\\:(){}[\]<>,"'`~!@#$%^&*+=|;?]+/).filter((t) => t.length >= 2 && t.length <= 64);
|
|
7022
7222
|
}
|
|
7023
7223
|
function nodeToDoc(node) {
|
|
7224
|
+
const fileBaseName = node.filePath.split(/[/\\]/).pop()?.replace(/\.[^.]+$/, "") ?? "";
|
|
7024
7225
|
return [
|
|
7025
7226
|
node.name,
|
|
7227
|
+
node.name,
|
|
7228
|
+
// repeat name to boost exact-name matches
|
|
7026
7229
|
node.kind,
|
|
7027
7230
|
node.filePath,
|
|
7028
|
-
|
|
7231
|
+
fileBaseName,
|
|
7232
|
+
// filename stem (class name) — extra weight
|
|
7233
|
+
fileBaseName,
|
|
7234
|
+
// repeat again for stronger class-name boosting
|
|
7235
|
+
(node.content ?? "").slice(0, 1500)
|
|
7236
|
+
// increased from 1000
|
|
7029
7237
|
].join(" ");
|
|
7030
7238
|
}
|
|
7031
7239
|
function heapTopK(scores, k) {
|
|
@@ -7122,7 +7330,7 @@ var Bm25Index = class {
|
|
|
7122
7330
|
} catch {
|
|
7123
7331
|
}
|
|
7124
7332
|
}
|
|
7125
|
-
const db = new
|
|
7333
|
+
const db = new Database3(this.dbPath);
|
|
7126
7334
|
db.pragma("journal_mode = WAL");
|
|
7127
7335
|
db.exec(`
|
|
7128
7336
|
CREATE TABLE bm25_index (term TEXT PRIMARY KEY, postings TEXT NOT NULL);
|
|
@@ -7157,7 +7365,7 @@ var Bm25Index = class {
|
|
|
7157
7365
|
*/
|
|
7158
7366
|
load() {
|
|
7159
7367
|
if (!fs26.existsSync(this.dbPath)) return;
|
|
7160
|
-
const db = new
|
|
7368
|
+
const db = new Database3(this.dbPath, { readonly: true });
|
|
7161
7369
|
try {
|
|
7162
7370
|
const getMeta = db.prepare("SELECT value FROM bm25_meta WHERE key = ?");
|
|
7163
7371
|
this.avgdl = parseFloat(getMeta.get("avgdl")?.value ?? "1");
|
|
@@ -7250,7 +7458,7 @@ var Bm25Index = class {
|
|
|
7250
7458
|
newTermFreqs.set(node.id, tf);
|
|
7251
7459
|
}
|
|
7252
7460
|
const newTermSet = new Set([...newTermFreqs.values()].flatMap((m) => [...m.keys()]));
|
|
7253
|
-
const db = new
|
|
7461
|
+
const db = new Database3(this.dbPath);
|
|
7254
7462
|
db.pragma("journal_mode = WAL");
|
|
7255
7463
|
const termsToRewrite = /* @__PURE__ */ new Map();
|
|
7256
7464
|
for (const term of newTermSet) {
|
|
@@ -7512,19 +7720,24 @@ function buildNodeProps(node) {
|
|
|
7512
7720
|
function escCypher(s) {
|
|
7513
7721
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
|
|
7514
7722
|
}
|
|
7515
|
-
|
|
7516
|
-
|
|
7723
|
+
function getGlobalDir() {
|
|
7724
|
+
return path32.join(process.env["CODE_INTEL_HOME"] ?? os13.homedir(), ".code-intel");
|
|
7725
|
+
}
|
|
7726
|
+
function getReposFile() {
|
|
7727
|
+
return path32.join(getGlobalDir(), "repos.json");
|
|
7728
|
+
}
|
|
7517
7729
|
function loadRegistry() {
|
|
7518
7730
|
try {
|
|
7519
|
-
const data = fs26.readFileSync(
|
|
7731
|
+
const data = fs26.readFileSync(getReposFile(), "utf-8");
|
|
7520
7732
|
return JSON.parse(data);
|
|
7521
7733
|
} catch {
|
|
7522
7734
|
return [];
|
|
7523
7735
|
}
|
|
7524
7736
|
}
|
|
7525
7737
|
function saveRegistry(entries) {
|
|
7526
|
-
|
|
7527
|
-
fs26.
|
|
7738
|
+
const globalDir = getGlobalDir();
|
|
7739
|
+
fs26.mkdirSync(globalDir, { recursive: true });
|
|
7740
|
+
fs26.writeFileSync(getReposFile(), JSON.stringify(entries, null, 2));
|
|
7528
7741
|
}
|
|
7529
7742
|
function upsertRepo(entry) {
|
|
7530
7743
|
const entries = loadRegistry();
|
|
@@ -8741,7 +8954,7 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8741
8954
|
// ── Search & inspect ─────────────────────────────────────────────────
|
|
8742
8955
|
{
|
|
8743
8956
|
name: "search",
|
|
8744
|
-
description:
|
|
8957
|
+
description: `BM25 keyword search across all indexed symbols \u2014 functions, classes, files, routes, etc. Optionally scope to a specific repo or group. TIP: To find a method in a specific class when multiple classes have the same method name, include the class/file name in the query (e.g. search("Token requestAccessToken redis save") to find Token.php's version vs JWT.php's). This is the preferred alternative to bare inspect() for ambiguous method names.`,
|
|
8745
8958
|
inputSchema: {
|
|
8746
8959
|
type: "object",
|
|
8747
8960
|
properties: {
|
|
@@ -8757,16 +8970,31 @@ function createMcpServer(graph, repoName, workspaceRoot) {
|
|
|
8757
8970
|
},
|
|
8758
8971
|
{
|
|
8759
8972
|
name: "inspect",
|
|
8760
|
-
description:
|
|
8973
|
+
description: '360\xB0 view of a symbol: definition location, callers, callees, heritage (extends/implements), members, cluster, and source preview. When multiple files define the same symbol name, returns a disambiguation list \u2014 re-call with file_path to select the correct implementation. IMPORTANT: When you expect a symbol to be in a specific class (e.g. Token vs JWT, or CMS vs API module), always pass file_path to avoid silently resolving the wrong class. For methods that exist in multiple classes (requestAccessToken, verify, revoke, login, logout), prefer search("ClassName methodName unique-context") over bare inspect to guarantee the right class.',
|
|
8761
8974
|
inputSchema: {
|
|
8762
8975
|
type: "object",
|
|
8763
8976
|
properties: {
|
|
8764
8977
|
symbol_name: { type: "string", description: "Exact symbol name to inspect" },
|
|
8978
|
+
file_path: { type: "string", description: 'Optional partial file path to disambiguate when multiple symbols share the same name (e.g. "Token.php" or "src/Modules/CMS"). Required when inspect returns a disambiguation list or when you know the target class.' },
|
|
8765
8979
|
..._tokenProp
|
|
8766
8980
|
},
|
|
8767
8981
|
required: ["symbol_name"]
|
|
8768
8982
|
}
|
|
8769
8983
|
},
|
|
8984
|
+
{
|
|
8985
|
+
name: "get_source",
|
|
8986
|
+
description: `Read raw source lines from any file in the workspace. Use this to verify exact implementation details that inspect's content preview may not fully show. Supports partial file path matching (e.g. "Token.php"). Returns numbered lines.`,
|
|
8987
|
+
inputSchema: {
|
|
8988
|
+
type: "object",
|
|
8989
|
+
properties: {
|
|
8990
|
+
file_path: { type: "string", description: 'File path to read (partial match supported, e.g. "Token.php" or "src/Modules/CMS/AuthController.php")' },
|
|
8991
|
+
start_line: { type: "number", description: "First line to return (1-indexed, default: 1)" },
|
|
8992
|
+
end_line: { type: "number", description: "Last line to return inclusive (default: start_line + 99). Max 300 lines per call." },
|
|
8993
|
+
..._tokenProp
|
|
8994
|
+
},
|
|
8995
|
+
required: ["file_path"]
|
|
8996
|
+
}
|
|
8997
|
+
},
|
|
8770
8998
|
{
|
|
8771
8999
|
name: "blast_radius",
|
|
8772
9000
|
description: "Impact analysis: traverse the call/import graph to find all symbols that depend on or are affected by a given symbol. Returns risk level (LOW / MEDIUM / HIGH).",
|
|
@@ -9319,8 +9547,41 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolve
|
|
|
9319
9547
|
// ── inspect ────────────────────────────────────────────────────────────
|
|
9320
9548
|
case "inspect": {
|
|
9321
9549
|
const symbolName = a.symbol_name;
|
|
9322
|
-
const
|
|
9323
|
-
|
|
9550
|
+
const filePathHint = a.file_path;
|
|
9551
|
+
const allMatchingNodes = findNodesByName(graph, symbolName);
|
|
9552
|
+
if (allMatchingNodes.length === 0) {
|
|
9553
|
+
return { content: [{ type: "text", text: `Symbol "${symbolName}" not found. Try search first.` }] };
|
|
9554
|
+
}
|
|
9555
|
+
let node = allMatchingNodes[0];
|
|
9556
|
+
if (allMatchingNodes.length > 1) {
|
|
9557
|
+
if (filePathHint) {
|
|
9558
|
+
const filtered = allMatchingNodes.filter((n) => n.filePath.includes(filePathHint));
|
|
9559
|
+
if (filtered.length === 0) {
|
|
9560
|
+
return {
|
|
9561
|
+
content: [{
|
|
9562
|
+
type: "text",
|
|
9563
|
+
text: compact({
|
|
9564
|
+
disambiguation: true,
|
|
9565
|
+
message: `No match for file_path "${filePathHint}". Available definitions of "${symbolName}":`,
|
|
9566
|
+
candidates: allMatchingNodes.map((n) => ({ filePath: n.filePath, kind: n.kind, startLine: n.startLine }))
|
|
9567
|
+
})
|
|
9568
|
+
}]
|
|
9569
|
+
};
|
|
9570
|
+
}
|
|
9571
|
+
node = filtered[0];
|
|
9572
|
+
} else {
|
|
9573
|
+
return {
|
|
9574
|
+
content: [{
|
|
9575
|
+
type: "text",
|
|
9576
|
+
text: compact({
|
|
9577
|
+
disambiguation: true,
|
|
9578
|
+
message: `Multiple definitions of "${symbolName}" found. Re-call inspect with file_path to select one.`,
|
|
9579
|
+
candidates: allMatchingNodes.map((n) => ({ filePath: n.filePath, kind: n.kind, startLine: n.startLine, content: n.content?.slice(0, 120) }))
|
|
9580
|
+
})
|
|
9581
|
+
}]
|
|
9582
|
+
};
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9324
9585
|
const incoming = [...graph.findEdgesTo(node.id)];
|
|
9325
9586
|
const outgoing = [...graph.findEdgesFrom(node.id)];
|
|
9326
9587
|
const callers = incoming.filter((e) => e.kind === "calls").map((e) => ({
|
|
@@ -9367,12 +9628,70 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolve
|
|
|
9367
9628
|
kind: graph.getNode(e.target)?.kind
|
|
9368
9629
|
})),
|
|
9369
9630
|
cluster,
|
|
9370
|
-
content: node.content?.slice(0,
|
|
9631
|
+
content: node.content?.slice(0, 1500),
|
|
9632
|
+
contentNote: node.content && node.content.length > 1500 ? `(truncated \u2014 use get_source with file_path="${node.filePath}" start_line=${node.startLine} end_line=${node.endLine} for full source)` : void 0,
|
|
9371
9633
|
...suggestEnabled ? { suggested_next_tools: suggestNextTools } : {}
|
|
9372
9634
|
})
|
|
9373
9635
|
}]
|
|
9374
9636
|
};
|
|
9375
9637
|
}
|
|
9638
|
+
// ── get_source ─────────────────────────────────────────────────────────
|
|
9639
|
+
case "get_source": {
|
|
9640
|
+
const filePathQuery = a.file_path;
|
|
9641
|
+
const startLine = Math.max(1, a.start_line ?? 1);
|
|
9642
|
+
const maxLines = 300;
|
|
9643
|
+
const endLine = Math.min(a.end_line ?? startLine + 99, startLine + maxLines - 1);
|
|
9644
|
+
if (!workspaceRoot) {
|
|
9645
|
+
return { content: [{ type: "text", text: "workspaceRoot not set \u2014 cannot read files." }] };
|
|
9646
|
+
}
|
|
9647
|
+
const matchedFiles = /* @__PURE__ */ new Set();
|
|
9648
|
+
for (const node of graph.allNodes()) {
|
|
9649
|
+
if (node.filePath && node.filePath.includes(filePathQuery)) {
|
|
9650
|
+
matchedFiles.add(node.filePath);
|
|
9651
|
+
}
|
|
9652
|
+
}
|
|
9653
|
+
if (matchedFiles.size === 0) {
|
|
9654
|
+
return { content: [{ type: "text", text: `No indexed file matching "${filePathQuery}". Try a different partial path or use file_symbols to browse.` }] };
|
|
9655
|
+
}
|
|
9656
|
+
if (matchedFiles.size > 1) {
|
|
9657
|
+
return {
|
|
9658
|
+
content: [{
|
|
9659
|
+
type: "text",
|
|
9660
|
+
text: compact({
|
|
9661
|
+
disambiguation: true,
|
|
9662
|
+
message: `Multiple files match "${filePathQuery}". Re-call with a more specific file_path.`,
|
|
9663
|
+
candidates: [...matchedFiles]
|
|
9664
|
+
})
|
|
9665
|
+
}]
|
|
9666
|
+
};
|
|
9667
|
+
}
|
|
9668
|
+
const [resolvedPath] = [...matchedFiles];
|
|
9669
|
+
let fileContent;
|
|
9670
|
+
try {
|
|
9671
|
+
fileContent = fs26.readFileSync(resolvedPath, "utf-8");
|
|
9672
|
+
} catch {
|
|
9673
|
+
return { content: [{ type: "text", text: `Could not read file: ${resolvedPath}` }] };
|
|
9674
|
+
}
|
|
9675
|
+
const lines = fileContent.split("\n");
|
|
9676
|
+
const totalLines = lines.length;
|
|
9677
|
+
const sliceStart = startLine - 1;
|
|
9678
|
+
const sliceEnd = Math.min(endLine, totalLines);
|
|
9679
|
+
const selectedLines = lines.slice(sliceStart, sliceEnd);
|
|
9680
|
+
const numbered = selectedLines.map((l, i) => `${sliceStart + i + 1}: ${l}`).join("\n");
|
|
9681
|
+
return {
|
|
9682
|
+
content: [{
|
|
9683
|
+
type: "text",
|
|
9684
|
+
text: compact({
|
|
9685
|
+
filePath: resolvedPath,
|
|
9686
|
+
startLine,
|
|
9687
|
+
endLine: sliceEnd,
|
|
9688
|
+
totalLines,
|
|
9689
|
+
hasMore: sliceEnd < totalLines,
|
|
9690
|
+
source: numbered
|
|
9691
|
+
})
|
|
9692
|
+
}]
|
|
9693
|
+
};
|
|
9694
|
+
}
|
|
9376
9695
|
// ── blast_radius ───────────────────────────────────────────────────────
|
|
9377
9696
|
case "blast_radius": {
|
|
9378
9697
|
const target = a.target;
|
|
@@ -9505,12 +9824,12 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot, bm25Resolve
|
|
|
9505
9824
|
allExports.push({ kind: node.kind, name: node.name, filePath: node.filePath, startLine: node.startLine });
|
|
9506
9825
|
}
|
|
9507
9826
|
const total = allExports.length;
|
|
9508
|
-
const exports
|
|
9827
|
+
const exports = allExports.slice(offset, offset + effectiveLimit);
|
|
9509
9828
|
const hasMore = offset + effectiveLimit < total;
|
|
9510
9829
|
return {
|
|
9511
9830
|
content: [{
|
|
9512
9831
|
type: "text",
|
|
9513
|
-
text: compact({ exports
|
|
9832
|
+
text: compact({ exports, total, offset, limit: effectiveLimit, hasMore })
|
|
9514
9833
|
}]
|
|
9515
9834
|
};
|
|
9516
9835
|
}
|
|
@@ -9991,6 +10310,13 @@ function findNodeByName(graph, name) {
|
|
|
9991
10310
|
}
|
|
9992
10311
|
return void 0;
|
|
9993
10312
|
}
|
|
10313
|
+
function findNodesByName(graph, name) {
|
|
10314
|
+
const results = [];
|
|
10315
|
+
for (const node of graph.allNodes()) {
|
|
10316
|
+
if (node.name === name) results.push(node);
|
|
10317
|
+
}
|
|
10318
|
+
return results;
|
|
10319
|
+
}
|
|
9994
10320
|
function parseDiff(diffText) {
|
|
9995
10321
|
const result = [];
|
|
9996
10322
|
let currentFile = null;
|
|
@@ -10034,7 +10360,7 @@ var JobsDB = class {
|
|
|
10034
10360
|
db;
|
|
10035
10361
|
constructor(dbPath) {
|
|
10036
10362
|
fs26.mkdirSync(path32.dirname(dbPath), { recursive: true });
|
|
10037
|
-
this.db = new
|
|
10363
|
+
this.db = new Database3(dbPath);
|
|
10038
10364
|
this.db.pragma("journal_mode = WAL");
|
|
10039
10365
|
this.db.pragma("foreign_keys = ON");
|
|
10040
10366
|
this.createTables();
|