kirograph 1.0.0 → 1.1.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 (84) hide show
  1. package/README.md +3 -2
  2. package/dist/architecture/manifest/python.js +2 -2
  3. package/dist/architecture/manifest/python.js.map +2 -2
  4. package/dist/bin/commands/help.js +3 -1
  5. package/dist/bin/commands/help.js.map +2 -2
  6. package/dist/bin/commands/memory.js +18 -8
  7. package/dist/bin/commands/memory.js.map +2 -2
  8. package/dist/bin/commands/reachability.js +7 -2
  9. package/dist/bin/commands/reachability.js.map +2 -2
  10. package/dist/bin/commands/vulns.js +16 -1
  11. package/dist/bin/commands/vulns.js.map +2 -2
  12. package/dist/bin/commands/wiki.js +56 -0
  13. package/dist/bin/commands/wiki.js.map +2 -2
  14. package/dist/bin/installer/steering.js +9 -0
  15. package/dist/bin/installer/steering.js.map +2 -2
  16. package/dist/bin/kirograph.js +1 -1
  17. package/dist/config.js +4 -0
  18. package/dist/config.js.map +2 -2
  19. package/dist/extraction/extractor.js +46 -41
  20. package/dist/extraction/extractor.js.map +2 -2
  21. package/dist/extraction/notebook.js +15 -7
  22. package/dist/extraction/notebook.js.map +2 -2
  23. package/dist/jev/client.js +99 -0
  24. package/dist/jev/client.js.map +7 -0
  25. package/dist/jev/types.js +17 -0
  26. package/dist/jev/types.js.map +7 -0
  27. package/dist/mcp/handlers/memory.js +14 -8
  28. package/dist/mcp/handlers/memory.js.map +2 -2
  29. package/dist/mcp/handlers/security.js +5 -3
  30. package/dist/mcp/handlers/security.js.map +2 -2
  31. package/dist/mcp/tools.js +1 -1
  32. package/dist/mcp/tools.js.map +1 -1
  33. package/dist/memory/index.js +13 -1
  34. package/dist/memory/index.js.map +3 -3
  35. package/dist/memory/relation-jev.js +59 -0
  36. package/dist/memory/relation-jev.js.map +7 -0
  37. package/dist/memory/schema.js +57 -0
  38. package/dist/memory/schema.js.map +7 -0
  39. package/dist/security/auth-detection-jev.js +47 -0
  40. package/dist/security/auth-detection-jev.js.map +7 -0
  41. package/dist/security/manifest/adapter.js +17 -3
  42. package/dist/security/manifest/adapter.js.map +3 -3
  43. package/dist/security/manifest/plugins/cargo.js +23 -0
  44. package/dist/security/manifest/plugins/cargo.js.map +2 -2
  45. package/dist/security/manifest/plugins/composer.js +12 -0
  46. package/dist/security/manifest/plugins/composer.js.map +2 -2
  47. package/dist/security/manifest/plugins/gradle.js +12 -0
  48. package/dist/security/manifest/plugins/gradle.js.map +2 -2
  49. package/dist/security/manifest/plugins/hex.js +12 -0
  50. package/dist/security/manifest/plugins/hex.js.map +2 -2
  51. package/dist/security/manifest/plugins/maven.js +68 -1
  52. package/dist/security/manifest/plugins/maven.js.map +2 -2
  53. package/dist/security/manifest/plugins/npm.js +86 -0
  54. package/dist/security/manifest/plugins/npm.js.map +2 -2
  55. package/dist/security/manifest/plugins/nuget.js +13 -0
  56. package/dist/security/manifest/plugins/nuget.js.map +2 -2
  57. package/dist/security/manifest/plugins/pubspec.js +12 -0
  58. package/dist/security/manifest/plugins/pubspec.js.map +2 -2
  59. package/dist/security/manifest/plugins/pyproject.js +12 -0
  60. package/dist/security/manifest/plugins/pyproject.js.map +2 -2
  61. package/dist/security/manifest/plugins/rubygems.js +12 -0
  62. package/dist/security/manifest/plugins/rubygems.js.map +2 -2
  63. package/dist/security/pipeline.js +1 -1
  64. package/dist/security/pipeline.js.map +2 -2
  65. package/dist/security/reachability.js +54 -5
  66. package/dist/security/reachability.js.map +3 -3
  67. package/dist/security/types.js.map +1 -1
  68. package/dist/security/vuln/client.js +45 -1
  69. package/dist/security/vuln/client.js.map +2 -2
  70. package/dist/security/vuln/osv-adapter.js +45 -1
  71. package/dist/security/vuln/osv-adapter.js.map +2 -2
  72. package/dist/wiki/contradiction-jev.js +49 -0
  73. package/dist/wiki/contradiction-jev.js.map +7 -0
  74. package/dist/wiki/index.js +13 -0
  75. package/dist/wiki/index.js.map +2 -2
  76. package/dist/wiki/ingest.js +2 -0
  77. package/dist/wiki/ingest.js.map +2 -2
  78. package/dist/wiki/links.js +53 -0
  79. package/dist/wiki/links.js.map +7 -0
  80. package/dist/wiki/lint.js +3 -6
  81. package/dist/wiki/lint.js.map +2 -2
  82. package/dist/wiki/rename.js +103 -0
  83. package/dist/wiki/rename.js.map +7 -0
  84. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extraction/extractor.ts"],
4
- "sourcesContent": ["/**\n * Symbol extractor using web-tree-sitter\n * Parses source files and extracts nodes + edges into the graph.\n * Handles functions, classes, methods, variables, constants, and imports.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as crypto from 'crypto';\nimport type { Node, Edge, NodeKind, Language } from '../types';\nimport { detectLanguage, isSupportedLanguage } from './languages';\nimport { initGrammars, getParser, hasWasmGrammar } from './grammars';\nimport { extractNotebook } from './notebook';\n\nexport interface UnresolvedRef {\n sourceId: string;\n refName: string;\n refKind: 'function' | 'import' | 'extends' | 'implements';\n line: number;\n column: number;\n}\n\nexport interface ExtractedFile {\n filePath: string;\n language: Language;\n contentHash: string;\n fileSize: number;\n nodes: Node[];\n edges: Edge[];\n unresolvedRefs: UnresolvedRef[];\n}\n\nexport function makeNodeId(filePath: string, kind: string, name: string, line: number): string {\n const hash = crypto.createHash('sha256').update(`${filePath}:${kind}:${name}:${line}`).digest('hex').slice(0, 32);\n return `${kind}:${hash}`;\n}\n\n/** Validate that filePath stays within projectRoot to prevent path traversal. */\nfunction validatePath(filePath: string, projectRoot: string): boolean {\n try {\n const real = fs.realpathSync(filePath);\n const realRoot = fs.realpathSync(projectRoot);\n return real.startsWith(realRoot + path.sep) || real === realRoot;\n } catch {\n // If we can't resolve, check with normalize\n const normalized = path.resolve(filePath);\n return normalized.startsWith(path.resolve(projectRoot));\n }\n}\n\n/**\n * Extract symbols from a single file.\n * Optionally accepts pre-read content for batch I/O efficiency.\n */\nexport async function extractFile(filePath: string, projectRoot: string, content?: Buffer | string, opts?: { enableComplexity?: boolean; maxFileSize?: number }): Promise<ExtractedFile | null> {\n const language = detectLanguage(filePath);\n if (!isSupportedLanguage(language)) return null;\n\n // Path traversal protection\n if (!validatePath(filePath, projectRoot)) return null;\n\n let source: string;\n try {\n if (content !== undefined) {\n source = typeof content === 'string' ? content : content.toString('utf8');\n } else {\n source = fs.readFileSync(filePath, 'utf8');\n }\n } catch {\n return null;\n }\n\n // Defensive size guard (issue #33): even though the scanner enforces\n // maxFileSize via fs.stat, some callers (e.g. git-changed-file sync) bypass\n // the scanner and hand paths straight here. A multi-MB file handed to the\n // tree-sitter WASM parser can abort the entire process, so skip oversized\n // files before parsing. We return the file as tracked-but-unparsed (empty\n // symbols) rather than null so it's still recorded, not silently dropped.\n if (opts?.maxFileSize !== undefined && Buffer.byteLength(source, 'utf8') > opts.maxFileSize) {\n const relPathSkip = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n return {\n filePath: relPathSkip,\n language,\n contentHash: crypto.createHash('sha256').update(source).digest('hex'),\n fileSize: Buffer.byteLength(source, 'utf8'),\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n\n // Delegate Jupyter notebooks to the dedicated notebook extractor\n if (language === 'jupyter') {\n return extractNotebook(filePath, projectRoot, source);\n }\n\n const contentHash = crypto.createHash('sha256').update(source).digest('hex');\n const fileSize = Buffer.byteLength(source, 'utf8');\n const relPath = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n\n await initGrammars();\n const parser = await getParser(language);\n if (!parser) {\n if (!hasWasmGrammar(language)) {\n // Language genuinely has no grammar (e.g. Liquid, unknown) \u2014 track file but no AST extraction\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n // Grammar should exist but parser failed (likely WASM crash) \u2014 signal extraction failure\n throw new Error(`Parser unavailable for ${language} (WASM grammar exists but failed to load)`);\n }\n\n // Skip Go template files disguised as YAML (e.g. Helm templates).\n // tree-sitter-yaml crashes with \"memory access out of bounds\" on {{ }} syntax.\n // These files produce valid YAML only after Helm renders them \u2014 raw they're not parseable.\n if (language === 'yaml' && source.includes('{{')) {\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n\n // Wrap parser.parse() to catch WASM runtime crashes (e.g. memory access out of bounds\n // on malformed files like Helm Go templates in .yaml). Skip the file gracefully instead\n // of letting the crash propagate and poison the entire language.\n let tree: any;\n try {\n tree = parser.parse(source);\n } catch (parseErr: any) {\n const parseMsg = parseErr?.message ?? String(parseErr);\n const isWasmCrash = parseErr?.constructor?.name === 'RuntimeError'\n || parseMsg.includes('memory access out of bounds')\n || parseMsg.includes('Aborted(');\n if (isWasmCrash) {\n // Individual file crashed the WASM parser \u2014 skip this file, return empty nodes.\n // The next file in this language may parse fine.\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n throw parseErr; // Re-throw non-WASM errors\n }\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const unresolvedRefs: UnresolvedRef[] = [];\n const now = Date.now();\n\n walkTree(tree.rootNode, source, relPath, language, nodes, edges, unresolvedRefs, now);\n\n // Complexity post-processing: compute metrics for function/method nodes when enabled.\n if (opts?.enableComplexity) {\n const sourceLines = source.split('\\n');\n for (const n of nodes) {\n if (n.kind !== 'function' && n.kind !== 'method') continue;\n const fnSource = sourceLines.slice(n.startLine - 1, n.endLine).join('\\n');\n const cc = computeTextCyclomaticComplexity(fnSource);\n const depth = computeTextNestingDepth(fnSource);\n const loc = n.endLine - n.startLine + 1;\n const tokens = fnSource.match(/\\b\\w+\\b|[+\\-*/=<>!&|^~%]+|[(){}[\\],;.]/g) ?? [];\n const volume = tokens.length > 0 && tokens.length > 1\n ? tokens.length * Math.log2(new Set(tokens).size || 1)\n : 0;\n const mi = Math.max(0, Math.min(100,\n 171 - 5.2 * Math.log(Math.max(1, volume)) - 0.23 * cc - 16.2 * Math.log(Math.max(1, loc))\n ));\n n.complexityCyclomatic = cc;\n n.nestingDepth = depth;\n n.maintainabilityIndex = Math.round(mi * 10) / 10;\n }\n }\n\n return { filePath: relPath, language, contentHash, fileSize, nodes, edges, unresolvedRefs };\n}\n\n// \u2500\u2500 Elixir helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ELIXIR_DEF_KINDS: Record<string, NodeKind> = {\n defmodule: 'module',\n def: 'function',\n defp: 'function',\n defmacro: 'function',\n defmacrop: 'function',\n defprotocol: 'interface',\n defimpl: 'class',\n defstruct: 'struct',\n};\n\nconst ELIXIR_IMPORT_TARGETS = new Set(['alias', 'import', 'require', 'use']);\n\nfunction elixirCallTarget(node: any, source: string): string | null {\n const target = node.childForFieldName?.('target') ?? node.child(0);\n if (!target) return null;\n return source.slice(target.startIndex, target.endIndex).trim() || null;\n}\n\nfunction elixirFirstArg(node: any): any | null {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'arguments') {\n return child.namedChild(0) ?? child.child(0) ?? null;\n }\n }\n return null;\n}\n\nfunction extractElixirName(node: any, source: string, kind: NodeKind): string | null {\n const firstArg = elixirFirstArg(node);\n if (!firstArg) return null;\n if (kind === 'module' || kind === 'interface' || kind === 'class') {\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim();\n }\n if (kind === 'function') {\n if (firstArg.type === 'call') {\n const nameNode = firstArg.childForFieldName?.('target') ?? firstArg.child(0);\n if (nameNode) return source.slice(nameNode.startIndex, nameNode.endIndex).trim();\n }\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim();\n }\n return null;\n}\n\nfunction extractElixirImportSource(node: any, source: string): string | null {\n const firstArg = elixirFirstArg(node);\n if (!firstArg) return null;\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim() || null;\n}\n\nfunction walkElixirCall(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): void {\n const targetText = elixirCallTarget(node, source);\n if (!targetText) {\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n return;\n }\n\n if (ELIXIR_IMPORT_TARGETS.has(targetText)) {\n const modPath = extractElixirImportSource(node, source);\n if (modPath) {\n const id = makeNodeId(filePath, 'import', modPath, node.startPosition.row + 1);\n nodes.push({\n id, kind: 'import', name: modPath,\n qualifiedName: `${filePath}::import:${modPath}`,\n filePath, language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n });\n unresolvedRefs.push({ sourceId: id, refName: modPath, refKind: 'import', line: node.startPosition.row + 1, column: node.startPosition.column });\n }\n return;\n }\n\n const defKind = ELIXIR_DEF_KINDS[targetText];\n if (defKind) {\n const name = extractElixirName(node, source, defKind);\n if (name) {\n const id = makeNodeId(filePath, defKind, name, node.startPosition.row + 1);\n const isPrivate = targetText === 'defp' || targetText === 'defmacrop';\n nodes.push({\n id, kind: defKind, name,\n qualifiedName: `${filePath}::${name}`,\n filePath, language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractDocstring(node, source),\n signature: extractSignature(node, source, defKind),\n visibility: isPrivate ? 'private' : 'public',\n isExported: !isPrivate,\n updatedAt: now,\n });\n if (parentId) edges.push({ source: parentId, target: id, kind: 'contains' });\n collectCallRefs(node, source, id, unresolvedRefs);\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, id);\n }\n return;\n }\n }\n\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n}\n\n// \u2500\u2500 Node type mappings \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** AST node types that should be descended without creating a symbol node */\nconst TRANSPARENT_TYPES = new Set([\n 'export_statement', 'program', 'source_file', 'module', 'translation_unit',\n 'chunk', // Lua root node type\n]);\n\n/** Mapping from tree-sitter node types to graph NodeKind */\nconst KIND_MAP: Record<string, NodeKind> = {\n // Functions\n function_declaration: 'function',\n function_expression: 'function',\n arrow_function: 'function',\n function_definition: 'function', // Python, Scala\n function_item: 'function', // Rust\n function_declaration_go: 'function',\n // Methods\n method_definition: 'method',\n method_declaration: 'method', // Go, Java, C#\n constructor_declaration: 'method', // Java, C#\n // Classes / structs\n class_declaration: 'class',\n abstract_class_declaration: 'class', // TypeScript `abstract class` (distinct AST node from class_declaration)\n class_expression: 'class',\n class_definition: 'class', // Python, Scala\n impl_item: 'class', // Rust (impl blocks)\n struct_item: 'struct', // Rust\n struct_definition: 'struct', // Zig\n // Interfaces / traits\n interface_declaration: 'interface',\n trait_item: 'trait', // Rust\n trait_definition: 'trait', // Scala\n protocol_declaration: 'interface', // Swift\n // Enums\n enum_declaration: 'enum',\n enum_item: 'enum', // Rust\n // Type aliases\n type_alias_declaration: 'type_alias',\n type_declaration: 'type_alias', // Go\n typealias_declaration: 'type_alias',// Swift\n type_item: 'type_alias', // Rust\n type_alias: 'type_alias', // Kotlin\n // Namespaces / modules\n namespace_declaration: 'namespace',\n module_definition: 'namespace', // OCaml\n // Variables / constants (language-specific, see extractVariableKind)\n lexical_declaration: 'variable', // TS/JS (const/let/var) \u2014 refined below\n variable_declaration: 'variable', // TS/JS (var)\n // Ruby\n method: 'method', // Ruby def (top-level and in class body)\n class: 'class', // Ruby class\n // Lua\n local_function_definition_statement: 'function',\n function_definition_statement: 'function',\n // Import statements (all handled via extractImport)\n};\n\n/** Refine variable declarations into 'variable' or 'constant' based on modifier */\nfunction extractVariableKind(node: any): NodeKind {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'const') return 'constant';\n }\n return 'variable';\n}\n\n/**\n * Widget superclass names that mark a Dart class as a UI component.\n * `State<Foo>` subclasses are kept as 'class' \u2014 they are state holders, not widgets.\n */\nconst DART_WIDGET_SUPERCLASSES = new Set([\n 'StatelessWidget',\n 'StatefulWidget',\n 'HookWidget',\n 'ConsumerWidget',\n 'ConsumerStatefulWidget',\n]);\n\n/**\n * Inspect a Dart class_definition node's superclass clause to decide if the\n * class is a widget component or a plain class.\n *\n * Dart tree-sitter grammar: class_definition \u2192 superclass \u2192 type_identifier\n * We look for any child/grandchild whose text is one of the known widget base classes.\n */\nfunction refineDartClassKind(node: any, source: string): NodeKind {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'superclass') {\n // Walk all descendants of the superclass clause looking for a type_identifier\n const superText = source.slice(child.startIndex, child.endIndex);\n // Quick string check first for performance\n if (DART_WIDGET_SUPERCLASSES.has(superText.replace(/^extends\\s+/, '').trim())) {\n return 'component';\n }\n // Drill into children to find the exact type_identifier\n for (let j = 0; j < child.childCount; j++) {\n const typeNode = child.child(j);\n if (typeNode.type === 'type_identifier' || typeNode.type === 'identifier') {\n const name = source.slice(typeNode.startIndex, typeNode.endIndex).trim();\n if (DART_WIDGET_SUPERCLASSES.has(name)) return 'component';\n // Also match any name that ends with \"Widget\" (catches custom base widgets)\n if (name.endsWith('Widget') && name !== 'State') return 'component';\n }\n }\n }\n }\n return 'class';\n}\n\n/** Languages/node-types that represent import statements */\nconst IMPORT_NODE_TYPES = new Set([\n 'import_statement', // TS/JS\n 'import_from_statement', // Python\n 'import_declaration', // Go, Java\n 'use_declaration', // Rust\n 'using_directive', // C#\n 'namespace_use_declaration',// PHP\n 'import_header', // Kotlin\n 'import_or_export', // Dart\n 'include_statement', // Pascal\n 'preproc_include', // C/C++\n]);\n\n// \u2500\u2500 Main tree walker \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction walkTree(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): void {\n if (TRANSPARENT_TYPES.has(node.type)) {\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n return;\n }\n\n // Elixir: all definitions and imports are `call` nodes\n if (language === 'elixir' && node.type === 'call') {\n walkElixirCall(node, source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n return;\n }\n\n // Handle import statements\n if (IMPORT_NODE_TYPES.has(node.type)) {\n const importNode = extractImport(node, source, filePath, language, unresolvedRefs, now, parentId);\n if (importNode) nodes.push(importNode);\n return; // Don't recurse into import nodes\n }\n\n let kind: NodeKind | null = KIND_MAP[node.type] ?? null;\n\n // Refine lexical_declaration into variable/constant\n if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {\n kind = extractVariableKind(node);\n }\n\n // Python/Go/Rust/Java/C# variable and constant types\n if (!kind) kind = getLanguageSpecificKind(node.type, language);\n\n // Dart: refine class_definition kind based on superclass (Widget \u2192 component)\n if (language === 'dart' && node.type === 'class_definition' && kind === 'class') {\n kind = refineDartClassKind(node, source);\n }\n\n if (kind) {\n const name = extractName(node, source, language, kind);\n if (name) {\n const id = makeNodeId(filePath, kind, name, node.startPosition.row + 1);\n const visibility = extractVisibility(node, source, language);\n const graphNode: Node = {\n id,\n kind,\n name,\n qualifiedName: `${filePath}::${name}`,\n filePath,\n language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractDocstring(node, source),\n signature: extractSignature(node, source, kind),\n visibility,\n isExported: isExported(node),\n isAsync: isAsync(node),\n isStatic: isStatic(node),\n isAbstract: isAbstract(node),\n updatedAt: now,\n };\n nodes.push(graphNode);\n\n if (parentId) {\n edges.push({ source: parentId, target: id, kind: 'contains' });\n }\n\n // Collect call references within this symbol\n collectCallRefs(node, source, id, unresolvedRefs);\n\n // Extract inheritance edges for C# and Java class/interface nodes\n if ((kind === 'class' || kind === 'interface') && (language === 'csharp' || language === 'java')) {\n extractInheritance(node, source, language, id, unresolvedRefs);\n }\n\n // Recurse with this node as parent\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, id);\n }\n return;\n }\n }\n\n // No symbol \u2014 recurse without changing parent\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n}\n\n// \u2500\u2500 Language-specific node kinds \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\n\nfunction getLanguageSpecificKind(type: string, lang: Language): NodeKind | null {\n switch (lang) {\n case 'python':\n if (type === 'assignment') return 'variable';\n break;\n case 'go':\n if (type === 'var_declaration' || type === 'short_var_declaration') return 'variable';\n if (type === 'const_declaration') return 'constant';\n if (type === 'type_spec') return 'type_alias';\n break;\n case 'rust':\n if (type === 'let_declaration') return 'variable';\n if (type === 'const_item') return 'constant';\n if (type === 'static_item') return 'variable';\n break;\n case 'java':\n if (type === 'field_declaration') return 'property';\n if (type === 'local_variable_declaration') return 'variable';\n break;\n case 'csharp':\n if (type === 'field_declaration') return 'property';\n break;\n case 'php':\n if (type === 'property_declaration') return 'property';\n if (type === 'const_declaration') return 'constant';\n break;\n case 'kotlin':\n if (type === 'property_declaration') return 'variable';\n break;\n case 'swift':\n if (type === 'property_declaration') return 'property';\n if (type === 'constant_declaration') return 'constant';\n break;\n case 'dart':\n if (type === 'function_signature') return 'function';\n if (type === 'method_signature') return 'method';\n break;\n case 'ruby':\n if (type === 'singleton_method') return 'method';\n break;\n case 'scala':\n if (type === 'object_definition') return 'class';\n if (type === 'val_definition') return 'variable';\n if (type === 'var_definition') return 'variable';\n if (type === 'type_definition') return 'type_alias';\n break;\n case 'lua':\n if (type === 'local_function') return 'function';\n if (type === 'local_variable_declaration') return 'variable';\n if (type === 'variable_assignment') return 'variable';\n break;\n case 'zig':\n if (type === 'VarDecl') return 'variable';\n if (type === 'ContainerDecl') return 'struct';\n break;\n case 'bash':\n if (type === 'variable_assignment') return 'variable';\n break;\n case 'ocaml':\n if (type === 'let_binding') return 'variable';\n if (type === 'type_binding') return 'type_alias';\n if (type === 'module_binding') return 'namespace';\n break;\n case 'elm':\n if (type === 'function_declaration_left') return 'function';\n if (type === 'type_alias_declaration') return 'type_alias';\n break;\n case 'solidity':\n if (type === 'contract_declaration') return 'class';\n if (type === 'event_definition') return 'function';\n if (type === 'modifier_definition') return 'function';\n if (type === 'state_variable_declaration') return 'variable';\n break;\n case 'objc':\n if (type === 'class_interface') return 'class';\n if (type === 'class_implementation') return 'class';\n if (type === 'protocol_declaration') return 'interface';\n if (type === 'method_declaration') return 'method';\n if (type === 'property_declaration') return 'property';\n break;\n case 'hcl':\n if (type === 'block') return 'namespace';\n if (type === 'attribute') return 'variable';\n break;\n case 'scss':\n if (type === 'mixin_statement') return 'function';\n if (type === 'function_statement') return 'function';\n if (type === 'variable_declaration') return 'variable';\n if (type === 'include_statement') return 'import';\n if (type === 'use_statement') return 'import';\n if (type === 'forward_statement') return 'import';\n if (type === 'placeholder') return 'class';\n break;\n case 'css':\n if (type === 'rule_set') return 'class';\n if (type === 'declaration') return 'variable';\n break;\n case 'rescript':\n if (type === 'let_binding') return 'function';\n if (type === 'type_definition') return 'type_alias';\n if (type === 'module_declaration') return 'module';\n break;\n case 'sql':\n if (type === 'create_function_statement') return 'function';\n if (type === 'create_table_statement') return 'class';\n if (type === 'create_view_statement') return 'function';\n if (type === 'create_procedure_statement') return 'function';\n break;\n case 'r':\n if (type === 'function_definition') return 'function';\n if (type === 'left_assignment') return 'variable';\n break;\n case 'julia':\n if (type === 'function_definition') return 'function';\n if (type === 'struct_definition') return 'struct';\n if (type === 'module_definition') return 'module';\n if (type === 'macro_definition') return 'function';\n break;\n case 'powershell':\n if (type === 'function_statement') return 'function';\n if (type === 'class_statement') return 'class';\n break;\n case 'perl':\n if (type === 'subroutine_declaration_statement') return 'function';\n if (type === 'package_statement') return 'module';\n break;\n case 'gdscript':\n if (type === 'function_definition') return 'function';\n if (type === 'class_definition') return 'class';\n if (type === 'variable_statement') return 'variable';\n break;\n case 'astro':\n if (type === 'function_declaration') return 'function';\n if (type === 'function_definition') return 'function';\n break;\n case 'nix':\n if (type === 'function') return 'function';\n if (type === 'binding') return 'variable';\n break;\n case 'verilog':\n if (type === 'module_declaration') return 'class';\n if (type === 'task_declaration') return 'function';\n if (type === 'function_declaration') return 'function';\n break;\n }\n return null;\n}\n\n// \u2500\u2500 Import extraction \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractImport(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): Node | null {\n const modulePath = extractImportSource(node, source, language);\n if (!modulePath) return null;\n\n const id = makeNodeId(filePath, 'import', modulePath, node.startPosition.row + 1);\n const importNode: Node = {\n id,\n kind: 'import',\n name: modulePath,\n qualifiedName: `${filePath}::import:${modulePath}`,\n filePath,\n language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n };\n\n // Register as unresolved import for later file-level resolution\n unresolvedRefs.push({\n sourceId: id,\n refName: modulePath,\n refKind: 'import',\n line: node.startPosition.row + 1,\n column: node.startPosition.column,\n });\n\n return importNode;\n}\n\n/** Extract the module path string from an import statement node */\nfunction extractImportSource(node: any, source: string, language: Language): string | null {\n // TS/JS/Svelte: import X from \"module\" \u2192 look for trailing string literal\n if (\n node.type === 'import_statement' ||\n (language === 'svelte' && node.type === 'import_statement')\n ) {\n for (let i = node.childCount - 1; i >= 0; i--) {\n const child = node.child(i);\n if (child.type === 'string') {\n return stripQuotes(source.slice(child.startIndex, child.endIndex));\n }\n }\n }\n\n // Python: from .module import X or import module\n if (node.type === 'import_from_statement') {\n // Look for dotted_name or relative_import after 'from'\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name' || child.type === 'relative_import') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n if (node.type === 'import_statement' && language === 'python') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // Go: import \"path\" or import ( \"path\" )\n if (node.type === 'import_declaration') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'string') {\n return stripQuotes(source.slice(child.startIndex, child.endIndex));\n }\n if (child.type === 'import_spec_list') {\n for (let j = 0; j < child.childCount; j++) {\n const spec = child.child(j);\n if (spec.type === 'import_spec') {\n for (let k = 0; k < spec.childCount; k++) {\n const s = spec.child(k);\n if (s.type === 'string') return stripQuotes(source.slice(s.startIndex, s.endIndex));\n }\n }\n }\n }\n }\n }\n\n // Rust: use path::to::module;\n if (node.type === 'use_declaration') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type !== 'use' && child.type !== ';') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // C#: using Namespace.Name;\n if (node.type === 'using_directive') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'qualified_name' || child.type === 'identifier') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // Java/Kotlin: import a.b.c\n if (node.type === 'import_declaration' || node.type === 'import_header') {\n const text = source.slice(node.startIndex, node.endIndex).trim();\n const m = text.match(/^import\\s+(.+?)[;\\s*]*$/);\n if (m) return m[1].trim();\n }\n\n // PHP: use Foo\\Bar\n if (node.type === 'namespace_use_declaration') {\n const text = source.slice(node.startIndex, node.endIndex).trim();\n const m = text.match(/^use\\s+(.+?)[;\\s]*$/);\n if (m) return m[1].trim();\n }\n\n // Swift/Dart: import SomeModule\n if (node.type === 'import_declaration' || node.type === 'import_or_export') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'identifier' || child.type === 'string') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // C/C++: #include \"file.h\" or #include <lib.h>\n if (node.type === 'preproc_include') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'string_literal' || child.type === 'system_lib_string') {\n return source.slice(child.startIndex, child.endIndex).replace(/[<>\"]/g, '');\n }\n }\n }\n\n return null;\n}\n\nfunction stripQuotes(s: string): string {\n return s.replace(/^['\"`]|['\"`]$/g, '');\n}\n\n// \u2500\u2500 Name extraction \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractName(node: any, source: string, _lang: Language, kind: NodeKind): string | null {\n // For variable/constant declarations (lexical_declaration, variable_declaration),\n // the identifier is inside a variable_declarator child\n if (kind === 'variable' || kind === 'constant') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'variable_declarator') {\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') return source.slice(gc.startIndex, gc.endIndex);\n }\n }\n }\n }\n\n // For Go const_declaration / var_declaration \u2192 look inside var_spec / const_spec\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'var_spec' || child.type === 'const_spec') {\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') return source.slice(gc.startIndex, gc.endIndex);\n }\n }\n }\n\n // Lua function_definition_statement: name lives inside a `variable` child (e.g. M.greet)\n // Extract the last identifier to get the function name (not the module prefix)\n if (node.type === 'function_definition_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'variable') {\n let lastId: string | null = null;\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') lastId = source.slice(gc.startIndex, gc.endIndex);\n }\n if (lastId) return lastId;\n }\n }\n }\n\n // Standard: look for first named-identifier child\n // Covers: identifier, property_identifier, type_identifier (most languages),\n // simple_identifier (Kotlin, Swift), word (Bash), name (PHP), constant (Ruby class)\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (\n child.type === 'identifier' ||\n child.type === 'property_identifier' ||\n child.type === 'type_identifier' ||\n child.type === 'simple_identifier' ||\n child.type === 'word' ||\n child.type === 'name' ||\n child.type === 'constant'\n ) {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n\n // Python assignment: the left side is the name\n if (node.type === 'assignment') {\n const left = node.child(0);\n if (left && left.type === 'identifier') {\n return source.slice(left.startIndex, left.endIndex);\n }\n }\n\n return null;\n}\n\n// \u2500\u2500 Signature extraction \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\u2500\u2500\u2500\u2500\u2500\n\nfunction extractSignature(node: any, source: string, kind: NodeKind): string | undefined {\n // For functions/methods, try to get the meaningful header without the body\n if (kind === 'function' || kind === 'method') {\n // Try to find parameter list \u2014 stop before the body block\n const text = source.slice(node.startIndex, node.endIndex);\n // Find opening brace or colon (Python), take everything before it\n const bodyStart = text.search(/\\s*[\\{:]\\s*\\n|=>|{/);\n const header = bodyStart > 0 ? text.slice(0, bodyStart).trim() : text.split('\\n')[0].trim();\n return header.length > 150 ? header.slice(0, 150) + '\u2026' : header || undefined;\n }\n\n // Default: first line truncated\n const text = source.slice(node.startIndex, node.endIndex);\n const firstLine = text.split('\\n')[0].trim();\n return firstLine.length > 120 ? firstLine.slice(0, 120) + '\u2026' : firstLine || undefined;\n}\n\n// \u2500\u2500 Visibility extraction \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\u2500\u2500\u2500\u2500\n\nfunction extractVisibility(node: any, source: string, language: Language): Node['visibility'] {\n // TypeScript / JavaScript: accessibility_modifier child\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'accessibility_modifier') {\n const mod = source.slice(child.startIndex, child.endIndex);\n if (mod === 'public') return 'public';\n if (mod === 'private') return 'private';\n if (mod === 'protected') return 'protected';\n }\n // Direct modifier keywords (Java, C#, PHP, Swift, Kotlin)\n if (child.type === 'public') return 'public';\n if (child.type === 'private') return 'private';\n if (child.type === 'protected') return 'protected';\n if (child.type === 'internal') return 'internal';\n }\n\n // Rust: no `pub` keyword = private, `pub` = public\n if (language === 'rust') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'visibility_modifier') {\n const v = source.slice(child.startIndex, child.endIndex);\n return v.startsWith('pub') ? 'public' : 'private';\n }\n }\n return 'private'; // Rust default\n }\n\n // C# default: private\n if (language === 'csharp') return 'private';\n\n // PHP default: public\n if (language === 'php') return 'public';\n\n // Swift: check for access modifiers\n if (language === 'swift') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const t = child.type;\n if (t === 'fileprivate') return 'private';\n }\n return 'internal'; // Swift default\n }\n\n // Python: underscore prefix convention\n if (language === 'python') {\n const name = extractName(node, source, language, 'function');\n if (name?.startsWith('_') && !name.startsWith('__')) return 'protected';\n if (name?.startsWith('__')) return 'private';\n return 'public';\n }\n\n return undefined;\n}\n\n// \u2500\u2500 Misc helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractDocstring(node: any, source: string): string | undefined {\n const commentTypes = new Set(['comment', 'block_comment', 'line_comment', 'documentation_comment']);\n const commentLines: string[] = [];\n\n let sibling = node.previousNamedSibling;\n while (sibling && commentTypes.has(sibling.type)) {\n commentLines.unshift(source.slice(sibling.startIndex, sibling.endIndex));\n sibling = sibling.previousNamedSibling;\n }\n\n if (commentLines.length === 0) return undefined;\n\n const cleaned = commentLines.join('\\n')\n .replace(/^\\/\\*+\\s*/gm, '')\n .replace(/\\s*\\*+\\/\\s*$/gm, '')\n .replace(/^\\s*\\*\\s?/gm, '')\n .replace(/^\\/\\/\\s?/gm, '')\n .trim();\n\n return cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction isExported(node: any): boolean {\n // Walk up the parent chain to find an export_statement ancestor\n let current = node.parent;\n while (current) {\n if (current.type === 'export_statement') return true;\n // Stop at scope boundaries\n if (current.type === 'function_body' || current.type === 'class_body' || current.type === 'statement_block') break;\n current = current.parent;\n }\n return false;\n}\n\nfunction isAsync(node: any): boolean {\n for (let i = 0; i < node.childCount; i++) {\n if (node.child(i).type === 'async') return true;\n }\n return false;\n}\n\nfunction isStatic(node: any): boolean {\n for (let i = 0; i < node.childCount; i++) {\n if (node.child(i).type === 'static') return true;\n }\n return false;\n}\n\n/**\n * Detect abstract declarations. Handles both grammars that use a dedicated\n * abstract node type (TypeScript: `abstract_class_declaration`) and those that\n * fold `abstract` into a modifier/keyword child (Java, C#, Kotlin, PHP).\n */\nfunction isAbstract(node: any): boolean {\n if (node.type === 'abstract_class_declaration') return true;\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'abstract') return true;\n // Java/C#/Kotlin wrap keywords in a `modifiers` child\n if (child.type === 'modifiers') {\n for (let j = 0; j < child.childCount; j++) {\n if (child.child(j).type === 'abstract') return true;\n }\n }\n }\n return false;\n}\n\n// \u2500\u2500 Call reference collection \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\n\n/**\n * Node types that represent a function/method call across all supported languages.\n * Using a combined set avoids threading a language parameter through the recursive walker.\n */\nconst CALL_NODE_TYPES = new Set([\n 'call_expression', // JS/TS/Go/Rust/Swift/Kotlin/Dart/C/C++\n 'invocation_expression', // C#\n 'method_invocation', // Java\n 'call', // Python/Ruby/Elixir (function calls in bodies)\n 'function_call_expression', // PHP\n]);\n\n/**\n * Extract the callee name from a call node.\n * Uses named field lookup first (more precise), then falls back to first-child heuristic.\n */\nfunction extractCallName(node: any, source: string): string | null {\n // Named fields \u2014 Java uses 'name', Python/Ruby use 'function'/'method', Elixir uses 'target'\n for (const field of ['name', 'method', 'function', 'target']) {\n const f = node.childForFieldName?.(field);\n if (f) {\n const t = f.type;\n if (t === 'identifier' || t === 'name' || t === 'property_identifier' || t === 'type_identifier') {\n const text = source.slice(f.startIndex, f.endIndex).trim();\n if (text && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) return text;\n }\n }\n }\n // Fallback: first child, take final segment of dotted access (covers JS/TS, C#, Go, Rust\u2026)\n const funcNode = node.child(0);\n if (funcNode) {\n const rawName = source.slice(funcNode.startIndex, funcNode.endIndex).split('(')[0].trim();\n if (rawName && rawName.length < 100) {\n const calleeName = rawName.split('.').pop()!.trim();\n if (calleeName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(calleeName)) return calleeName;\n }\n }\n return null;\n}\n\nfunction collectCallRefs(node: any, source: string, sourceId: string, unresolvedRefs: UnresolvedRef[]): void {\n walkForCalls(node, source, sourceId, unresolvedRefs);\n}\n\nfunction walkForCalls(node: any, source: string, sourceId: string, unresolvedRefs: UnresolvedRef[]): void {\n if (CALL_NODE_TYPES.has(node.type)) {\n const calleeName = extractCallName(node, source);\n if (calleeName) {\n unresolvedRefs.push({\n sourceId,\n refName: calleeName,\n refKind: 'function',\n line: node.startPosition.row + 1,\n column: node.startPosition.column,\n });\n }\n }\n for (let i = 0; i < node.childCount; i++) {\n walkForCalls(node.child(i), source, sourceId, unresolvedRefs);\n }\n}\n\n// \u2500\u2500 Inheritance extraction \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\u2500\u2500\u2500\n\n/**\n * Extract the simple type name from an AST node representing a type reference.\n * Strips generic parameters (e.g. \"List<T>\" \u2192 \"List\").\n */\nfunction extractTypeName(node: any, source: string): string | null {\n const t = node.type;\n if (t === 'identifier' || t === 'type_identifier' || t === 'name') {\n return source.slice(node.startIndex, node.endIndex).trim() || null;\n }\n // qualified_name / generic_name: take the rightmost identifier\n if (t === 'qualified_name' || t === 'generic_name' || t === 'scoped_type_identifier') {\n for (let i = node.childCount - 1; i >= 0; i--) {\n const child = node.child(i);\n if (child.type === 'identifier' || child.type === 'type_identifier' || child.type === 'name') {\n return source.slice(child.startIndex, child.endIndex).trim() || null;\n }\n }\n }\n return null;\n}\n\n/**\n * Scan a class/interface AST node for its base types and add them as unresolved\n * extends/implements refs, which the resolver later turns into graph edges.\n *\n * C#: base_list children \u2192 all are either base class or interface (can't distinguish without types)\n * Java: superclass \u2192 extends; super_interfaces / extends_interfaces \u2192 implements / extends\n */\nfunction extractInheritance(\n node: any,\n source: string,\n language: Language,\n classNodeId: string,\n unresolvedRefs: UnresolvedRef[],\n): void {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n\n if (language === 'csharp' && child.type === 'base_list') {\n for (let j = 0; j < child.childCount; j++) {\n const item = child.child(j);\n const name = extractTypeName(item, source);\n if (name) {\n unresolvedRefs.push({\n sourceId: classNodeId,\n refName: name,\n // In C# both classes and interfaces appear in the same base_list;\n // we emit 'extends' for all \u2014 the resolver maps it to the extends edge\n // which covers both parent classes and parent interfaces structurally.\n refKind: 'extends',\n line: item.startPosition.row + 1,\n column: item.startPosition.column,\n });\n }\n }\n }\n\n if (language === 'java') {\n if (child.type === 'superclass') {\n // superclass has a single type_identifier or scoped_type_identifier child\n const typeNode = child.namedChild?.(0);\n if (typeNode) {\n const name = extractTypeName(typeNode, source);\n if (name) unresolvedRefs.push({ sourceId: classNodeId, refName: name, refKind: 'extends', line: child.startPosition.row + 1, column: child.startPosition.column });\n }\n }\n if (child.type === 'super_interfaces' || child.type === 'extends_interfaces') {\n // contains a type_list with one or more types\n const typeList = child.namedChild?.(0);\n if (typeList) {\n const count = typeList.namedChildCount ?? 0;\n for (let j = 0; j < count; j++) {\n const typeNode = typeList.namedChild(j);\n if (typeNode) {\n const name = extractTypeName(typeNode, source);\n if (name) {\n unresolvedRefs.push({\n sourceId: classNodeId,\n refName: name,\n refKind: child.type === 'extends_interfaces' ? 'extends' : 'implements',\n line: typeNode.startPosition.row + 1,\n column: typeNode.startPosition.column,\n });\n }\n }\n }\n }\n }\n }\n }\n}\n\n// \u2500\u2500 Text-based complexity helpers \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\n// Used when enableComplexity=true in extractFile(). Text heuristics are less\n// accurate than full AST walking but require no per-language grammar handling.\n\nconst CC_PATTERNS = [\n /\\bif\\b/g, /\\belse\\s+if\\b/g, /\\belif\\b/g,\n /\\bfor\\b/g, /\\bwhile\\b/g, /\\bdo\\b/g,\n /\\bcase\\b/g, /\\bcatch\\b/g, /\\bexcept\\b/g,\n /&&/g, /\\|\\|/g, /\\?\\?/g,\n // ternary ? that is not ?. or ?: or optional chaining\n /(?<![?!])\\?(?![.?:])/g,\n];\n\nfunction computeTextCyclomaticComplexity(src: string): number {\n // Strip string literals and comments to avoid counting keywords inside them\n const stripped = src\n .replace(/`[^`]*`/g, '``')\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, '\"\"')\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, \"''\")\n .replace(/\\/\\/[^\\n]*/g, '')\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n let count = 0;\n for (const pat of CC_PATTERNS) count += (stripped.match(pat) ?? []).length;\n return 1 + count;\n}\n\nfunction computeTextNestingDepth(src: string): number {\n let depth = 0, max = 0;\n for (const ch of src) {\n if (ch === '{') { depth++; if (depth > max) max = depth; }\n else if (ch === '}') depth = Math.max(0, depth - 1);\n }\n return max;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Symbol extractor using web-tree-sitter\n * Parses source files and extracts nodes + edges into the graph.\n * Handles functions, classes, methods, variables, constants, and imports.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as crypto from 'crypto';\nimport type { Node, Edge, NodeKind, Language } from '../types';\nimport { detectLanguage, isSupportedLanguage } from './languages';\nimport { initGrammars, getParser, hasWasmGrammar } from './grammars';\nimport { extractNotebook } from './notebook';\n\nexport interface UnresolvedRef {\n sourceId: string;\n refName: string;\n refKind: 'function' | 'import' | 'extends' | 'implements';\n line: number;\n column: number;\n}\n\nexport interface ExtractedFile {\n filePath: string;\n language: Language;\n contentHash: string;\n fileSize: number;\n nodes: Node[];\n edges: Edge[];\n unresolvedRefs: UnresolvedRef[];\n}\n\nexport function makeNodeId(filePath: string, kind: string, name: string, line: number): string {\n const hash = crypto.createHash('sha256').update(`${filePath}:${kind}:${name}:${line}`).digest('hex').slice(0, 32);\n return `${kind}:${hash}`;\n}\n\n/** Validate that filePath stays within projectRoot to prevent path traversal. */\nfunction validatePath(filePath: string, projectRoot: string): boolean {\n try {\n const real = fs.realpathSync(filePath);\n const realRoot = fs.realpathSync(projectRoot);\n return real.startsWith(realRoot + path.sep) || real === realRoot;\n } catch {\n // If we can't resolve, check with normalize\n const normalized = path.resolve(filePath);\n return normalized.startsWith(path.resolve(projectRoot));\n }\n}\n\n/**\n * Extract symbols from a single file.\n * Optionally accepts pre-read content for batch I/O efficiency.\n */\nexport async function extractFile(filePath: string, projectRoot: string, content?: Buffer | string, opts?: { enableComplexity?: boolean; maxFileSize?: number }): Promise<ExtractedFile | null> {\n const language = detectLanguage(filePath);\n if (!isSupportedLanguage(language)) return null;\n\n // Path traversal protection\n if (!validatePath(filePath, projectRoot)) return null;\n\n let source: string;\n try {\n if (content !== undefined) {\n source = typeof content === 'string' ? content : content.toString('utf8');\n } else {\n source = fs.readFileSync(filePath, 'utf8');\n }\n } catch {\n return null;\n }\n\n // Defensive size guard (issue #33): even though the scanner enforces\n // maxFileSize via fs.stat, some callers (e.g. git-changed-file sync) bypass\n // the scanner and hand paths straight here. A multi-MB file handed to the\n // tree-sitter WASM parser can abort the entire process, so skip oversized\n // files before parsing. We return the file as tracked-but-unparsed (empty\n // symbols) rather than null so it's still recorded, not silently dropped.\n if (opts?.maxFileSize !== undefined && Buffer.byteLength(source, 'utf8') > opts.maxFileSize) {\n const relPathSkip = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n return {\n filePath: relPathSkip,\n language,\n contentHash: crypto.createHash('sha256').update(source).digest('hex'),\n fileSize: Buffer.byteLength(source, 'utf8'),\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n\n // Delegate Jupyter notebooks to the dedicated notebook extractor\n if (language === 'jupyter') {\n return extractNotebook(filePath, projectRoot, source);\n }\n\n const contentHash = crypto.createHash('sha256').update(source).digest('hex');\n const fileSize = Buffer.byteLength(source, 'utf8');\n const relPath = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n\n await initGrammars();\n const parser = await getParser(language);\n if (!parser) {\n if (!hasWasmGrammar(language)) {\n // Language genuinely has no grammar (e.g. Liquid, unknown) \u2014 track file but no AST extraction\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n // Grammar should exist but parser failed (likely WASM crash) \u2014 signal extraction failure\n throw new Error(`Parser unavailable for ${language} (WASM grammar exists but failed to load)`);\n }\n\n // web-tree-sitter allocates Parser/Tree state in WASM linear memory, which JS GC\n // does not reclaim \u2014 both must be explicitly .delete()'d once we're done with them\n // (issue #49), otherwise long `sync` runs across many files leak memory and hang.\n let tree: any;\n try {\n // Skip Go template files disguised as YAML (e.g. Helm templates).\n // tree-sitter-yaml crashes with \"memory access out of bounds\" on {{ }} syntax.\n // These files produce valid YAML only after Helm renders them \u2014 raw they're not parseable.\n if (language === 'yaml' && source.includes('{{')) {\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n\n // Wrap parser.parse() to catch WASM runtime crashes (e.g. memory access out of bounds\n // on malformed files like Helm Go templates in .yaml). Skip the file gracefully instead\n // of letting the crash propagate and poison the entire language.\n try {\n tree = parser.parse(source);\n } catch (parseErr: any) {\n const parseMsg = parseErr?.message ?? String(parseErr);\n const isWasmCrash = parseErr?.constructor?.name === 'RuntimeError'\n || parseMsg.includes('memory access out of bounds')\n || parseMsg.includes('Aborted(');\n if (isWasmCrash) {\n // Individual file crashed the WASM parser \u2014 skip this file, return empty nodes.\n // The next file in this language may parse fine.\n return {\n filePath: relPath,\n language,\n contentHash,\n fileSize,\n nodes: [],\n edges: [],\n unresolvedRefs: [],\n };\n }\n throw parseErr; // Re-throw non-WASM errors\n }\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const unresolvedRefs: UnresolvedRef[] = [];\n const now = Date.now();\n\n walkTree(tree.rootNode, source, relPath, language, nodes, edges, unresolvedRefs, now);\n\n // Complexity post-processing: compute metrics for function/method nodes when enabled.\n if (opts?.enableComplexity) {\n const sourceLines = source.split('\\n');\n for (const n of nodes) {\n if (n.kind !== 'function' && n.kind !== 'method') continue;\n const fnSource = sourceLines.slice(n.startLine - 1, n.endLine).join('\\n');\n const cc = computeTextCyclomaticComplexity(fnSource);\n const depth = computeTextNestingDepth(fnSource);\n const loc = n.endLine - n.startLine + 1;\n const tokens = fnSource.match(/\\b\\w+\\b|[+\\-*/=<>!&|^~%]+|[(){}[\\],;.]/g) ?? [];\n const volume = tokens.length > 0 && tokens.length > 1\n ? tokens.length * Math.log2(new Set(tokens).size || 1)\n : 0;\n const mi = Math.max(0, Math.min(100,\n 171 - 5.2 * Math.log(Math.max(1, volume)) - 0.23 * cc - 16.2 * Math.log(Math.max(1, loc))\n ));\n n.complexityCyclomatic = cc;\n n.nestingDepth = depth;\n n.maintainabilityIndex = Math.round(mi * 10) / 10;\n }\n }\n\n return { filePath: relPath, language, contentHash, fileSize, nodes, edges, unresolvedRefs };\n } finally {\n tree?.delete();\n parser.delete();\n }\n}\n\n// \u2500\u2500 Elixir helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ELIXIR_DEF_KINDS: Record<string, NodeKind> = {\n defmodule: 'module',\n def: 'function',\n defp: 'function',\n defmacro: 'function',\n defmacrop: 'function',\n defprotocol: 'interface',\n defimpl: 'class',\n defstruct: 'struct',\n};\n\nconst ELIXIR_IMPORT_TARGETS = new Set(['alias', 'import', 'require', 'use']);\n\nfunction elixirCallTarget(node: any, source: string): string | null {\n const target = node.childForFieldName?.('target') ?? node.child(0);\n if (!target) return null;\n return source.slice(target.startIndex, target.endIndex).trim() || null;\n}\n\nfunction elixirFirstArg(node: any): any | null {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'arguments') {\n return child.namedChild(0) ?? child.child(0) ?? null;\n }\n }\n return null;\n}\n\nfunction extractElixirName(node: any, source: string, kind: NodeKind): string | null {\n const firstArg = elixirFirstArg(node);\n if (!firstArg) return null;\n if (kind === 'module' || kind === 'interface' || kind === 'class') {\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim();\n }\n if (kind === 'function') {\n if (firstArg.type === 'call') {\n const nameNode = firstArg.childForFieldName?.('target') ?? firstArg.child(0);\n if (nameNode) return source.slice(nameNode.startIndex, nameNode.endIndex).trim();\n }\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim();\n }\n return null;\n}\n\nfunction extractElixirImportSource(node: any, source: string): string | null {\n const firstArg = elixirFirstArg(node);\n if (!firstArg) return null;\n return source.slice(firstArg.startIndex, firstArg.endIndex).trim() || null;\n}\n\nfunction walkElixirCall(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): void {\n const targetText = elixirCallTarget(node, source);\n if (!targetText) {\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n return;\n }\n\n if (ELIXIR_IMPORT_TARGETS.has(targetText)) {\n const modPath = extractElixirImportSource(node, source);\n if (modPath) {\n const id = makeNodeId(filePath, 'import', modPath, node.startPosition.row + 1);\n nodes.push({\n id, kind: 'import', name: modPath,\n qualifiedName: `${filePath}::import:${modPath}`,\n filePath, language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n });\n unresolvedRefs.push({ sourceId: id, refName: modPath, refKind: 'import', line: node.startPosition.row + 1, column: node.startPosition.column });\n }\n return;\n }\n\n const defKind = ELIXIR_DEF_KINDS[targetText];\n if (defKind) {\n const name = extractElixirName(node, source, defKind);\n if (name) {\n const id = makeNodeId(filePath, defKind, name, node.startPosition.row + 1);\n const isPrivate = targetText === 'defp' || targetText === 'defmacrop';\n nodes.push({\n id, kind: defKind, name,\n qualifiedName: `${filePath}::${name}`,\n filePath, language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractDocstring(node, source),\n signature: extractSignature(node, source, defKind),\n visibility: isPrivate ? 'private' : 'public',\n isExported: !isPrivate,\n updatedAt: now,\n });\n if (parentId) edges.push({ source: parentId, target: id, kind: 'contains' });\n collectCallRefs(node, source, id, unresolvedRefs);\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, id);\n }\n return;\n }\n }\n\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n}\n\n// \u2500\u2500 Node type mappings \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** AST node types that should be descended without creating a symbol node */\nconst TRANSPARENT_TYPES = new Set([\n 'export_statement', 'program', 'source_file', 'module', 'translation_unit',\n 'chunk', // Lua root node type\n]);\n\n/** Mapping from tree-sitter node types to graph NodeKind */\nconst KIND_MAP: Record<string, NodeKind> = {\n // Functions\n function_declaration: 'function',\n function_expression: 'function',\n arrow_function: 'function',\n function_definition: 'function', // Python, Scala\n function_item: 'function', // Rust\n function_declaration_go: 'function',\n // Methods\n method_definition: 'method',\n method_declaration: 'method', // Go, Java, C#\n constructor_declaration: 'method', // Java, C#\n // Classes / structs\n class_declaration: 'class',\n abstract_class_declaration: 'class', // TypeScript `abstract class` (distinct AST node from class_declaration)\n class_expression: 'class',\n class_definition: 'class', // Python, Scala\n impl_item: 'class', // Rust (impl blocks)\n struct_item: 'struct', // Rust\n struct_definition: 'struct', // Zig\n // Interfaces / traits\n interface_declaration: 'interface',\n trait_item: 'trait', // Rust\n trait_definition: 'trait', // Scala\n protocol_declaration: 'interface', // Swift\n // Enums\n enum_declaration: 'enum',\n enum_item: 'enum', // Rust\n // Type aliases\n type_alias_declaration: 'type_alias',\n type_declaration: 'type_alias', // Go\n typealias_declaration: 'type_alias',// Swift\n type_item: 'type_alias', // Rust\n type_alias: 'type_alias', // Kotlin\n // Namespaces / modules\n namespace_declaration: 'namespace',\n module_definition: 'namespace', // OCaml\n // Variables / constants (language-specific, see extractVariableKind)\n lexical_declaration: 'variable', // TS/JS (const/let/var) \u2014 refined below\n variable_declaration: 'variable', // TS/JS (var)\n // Ruby\n method: 'method', // Ruby def (top-level and in class body)\n class: 'class', // Ruby class\n // Lua\n local_function_definition_statement: 'function',\n function_definition_statement: 'function',\n // Import statements (all handled via extractImport)\n};\n\n/** Refine variable declarations into 'variable' or 'constant' based on modifier */\nfunction extractVariableKind(node: any): NodeKind {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'const') return 'constant';\n }\n return 'variable';\n}\n\n/**\n * Widget superclass names that mark a Dart class as a UI component.\n * `State<Foo>` subclasses are kept as 'class' \u2014 they are state holders, not widgets.\n */\nconst DART_WIDGET_SUPERCLASSES = new Set([\n 'StatelessWidget',\n 'StatefulWidget',\n 'HookWidget',\n 'ConsumerWidget',\n 'ConsumerStatefulWidget',\n]);\n\n/**\n * Inspect a Dart class_definition node's superclass clause to decide if the\n * class is a widget component or a plain class.\n *\n * Dart tree-sitter grammar: class_definition \u2192 superclass \u2192 type_identifier\n * We look for any child/grandchild whose text is one of the known widget base classes.\n */\nfunction refineDartClassKind(node: any, source: string): NodeKind {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'superclass') {\n // Walk all descendants of the superclass clause looking for a type_identifier\n const superText = source.slice(child.startIndex, child.endIndex);\n // Quick string check first for performance\n if (DART_WIDGET_SUPERCLASSES.has(superText.replace(/^extends\\s+/, '').trim())) {\n return 'component';\n }\n // Drill into children to find the exact type_identifier\n for (let j = 0; j < child.childCount; j++) {\n const typeNode = child.child(j);\n if (typeNode.type === 'type_identifier' || typeNode.type === 'identifier') {\n const name = source.slice(typeNode.startIndex, typeNode.endIndex).trim();\n if (DART_WIDGET_SUPERCLASSES.has(name)) return 'component';\n // Also match any name that ends with \"Widget\" (catches custom base widgets)\n if (name.endsWith('Widget') && name !== 'State') return 'component';\n }\n }\n }\n }\n return 'class';\n}\n\n/** Languages/node-types that represent import statements */\nconst IMPORT_NODE_TYPES = new Set([\n 'import_statement', // TS/JS\n 'import_from_statement', // Python\n 'import_declaration', // Go, Java\n 'use_declaration', // Rust\n 'using_directive', // C#\n 'namespace_use_declaration',// PHP\n 'import_header', // Kotlin\n 'import_or_export', // Dart\n 'include_statement', // Pascal\n 'preproc_include', // C/C++\n]);\n\n// \u2500\u2500 Main tree walker \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction walkTree(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): void {\n if (TRANSPARENT_TYPES.has(node.type)) {\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n return;\n }\n\n // Elixir: all definitions and imports are `call` nodes\n if (language === 'elixir' && node.type === 'call') {\n walkElixirCall(node, source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n return;\n }\n\n // Handle import statements\n if (IMPORT_NODE_TYPES.has(node.type)) {\n const importNode = extractImport(node, source, filePath, language, unresolvedRefs, now, parentId);\n if (importNode) nodes.push(importNode);\n return; // Don't recurse into import nodes\n }\n\n let kind: NodeKind | null = KIND_MAP[node.type] ?? null;\n\n // Refine lexical_declaration into variable/constant\n if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {\n kind = extractVariableKind(node);\n }\n\n // Python/Go/Rust/Java/C# variable and constant types\n if (!kind) kind = getLanguageSpecificKind(node.type, language);\n\n // Dart: refine class_definition kind based on superclass (Widget \u2192 component)\n if (language === 'dart' && node.type === 'class_definition' && kind === 'class') {\n kind = refineDartClassKind(node, source);\n }\n\n if (kind) {\n const name = extractName(node, source, language, kind);\n if (name) {\n const id = makeNodeId(filePath, kind, name, node.startPosition.row + 1);\n const visibility = extractVisibility(node, source, language);\n const graphNode: Node = {\n id,\n kind,\n name,\n qualifiedName: `${filePath}::${name}`,\n filePath,\n language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractDocstring(node, source),\n signature: extractSignature(node, source, kind),\n visibility,\n isExported: isExported(node),\n isAsync: isAsync(node),\n isStatic: isStatic(node),\n isAbstract: isAbstract(node),\n updatedAt: now,\n };\n nodes.push(graphNode);\n\n if (parentId) {\n edges.push({ source: parentId, target: id, kind: 'contains' });\n }\n\n // Collect call references within this symbol\n collectCallRefs(node, source, id, unresolvedRefs);\n\n // Extract inheritance edges for C# and Java class/interface nodes\n if ((kind === 'class' || kind === 'interface') && (language === 'csharp' || language === 'java')) {\n extractInheritance(node, source, language, id, unresolvedRefs);\n }\n\n // Recurse with this node as parent\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, id);\n }\n return;\n }\n }\n\n // No symbol \u2014 recurse without changing parent\n for (let i = 0; i < node.childCount; i++) {\n walkTree(node.child(i), source, filePath, language, nodes, edges, unresolvedRefs, now, parentId);\n }\n}\n\n// \u2500\u2500 Language-specific node kinds \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\n\nfunction getLanguageSpecificKind(type: string, lang: Language): NodeKind | null {\n switch (lang) {\n case 'python':\n if (type === 'assignment') return 'variable';\n break;\n case 'go':\n if (type === 'var_declaration' || type === 'short_var_declaration') return 'variable';\n if (type === 'const_declaration') return 'constant';\n if (type === 'type_spec') return 'type_alias';\n break;\n case 'rust':\n if (type === 'let_declaration') return 'variable';\n if (type === 'const_item') return 'constant';\n if (type === 'static_item') return 'variable';\n break;\n case 'java':\n if (type === 'field_declaration') return 'property';\n if (type === 'local_variable_declaration') return 'variable';\n break;\n case 'csharp':\n if (type === 'field_declaration') return 'property';\n break;\n case 'php':\n if (type === 'property_declaration') return 'property';\n if (type === 'const_declaration') return 'constant';\n break;\n case 'kotlin':\n if (type === 'property_declaration') return 'variable';\n break;\n case 'swift':\n if (type === 'property_declaration') return 'property';\n if (type === 'constant_declaration') return 'constant';\n break;\n case 'dart':\n if (type === 'function_signature') return 'function';\n if (type === 'method_signature') return 'method';\n break;\n case 'ruby':\n if (type === 'singleton_method') return 'method';\n break;\n case 'scala':\n if (type === 'object_definition') return 'class';\n if (type === 'val_definition') return 'variable';\n if (type === 'var_definition') return 'variable';\n if (type === 'type_definition') return 'type_alias';\n break;\n case 'lua':\n if (type === 'local_function') return 'function';\n if (type === 'local_variable_declaration') return 'variable';\n if (type === 'variable_assignment') return 'variable';\n break;\n case 'zig':\n if (type === 'VarDecl') return 'variable';\n if (type === 'ContainerDecl') return 'struct';\n break;\n case 'bash':\n if (type === 'variable_assignment') return 'variable';\n break;\n case 'ocaml':\n if (type === 'let_binding') return 'variable';\n if (type === 'type_binding') return 'type_alias';\n if (type === 'module_binding') return 'namespace';\n break;\n case 'elm':\n if (type === 'function_declaration_left') return 'function';\n if (type === 'type_alias_declaration') return 'type_alias';\n break;\n case 'solidity':\n if (type === 'contract_declaration') return 'class';\n if (type === 'event_definition') return 'function';\n if (type === 'modifier_definition') return 'function';\n if (type === 'state_variable_declaration') return 'variable';\n break;\n case 'objc':\n if (type === 'class_interface') return 'class';\n if (type === 'class_implementation') return 'class';\n if (type === 'protocol_declaration') return 'interface';\n if (type === 'method_declaration') return 'method';\n if (type === 'property_declaration') return 'property';\n break;\n case 'hcl':\n if (type === 'block') return 'namespace';\n if (type === 'attribute') return 'variable';\n break;\n case 'scss':\n if (type === 'mixin_statement') return 'function';\n if (type === 'function_statement') return 'function';\n if (type === 'variable_declaration') return 'variable';\n if (type === 'include_statement') return 'import';\n if (type === 'use_statement') return 'import';\n if (type === 'forward_statement') return 'import';\n if (type === 'placeholder') return 'class';\n break;\n case 'css':\n if (type === 'rule_set') return 'class';\n if (type === 'declaration') return 'variable';\n break;\n case 'rescript':\n if (type === 'let_binding') return 'function';\n if (type === 'type_definition') return 'type_alias';\n if (type === 'module_declaration') return 'module';\n break;\n case 'sql':\n if (type === 'create_function_statement') return 'function';\n if (type === 'create_table_statement') return 'class';\n if (type === 'create_view_statement') return 'function';\n if (type === 'create_procedure_statement') return 'function';\n break;\n case 'r':\n if (type === 'function_definition') return 'function';\n if (type === 'left_assignment') return 'variable';\n break;\n case 'julia':\n if (type === 'function_definition') return 'function';\n if (type === 'struct_definition') return 'struct';\n if (type === 'module_definition') return 'module';\n if (type === 'macro_definition') return 'function';\n break;\n case 'powershell':\n if (type === 'function_statement') return 'function';\n if (type === 'class_statement') return 'class';\n break;\n case 'perl':\n if (type === 'subroutine_declaration_statement') return 'function';\n if (type === 'package_statement') return 'module';\n break;\n case 'gdscript':\n if (type === 'function_definition') return 'function';\n if (type === 'class_definition') return 'class';\n if (type === 'variable_statement') return 'variable';\n break;\n case 'astro':\n if (type === 'function_declaration') return 'function';\n if (type === 'function_definition') return 'function';\n break;\n case 'nix':\n if (type === 'function') return 'function';\n if (type === 'binding') return 'variable';\n break;\n case 'verilog':\n if (type === 'module_declaration') return 'class';\n if (type === 'task_declaration') return 'function';\n if (type === 'function_declaration') return 'function';\n break;\n }\n return null;\n}\n\n// \u2500\u2500 Import extraction \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractImport(\n node: any,\n source: string,\n filePath: string,\n language: Language,\n unresolvedRefs: UnresolvedRef[],\n now: number,\n parentId?: string\n): Node | null {\n const modulePath = extractImportSource(node, source, language);\n if (!modulePath) return null;\n\n const id = makeNodeId(filePath, 'import', modulePath, node.startPosition.row + 1);\n const importNode: Node = {\n id,\n kind: 'import',\n name: modulePath,\n qualifiedName: `${filePath}::import:${modulePath}`,\n filePath,\n language,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n };\n\n // Register as unresolved import for later file-level resolution\n unresolvedRefs.push({\n sourceId: id,\n refName: modulePath,\n refKind: 'import',\n line: node.startPosition.row + 1,\n column: node.startPosition.column,\n });\n\n return importNode;\n}\n\n/** Extract the module path string from an import statement node */\nfunction extractImportSource(node: any, source: string, language: Language): string | null {\n // TS/JS/Svelte: import X from \"module\" \u2192 look for trailing string literal\n if (\n node.type === 'import_statement' ||\n (language === 'svelte' && node.type === 'import_statement')\n ) {\n for (let i = node.childCount - 1; i >= 0; i--) {\n const child = node.child(i);\n if (child.type === 'string') {\n return stripQuotes(source.slice(child.startIndex, child.endIndex));\n }\n }\n }\n\n // Python: from .module import X or import module\n if (node.type === 'import_from_statement') {\n // Look for dotted_name or relative_import after 'from'\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name' || child.type === 'relative_import') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n if (node.type === 'import_statement' && language === 'python') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // Go: import \"path\" or import ( \"path\" )\n if (node.type === 'import_declaration') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'string') {\n return stripQuotes(source.slice(child.startIndex, child.endIndex));\n }\n if (child.type === 'import_spec_list') {\n for (let j = 0; j < child.childCount; j++) {\n const spec = child.child(j);\n if (spec.type === 'import_spec') {\n for (let k = 0; k < spec.childCount; k++) {\n const s = spec.child(k);\n if (s.type === 'string') return stripQuotes(source.slice(s.startIndex, s.endIndex));\n }\n }\n }\n }\n }\n }\n\n // Rust: use path::to::module;\n if (node.type === 'use_declaration') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type !== 'use' && child.type !== ';') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // C#: using Namespace.Name;\n if (node.type === 'using_directive') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'qualified_name' || child.type === 'identifier') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // Java/Kotlin: import a.b.c\n if (node.type === 'import_declaration' || node.type === 'import_header') {\n const text = source.slice(node.startIndex, node.endIndex).trim();\n const m = text.match(/^import\\s+(.+?)[;\\s*]*$/);\n if (m) return m[1].trim();\n }\n\n // PHP: use Foo\\Bar\n if (node.type === 'namespace_use_declaration') {\n const text = source.slice(node.startIndex, node.endIndex).trim();\n const m = text.match(/^use\\s+(.+?)[;\\s]*$/);\n if (m) return m[1].trim();\n }\n\n // Swift/Dart: import SomeModule\n if (node.type === 'import_declaration' || node.type === 'import_or_export') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'identifier' || child.type === 'string') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n\n // C/C++: #include \"file.h\" or #include <lib.h>\n if (node.type === 'preproc_include') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'string_literal' || child.type === 'system_lib_string') {\n return source.slice(child.startIndex, child.endIndex).replace(/[<>\"]/g, '');\n }\n }\n }\n\n return null;\n}\n\nfunction stripQuotes(s: string): string {\n return s.replace(/^['\"`]|['\"`]$/g, '');\n}\n\n// \u2500\u2500 Name extraction \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractName(node: any, source: string, _lang: Language, kind: NodeKind): string | null {\n // For variable/constant declarations (lexical_declaration, variable_declaration),\n // the identifier is inside a variable_declarator child\n if (kind === 'variable' || kind === 'constant') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'variable_declarator') {\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') return source.slice(gc.startIndex, gc.endIndex);\n }\n }\n }\n }\n\n // For Go const_declaration / var_declaration \u2192 look inside var_spec / const_spec\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'var_spec' || child.type === 'const_spec') {\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') return source.slice(gc.startIndex, gc.endIndex);\n }\n }\n }\n\n // Lua function_definition_statement: name lives inside a `variable` child (e.g. M.greet)\n // Extract the last identifier to get the function name (not the module prefix)\n if (node.type === 'function_definition_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'variable') {\n let lastId: string | null = null;\n for (let j = 0; j < child.childCount; j++) {\n const gc = child.child(j);\n if (gc.type === 'identifier') lastId = source.slice(gc.startIndex, gc.endIndex);\n }\n if (lastId) return lastId;\n }\n }\n }\n\n // Standard: look for first named-identifier child\n // Covers: identifier, property_identifier, type_identifier (most languages),\n // simple_identifier (Kotlin, Swift), word (Bash), name (PHP), constant (Ruby class)\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (\n child.type === 'identifier' ||\n child.type === 'property_identifier' ||\n child.type === 'type_identifier' ||\n child.type === 'simple_identifier' ||\n child.type === 'word' ||\n child.type === 'name' ||\n child.type === 'constant'\n ) {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n\n // Python assignment: the left side is the name\n if (node.type === 'assignment') {\n const left = node.child(0);\n if (left && left.type === 'identifier') {\n return source.slice(left.startIndex, left.endIndex);\n }\n }\n\n return null;\n}\n\n// \u2500\u2500 Signature extraction \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\u2500\u2500\u2500\u2500\u2500\n\nfunction extractSignature(node: any, source: string, kind: NodeKind): string | undefined {\n // For functions/methods, try to get the meaningful header without the body\n if (kind === 'function' || kind === 'method') {\n // Try to find parameter list \u2014 stop before the body block\n const text = source.slice(node.startIndex, node.endIndex);\n // Find opening brace or colon (Python), take everything before it\n const bodyStart = text.search(/\\s*[\\{:]\\s*\\n|=>|{/);\n const header = bodyStart > 0 ? text.slice(0, bodyStart).trim() : text.split('\\n')[0].trim();\n return header.length > 150 ? header.slice(0, 150) + '\u2026' : header || undefined;\n }\n\n // Default: first line truncated\n const text = source.slice(node.startIndex, node.endIndex);\n const firstLine = text.split('\\n')[0].trim();\n return firstLine.length > 120 ? firstLine.slice(0, 120) + '\u2026' : firstLine || undefined;\n}\n\n// \u2500\u2500 Visibility extraction \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\u2500\u2500\u2500\u2500\n\nfunction extractVisibility(node: any, source: string, language: Language): Node['visibility'] {\n // TypeScript / JavaScript: accessibility_modifier child\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'accessibility_modifier') {\n const mod = source.slice(child.startIndex, child.endIndex);\n if (mod === 'public') return 'public';\n if (mod === 'private') return 'private';\n if (mod === 'protected') return 'protected';\n }\n // Direct modifier keywords (Java, C#, PHP, Swift, Kotlin)\n if (child.type === 'public') return 'public';\n if (child.type === 'private') return 'private';\n if (child.type === 'protected') return 'protected';\n if (child.type === 'internal') return 'internal';\n }\n\n // Rust: no `pub` keyword = private, `pub` = public\n if (language === 'rust') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'visibility_modifier') {\n const v = source.slice(child.startIndex, child.endIndex);\n return v.startsWith('pub') ? 'public' : 'private';\n }\n }\n return 'private'; // Rust default\n }\n\n // C# default: private\n if (language === 'csharp') return 'private';\n\n // PHP default: public\n if (language === 'php') return 'public';\n\n // Swift: check for access modifiers\n if (language === 'swift') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const t = child.type;\n if (t === 'fileprivate') return 'private';\n }\n return 'internal'; // Swift default\n }\n\n // Python: underscore prefix convention\n if (language === 'python') {\n const name = extractName(node, source, language, 'function');\n if (name?.startsWith('_') && !name.startsWith('__')) return 'protected';\n if (name?.startsWith('__')) return 'private';\n return 'public';\n }\n\n return undefined;\n}\n\n// \u2500\u2500 Misc helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractDocstring(node: any, source: string): string | undefined {\n const commentTypes = new Set(['comment', 'block_comment', 'line_comment', 'documentation_comment']);\n const commentLines: string[] = [];\n\n let sibling = node.previousNamedSibling;\n while (sibling && commentTypes.has(sibling.type)) {\n commentLines.unshift(source.slice(sibling.startIndex, sibling.endIndex));\n sibling = sibling.previousNamedSibling;\n }\n\n if (commentLines.length === 0) return undefined;\n\n const cleaned = commentLines.join('\\n')\n .replace(/^\\/\\*+\\s*/gm, '')\n .replace(/\\s*\\*+\\/\\s*$/gm, '')\n .replace(/^\\s*\\*\\s?/gm, '')\n .replace(/^\\/\\/\\s?/gm, '')\n .trim();\n\n return cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction isExported(node: any): boolean {\n // Walk up the parent chain to find an export_statement ancestor\n let current = node.parent;\n while (current) {\n if (current.type === 'export_statement') return true;\n // Stop at scope boundaries\n if (current.type === 'function_body' || current.type === 'class_body' || current.type === 'statement_block') break;\n current = current.parent;\n }\n return false;\n}\n\nfunction isAsync(node: any): boolean {\n for (let i = 0; i < node.childCount; i++) {\n if (node.child(i).type === 'async') return true;\n }\n return false;\n}\n\nfunction isStatic(node: any): boolean {\n for (let i = 0; i < node.childCount; i++) {\n if (node.child(i).type === 'static') return true;\n }\n return false;\n}\n\n/**\n * Detect abstract declarations. Handles both grammars that use a dedicated\n * abstract node type (TypeScript: `abstract_class_declaration`) and those that\n * fold `abstract` into a modifier/keyword child (Java, C#, Kotlin, PHP).\n */\nfunction isAbstract(node: any): boolean {\n if (node.type === 'abstract_class_declaration') return true;\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'abstract') return true;\n // Java/C#/Kotlin wrap keywords in a `modifiers` child\n if (child.type === 'modifiers') {\n for (let j = 0; j < child.childCount; j++) {\n if (child.child(j).type === 'abstract') return true;\n }\n }\n }\n return false;\n}\n\n// \u2500\u2500 Call reference collection \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\n\n/**\n * Node types that represent a function/method call across all supported languages.\n * Using a combined set avoids threading a language parameter through the recursive walker.\n */\nconst CALL_NODE_TYPES = new Set([\n 'call_expression', // JS/TS/Go/Rust/Swift/Kotlin/Dart/C/C++\n 'invocation_expression', // C#\n 'method_invocation', // Java\n 'call', // Python/Ruby/Elixir (function calls in bodies)\n 'function_call_expression', // PHP\n]);\n\n/**\n * Extract the callee name from a call node.\n * Uses named field lookup first (more precise), then falls back to first-child heuristic.\n */\nfunction extractCallName(node: any, source: string): string | null {\n // Named fields \u2014 Java uses 'name', Python/Ruby use 'function'/'method', Elixir uses 'target'\n for (const field of ['name', 'method', 'function', 'target']) {\n const f = node.childForFieldName?.(field);\n if (f) {\n const t = f.type;\n if (t === 'identifier' || t === 'name' || t === 'property_identifier' || t === 'type_identifier') {\n const text = source.slice(f.startIndex, f.endIndex).trim();\n if (text && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) return text;\n }\n }\n }\n // Fallback: first child, take final segment of dotted access (covers JS/TS, C#, Go, Rust\u2026)\n const funcNode = node.child(0);\n if (funcNode) {\n const rawName = source.slice(funcNode.startIndex, funcNode.endIndex).split('(')[0].trim();\n if (rawName && rawName.length < 100) {\n const calleeName = rawName.split('.').pop()!.trim();\n if (calleeName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(calleeName)) return calleeName;\n }\n }\n return null;\n}\n\nfunction collectCallRefs(node: any, source: string, sourceId: string, unresolvedRefs: UnresolvedRef[]): void {\n walkForCalls(node, source, sourceId, unresolvedRefs);\n}\n\nfunction walkForCalls(node: any, source: string, sourceId: string, unresolvedRefs: UnresolvedRef[]): void {\n if (CALL_NODE_TYPES.has(node.type)) {\n const calleeName = extractCallName(node, source);\n if (calleeName) {\n unresolvedRefs.push({\n sourceId,\n refName: calleeName,\n refKind: 'function',\n line: node.startPosition.row + 1,\n column: node.startPosition.column,\n });\n }\n }\n for (let i = 0; i < node.childCount; i++) {\n walkForCalls(node.child(i), source, sourceId, unresolvedRefs);\n }\n}\n\n// \u2500\u2500 Inheritance extraction \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\u2500\u2500\u2500\n\n/**\n * Extract the simple type name from an AST node representing a type reference.\n * Strips generic parameters (e.g. \"List<T>\" \u2192 \"List\").\n */\nfunction extractTypeName(node: any, source: string): string | null {\n const t = node.type;\n if (t === 'identifier' || t === 'type_identifier' || t === 'name') {\n return source.slice(node.startIndex, node.endIndex).trim() || null;\n }\n // qualified_name / generic_name: take the rightmost identifier\n if (t === 'qualified_name' || t === 'generic_name' || t === 'scoped_type_identifier') {\n for (let i = node.childCount - 1; i >= 0; i--) {\n const child = node.child(i);\n if (child.type === 'identifier' || child.type === 'type_identifier' || child.type === 'name') {\n return source.slice(child.startIndex, child.endIndex).trim() || null;\n }\n }\n }\n return null;\n}\n\n/**\n * Scan a class/interface AST node for its base types and add them as unresolved\n * extends/implements refs, which the resolver later turns into graph edges.\n *\n * C#: base_list children \u2192 all are either base class or interface (can't distinguish without types)\n * Java: superclass \u2192 extends; super_interfaces / extends_interfaces \u2192 implements / extends\n */\nfunction extractInheritance(\n node: any,\n source: string,\n language: Language,\n classNodeId: string,\n unresolvedRefs: UnresolvedRef[],\n): void {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n\n if (language === 'csharp' && child.type === 'base_list') {\n for (let j = 0; j < child.childCount; j++) {\n const item = child.child(j);\n const name = extractTypeName(item, source);\n if (name) {\n unresolvedRefs.push({\n sourceId: classNodeId,\n refName: name,\n // In C# both classes and interfaces appear in the same base_list;\n // we emit 'extends' for all \u2014 the resolver maps it to the extends edge\n // which covers both parent classes and parent interfaces structurally.\n refKind: 'extends',\n line: item.startPosition.row + 1,\n column: item.startPosition.column,\n });\n }\n }\n }\n\n if (language === 'java') {\n if (child.type === 'superclass') {\n // superclass has a single type_identifier or scoped_type_identifier child\n const typeNode = child.namedChild?.(0);\n if (typeNode) {\n const name = extractTypeName(typeNode, source);\n if (name) unresolvedRefs.push({ sourceId: classNodeId, refName: name, refKind: 'extends', line: child.startPosition.row + 1, column: child.startPosition.column });\n }\n }\n if (child.type === 'super_interfaces' || child.type === 'extends_interfaces') {\n // contains a type_list with one or more types\n const typeList = child.namedChild?.(0);\n if (typeList) {\n const count = typeList.namedChildCount ?? 0;\n for (let j = 0; j < count; j++) {\n const typeNode = typeList.namedChild(j);\n if (typeNode) {\n const name = extractTypeName(typeNode, source);\n if (name) {\n unresolvedRefs.push({\n sourceId: classNodeId,\n refName: name,\n refKind: child.type === 'extends_interfaces' ? 'extends' : 'implements',\n line: typeNode.startPosition.row + 1,\n column: typeNode.startPosition.column,\n });\n }\n }\n }\n }\n }\n }\n }\n}\n\n// \u2500\u2500 Text-based complexity helpers \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\n// Used when enableComplexity=true in extractFile(). Text heuristics are less\n// accurate than full AST walking but require no per-language grammar handling.\n\nconst CC_PATTERNS = [\n /\\bif\\b/g, /\\belse\\s+if\\b/g, /\\belif\\b/g,\n /\\bfor\\b/g, /\\bwhile\\b/g, /\\bdo\\b/g,\n /\\bcase\\b/g, /\\bcatch\\b/g, /\\bexcept\\b/g,\n /&&/g, /\\|\\|/g, /\\?\\?/g,\n // ternary ? that is not ?. or ?: or optional chaining\n /(?<![?!])\\?(?![.?:])/g,\n];\n\nfunction computeTextCyclomaticComplexity(src: string): number {\n // Strip string literals and comments to avoid counting keywords inside them\n const stripped = src\n .replace(/`[^`]*`/g, '``')\n .replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, '\"\"')\n .replace(/'(?:[^'\\\\]|\\\\.)*'/g, \"''\")\n .replace(/\\/\\/[^\\n]*/g, '')\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n let count = 0;\n for (const pat of CC_PATTERNS) count += (stripped.match(pat) ?? []).length;\n return 1 + count;\n}\n\nfunction computeTextNestingDepth(src: string): number {\n let depth = 0, max = 0;\n for (const ch of src) {\n if (ch === '{') { depth++; if (depth > max) max = depth; }\n else if (ch === '}') depth = Math.max(0, depth - 1);\n }\n return max;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["text"]
7
7
  }
@@ -79,13 +79,21 @@ async function extractNotebook(filePath, projectRoot, content) {
79
79
  if (!parser) {
80
80
  return { filePath: relPath, language: "jupyter", contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };
81
81
  }
82
- const tree = parser.parse(concatenated);
83
- const nodes = [];
84
- const edges = [];
85
- const unresolvedRefs = [];
86
- const now = Date.now();
87
- walkPythonTree(tree.rootNode, concatenated, relPath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks);
88
- return { filePath: relPath, language: "jupyter", contentHash, fileSize, nodes, edges, unresolvedRefs };
82
+ try {
83
+ const tree = parser.parse(concatenated);
84
+ try {
85
+ const nodes = [];
86
+ const edges = [];
87
+ const unresolvedRefs = [];
88
+ const now = Date.now();
89
+ walkPythonTree(tree.rootNode, concatenated, relPath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks);
90
+ return { filePath: relPath, language: "jupyter", contentHash, fileSize, nodes, edges, unresolvedRefs };
91
+ } finally {
92
+ tree.delete();
93
+ }
94
+ } finally {
95
+ parser.delete();
96
+ }
89
97
  }
90
98
  const TRANSPARENT_TYPES = /* @__PURE__ */ new Set([
91
99
  "export_statement",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extraction/notebook.ts"],
4
- "sourcesContent": ["/**\n * Notebook extractor for Jupyter (.ipynb) files.\n * Parses the JSON notebook format, extracts code cells, concatenates them,\n * runs the Python tree-sitter parser, and adjusts line numbers so they\n * reflect the position of each cell within the notebook.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as crypto from 'crypto';\nimport type { Node, Edge } from '../types';\nimport type { ExtractedFile, UnresolvedRef } from './extractor';\nimport { makeNodeId } from './extractor';\nimport { initGrammars, getParser } from './grammars';\n\ninterface NotebookCell {\n cell_type: string;\n source: string[];\n}\n\ninterface NotebookMetadata {\n kernelspec?: { language?: string };\n language_info?: { name?: string };\n}\n\ninterface NotebookJson {\n metadata?: NotebookMetadata;\n cells?: NotebookCell[];\n}\n\n/** Each code cell mapped to its starting line in the concatenated source */\ninterface CellOffset {\n /** 1-based starting line of this cell in the concatenated code string */\n startLine: number;\n}\n\n/**\n * Extract symbols from a Jupyter notebook file.\n * Concatenates all code cells, parses with the Python grammar,\n * and adjusts node line numbers to match cell positions.\n */\nexport async function extractNotebook(\n filePath: string,\n projectRoot: string,\n content?: Buffer | string\n): Promise<ExtractedFile | null> {\n let raw: string;\n try {\n if (content !== undefined) {\n raw = typeof content === 'string' ? content : content.toString('utf8');\n } else {\n raw = fs.readFileSync(filePath, 'utf8');\n }\n } catch {\n return null;\n }\n\n const contentHash = crypto.createHash('sha256').update(raw).digest('hex');\n const fileSize = Buffer.byteLength(raw, 'utf8');\n const relPath = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n\n let notebook: NotebookJson;\n try {\n notebook = JSON.parse(raw);\n } catch {\n // Not valid JSON \u2014 return file tracked with no symbols\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n const cells: NotebookCell[] = notebook.cells ?? [];\n const codeCells = cells.filter((c) => c.cell_type === 'code');\n\n if (codeCells.length === 0) {\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n // Build concatenated source and track the starting line of each cell\n const cellOffsets: CellOffset[] = [];\n const codeChunks: string[] = [];\n let currentLine = 1; // 1-based\n\n for (let i = 0; i < codeCells.length; i++) {\n const cell = codeCells[i];\n const cellSource = (cell.source ?? []).join('');\n // Ensure each cell ends with a newline so cells don't bleed into each other\n const cellCode = cellSource.endsWith('\\n') ? cellSource : cellSource + '\\n';\n cellOffsets.push({ startLine: currentLine });\n codeChunks.push(cellCode);\n // Number of lines contributed by this cell's code\n const cellLineCount = cellCode.split('\\n').length - 1; // trailing \\n creates empty last entry\n // Advance by cell lines; join('\\n') adds one extra separator line between cells\n currentLine += cellLineCount + (i < codeCells.length - 1 ? 1 : 0);\n }\n\n const concatenated = codeChunks.join('\\n');\n\n // Load Python parser and parse concatenated source\n await initGrammars();\n const parser = await getParser('python');\n if (!parser) {\n // Python grammar unavailable \u2014 return file node with no symbols\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n const tree = parser.parse(concatenated);\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const unresolvedRefs: UnresolvedRef[] = [];\n const now = Date.now();\n\n // We reuse the same AST walking logic from extractor.ts but with 'python' language.\n // Import walkTree-equivalent logic inline to avoid circular imports by using the\n // exported makeNodeId and reimporting the walk from extractor indirectly.\n // Instead, we call extractFile on the concatenated source as a virtual Python file\n // and then remap file paths and languages back.\n //\n // To avoid code duplication we use a direct re-export path: import the internal\n // walkTree equivalent by parsing via a private helper below.\n\n walkPythonTree(tree.rootNode, concatenated, relPath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks);\n\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes, edges, unresolvedRefs };\n}\n\n// \u2500\u2500 Transparent node types (same as extractor.ts) \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\n\nconst TRANSPARENT_TYPES = new Set([\n 'export_statement', 'program', 'source_file', 'module', 'translation_unit',\n]);\n\n// \u2500\u2500 Import node types relevant to Python \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\n\nconst PYTHON_IMPORT_TYPES = new Set([\n 'import_statement',\n 'import_from_statement',\n]);\n\n// \u2500\u2500 KIND_MAP subset for Python \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\n\ntype NodeKindLocal = Node['kind'];\n\nconst PYTHON_KIND_MAP: Record<string, NodeKindLocal> = {\n function_definition: 'function',\n class_definition: 'class',\n assignment: 'variable',\n};\n\n// \u2500\u2500 CALL_NODE_TYPES for Python \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\n\nconst CALL_NODE_TYPES = new Set(['call']);\n\n// \u2500\u2500 Line offset helper \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Given a 1-based line number within the concatenated source, return the\n * corrected 1-based line number relative to the notebook.\n *\n * The concatenated source is built as:\n * cell[0]\\n cell[1]\\n cell[2]\\n ...\n * where cell[i] starts at cellOffsets[i].startLine (1-based) within the concatenation.\n *\n * Each cell chunk in codeChunks already ends with \\n; cells are separated by an\n * extra \\n (the join('\\n') adds one extra newline between chunks).\n *\n * We build a lookup of the absolute start lines in the concatenated string by\n * re-counting: cell 0 starts at line 1; cell 1 starts at line (lines-in-cell-0 + 1 + 1),\n * etc. (The +1 accounts for the join separator).\n *\n * For simplicity we use the pre-computed cellOffsets directly \u2014 they track the\n * cumulative line position in the concatenated string at each cell boundary.\n */\nfunction remapLine(lineInConcat: number, cellOffsets: CellOffset[], codeChunks: string[]): number {\n // cellOffsets[i].startLine is the 1-based line of cell i in the concatenated string\n // (built with join('\\n') which adds +1 separator line between cells).\n // We need to find which cell this line belongs to.\n //\n // Rebuild absolute offsets accounting for the '\\n' separator added by join:\n // cell 0: lines [offset0, offset0 + cellLines0 - 1]\n // separator line: offset0 + cellLines0\n // cell 1: offset0 + cellLines0 + 1\n // etc.\n //\n // cellOffsets already accounts for this (built with currentLine += cellLines + 1\n // because codeChunks.join('\\n') adds a newline between chunks).\n\n let cellIdx = 0;\n for (let i = cellOffsets.length - 1; i >= 0; i--) {\n if (lineInConcat >= cellOffsets[i].startLine) {\n cellIdx = i;\n break;\n }\n }\n // Position within the cell (0-based)\n const posInCell = lineInConcat - cellOffsets[cellIdx].startLine;\n // Return 1-based position within notebook (cell starts at cellOffsets[cellIdx].startLine\n // which IS the 1-based line in the notebook for cell 0 = line 1)\n return cellOffsets[cellIdx].startLine + posInCell;\n}\n\n// \u2500\u2500 Python-specific AST walker \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\n\nfunction walkPythonTree(\n node: any,\n source: string,\n filePath: string,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n cellOffsets: CellOffset[],\n codeChunks: string[],\n parentId?: string\n): void {\n if (TRANSPARENT_TYPES.has(node.type)) {\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, parentId);\n }\n return;\n }\n\n if (PYTHON_IMPORT_TYPES.has(node.type)) {\n const modulePath = extractPythonImportSource(node, source);\n if (modulePath) {\n const rawLine = node.startPosition.row + 1;\n const line = remapLine(rawLine, cellOffsets, codeChunks);\n const id = makeNodeId(filePath, 'import', modulePath, line);\n nodes.push({\n id,\n kind: 'import',\n name: modulePath,\n qualifiedName: `${filePath}::import:${modulePath}`,\n filePath,\n language: 'jupyter',\n startLine: line,\n endLine: remapLine(node.endPosition.row + 1, cellOffsets, codeChunks),\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n });\n unresolvedRefs.push({ sourceId: id, refName: modulePath, refKind: 'import', line, column: node.startPosition.column });\n }\n return;\n }\n\n let kind: NodeKindLocal | null = PYTHON_KIND_MAP[node.type] ?? null;\n\n if (kind) {\n const name = extractPythonName(node, source, kind);\n if (name) {\n const rawStartLine = node.startPosition.row + 1;\n const rawEndLine = node.endPosition.row + 1;\n const startLine = remapLine(rawStartLine, cellOffsets, codeChunks);\n const endLine = remapLine(rawEndLine, cellOffsets, codeChunks);\n const id = makeNodeId(filePath, kind, name, startLine);\n const visibility = extractPythonVisibility(name);\n\n const graphNode: Node = {\n id,\n kind,\n name,\n qualifiedName: `${filePath}::${name}`,\n filePath,\n language: 'jupyter',\n startLine,\n endLine,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractPythonDocstring(node, source),\n signature: extractPythonSignature(node, source, kind),\n visibility,\n isExported: false,\n updatedAt: now,\n };\n nodes.push(graphNode);\n\n if (parentId) {\n edges.push({ source: parentId, target: id, kind: 'contains' });\n }\n\n collectPythonCallRefs(node, source, id, unresolvedRefs, cellOffsets, codeChunks);\n\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, id);\n }\n return;\n }\n }\n\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, parentId);\n }\n}\n\n// \u2500\u2500 Python helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractPythonImportSource(node: any, source: string): string | null {\n if (node.type === 'import_from_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name' || child.type === 'relative_import') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n if (node.type === 'import_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n return null;\n}\n\nfunction extractPythonName(node: any, source: string, kind: NodeKindLocal): string | null {\n if (kind === 'variable' && node.type === 'assignment') {\n const left = node.child(0);\n if (left && left.type === 'identifier') {\n return source.slice(left.startIndex, left.endIndex);\n }\n return null;\n }\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'identifier') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n return null;\n}\n\nfunction extractPythonVisibility(name: string): Node['visibility'] {\n if (name.startsWith('__')) return 'private';\n if (name.startsWith('_')) return 'protected';\n return 'public';\n}\n\nfunction extractPythonDocstring(node: any, source: string): string | undefined {\n const commentTypes = new Set(['comment']);\n const commentLines: string[] = [];\n let sibling = node.previousNamedSibling;\n while (sibling && commentTypes.has(sibling.type)) {\n commentLines.unshift(source.slice(sibling.startIndex, sibling.endIndex));\n sibling = sibling.previousNamedSibling;\n }\n if (commentLines.length === 0) return undefined;\n const cleaned = commentLines.join('\\n').replace(/^#\\s?/gm, '').trim();\n return cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction extractPythonSignature(node: any, source: string, kind: NodeKindLocal): string | undefined {\n if (kind === 'function' || kind === 'method') {\n const text = source.slice(node.startIndex, node.endIndex);\n const bodyStart = text.search(/\\s*:\\s*\\n/);\n const header = bodyStart > 0 ? text.slice(0, bodyStart).trim() : text.split('\\n')[0].trim();\n return header.length > 150 ? header.slice(0, 150) + '\u2026' : header || undefined;\n }\n const text = source.slice(node.startIndex, node.endIndex);\n const firstLine = text.split('\\n')[0].trim();\n return firstLine.length > 120 ? firstLine.slice(0, 120) + '\u2026' : firstLine || undefined;\n}\n\nfunction collectPythonCallRefs(\n node: any,\n source: string,\n sourceId: string,\n unresolvedRefs: UnresolvedRef[],\n cellOffsets: CellOffset[],\n codeChunks: string[]\n): void {\n walkForPythonCalls(node, source, sourceId, unresolvedRefs, cellOffsets, codeChunks);\n}\n\nfunction walkForPythonCalls(\n node: any,\n source: string,\n sourceId: string,\n unresolvedRefs: UnresolvedRef[],\n cellOffsets: CellOffset[],\n codeChunks: string[]\n): void {\n if (CALL_NODE_TYPES.has(node.type)) {\n const calleeName = extractPythonCallName(node, source);\n if (calleeName) {\n const rawLine = node.startPosition.row + 1;\n const line = remapLine(rawLine, cellOffsets, codeChunks);\n unresolvedRefs.push({\n sourceId,\n refName: calleeName,\n refKind: 'function',\n line,\n column: node.startPosition.column,\n });\n }\n }\n for (let i = 0; i < node.childCount; i++) {\n walkForPythonCalls(node.child(i), source, sourceId, unresolvedRefs, cellOffsets, codeChunks);\n }\n}\n\nfunction extractPythonCallName(node: any, source: string): string | null {\n for (const field of ['function', 'method']) {\n const f = node.childForFieldName?.(field);\n if (f) {\n const t = f.type;\n if (t === 'identifier' || t === 'name') {\n const text = source.slice(f.startIndex, f.endIndex).trim();\n if (text && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) return text;\n }\n }\n }\n const funcNode = node.child(0);\n if (funcNode) {\n const rawName = source.slice(funcNode.startIndex, funcNode.endIndex).split('(')[0].trim();\n if (rawName && rawName.length < 100) {\n const calleeName = rawName.split('.').pop()!.trim();\n if (calleeName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(calleeName)) return calleeName;\n }\n }\n return null;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Notebook extractor for Jupyter (.ipynb) files.\n * Parses the JSON notebook format, extracts code cells, concatenates them,\n * runs the Python tree-sitter parser, and adjusts line numbers so they\n * reflect the position of each cell within the notebook.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as crypto from 'crypto';\nimport type { Node, Edge } from '../types';\nimport type { ExtractedFile, UnresolvedRef } from './extractor';\nimport { makeNodeId } from './extractor';\nimport { initGrammars, getParser } from './grammars';\n\ninterface NotebookCell {\n cell_type: string;\n source: string[];\n}\n\ninterface NotebookMetadata {\n kernelspec?: { language?: string };\n language_info?: { name?: string };\n}\n\ninterface NotebookJson {\n metadata?: NotebookMetadata;\n cells?: NotebookCell[];\n}\n\n/** Each code cell mapped to its starting line in the concatenated source */\ninterface CellOffset {\n /** 1-based starting line of this cell in the concatenated code string */\n startLine: number;\n}\n\n/**\n * Extract symbols from a Jupyter notebook file.\n * Concatenates all code cells, parses with the Python grammar,\n * and adjusts node line numbers to match cell positions.\n */\nexport async function extractNotebook(\n filePath: string,\n projectRoot: string,\n content?: Buffer | string\n): Promise<ExtractedFile | null> {\n let raw: string;\n try {\n if (content !== undefined) {\n raw = typeof content === 'string' ? content : content.toString('utf8');\n } else {\n raw = fs.readFileSync(filePath, 'utf8');\n }\n } catch {\n return null;\n }\n\n const contentHash = crypto.createHash('sha256').update(raw).digest('hex');\n const fileSize = Buffer.byteLength(raw, 'utf8');\n const relPath = path.relative(projectRoot, filePath).replace(/\\\\/g, '/');\n\n let notebook: NotebookJson;\n try {\n notebook = JSON.parse(raw);\n } catch {\n // Not valid JSON \u2014 return file tracked with no symbols\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n const cells: NotebookCell[] = notebook.cells ?? [];\n const codeCells = cells.filter((c) => c.cell_type === 'code');\n\n if (codeCells.length === 0) {\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n // Build concatenated source and track the starting line of each cell\n const cellOffsets: CellOffset[] = [];\n const codeChunks: string[] = [];\n let currentLine = 1; // 1-based\n\n for (let i = 0; i < codeCells.length; i++) {\n const cell = codeCells[i];\n const cellSource = (cell.source ?? []).join('');\n // Ensure each cell ends with a newline so cells don't bleed into each other\n const cellCode = cellSource.endsWith('\\n') ? cellSource : cellSource + '\\n';\n cellOffsets.push({ startLine: currentLine });\n codeChunks.push(cellCode);\n // Number of lines contributed by this cell's code\n const cellLineCount = cellCode.split('\\n').length - 1; // trailing \\n creates empty last entry\n // Advance by cell lines; join('\\n') adds one extra separator line between cells\n currentLine += cellLineCount + (i < codeCells.length - 1 ? 1 : 0);\n }\n\n const concatenated = codeChunks.join('\\n');\n\n // Load Python parser and parse concatenated source\n await initGrammars();\n const parser = await getParser('python');\n if (!parser) {\n // Python grammar unavailable \u2014 return file node with no symbols\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes: [], edges: [], unresolvedRefs: [] };\n }\n\n // web-tree-sitter allocates Parser/Tree state in WASM linear memory, which JS GC\n // does not reclaim \u2014 both must be explicitly .delete()'d once we're done with them\n // (issue #49), otherwise long `sync` runs across many notebooks leak memory and hang.\n try {\n const tree = parser.parse(concatenated);\n try {\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const unresolvedRefs: UnresolvedRef[] = [];\n const now = Date.now();\n\n // We reuse the same AST walking logic from extractor.ts but with 'python' language.\n // Import walkTree-equivalent logic inline to avoid circular imports by using the\n // exported makeNodeId and reimporting the walk from extractor indirectly.\n // Instead, we call extractFile on the concatenated source as a virtual Python file\n // and then remap file paths and languages back.\n //\n // To avoid code duplication we use a direct re-export path: import the internal\n // walkTree equivalent by parsing via a private helper below.\n\n walkPythonTree(tree.rootNode, concatenated, relPath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks);\n\n return { filePath: relPath, language: 'jupyter', contentHash, fileSize, nodes, edges, unresolvedRefs };\n } finally {\n tree.delete();\n }\n } finally {\n parser.delete();\n }\n}\n\n// \u2500\u2500 Transparent node types (same as extractor.ts) \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\n\nconst TRANSPARENT_TYPES = new Set([\n 'export_statement', 'program', 'source_file', 'module', 'translation_unit',\n]);\n\n// \u2500\u2500 Import node types relevant to Python \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\n\nconst PYTHON_IMPORT_TYPES = new Set([\n 'import_statement',\n 'import_from_statement',\n]);\n\n// \u2500\u2500 KIND_MAP subset for Python \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\n\ntype NodeKindLocal = Node['kind'];\n\nconst PYTHON_KIND_MAP: Record<string, NodeKindLocal> = {\n function_definition: 'function',\n class_definition: 'class',\n assignment: 'variable',\n};\n\n// \u2500\u2500 CALL_NODE_TYPES for Python \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\n\nconst CALL_NODE_TYPES = new Set(['call']);\n\n// \u2500\u2500 Line offset helper \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Given a 1-based line number within the concatenated source, return the\n * corrected 1-based line number relative to the notebook.\n *\n * The concatenated source is built as:\n * cell[0]\\n cell[1]\\n cell[2]\\n ...\n * where cell[i] starts at cellOffsets[i].startLine (1-based) within the concatenation.\n *\n * Each cell chunk in codeChunks already ends with \\n; cells are separated by an\n * extra \\n (the join('\\n') adds one extra newline between chunks).\n *\n * We build a lookup of the absolute start lines in the concatenated string by\n * re-counting: cell 0 starts at line 1; cell 1 starts at line (lines-in-cell-0 + 1 + 1),\n * etc. (The +1 accounts for the join separator).\n *\n * For simplicity we use the pre-computed cellOffsets directly \u2014 they track the\n * cumulative line position in the concatenated string at each cell boundary.\n */\nfunction remapLine(lineInConcat: number, cellOffsets: CellOffset[], codeChunks: string[]): number {\n // cellOffsets[i].startLine is the 1-based line of cell i in the concatenated string\n // (built with join('\\n') which adds +1 separator line between cells).\n // We need to find which cell this line belongs to.\n //\n // Rebuild absolute offsets accounting for the '\\n' separator added by join:\n // cell 0: lines [offset0, offset0 + cellLines0 - 1]\n // separator line: offset0 + cellLines0\n // cell 1: offset0 + cellLines0 + 1\n // etc.\n //\n // cellOffsets already accounts for this (built with currentLine += cellLines + 1\n // because codeChunks.join('\\n') adds a newline between chunks).\n\n let cellIdx = 0;\n for (let i = cellOffsets.length - 1; i >= 0; i--) {\n if (lineInConcat >= cellOffsets[i].startLine) {\n cellIdx = i;\n break;\n }\n }\n // Position within the cell (0-based)\n const posInCell = lineInConcat - cellOffsets[cellIdx].startLine;\n // Return 1-based position within notebook (cell starts at cellOffsets[cellIdx].startLine\n // which IS the 1-based line in the notebook for cell 0 = line 1)\n return cellOffsets[cellIdx].startLine + posInCell;\n}\n\n// \u2500\u2500 Python-specific AST walker \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\n\nfunction walkPythonTree(\n node: any,\n source: string,\n filePath: string,\n nodes: Node[],\n edges: Edge[],\n unresolvedRefs: UnresolvedRef[],\n now: number,\n cellOffsets: CellOffset[],\n codeChunks: string[],\n parentId?: string\n): void {\n if (TRANSPARENT_TYPES.has(node.type)) {\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, parentId);\n }\n return;\n }\n\n if (PYTHON_IMPORT_TYPES.has(node.type)) {\n const modulePath = extractPythonImportSource(node, source);\n if (modulePath) {\n const rawLine = node.startPosition.row + 1;\n const line = remapLine(rawLine, cellOffsets, codeChunks);\n const id = makeNodeId(filePath, 'import', modulePath, line);\n nodes.push({\n id,\n kind: 'import',\n name: modulePath,\n qualifiedName: `${filePath}::import:${modulePath}`,\n filePath,\n language: 'jupyter',\n startLine: line,\n endLine: remapLine(node.endPosition.row + 1, cellOffsets, codeChunks),\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n updatedAt: now,\n });\n unresolvedRefs.push({ sourceId: id, refName: modulePath, refKind: 'import', line, column: node.startPosition.column });\n }\n return;\n }\n\n let kind: NodeKindLocal | null = PYTHON_KIND_MAP[node.type] ?? null;\n\n if (kind) {\n const name = extractPythonName(node, source, kind);\n if (name) {\n const rawStartLine = node.startPosition.row + 1;\n const rawEndLine = node.endPosition.row + 1;\n const startLine = remapLine(rawStartLine, cellOffsets, codeChunks);\n const endLine = remapLine(rawEndLine, cellOffsets, codeChunks);\n const id = makeNodeId(filePath, kind, name, startLine);\n const visibility = extractPythonVisibility(name);\n\n const graphNode: Node = {\n id,\n kind,\n name,\n qualifiedName: `${filePath}::${name}`,\n filePath,\n language: 'jupyter',\n startLine,\n endLine,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n docstring: extractPythonDocstring(node, source),\n signature: extractPythonSignature(node, source, kind),\n visibility,\n isExported: false,\n updatedAt: now,\n };\n nodes.push(graphNode);\n\n if (parentId) {\n edges.push({ source: parentId, target: id, kind: 'contains' });\n }\n\n collectPythonCallRefs(node, source, id, unresolvedRefs, cellOffsets, codeChunks);\n\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, id);\n }\n return;\n }\n }\n\n for (let i = 0; i < node.childCount; i++) {\n walkPythonTree(node.child(i), source, filePath, nodes, edges, unresolvedRefs, now, cellOffsets, codeChunks, parentId);\n }\n}\n\n// \u2500\u2500 Python helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractPythonImportSource(node: any, source: string): string | null {\n if (node.type === 'import_from_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name' || child.type === 'relative_import') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n if (node.type === 'import_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'dotted_name') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n }\n return null;\n}\n\nfunction extractPythonName(node: any, source: string, kind: NodeKindLocal): string | null {\n if (kind === 'variable' && node.type === 'assignment') {\n const left = node.child(0);\n if (left && left.type === 'identifier') {\n return source.slice(left.startIndex, left.endIndex);\n }\n return null;\n }\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n if (child.type === 'identifier') {\n return source.slice(child.startIndex, child.endIndex);\n }\n }\n return null;\n}\n\nfunction extractPythonVisibility(name: string): Node['visibility'] {\n if (name.startsWith('__')) return 'private';\n if (name.startsWith('_')) return 'protected';\n return 'public';\n}\n\nfunction extractPythonDocstring(node: any, source: string): string | undefined {\n const commentTypes = new Set(['comment']);\n const commentLines: string[] = [];\n let sibling = node.previousNamedSibling;\n while (sibling && commentTypes.has(sibling.type)) {\n commentLines.unshift(source.slice(sibling.startIndex, sibling.endIndex));\n sibling = sibling.previousNamedSibling;\n }\n if (commentLines.length === 0) return undefined;\n const cleaned = commentLines.join('\\n').replace(/^#\\s?/gm, '').trim();\n return cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction extractPythonSignature(node: any, source: string, kind: NodeKindLocal): string | undefined {\n if (kind === 'function' || kind === 'method') {\n const text = source.slice(node.startIndex, node.endIndex);\n const bodyStart = text.search(/\\s*:\\s*\\n/);\n const header = bodyStart > 0 ? text.slice(0, bodyStart).trim() : text.split('\\n')[0].trim();\n return header.length > 150 ? header.slice(0, 150) + '\u2026' : header || undefined;\n }\n const text = source.slice(node.startIndex, node.endIndex);\n const firstLine = text.split('\\n')[0].trim();\n return firstLine.length > 120 ? firstLine.slice(0, 120) + '\u2026' : firstLine || undefined;\n}\n\nfunction collectPythonCallRefs(\n node: any,\n source: string,\n sourceId: string,\n unresolvedRefs: UnresolvedRef[],\n cellOffsets: CellOffset[],\n codeChunks: string[]\n): void {\n walkForPythonCalls(node, source, sourceId, unresolvedRefs, cellOffsets, codeChunks);\n}\n\nfunction walkForPythonCalls(\n node: any,\n source: string,\n sourceId: string,\n unresolvedRefs: UnresolvedRef[],\n cellOffsets: CellOffset[],\n codeChunks: string[]\n): void {\n if (CALL_NODE_TYPES.has(node.type)) {\n const calleeName = extractPythonCallName(node, source);\n if (calleeName) {\n const rawLine = node.startPosition.row + 1;\n const line = remapLine(rawLine, cellOffsets, codeChunks);\n unresolvedRefs.push({\n sourceId,\n refName: calleeName,\n refKind: 'function',\n line,\n column: node.startPosition.column,\n });\n }\n }\n for (let i = 0; i < node.childCount; i++) {\n walkForPythonCalls(node.child(i), source, sourceId, unresolvedRefs, cellOffsets, codeChunks);\n }\n}\n\nfunction extractPythonCallName(node: any, source: string): string | null {\n for (const field of ['function', 'method']) {\n const f = node.childForFieldName?.(field);\n if (f) {\n const t = f.type;\n if (t === 'identifier' || t === 'name') {\n const text = source.slice(f.startIndex, f.endIndex).trim();\n if (text && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(text)) return text;\n }\n }\n }\n const funcNode = node.child(0);\n if (funcNode) {\n const rawName = source.slice(funcNode.startIndex, funcNode.endIndex).split('(')[0].trim();\n if (rawName && rawName.length < 100) {\n const calleeName = rawName.split('.').pop()!.trim();\n if (calleeName && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(calleeName)) return calleeName;\n }\n }\n return null;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["text"]
7
7
  }