@esankhan3/code-search-mcp 0.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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -0
  3. package/dist/core/ast-graph-builder.js +800 -0
  4. package/dist/core/chunker.js +368 -0
  5. package/dist/core/claude-runner.js +173 -0
  6. package/dist/core/config.js +59 -0
  7. package/dist/core/cross-repo-detector.js +887 -0
  8. package/dist/core/embedder.js +334 -0
  9. package/dist/core/env-config.js +86 -0
  10. package/dist/core/file-walker.js +186 -0
  11. package/dist/core/git-diff.js +160 -0
  12. package/dist/core/graph-metrics.js +233 -0
  13. package/dist/core/indexer.js +552 -0
  14. package/dist/core/project-graph-builder.js +202 -0
  15. package/dist/core/query-classifier.js +144 -0
  16. package/dist/core/query-router.js +210 -0
  17. package/dist/core/rag-evaluator.js +213 -0
  18. package/dist/core/repo-profiler.js +383 -0
  19. package/dist/core/reranker.js +218 -0
  20. package/dist/core/retriever.js +242 -0
  21. package/dist/core/semantic-edge-detector.js +147 -0
  22. package/dist/core/service-mesh-inferrer.js +241 -0
  23. package/dist/core/structural-hasher.js +316 -0
  24. package/dist/core/tree-sitter-parser.js +539 -0
  25. package/dist/core/types.js +0 -0
  26. package/dist/core/vector-store.js +259 -0
  27. package/dist/core/workspace-detector.js +481 -0
  28. package/dist/index.js +177 -0
  29. package/dist/middleware/auth.js +109 -0
  30. package/dist/resources/resources.js +79 -0
  31. package/dist/server.js +242 -0
  32. package/dist/sources/github-org.js +109 -0
  33. package/dist/sources/local-path.js +44 -0
  34. package/dist/tools/graph.js +193 -0
  35. package/dist/tools/index-tools.js +43 -0
  36. package/dist/tools/profile.js +88 -0
  37. package/dist/tools/search.js +84 -0
  38. package/dist/transports/http-transport.js +162 -0
  39. package/dist/transports/remote-proxy.js +152 -0
  40. package/package.json +33 -0
@@ -0,0 +1,160 @@
1
+ import { execSync } from "node:child_process";
2
+ import { extname } from "node:path";
3
+ const SOURCE_EXTS = /* @__PURE__ */ new Set([
4
+ ".ts",
5
+ ".tsx",
6
+ ".js",
7
+ ".jsx",
8
+ ".mjs",
9
+ ".cjs",
10
+ ".go",
11
+ ".py",
12
+ ".rs",
13
+ ".java",
14
+ ".php",
15
+ ".rb",
16
+ ".swift",
17
+ ".kt",
18
+ ".cs",
19
+ ".scala",
20
+ ".c",
21
+ ".cpp",
22
+ ".h",
23
+ ".hpp",
24
+ ".yaml",
25
+ ".yml",
26
+ ".json",
27
+ ".toml",
28
+ ".xml",
29
+ ".sql",
30
+ ".graphql",
31
+ ".proto",
32
+ ".md",
33
+ ".txt"
34
+ ]);
35
+ function isSourceFile(filePath) {
36
+ return SOURCE_EXTS.has(extname(filePath).toLowerCase());
37
+ }
38
+ function parseNameStatus(output) {
39
+ const added = [];
40
+ const modified = [];
41
+ const deleted = [];
42
+ const renamed = [];
43
+ if (!output) return { added, modified, deleted, renamed, fallbackToFull: false };
44
+ for (const line of output.split("\n")) {
45
+ if (!line.trim()) continue;
46
+ const parts = line.split(" ");
47
+ const status = parts[0];
48
+ const filePath = parts[1];
49
+ if (!filePath || !status) continue;
50
+ if (!isSourceFile(filePath) && status[0] !== "R" && status[0] !== "C") continue;
51
+ switch (status[0]) {
52
+ case "A":
53
+ added.push(filePath);
54
+ break;
55
+ case "M":
56
+ modified.push(filePath);
57
+ break;
58
+ case "D":
59
+ deleted.push(filePath);
60
+ break;
61
+ case "R": {
62
+ const newPath = parts[2];
63
+ if (newPath) {
64
+ renamed.push({ from: filePath, to: newPath });
65
+ if (isSourceFile(filePath)) deleted.push(filePath);
66
+ if (isSourceFile(newPath)) added.push(newPath);
67
+ }
68
+ break;
69
+ }
70
+ case "C": {
71
+ const copyPath = parts[2];
72
+ if (copyPath && isSourceFile(copyPath)) added.push(copyPath);
73
+ break;
74
+ }
75
+ }
76
+ }
77
+ return { added, modified, deleted, renamed, fallbackToFull: false };
78
+ }
79
+ function mergeDiffs(a, b, extraAdded) {
80
+ const allAdded = /* @__PURE__ */ new Set([...a.added, ...b.added, ...extraAdded]);
81
+ const allModified = /* @__PURE__ */ new Set([...a.modified, ...b.modified]);
82
+ const allDeleted = /* @__PURE__ */ new Set([...a.deleted, ...b.deleted]);
83
+ const allRenamed = [...a.renamed, ...b.renamed];
84
+ for (const f of allAdded) allModified.delete(f);
85
+ for (const f of [...allAdded]) {
86
+ if (allDeleted.has(f)) {
87
+ allAdded.delete(f);
88
+ allDeleted.delete(f);
89
+ allModified.add(f);
90
+ }
91
+ }
92
+ return {
93
+ added: [...allAdded],
94
+ modified: [...allModified],
95
+ deleted: [...allDeleted],
96
+ renamed: allRenamed,
97
+ fallbackToFull: a.fallbackToFull || b.fallbackToFull
98
+ };
99
+ }
100
+ function getCommittedChanges(repoPath, sinceCommit) {
101
+ try {
102
+ const output = execSync(
103
+ `git diff --name-status ${sinceCommit}..HEAD`,
104
+ { cwd: repoPath, encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
105
+ ).trim();
106
+ return parseNameStatus(output);
107
+ } catch {
108
+ return { added: [], modified: [], deleted: [], renamed: [], fallbackToFull: true };
109
+ }
110
+ }
111
+ function getUncommittedChanges(repoPath) {
112
+ try {
113
+ const staged = execSync("git diff --name-status --cached", {
114
+ cwd: repoPath,
115
+ encoding: "utf-8",
116
+ timeout: 5e3,
117
+ stdio: ["pipe", "pipe", "pipe"]
118
+ }).trim();
119
+ const unstaged = execSync("git diff --name-status", {
120
+ cwd: repoPath,
121
+ encoding: "utf-8",
122
+ timeout: 5e3,
123
+ stdio: ["pipe", "pipe", "pipe"]
124
+ }).trim();
125
+ const untracked = execSync("git ls-files --others --exclude-standard", {
126
+ cwd: repoPath,
127
+ encoding: "utf-8",
128
+ timeout: 5e3,
129
+ stdio: ["pipe", "pipe", "pipe"]
130
+ }).trim();
131
+ const stagedDiff = parseNameStatus(staged);
132
+ const unstagedDiff = parseNameStatus(unstaged);
133
+ const untrackedFiles = untracked ? untracked.split("\n").filter((f) => f && isSourceFile(f)) : [];
134
+ return mergeDiffs(stagedDiff, unstagedDiff, untrackedFiles);
135
+ } catch {
136
+ return { added: [], modified: [], deleted: [], renamed: [], fallbackToFull: false };
137
+ }
138
+ }
139
+ function getAllChanges(repoPath, lastIndexedSha) {
140
+ if (!lastIndexedSha) {
141
+ return { added: [], modified: [], deleted: [], renamed: [], fallbackToFull: true };
142
+ }
143
+ const committed = getCommittedChanges(repoPath, lastIndexedSha);
144
+ if (committed.fallbackToFull) return committed;
145
+ const uncommitted = getUncommittedChanges(repoPath);
146
+ return mergeDiffs(committed, uncommitted, []);
147
+ }
148
+ function getChangedFilesList(diff) {
149
+ return [...diff.added, ...diff.modified];
150
+ }
151
+ function getDeletedFilesList(diff) {
152
+ return [...diff.deleted];
153
+ }
154
+ export {
155
+ getAllChanges,
156
+ getChangedFilesList,
157
+ getCommittedChanges,
158
+ getDeletedFilesList,
159
+ getUncommittedChanges
160
+ };
@@ -0,0 +1,233 @@
1
+ function isEntityNode(node) {
2
+ return node.type !== "module" && node.type !== "package";
3
+ }
4
+ function isContainsEdge(edge) {
5
+ return edge.type === "contains";
6
+ }
7
+ function deterministicSample(arr, n) {
8
+ if (arr.length <= n) return arr;
9
+ const step = arr.length / n;
10
+ const result = [];
11
+ for (let i = 0; i < n; i++) {
12
+ result.push(arr[Math.floor(i * step)]);
13
+ }
14
+ return result;
15
+ }
16
+ function generateGraphQualityReport(graph) {
17
+ const { nodes, links: edges } = graph;
18
+ const edgesByType = {};
19
+ for (const edge of edges) {
20
+ const t = edge.type ?? "unknown";
21
+ edgesByType[t] = (edgesByType[t] ?? 0) + 1;
22
+ }
23
+ const edgesByConfidence = { high: 0, medium: 0, low: 0, unscored: 0 };
24
+ for (const edge of edges) {
25
+ const conf = edge.confidence;
26
+ if (conf == null) {
27
+ edgesByConfidence.unscored++;
28
+ } else if (conf > 0.8) {
29
+ edgesByConfidence.high++;
30
+ } else if (conf >= 0.5) {
31
+ edgesByConfidence.medium++;
32
+ } else {
33
+ edgesByConfidence.low++;
34
+ }
35
+ }
36
+ const outgoing = /* @__PURE__ */ new Map();
37
+ const incoming = /* @__PURE__ */ new Map();
38
+ const fullAdj = /* @__PURE__ */ new Map();
39
+ for (const node of nodes) {
40
+ outgoing.set(node.id, []);
41
+ incoming.set(node.id, []);
42
+ fullAdj.set(node.id, /* @__PURE__ */ new Set());
43
+ }
44
+ for (const edge of edges) {
45
+ let setA = fullAdj.get(edge.source);
46
+ if (!setA) {
47
+ setA = /* @__PURE__ */ new Set();
48
+ fullAdj.set(edge.source, setA);
49
+ }
50
+ setA.add(edge.target);
51
+ let setB = fullAdj.get(edge.target);
52
+ if (!setB) {
53
+ setB = /* @__PURE__ */ new Set();
54
+ fullAdj.set(edge.target, setB);
55
+ }
56
+ setB.add(edge.source);
57
+ if (!isContainsEdge(edge)) {
58
+ const out = outgoing.get(edge.source);
59
+ if (out) out.push(edge.target);
60
+ const inc = incoming.get(edge.target);
61
+ if (inc) inc.push(edge.source);
62
+ }
63
+ }
64
+ const nodeMap = /* @__PURE__ */ new Map();
65
+ for (const node of nodes) {
66
+ nodeMap.set(node.id, node);
67
+ }
68
+ const entityNodes = nodes.filter(isEntityNode);
69
+ const orphanEntities = entityNodes.filter(
70
+ (n) => (outgoing.get(n.id)?.length ?? 0) === 0
71
+ );
72
+ const orphanEntityCount = orphanEntities.length;
73
+ const orphanEntityPercent = entityNodes.length > 0 ? Math.round(orphanEntityCount / entityNodes.length * 1e4) / 100 : 0;
74
+ let totalOutDegree = 0;
75
+ for (const n of entityNodes) {
76
+ totalOutDegree += outgoing.get(n.id)?.length ?? 0;
77
+ }
78
+ const avgOutDegreeEntities = entityNodes.length > 0 ? Math.round(totalOutDegree / entityNodes.length * 100) / 100 : 0;
79
+ const seeds = deterministicSample(entityNodes, 20);
80
+ let sumDepth1 = 0;
81
+ let sumDepth2 = 0;
82
+ let sumEntityDepth2 = 0;
83
+ for (const seed of seeds) {
84
+ const visited = /* @__PURE__ */ new Set([seed.id]);
85
+ let frontier = [seed.id];
86
+ const depth1Frontier = [];
87
+ for (const nid of frontier) {
88
+ for (const neighbor of outgoing.get(nid) ?? []) {
89
+ if (!visited.has(neighbor)) {
90
+ visited.add(neighbor);
91
+ depth1Frontier.push(neighbor);
92
+ }
93
+ }
94
+ for (const neighbor of incoming.get(nid) ?? []) {
95
+ if (!visited.has(neighbor)) {
96
+ visited.add(neighbor);
97
+ depth1Frontier.push(neighbor);
98
+ }
99
+ }
100
+ }
101
+ sumDepth1 += depth1Frontier.length;
102
+ frontier = depth1Frontier;
103
+ const depth2New = [];
104
+ for (const nid of frontier) {
105
+ for (const neighbor of outgoing.get(nid) ?? []) {
106
+ if (!visited.has(neighbor)) {
107
+ visited.add(neighbor);
108
+ depth2New.push(neighbor);
109
+ }
110
+ }
111
+ for (const neighbor of incoming.get(nid) ?? []) {
112
+ if (!visited.has(neighbor)) {
113
+ visited.add(neighbor);
114
+ depth2New.push(neighbor);
115
+ }
116
+ }
117
+ }
118
+ const totalDepth2 = depth1Frontier.length + depth2New.length;
119
+ sumDepth2 += totalDepth2;
120
+ let entityCount = 0;
121
+ for (const nid of [...depth1Frontier, ...depth2New]) {
122
+ const node = nodeMap.get(nid);
123
+ if (node && isEntityNode(node)) entityCount++;
124
+ }
125
+ sumEntityDepth2 += entityCount;
126
+ }
127
+ const sampleSize = seeds.length;
128
+ const bfsReachability = {
129
+ sampleSize,
130
+ avgReachedDepth1: sampleSize > 0 ? Math.round(sumDepth1 / sampleSize * 100) / 100 : 0,
131
+ avgReachedDepth2: sampleSize > 0 ? Math.round(sumDepth2 / sampleSize * 100) / 100 : 0,
132
+ avgEntityReachedDepth2: sampleSize > 0 ? Math.round(sumEntityDepth2 / sampleSize * 100) / 100 : 0
133
+ };
134
+ const degreeList = [];
135
+ for (const node of nodes) {
136
+ const deg = fullAdj.get(node.id)?.size ?? 0;
137
+ degreeList.push({ id: node.id, degree: deg, type: node.type ?? "unknown" });
138
+ }
139
+ degreeList.sort((a, b) => b.degree - a.degree);
140
+ const hubNodes = degreeList.slice(0, 10);
141
+ const componentVisited = /* @__PURE__ */ new Set();
142
+ const componentSizes = [];
143
+ for (const node of nodes) {
144
+ if (componentVisited.has(node.id)) continue;
145
+ let size = 0;
146
+ const queue = [node.id];
147
+ componentVisited.add(node.id);
148
+ while (queue.length > 0) {
149
+ const current = queue.pop();
150
+ size++;
151
+ for (const neighbor of fullAdj.get(current) ?? []) {
152
+ if (!componentVisited.has(neighbor)) {
153
+ componentVisited.add(neighbor);
154
+ queue.push(neighbor);
155
+ }
156
+ }
157
+ }
158
+ componentSizes.push(size);
159
+ }
160
+ const connectedComponents = componentSizes.length;
161
+ const largestComponent = componentSizes.length > 0 ? Math.max(...componentSizes) : 0;
162
+ const largestComponentPercent = nodes.length > 0 ? Math.round(largestComponent / nodes.length * 1e4) / 100 : 0;
163
+ return {
164
+ nodeCount: nodes.length,
165
+ edgeCount: edges.length,
166
+ edgesByType,
167
+ edgesByConfidence,
168
+ orphanEntityCount,
169
+ orphanEntityPercent,
170
+ avgOutDegreeEntities,
171
+ bfsReachability,
172
+ hubNodes,
173
+ connectedComponents,
174
+ largestComponentPercent
175
+ };
176
+ }
177
+ function formatQualityReport(report) {
178
+ const lines = [];
179
+ lines.push("# Graph Quality Report");
180
+ lines.push("");
181
+ lines.push("## Overview");
182
+ lines.push("");
183
+ lines.push(`| Metric | Value |`);
184
+ lines.push(`|--------|-------|`);
185
+ lines.push(`| Nodes | ${report.nodeCount} |`);
186
+ lines.push(`| Edges | ${report.edgeCount} |`);
187
+ lines.push(`| Connected components | ${report.connectedComponents} |`);
188
+ lines.push(`| Largest component | ${report.largestComponentPercent}% of nodes |`);
189
+ lines.push(`| Orphan entities | ${report.orphanEntityCount} (${report.orphanEntityPercent}%) |`);
190
+ lines.push(`| Avg out-degree (entities) | ${report.avgOutDegreeEntities} |`);
191
+ lines.push("");
192
+ lines.push("## Edges by Type");
193
+ lines.push("");
194
+ lines.push("| Type | Count |");
195
+ lines.push("|------|-------|");
196
+ const sortedTypes = Object.entries(report.edgesByType).sort((a, b) => b[1] - a[1]);
197
+ for (const [type, count] of sortedTypes) {
198
+ lines.push(`| ${type} | ${count} |`);
199
+ }
200
+ lines.push("");
201
+ lines.push("## Edges by Confidence");
202
+ lines.push("");
203
+ lines.push("| Bucket | Count |");
204
+ lines.push("|--------|-------|");
205
+ lines.push(`| High (>0.8) | ${report.edgesByConfidence.high} |`);
206
+ lines.push(`| Medium (0.5-0.8) | ${report.edgesByConfidence.medium} |`);
207
+ lines.push(`| Low (<0.5) | ${report.edgesByConfidence.low} |`);
208
+ lines.push(`| Unscored | ${report.edgesByConfidence.unscored} |`);
209
+ lines.push("");
210
+ lines.push("## BFS Reachability (non-contains edges)");
211
+ lines.push("");
212
+ lines.push(`Sample size: ${report.bfsReachability.sampleSize} entity nodes`);
213
+ lines.push("");
214
+ lines.push("| Metric | Value |");
215
+ lines.push("|--------|-------|");
216
+ lines.push(`| Avg reached at depth 1 | ${report.bfsReachability.avgReachedDepth1} |`);
217
+ lines.push(`| Avg reached at depth 2 | ${report.bfsReachability.avgReachedDepth2} |`);
218
+ lines.push(`| Avg entities reached at depth 2 | ${report.bfsReachability.avgEntityReachedDepth2} |`);
219
+ lines.push("");
220
+ lines.push("## Hub Nodes (top 10 by degree)");
221
+ lines.push("");
222
+ lines.push("| Node | Degree | Type |");
223
+ lines.push("|------|--------|------|");
224
+ for (const hub of report.hubNodes) {
225
+ lines.push(`| ${hub.id} | ${hub.degree} | ${hub.type} |`);
226
+ }
227
+ lines.push("");
228
+ return lines.join("\n");
229
+ }
230
+ export {
231
+ formatQualityReport,
232
+ generateGraphQualityReport
233
+ };