* Retrieve relevant context for a user message via code graph walk. * Zero LLM calls — pure graph traversal. * * @param {string} userMessage - The user's message/task * @param {function} mcpCall - mcpCall(tool, args) from mcp_bridge.js * @param {number} maxFiles - Max files to include (default
(userMessage, mcpCall, maxFiles = 8)
| 13 | * @returns {{ files: string[], symbols: string[], tokenEstimate: number }} |
| 14 | */ |
| 15 | async function retrieveContext(userMessage, mcpCall, maxFiles = 8) { |
| 16 | if (!mcpCall || !userMessage) return { files: [], symbols: [], tokenEstimate: 0 }; |
| 17 | |
| 18 | try { |
| 19 | // Extract keywords from user message for graph search |
| 20 | const keywords = extractKeywords(userMessage); |
| 21 | if (keywords.length === 0) return { files: [], symbols: [], tokenEstimate: 0 }; |
| 22 | |
| 23 | const results = []; |
| 24 | // Search top 3 keywords to avoid over-fetching |
| 25 | for (const kw of keywords.slice(0, 3)) { |
| 26 | try { |
| 27 | const r = await mcpCall('tools/call', { name: 'search_graph', arguments: { query: kw, max_tokens: 2000 } }); |
| 28 | if (r && !r.error) results.push(r); |
| 29 | } catch {} |
| 30 | } |
| 31 | |
| 32 | if (results.length === 0) return { files: [], symbols: [], tokenEstimate: 0 }; |
| 33 | |
| 34 | // Deduplicate and extract file paths from results |
| 35 | const fileSet = new Set(); |
| 36 | const symbolSet = new Set(); |
| 37 | for (const r of results) { |
| 38 | // Handle MCP tools/call format: { content: [{ text: '...' }] } |
| 39 | let text; |
| 40 | if (r && r.content && Array.isArray(r.content)) { |
| 41 | text = r.content.map(c => c.text || '').join('\n'); |
| 42 | } else { |
| 43 | text = typeof r === 'string' ? r : JSON.stringify(r); |
| 44 | } |
| 45 | // Extract file paths from graph results |
| 46 | const pathMatches = text.match(/[a-zA-Z0-9_\-/\\]+\.(ts|js|py|rs|go|java|c|cpp|md)/g) || []; |
| 47 | for (const p of pathMatches.slice(0, maxFiles)) fileSet.add(p); |
| 48 | // Extract symbol names (capitalized words or function patterns) |
| 49 | const symMatches = text.match(/\b[A-Z][a-zA-Z0-9]+\b|\bfunction\s+(\w+)/g) || []; |
| 50 | for (const s of symMatches.slice(0, 20)) symbolSet.add(s.replace(/^function\s+/, '')); |
| 51 | } |
| 52 | |
| 53 | const files = [...fileSet].slice(0, maxFiles); |
| 54 | const symbols = [...symbolSet].slice(0, 20); |
| 55 | const tokenEstimate = files.length * 50 + symbols.length * 10; |
| 56 | |
| 57 | return { files, symbols, tokenEstimate }; |
| 58 | } catch { |
| 59 | return { files: [], symbols: [], tokenEstimate: 0 }; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Extract search keywords from a user message. |
nothing calls this directly
no test coverage detected