codemap-ai 3.2.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,7 @@ function getStorage() {
29
29
  var server = new Server(
30
30
  {
31
31
  name: "codemap-flow",
32
- version: "3.2.0"
32
+ version: "3.4.0"
33
33
  },
34
34
  {
35
35
  capabilities: {
@@ -243,4 +243,4 @@ server.connect(transport).catch((error) => {
243
243
  console.error("Failed to start MCP server:", error);
244
244
  process.exit(1);
245
245
  });
246
- //# sourceMappingURL=flow-server-UQEOUP2C.js.map
246
+ //# sourceMappingURL=flow-server-4XSR5P4N.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp/flow-server.ts"],"sourcesContent":["/**\n * MCP Server for CodeMap Flow Edition\n * Provides 3 focused tools: impact analysis, flow tracing, and caller lookup\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { FlowStorage } from \"../flow/storage.js\";\nimport { resolve } from \"path\";\nimport { existsSync } from \"fs\";\n\n// Get config from environment\nconst DB_PATH = process.env.CODEMAP_DB_PATH || \".codemap/graph.db\";\nconst PROJECT_ROOT = process.env.CODEMAP_PROJECT_ROOT || process.cwd();\n\nlet storage: FlowStorage | null = null;\n\nfunction getStorage(): FlowStorage {\n if (!storage) {\n const dbPath = resolve(PROJECT_ROOT, DB_PATH);\n if (!existsSync(dbPath)) {\n throw new Error(`CodeMap database not found at ${dbPath}. Run 'codemap index' first.`);\n }\n storage = new FlowStorage(dbPath);\n }\n return storage;\n}\n\nconst server = new Server(\n {\n name: \"codemap-flow\",\n version: \"3.2.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\n// ============ Tools ============\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: \"codemap_impact\",\n description: \"Analyze what would break if you change a function/class. Shows all callers and affected files with risk assessment.\",\n inputSchema: {\n type: \"object\",\n properties: {\n target: {\n type: \"string\",\n description: \"Function or class name to analyze\",\n },\n depth: {\n type: \"number\",\n description: \"How many levels deep to analyze (default: 3)\",\n default: 3,\n },\n },\n required: [\"target\"],\n },\n },\n {\n name: \"codemap_trace_flow\",\n description: \"Trace the execution flow between two functions. Shows the complete call path from source to target.\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: {\n type: \"string\",\n description: \"Starting function name\",\n },\n to: {\n type: \"string\",\n description: \"Target function name\",\n },\n },\n required: [\"from\", \"to\"],\n },\n },\n {\n name: \"codemap_callers\",\n description: \"Find all functions that call a specific function. Shows where and how a function is used.\",\n inputSchema: {\n type: \"object\",\n properties: {\n function: {\n type: \"string\",\n description: \"Function name to find callers for\",\n },\n },\n required: [\"function\"],\n },\n },\n ],\n };\n});\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const db = getStorage();\n\n try {\n switch (name) {\n case \"codemap_impact\": {\n const target = args?.target as string;\n const depth = (args?.depth as number) || 3;\n\n // Find the target node(s)\n const nodes = db.searchNodesByName(target);\n\n if (nodes.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No function or class found with name \"${target}\"`,\n },\n ],\n };\n }\n\n const targetNode = nodes[0];\n\n // Get impacted nodes\n const impacted = db.getImpactedNodes(targetNode.id, depth);\n\n // Get unique files\n const affectedFiles = new Set(impacted.map((n) => n.filePath));\n\n // Assess risk\n const directCallers = db.getResolvedCallers(targetNode.id);\n let risk = \"LOW\";\n if (directCallers.length > 10) risk = \"HIGH\";\n else if (directCallers.length > 5) risk = \"MEDIUM\";\n\n const response = `# Impact Analysis: ${targetNode.name}\n\n**Location:** ${targetNode.filePath}:${targetNode.startLine}\n**Type:** ${targetNode.type}\n**Risk Level:** ${risk}\n\n## Direct Callers (${directCallers.length})\n${directCallers.slice(0, 10).map((c) => `- ${c.name} in ${c.filePath}:${c.startLine}`).join(\"\\n\")}\n${directCallers.length > 10 ? `\\n... and ${directCallers.length - 10} more` : \"\"}\n\n## Total Impact\n- **Affected functions:** ${impacted.length}\n- **Affected files:** ${affectedFiles.size}\n\n## Affected Files\n${[...affectedFiles].slice(0, 20).map((f) => `- ${f}`).join(\"\\n\")}\n${affectedFiles.size > 20 ? `\\n... and ${affectedFiles.size - 20} more` : \"\"}\n\n⚠️ **Recommendation:** ${risk === \"HIGH\" ? \"High-risk change. Review all callers before modifying.\" : risk === \"MEDIUM\" ? \"Moderate risk. Test affected files after changes.\" : \"Low risk. Changes unlikely to cause wide impact.\"}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n case \"codemap_trace_flow\": {\n const from = args?.from as string;\n const to = args?.to as string;\n\n // Find source and target nodes\n const sourceNodes = db.searchNodesByName(from);\n const targetNodes = db.searchNodesByName(to);\n\n if (sourceNodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Source function \"${from}\" not found` }],\n };\n }\n\n if (targetNodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Target function \"${to}\" not found` }],\n };\n }\n\n const sourceNode = sourceNodes[0];\n const targetNode = targetNodes[0];\n\n // Trace path\n const path = db.traceCallPath(sourceNode.id, targetNode.id);\n\n if (!path) {\n return {\n content: [\n {\n type: \"text\",\n text: `No call path found from \"${from}\" to \"${to}\". They may not be connected.`,\n },\n ],\n };\n }\n\n const response = `# Execution Flow: ${from} → ${to}\n\n${path.nodes\n .map(\n (n, i) =>\n `${i + 1}. **${n.name}**\\n File: ${n.file}:${n.line}${i < path.nodes.length - 1 ? \"\\n ↓ calls\" : \"\"}`\n )\n .join(\"\\n\\n\")}\n\n**Total steps:** ${path.nodes.length}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n case \"codemap_callers\": {\n const functionName = args?.function as string;\n\n // Find the function\n const nodes = db.searchNodesByName(functionName);\n\n if (nodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Function \"${functionName}\" not found` }],\n };\n }\n\n const targetNode = nodes[0];\n\n // Get all callers\n const callers = db.getResolvedCallers(targetNode.id);\n\n if (callers.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No callers found for \"${functionName}\". It may be unused or only called externally.`,\n },\n ],\n };\n }\n\n const response = `# Callers of ${functionName}\n\n**Function:** ${targetNode.name} (${targetNode.type})\n**Location:** ${targetNode.filePath}:${targetNode.startLine}\n**Total callers:** ${callers.length}\n\n## Who Calls This Function\n\n${callers\n .map(\n (c) => `- **${c.name}** (${c.type})\n File: ${c.filePath}:${c.startLine}`\n )\n .join(\"\\n\\n\")}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n default:\n return {\n content: [{ type: \"text\", text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\", text: `Error: ${errorMessage}` }],\n isError: true,\n };\n }\n});\n\n// Start server\nconst transport = new StdioServerTransport();\nserver.connect(transport).catch((error) => {\n console.error(\"Failed to start MCP server:\", error);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mcp/flow-server.ts"],"sourcesContent":["/**\n * MCP Server for CodeMap Flow Edition\n * Provides 3 focused tools: impact analysis, flow tracing, and caller lookup\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { FlowStorage } from \"../flow/storage.js\";\nimport { resolve } from \"path\";\nimport { existsSync } from \"fs\";\n\n// Get config from environment\nconst DB_PATH = process.env.CODEMAP_DB_PATH || \".codemap/graph.db\";\nconst PROJECT_ROOT = process.env.CODEMAP_PROJECT_ROOT || process.cwd();\n\nlet storage: FlowStorage | null = null;\n\nfunction getStorage(): FlowStorage {\n if (!storage) {\n const dbPath = resolve(PROJECT_ROOT, DB_PATH);\n if (!existsSync(dbPath)) {\n throw new Error(`CodeMap database not found at ${dbPath}. Run 'codemap index' first.`);\n }\n storage = new FlowStorage(dbPath);\n }\n return storage;\n}\n\nconst server = new Server(\n {\n name: \"codemap-flow\",\n version: \"3.4.0\",\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\n// ============ Tools ============\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: \"codemap_impact\",\n description: \"Analyze what would break if you change a function/class. Shows all callers and affected files with risk assessment.\",\n inputSchema: {\n type: \"object\",\n properties: {\n target: {\n type: \"string\",\n description: \"Function or class name to analyze\",\n },\n depth: {\n type: \"number\",\n description: \"How many levels deep to analyze (default: 3)\",\n default: 3,\n },\n },\n required: [\"target\"],\n },\n },\n {\n name: \"codemap_trace_flow\",\n description: \"Trace the execution flow between two functions. Shows the complete call path from source to target.\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: {\n type: \"string\",\n description: \"Starting function name\",\n },\n to: {\n type: \"string\",\n description: \"Target function name\",\n },\n },\n required: [\"from\", \"to\"],\n },\n },\n {\n name: \"codemap_callers\",\n description: \"Find all functions that call a specific function. Shows where and how a function is used.\",\n inputSchema: {\n type: \"object\",\n properties: {\n function: {\n type: \"string\",\n description: \"Function name to find callers for\",\n },\n },\n required: [\"function\"],\n },\n },\n ],\n };\n});\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const db = getStorage();\n\n try {\n switch (name) {\n case \"codemap_impact\": {\n const target = args?.target as string;\n const depth = (args?.depth as number) || 3;\n\n // Find the target node(s)\n const nodes = db.searchNodesByName(target);\n\n if (nodes.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No function or class found with name \"${target}\"`,\n },\n ],\n };\n }\n\n const targetNode = nodes[0];\n\n // Get impacted nodes\n const impacted = db.getImpactedNodes(targetNode.id, depth);\n\n // Get unique files\n const affectedFiles = new Set(impacted.map((n) => n.filePath));\n\n // Assess risk\n const directCallers = db.getResolvedCallers(targetNode.id);\n let risk = \"LOW\";\n if (directCallers.length > 10) risk = \"HIGH\";\n else if (directCallers.length > 5) risk = \"MEDIUM\";\n\n const response = `# Impact Analysis: ${targetNode.name}\n\n**Location:** ${targetNode.filePath}:${targetNode.startLine}\n**Type:** ${targetNode.type}\n**Risk Level:** ${risk}\n\n## Direct Callers (${directCallers.length})\n${directCallers.slice(0, 10).map((c) => `- ${c.name} in ${c.filePath}:${c.startLine}`).join(\"\\n\")}\n${directCallers.length > 10 ? `\\n... and ${directCallers.length - 10} more` : \"\"}\n\n## Total Impact\n- **Affected functions:** ${impacted.length}\n- **Affected files:** ${affectedFiles.size}\n\n## Affected Files\n${[...affectedFiles].slice(0, 20).map((f) => `- ${f}`).join(\"\\n\")}\n${affectedFiles.size > 20 ? `\\n... and ${affectedFiles.size - 20} more` : \"\"}\n\n⚠️ **Recommendation:** ${risk === \"HIGH\" ? \"High-risk change. Review all callers before modifying.\" : risk === \"MEDIUM\" ? \"Moderate risk. Test affected files after changes.\" : \"Low risk. Changes unlikely to cause wide impact.\"}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n case \"codemap_trace_flow\": {\n const from = args?.from as string;\n const to = args?.to as string;\n\n // Find source and target nodes\n const sourceNodes = db.searchNodesByName(from);\n const targetNodes = db.searchNodesByName(to);\n\n if (sourceNodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Source function \"${from}\" not found` }],\n };\n }\n\n if (targetNodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Target function \"${to}\" not found` }],\n };\n }\n\n const sourceNode = sourceNodes[0];\n const targetNode = targetNodes[0];\n\n // Trace path\n const path = db.traceCallPath(sourceNode.id, targetNode.id);\n\n if (!path) {\n return {\n content: [\n {\n type: \"text\",\n text: `No call path found from \"${from}\" to \"${to}\". They may not be connected.`,\n },\n ],\n };\n }\n\n const response = `# Execution Flow: ${from} → ${to}\n\n${path.nodes\n .map(\n (n, i) =>\n `${i + 1}. **${n.name}**\\n File: ${n.file}:${n.line}${i < path.nodes.length - 1 ? \"\\n ↓ calls\" : \"\"}`\n )\n .join(\"\\n\\n\")}\n\n**Total steps:** ${path.nodes.length}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n case \"codemap_callers\": {\n const functionName = args?.function as string;\n\n // Find the function\n const nodes = db.searchNodesByName(functionName);\n\n if (nodes.length === 0) {\n return {\n content: [{ type: \"text\", text: `Function \"${functionName}\" not found` }],\n };\n }\n\n const targetNode = nodes[0];\n\n // Get all callers\n const callers = db.getResolvedCallers(targetNode.id);\n\n if (callers.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: `No callers found for \"${functionName}\". It may be unused or only called externally.`,\n },\n ],\n };\n }\n\n const response = `# Callers of ${functionName}\n\n**Function:** ${targetNode.name} (${targetNode.type})\n**Location:** ${targetNode.filePath}:${targetNode.startLine}\n**Total callers:** ${callers.length}\n\n## Who Calls This Function\n\n${callers\n .map(\n (c) => `- **${c.name}** (${c.type})\n File: ${c.filePath}:${c.startLine}`\n )\n .join(\"\\n\\n\")}\n`;\n\n return {\n content: [{ type: \"text\", text: response }],\n };\n }\n\n default:\n return {\n content: [{ type: \"text\", text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\", text: `Error: ${errorMessage}` }],\n isError: true,\n };\n }\n});\n\n// Start server\nconst transport = new StdioServerTransport();\nserver.connect(transport).catch((error) => {\n console.error(\"Failed to start MCP server:\", error);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -8,6 +8,392 @@ import { createHash } from "crypto";
8
8
  import { glob } from "glob";
9
9
  import { resolve, relative, extname } from "path";
10
10
 
11
+ // src/analysis/class-hierarchy.ts
12
+ function buildClassHierarchy(nodes) {
13
+ const classes = /* @__PURE__ */ new Map();
14
+ const children = /* @__PURE__ */ new Map();
15
+ const parents = /* @__PURE__ */ new Map();
16
+ for (const node of nodes) {
17
+ if (node.type === "class") {
18
+ const classNode = {
19
+ id: node.id,
20
+ name: node.name,
21
+ file: node.filePath,
22
+ line: node.startLine,
23
+ extends: node.extends || [],
24
+ methods: []
25
+ // Will populate in next step
26
+ };
27
+ classes.set(node.name, classNode);
28
+ }
29
+ }
30
+ for (const node of nodes) {
31
+ if (node.type === "method" && node.parentClass) {
32
+ const classNode = classes.get(node.parentClass);
33
+ if (classNode) {
34
+ classNode.methods.push(node.name);
35
+ }
36
+ }
37
+ }
38
+ for (const [className, classNode] of classes) {
39
+ for (const parentName of classNode.extends) {
40
+ if (!children.has(parentName)) {
41
+ children.set(parentName, []);
42
+ }
43
+ children.get(parentName).push(className);
44
+ if (!parents.has(className)) {
45
+ parents.set(className, []);
46
+ }
47
+ parents.get(className).push(parentName);
48
+ }
49
+ }
50
+ return {
51
+ classes,
52
+ children,
53
+ parents
54
+ };
55
+ }
56
+ function findMethodDefinition(className, methodName, hierarchy) {
57
+ const visited = /* @__PURE__ */ new Set();
58
+ const queue = [className];
59
+ while (queue.length > 0) {
60
+ const current = queue.shift();
61
+ if (visited.has(current)) {
62
+ continue;
63
+ }
64
+ visited.add(current);
65
+ const classNode = hierarchy.classes.get(current);
66
+ if (classNode) {
67
+ if (classNode.methods.includes(methodName)) {
68
+ return classNode;
69
+ }
70
+ const parentNames = hierarchy.parents.get(current) || [];
71
+ for (const parent of parentNames) {
72
+ queue.push(parent);
73
+ }
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ function resolveMethodCall(instanceType, methodName, hierarchy) {
79
+ return findMethodDefinition(instanceType, methodName, hierarchy);
80
+ }
81
+
82
+ // src/flow/enhanced-resolver.ts
83
+ var EnhancedResolver = class {
84
+ constructor(storage) {
85
+ this.storage = storage;
86
+ }
87
+ hierarchy = null;
88
+ variableTypes = /* @__PURE__ */ new Map();
89
+ /**
90
+ * Build class hierarchy from all nodes in storage
91
+ */
92
+ buildHierarchy() {
93
+ const nodes = this.getAllNodes();
94
+ this.hierarchy = buildClassHierarchy(nodes);
95
+ }
96
+ /**
97
+ * Load variable type information from storage
98
+ */
99
+ loadVariableTypes() {
100
+ const stmt = this.storage.db.prepare(`
101
+ SELECT * FROM variable_types
102
+ `);
103
+ const rows = stmt.all();
104
+ for (const row of rows) {
105
+ const scopeId = row.scope_node_id;
106
+ if (!this.variableTypes.has(scopeId)) {
107
+ this.variableTypes.set(scopeId, []);
108
+ }
109
+ this.variableTypes.get(scopeId).push({
110
+ name: row.variable_name,
111
+ type: row.type_name,
112
+ scope: scopeId,
113
+ line: row.line
114
+ });
115
+ }
116
+ }
117
+ /**
118
+ * Resolve all unresolved edges using type information
119
+ */
120
+ resolveAllCalls() {
121
+ if (!this.hierarchy) {
122
+ this.buildHierarchy();
123
+ }
124
+ this.loadVariableTypes();
125
+ const resolvedCalls = [];
126
+ const stmt = this.storage.db.prepare(`
127
+ SELECT * FROM edges
128
+ WHERE target_id LIKE 'ref:%'
129
+ AND type = 'calls'
130
+ `);
131
+ const edges = stmt.all();
132
+ for (const edgeRow of edges) {
133
+ const edge = {
134
+ id: edgeRow.id,
135
+ type: edgeRow.type,
136
+ sourceId: edgeRow.source_id,
137
+ targetId: edgeRow.target_id,
138
+ metadata: edgeRow.metadata ? JSON.parse(edgeRow.metadata) : void 0
139
+ };
140
+ const resolved = this.resolveCall(edge);
141
+ if (resolved.targetNode) {
142
+ resolvedCalls.push(resolved);
143
+ this.updateEdgeTarget(edge.id, resolved.targetNode.id);
144
+ }
145
+ }
146
+ return resolvedCalls;
147
+ }
148
+ /**
149
+ * Resolve a single call edge
150
+ */
151
+ resolveCall(edge) {
152
+ const unresolvedName = edge.metadata?.unresolvedName;
153
+ if (!unresolvedName) {
154
+ return {
155
+ originalEdge: edge,
156
+ targetNode: null,
157
+ confidence: "LOW",
158
+ reason: "No unresolved name in metadata"
159
+ };
160
+ }
161
+ const sourceNode = this.storage.getNode(edge.sourceId);
162
+ if (!sourceNode) {
163
+ return {
164
+ originalEdge: edge,
165
+ targetNode: null,
166
+ confidence: "LOW",
167
+ reason: "Source node not found"
168
+ };
169
+ }
170
+ if (!unresolvedName.includes(".")) {
171
+ return this.resolveSimpleCall(unresolvedName, sourceNode, edge);
172
+ }
173
+ return this.resolveMethodCall(unresolvedName, sourceNode, edge);
174
+ }
175
+ /**
176
+ * Resolve simple function call (no dot notation)
177
+ */
178
+ resolveSimpleCall(functionName, sourceNode, edge) {
179
+ const sameFileNodes = this.storage.getNodesByFile(sourceNode.filePath);
180
+ const localMatch = sameFileNodes.find(
181
+ (n) => (n.type === "function" || n.type === "method") && n.name === functionName
182
+ );
183
+ if (localMatch) {
184
+ return {
185
+ originalEdge: edge,
186
+ targetNode: localMatch,
187
+ confidence: "HIGH",
188
+ reason: "Found in same file"
189
+ };
190
+ }
191
+ const globalMatches = this.storage.searchNodesByName(functionName);
192
+ if (globalMatches.length === 1) {
193
+ return {
194
+ originalEdge: edge,
195
+ targetNode: globalMatches[0],
196
+ confidence: "MEDIUM",
197
+ reason: "Single global match"
198
+ };
199
+ }
200
+ if (globalMatches.length > 1) {
201
+ const sourceDir = sourceNode.filePath.split("/").slice(0, -1).join("/");
202
+ const sameDirMatch = globalMatches.find((n) => n.filePath.startsWith(sourceDir));
203
+ if (sameDirMatch) {
204
+ return {
205
+ originalEdge: edge,
206
+ targetNode: sameDirMatch,
207
+ confidence: "MEDIUM",
208
+ reason: "Multiple matches, picked same directory"
209
+ };
210
+ }
211
+ return {
212
+ originalEdge: edge,
213
+ targetNode: globalMatches[0],
214
+ confidence: "LOW",
215
+ reason: `Multiple matches (${globalMatches.length}), picked first`
216
+ };
217
+ }
218
+ return {
219
+ originalEdge: edge,
220
+ targetNode: null,
221
+ confidence: "LOW",
222
+ reason: "No matches found"
223
+ };
224
+ }
225
+ /**
226
+ * Resolve method call using type information
227
+ * Pattern: obj.method() or self.method()
228
+ */
229
+ resolveMethodCall(fullName, sourceNode, edge) {
230
+ const parts = fullName.split(".");
231
+ const varName = parts[0];
232
+ const methodName = parts.slice(1).join(".");
233
+ if (varName === "self" || varName === "this") {
234
+ return this.resolveSelfMethodCall(methodName, sourceNode, edge);
235
+ }
236
+ const scopeTypes = this.variableTypes.get(sourceNode.id) || [];
237
+ const varType = scopeTypes.find((v) => v.name === varName);
238
+ if (!varType) {
239
+ return this.resolveSimpleCall(methodName, sourceNode, edge);
240
+ }
241
+ if (!this.hierarchy) {
242
+ return {
243
+ originalEdge: edge,
244
+ targetNode: null,
245
+ confidence: "LOW",
246
+ reason: "Class hierarchy not built"
247
+ };
248
+ }
249
+ const classNode = resolveMethodCall(varType.type, methodName, this.hierarchy);
250
+ if (classNode) {
251
+ const methodNode = this.findMethodInClass(classNode.name, methodName);
252
+ if (methodNode) {
253
+ return {
254
+ originalEdge: edge,
255
+ targetNode: methodNode,
256
+ confidence: "HIGH",
257
+ reason: `Resolved via type tracking: ${varName}: ${varType.type}`
258
+ };
259
+ }
260
+ }
261
+ return {
262
+ originalEdge: edge,
263
+ targetNode: null,
264
+ confidence: "LOW",
265
+ reason: `Type found (${varType.type}) but method not resolved`
266
+ };
267
+ }
268
+ /**
269
+ * Resolve self.method() or this.method()
270
+ */
271
+ resolveSelfMethodCall(methodName, sourceNode, edge) {
272
+ const parentClass = sourceNode.metadata?.parentClass;
273
+ if (!parentClass) {
274
+ return {
275
+ originalEdge: edge,
276
+ targetNode: null,
277
+ confidence: "LOW",
278
+ reason: "self/this call but no parent class"
279
+ };
280
+ }
281
+ if (!this.hierarchy) {
282
+ return {
283
+ originalEdge: edge,
284
+ targetNode: null,
285
+ confidence: "LOW",
286
+ reason: "Class hierarchy not built"
287
+ };
288
+ }
289
+ const classNode = resolveMethodCall(parentClass, methodName, this.hierarchy);
290
+ if (classNode) {
291
+ const methodNode2 = this.findMethodInClass(classNode.name, methodName);
292
+ if (methodNode2) {
293
+ return {
294
+ originalEdge: edge,
295
+ targetNode: methodNode2,
296
+ confidence: "HIGH",
297
+ reason: `Resolved self.${methodName} in ${parentClass}`
298
+ };
299
+ }
300
+ }
301
+ const sameFileNodes = this.storage.getNodesByFile(sourceNode.filePath);
302
+ const methodNode = sameFileNodes.find(
303
+ (n) => n.type === "method" && n.name === methodName
304
+ );
305
+ if (methodNode) {
306
+ return {
307
+ originalEdge: edge,
308
+ targetNode: methodNode,
309
+ confidence: "MEDIUM",
310
+ reason: "Found method in same file"
311
+ };
312
+ }
313
+ return {
314
+ originalEdge: edge,
315
+ targetNode: null,
316
+ confidence: "LOW",
317
+ reason: `Method ${methodName} not found in ${parentClass}`
318
+ };
319
+ }
320
+ /**
321
+ * Find method node in a class
322
+ */
323
+ findMethodInClass(className, methodName) {
324
+ const stmt = this.storage.db.prepare(`
325
+ SELECT * FROM nodes
326
+ WHERE type = 'method'
327
+ AND name = ?
328
+ AND json_extract(metadata, '$.parentClass') = ?
329
+ `);
330
+ const row = stmt.get(methodName, className);
331
+ if (!row) return null;
332
+ return {
333
+ id: row.id,
334
+ type: row.type,
335
+ name: row.name,
336
+ filePath: row.file_path,
337
+ startLine: row.start_line,
338
+ endLine: row.end_line,
339
+ language: row.language,
340
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
341
+ };
342
+ }
343
+ /**
344
+ * Get all nodes from storage
345
+ */
346
+ getAllNodes() {
347
+ const stmt = this.storage.db.prepare("SELECT * FROM nodes");
348
+ const rows = stmt.all();
349
+ return rows.map((row) => ({
350
+ id: row.id,
351
+ type: row.type,
352
+ name: row.name,
353
+ filePath: row.file_path,
354
+ startLine: row.start_line,
355
+ endLine: row.end_line,
356
+ language: row.language,
357
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
358
+ }));
359
+ }
360
+ /**
361
+ * Update edge target in database
362
+ */
363
+ updateEdgeTarget(edgeId, newTargetId) {
364
+ const stmt = this.storage.db.prepare(`
365
+ UPDATE edges
366
+ SET target_id = ?
367
+ WHERE id = ?
368
+ `);
369
+ stmt.run(newTargetId, edgeId);
370
+ }
371
+ /**
372
+ * Get resolution statistics
373
+ */
374
+ getStats() {
375
+ const totalStmt = this.storage.db.prepare(`
376
+ SELECT COUNT(*) as count FROM edges WHERE type = 'calls'
377
+ `);
378
+ const totalRow = totalStmt.get();
379
+ const totalCalls = totalRow.count;
380
+ const resolvedStmt = this.storage.db.prepare(`
381
+ SELECT COUNT(*) as count FROM edges
382
+ WHERE type = 'calls' AND target_id NOT LIKE 'ref:%'
383
+ `);
384
+ const resolvedRow = resolvedStmt.get();
385
+ const resolvedCalls = resolvedRow.count;
386
+ const unresolvedCalls = totalCalls - resolvedCalls;
387
+ const resolutionRate = totalCalls > 0 ? resolvedCalls / totalCalls * 100 : 0;
388
+ return {
389
+ totalCalls,
390
+ resolvedCalls,
391
+ unresolvedCalls,
392
+ resolutionRate
393
+ };
394
+ }
395
+ };
396
+
11
397
  // src/parsers/base.ts
12
398
  var BaseParser = class {
13
399
  nodeIdCounter = 0;
@@ -262,31 +648,38 @@ var TypeScriptParser = class extends BaseParser {
262
648
  handleClass(node, filePath, analysis, parentId) {
263
649
  const nameNode = node.childForFieldName("name");
264
650
  if (!nameNode) return;
651
+ const extends_ = [];
652
+ for (const child of node.children) {
653
+ if (child.type === "class_heritage") {
654
+ const extendsClause = child.children.find((c) => c.type === "extends_clause");
655
+ if (extendsClause) {
656
+ const baseClass = extendsClause.children.find((c) => c.type === "identifier");
657
+ if (baseClass) {
658
+ extends_.push(baseClass.text);
659
+ }
660
+ }
661
+ }
662
+ }
265
663
  const classNode = this.createNode(
266
664
  "class",
267
665
  nameNode.text,
268
666
  filePath,
269
667
  node.startPosition.row + 1,
270
- node.endPosition.row + 1
668
+ node.endPosition.row + 1,
669
+ {
670
+ extends: extends_
671
+ }
271
672
  );
272
673
  analysis.nodes.push(classNode);
273
674
  analysis.edges.push(
274
675
  this.createEdge("contains", parentId, classNode.id)
275
676
  );
276
- for (const child of node.children) {
277
- if (child.type === "class_heritage") {
278
- const extendsClause = child.children.find((c) => c.type === "extends_clause");
279
- if (extendsClause) {
280
- const baseClass = extendsClause.children.find((c) => c.type === "identifier");
281
- if (baseClass) {
282
- analysis.edges.push(
283
- this.createEdge("extends", classNode.id, `ref:${baseClass.text}`, {
284
- unresolvedName: baseClass.text
285
- })
286
- );
287
- }
288
- }
289
- }
677
+ for (const baseClass of extends_) {
678
+ analysis.edges.push(
679
+ this.createEdge("extends", classNode.id, `ref:${baseClass}`, {
680
+ unresolvedName: baseClass
681
+ })
682
+ );
290
683
  }
291
684
  const body = node.childForFieldName("body");
292
685
  if (body) {
@@ -300,6 +693,8 @@ var TypeScriptParser = class extends BaseParser {
300
693
  handleMethod(node, filePath, analysis, parentId) {
301
694
  const nameNode = node.childForFieldName("name");
302
695
  if (!nameNode) return;
696
+ const parentNode = analysis.nodes.find((n) => n.id === parentId);
697
+ const parentClass = parentNode?.type === "class" ? parentNode.name : void 0;
303
698
  const methodNode = this.createNode(
304
699
  "method",
305
700
  nameNode.text,
@@ -308,7 +703,8 @@ var TypeScriptParser = class extends BaseParser {
308
703
  node.endPosition.row + 1,
309
704
  {
310
705
  static: node.children.some((c) => c.type === "static"),
311
- async: node.children.some((c) => c.type === "async")
706
+ async: node.children.some((c) => c.type === "async"),
707
+ parentClass
312
708
  }
313
709
  );
314
710
  analysis.nodes.push(methodNode);
@@ -743,6 +1139,8 @@ var PythonParser = class extends BaseParser {
743
1139
  node.endPosition.row + 1,
744
1140
  {
745
1141
  bases,
1142
+ extends: bases,
1143
+ // Add extends for consistency with TypeScript
746
1144
  isPrivate: name.startsWith("_")
747
1145
  }
748
1146
  );
@@ -780,6 +1178,8 @@ var PythonParser = class extends BaseParser {
780
1178
  const nameNode = node.childForFieldName("name");
781
1179
  if (!nameNode) return;
782
1180
  const name = nameNode.text;
1181
+ const parentNode = analysis.nodes.find((n) => n.id === classId);
1182
+ const parentClass = parentNode?.type === "class" ? parentNode.name : void 0;
783
1183
  const decorators = [];
784
1184
  if (node.parent?.type === "decorated_definition") {
785
1185
  for (const sibling of node.parent.children) {
@@ -808,7 +1208,8 @@ var PythonParser = class extends BaseParser {
808
1208
  classmethod: isClassMethod,
809
1209
  property: isProperty,
810
1210
  isPrivate: name.startsWith("_"),
811
- isDunder: name.startsWith("__") && name.endsWith("__")
1211
+ isDunder: name.startsWith("__") && name.endsWith("__"),
1212
+ parentClass
812
1213
  }
813
1214
  );
814
1215
  analysis.nodes.push(methodNode);
@@ -1011,6 +1412,12 @@ var FlowBuilder = class {
1011
1412
  currentFile: ""
1012
1413
  });
1013
1414
  this.resolveAllCalls();
1415
+ const enhancedResolver = new EnhancedResolver(this.storage);
1416
+ enhancedResolver.buildHierarchy();
1417
+ const enhancedResolved = enhancedResolver.resolveAllCalls();
1418
+ if (enhancedResolved.length > 0) {
1419
+ console.log(`\u2728 Enhanced resolver: resolved ${enhancedResolved.length} additional calls`);
1420
+ }
1014
1421
  const stats = this.storage.getStats();
1015
1422
  this.emitProgress({
1016
1423
  phase: "complete",