kirograph 0.28.1 → 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 (177) hide show
  1. package/README.md +3 -2
  2. package/dist/architecture/index.js +4 -1
  3. package/dist/architecture/index.js.map +2 -2
  4. package/dist/architecture/manifest/index.js +13 -2
  5. package/dist/architecture/manifest/index.js.map +2 -2
  6. package/dist/architecture/manifest/python.js +2 -2
  7. package/dist/architecture/manifest/python.js.map +2 -2
  8. package/dist/bin/commands/help.js +3 -1
  9. package/dist/bin/commands/help.js.map +2 -2
  10. package/dist/bin/commands/install.js +2 -1
  11. package/dist/bin/commands/install.js.map +2 -2
  12. package/dist/bin/commands/memory.js +18 -8
  13. package/dist/bin/commands/memory.js.map +2 -2
  14. package/dist/bin/commands/reachability.js +7 -2
  15. package/dist/bin/commands/reachability.js.map +2 -2
  16. package/dist/bin/commands/status.js +2 -1
  17. package/dist/bin/commands/status.js.map +2 -2
  18. package/dist/bin/commands/uninit.js +2 -1
  19. package/dist/bin/commands/uninit.js.map +2 -2
  20. package/dist/bin/commands/vulns.js +26 -6
  21. package/dist/bin/commands/vulns.js.map +2 -2
  22. package/dist/bin/commands/wiki.js +56 -0
  23. package/dist/bin/commands/wiki.js.map +2 -2
  24. package/dist/bin/installer/common.js +7 -1
  25. package/dist/bin/installer/common.js.map +2 -2
  26. package/dist/bin/installer/detect.js +10 -0
  27. package/dist/bin/installer/detect.js.map +2 -2
  28. package/dist/bin/installer/hooks.js +13 -8
  29. package/dist/bin/installer/hooks.js.map +2 -2
  30. package/dist/bin/installer/steering.js +9 -0
  31. package/dist/bin/installer/steering.js.map +2 -2
  32. package/dist/bin/installer/targets/antigravity.js +1 -1
  33. package/dist/bin/installer/targets/antigravity.js.map +2 -2
  34. package/dist/bin/installer/targets/claude.js +1 -1
  35. package/dist/bin/installer/targets/claude.js.map +2 -2
  36. package/dist/bin/installer/targets/codex.js +1 -1
  37. package/dist/bin/installer/targets/codex.js.map +2 -2
  38. package/dist/bin/installer/targets/commandcode.js +84 -0
  39. package/dist/bin/installer/targets/commandcode.js.map +7 -0
  40. package/dist/bin/installer/targets/copilot.js +1 -1
  41. package/dist/bin/installer/targets/copilot.js.map +2 -2
  42. package/dist/bin/installer/targets/cursor.js +2 -2
  43. package/dist/bin/installer/targets/cursor.js.map +2 -2
  44. package/dist/bin/installer/targets/devin.js +1 -1
  45. package/dist/bin/installer/targets/devin.js.map +2 -2
  46. package/dist/bin/installer/targets/gemini-cli.js +1 -1
  47. package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
  48. package/dist/bin/installer/targets/index.js +10 -0
  49. package/dist/bin/installer/targets/index.js.map +2 -2
  50. package/dist/bin/installer/targets/opencode.js +1 -1
  51. package/dist/bin/installer/targets/opencode.js.map +2 -2
  52. package/dist/bin/installer/targets/windsurf.js +1 -1
  53. package/dist/bin/installer/targets/windsurf.js.map +2 -2
  54. package/dist/bin/kirograph.js +1 -1
  55. package/dist/bin/kirograph.js.map +1 -1
  56. package/dist/config.js +25 -1
  57. package/dist/config.js.map +2 -2
  58. package/dist/core/pipeline.js +18 -6
  59. package/dist/core/pipeline.js.map +2 -2
  60. package/dist/extraction/extractor.js +88 -26
  61. package/dist/extraction/extractor.js.map +2 -2
  62. package/dist/extraction/grammars.js +38 -3
  63. package/dist/extraction/grammars.js.map +2 -2
  64. package/dist/extraction/notebook.js +15 -7
  65. package/dist/extraction/notebook.js.map +2 -2
  66. package/dist/extraction/wasm/tree-sitter-bash.wasm +0 -0
  67. package/dist/extraction/wasm/tree-sitter-c.wasm +0 -0
  68. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  69. package/dist/extraction/wasm/tree-sitter-cpp.wasm +0 -0
  70. package/dist/extraction/wasm/tree-sitter-csharp.wasm +0 -0
  71. package/dist/extraction/wasm/tree-sitter-css.wasm +0 -0
  72. package/dist/extraction/wasm/tree-sitter-dart.wasm +0 -0
  73. package/dist/extraction/wasm/tree-sitter-elisp.wasm +0 -0
  74. package/dist/extraction/wasm/tree-sitter-elixir.wasm +0 -0
  75. package/dist/extraction/wasm/tree-sitter-elm.wasm +0 -0
  76. package/dist/extraction/wasm/tree-sitter-embedded_template.wasm +0 -0
  77. package/dist/extraction/wasm/tree-sitter-embeddedtemplate.wasm +0 -0
  78. package/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  79. package/dist/extraction/wasm/tree-sitter-html.wasm +0 -0
  80. package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  81. package/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  82. package/dist/extraction/wasm/tree-sitter-json.wasm +0 -0
  83. package/dist/extraction/wasm/tree-sitter-kotlin.wasm +0 -0
  84. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  85. package/dist/extraction/wasm/tree-sitter-objc.wasm +0 -0
  86. package/dist/extraction/wasm/tree-sitter-ocaml.wasm +0 -0
  87. package/dist/extraction/wasm/tree-sitter-php.wasm +0 -0
  88. package/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  89. package/dist/extraction/wasm/tree-sitter-ql.wasm +0 -0
  90. package/dist/extraction/wasm/tree-sitter-rescript.wasm +0 -0
  91. package/dist/extraction/wasm/tree-sitter-ruby.wasm +0 -0
  92. package/dist/extraction/wasm/tree-sitter-rust.wasm +0 -0
  93. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  94. package/dist/extraction/wasm/tree-sitter-solidity.wasm +0 -0
  95. package/dist/extraction/wasm/tree-sitter-swift.wasm +0 -0
  96. package/dist/extraction/wasm/tree-sitter-systemrdl.wasm +0 -0
  97. package/dist/extraction/wasm/tree-sitter-tlaplus.wasm +0 -0
  98. package/dist/extraction/wasm/tree-sitter-toml.wasm +0 -0
  99. package/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  100. package/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  101. package/dist/extraction/wasm/tree-sitter-vue.wasm +0 -0
  102. package/dist/extraction/wasm/tree-sitter-yaml.wasm +0 -0
  103. package/dist/extraction/wasm/tree-sitter-zig.wasm +0 -0
  104. package/dist/frameworks/index.js +68 -7
  105. package/dist/frameworks/index.js.map +2 -2
  106. package/dist/jev/client.js +99 -0
  107. package/dist/jev/client.js.map +7 -0
  108. package/dist/jev/types.js +17 -0
  109. package/dist/jev/types.js.map +7 -0
  110. package/dist/mcp/handlers/memory.js +14 -8
  111. package/dist/mcp/handlers/memory.js.map +2 -2
  112. package/dist/mcp/handlers/security.js +5 -3
  113. package/dist/mcp/handlers/security.js.map +2 -2
  114. package/dist/mcp/tools.js +1 -1
  115. package/dist/mcp/tools.js.map +1 -1
  116. package/dist/memory/index.js +13 -1
  117. package/dist/memory/index.js.map +3 -3
  118. package/dist/memory/relation-jev.js +59 -0
  119. package/dist/memory/relation-jev.js.map +7 -0
  120. package/dist/memory/schema.js +57 -0
  121. package/dist/memory/schema.js.map +7 -0
  122. package/dist/security/auth-detection-jev.js +47 -0
  123. package/dist/security/auth-detection-jev.js.map +7 -0
  124. package/dist/security/manifest/adapter.js +34 -5
  125. package/dist/security/manifest/adapter.js.map +3 -3
  126. package/dist/security/manifest/parser.js +2 -2
  127. package/dist/security/manifest/parser.js.map +2 -2
  128. package/dist/security/manifest/plugins/cargo.js +23 -0
  129. package/dist/security/manifest/plugins/cargo.js.map +2 -2
  130. package/dist/security/manifest/plugins/composer.js +12 -0
  131. package/dist/security/manifest/plugins/composer.js.map +2 -2
  132. package/dist/security/manifest/plugins/gradle.js +12 -0
  133. package/dist/security/manifest/plugins/gradle.js.map +2 -2
  134. package/dist/security/manifest/plugins/hex.js +12 -0
  135. package/dist/security/manifest/plugins/hex.js.map +2 -2
  136. package/dist/security/manifest/plugins/maven.js +68 -1
  137. package/dist/security/manifest/plugins/maven.js.map +2 -2
  138. package/dist/security/manifest/plugins/npm.js +86 -0
  139. package/dist/security/manifest/plugins/npm.js.map +2 -2
  140. package/dist/security/manifest/plugins/nuget.js +13 -0
  141. package/dist/security/manifest/plugins/nuget.js.map +2 -2
  142. package/dist/security/manifest/plugins/pubspec.js +12 -0
  143. package/dist/security/manifest/plugins/pubspec.js.map +2 -2
  144. package/dist/security/manifest/plugins/pyproject.js +12 -0
  145. package/dist/security/manifest/plugins/pyproject.js.map +2 -2
  146. package/dist/security/manifest/plugins/rubygems.js +12 -0
  147. package/dist/security/manifest/plugins/rubygems.js.map +2 -2
  148. package/dist/security/pipeline.js +5 -2
  149. package/dist/security/pipeline.js.map +2 -2
  150. package/dist/security/reachability.js +115 -50
  151. package/dist/security/reachability.js.map +2 -2
  152. package/dist/security/types.js.map +1 -1
  153. package/dist/security/vuln/client.js +45 -1
  154. package/dist/security/vuln/client.js.map +2 -2
  155. package/dist/security/vuln/epss-client.js +1 -1
  156. package/dist/security/vuln/epss-client.js.map +1 -1
  157. package/dist/security/vuln/osv-adapter.js +144 -32
  158. package/dist/security/vuln/osv-adapter.js.map +2 -2
  159. package/dist/sync/index.js +9 -2
  160. package/dist/sync/index.js.map +2 -2
  161. package/dist/vectors/libsql-index.js +287 -0
  162. package/dist/vectors/libsql-index.js.map +7 -0
  163. package/dist/vectors/turso-index.js +307 -0
  164. package/dist/vectors/turso-index.js.map +7 -0
  165. package/dist/wiki/contradiction-jev.js +49 -0
  166. package/dist/wiki/contradiction-jev.js.map +7 -0
  167. package/dist/wiki/index.js +13 -0
  168. package/dist/wiki/index.js.map +2 -2
  169. package/dist/wiki/ingest.js +2 -0
  170. package/dist/wiki/ingest.js.map +2 -2
  171. package/dist/wiki/links.js +53 -0
  172. package/dist/wiki/links.js.map +7 -0
  173. package/dist/wiki/lint.js +3 -6
  174. package/dist/wiki/lint.js.map +2 -2
  175. package/dist/wiki/rename.js +103 -0
  176. package/dist/wiki/rename.js.map +7 -0
  177. package/package.json +3 -2
@@ -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
  }
@@ -188,6 +188,7 @@ function registerFrameworkResolver(resolver) {
188
188
  function buildResolutionContext(projectRoot, db) {
189
189
  const fileCache = /* @__PURE__ */ new Map();
190
190
  const nodeCache = /* @__PURE__ */ new Map();
191
+ const allIndexedFiles = db.getAllFiles().map((f) => f.path);
191
192
  return {
192
193
  getNodesInFile(filePath) {
193
194
  if (!nodeCache.has(filePath)) {
@@ -202,24 +203,84 @@ function buildResolutionContext(projectRoot, db) {
202
203
  return db.getNodesByKind(kind);
203
204
  },
204
205
  fileExists(filePath) {
205
- return fs.existsSync(path.join(projectRoot, filePath));
206
+ if (fs.existsSync(path.join(projectRoot, filePath))) return true;
207
+ const basename = path.basename(filePath);
208
+ if (allIndexedFiles.some((f) => f.endsWith("/" + basename) || f === basename)) return true;
209
+ try {
210
+ const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
211
+ for (const entry of entries) {
212
+ if (!entry.isDirectory()) continue;
213
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
214
+ const candidate = path.join(projectRoot, entry.name, filePath);
215
+ if (fs.existsSync(candidate)) return true;
216
+ try {
217
+ const subEntries = fs.readdirSync(path.join(projectRoot, entry.name), { withFileTypes: true });
218
+ for (const sub of subEntries) {
219
+ if (!sub.isDirectory() || sub.name === "node_modules" || sub.name === ".git") continue;
220
+ if (fs.existsSync(path.join(projectRoot, entry.name, sub.name, filePath))) return true;
221
+ }
222
+ } catch {
223
+ }
224
+ }
225
+ } catch {
226
+ }
227
+ return false;
206
228
  },
207
229
  readFile(filePath) {
208
230
  if (fileCache.has(filePath)) return fileCache.get(filePath);
231
+ const rootPath = path.join(projectRoot, filePath);
232
+ if (fs.existsSync(rootPath)) {
233
+ try {
234
+ const content = fs.readFileSync(rootPath, "utf8");
235
+ fileCache.set(filePath, content);
236
+ return content;
237
+ } catch {
238
+ }
239
+ }
240
+ const basename = path.basename(filePath);
241
+ const match = allIndexedFiles.find((f) => f.endsWith("/" + basename) || f === basename);
242
+ if (match) {
243
+ try {
244
+ const content = fs.readFileSync(path.join(projectRoot, match), "utf8");
245
+ fileCache.set(filePath, content);
246
+ return content;
247
+ } catch {
248
+ }
249
+ }
209
250
  try {
210
- const content = fs.readFileSync(path.join(projectRoot, filePath), "utf8");
211
- fileCache.set(filePath, content);
212
- return content;
251
+ const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
252
+ for (const entry of entries) {
253
+ if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git") continue;
254
+ const candidate = path.join(projectRoot, entry.name, filePath);
255
+ if (fs.existsSync(candidate)) {
256
+ const content = fs.readFileSync(candidate, "utf8");
257
+ fileCache.set(filePath, content);
258
+ return content;
259
+ }
260
+ try {
261
+ const subEntries = fs.readdirSync(path.join(projectRoot, entry.name), { withFileTypes: true });
262
+ for (const sub of subEntries) {
263
+ if (!sub.isDirectory() || sub.name === "node_modules" || sub.name === ".git") continue;
264
+ const deepCandidate = path.join(projectRoot, entry.name, sub.name, filePath);
265
+ if (fs.existsSync(deepCandidate)) {
266
+ const content = fs.readFileSync(deepCandidate, "utf8");
267
+ fileCache.set(filePath, content);
268
+ return content;
269
+ }
270
+ }
271
+ } catch {
272
+ }
273
+ }
213
274
  } catch {
214
- fileCache.set(filePath, null);
215
- return null;
216
275
  }
276
+ fileCache.set(filePath, null);
277
+ return null;
217
278
  },
218
279
  getProjectRoot() {
219
280
  return projectRoot;
220
281
  },
221
282
  getAllFiles() {
222
- return db.getAllFiles().map((f) => f.path);
283
+ return allIndexedFiles;
223
284
  }
224
285
  };
225
286
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/frameworks/index.ts"],
4
- "sourcesContent": ["/**\n * Framework Resolver Registry\n *\n * Mirrors CodeGraph src/resolution/frameworks/index.ts\n * Manages framework-specific resolvers and detection.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { logDebug, logWarn } from '../errors';\nimport { updateConfig } from '../config';\nimport type { Node } from '../types';\nimport type { GraphDatabase } from '../db/database';\nimport type { FrameworkResolver, ResolutionContext } from './types';\n\n// Re-export types\nexport type { FrameworkResolver, ResolutionContext, UnresolvedRef, ResolvedRef } from './types';\n\n// Re-export individual resolvers\nexport { reactResolver } from './react';\nexport { svelteResolver } from './svelte';\nexport { expressResolver } from './express';\nexport { djangoResolver, flaskResolver, fastapiResolver } from './python';\nexport { railsResolver } from './ruby';\nexport { springResolver } from './java';\nexport { goResolver } from './go';\nexport { rustResolver } from './rust';\nexport { aspnetResolver } from './csharp';\nexport { swiftUIResolver, uikitResolver, vaporResolver } from './swift';\nexport { laravelResolver, FACADE_MAPPINGS } from './laravel';\nexport { phoenixResolver } from './elixir';\nexport { playResolver } from './scala';\nexport { nuxtResolver, vueResolver } from './vue';\nexport { solidityResolver } from './solidity';\nexport { sstResolver, cdkResolver, serverlessResolver, samResolver } from './iac';\nexport { terraformResolver } from './terraform';\nexport { pulumiResolver } from './pulumi';\nexport { cloudformationResolver } from './cloudformation';\nexport { kubernetesResolver } from './kubernetes';\nexport { dockerComposeResolver } from './docker';\nexport { ansibleResolver } from './ansible';\nexport { angularResolver } from './angular';\nexport { amplifyResolver } from './amplify';\nexport { flutterResolver } from './flutter';\n\n// \u2500\u2500 Registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nimport { reactResolver } from './react';\nimport { svelteResolver } from './svelte';\nimport { expressResolver } from './express';\nimport { djangoResolver, flaskResolver, fastapiResolver } from './python';\nimport { railsResolver } from './ruby';\nimport { springResolver } from './java';\nimport { goResolver } from './go';\nimport { rustResolver } from './rust';\nimport { aspnetResolver } from './csharp';\nimport { swiftUIResolver, uikitResolver, vaporResolver } from './swift';\nimport { laravelResolver } from './laravel';\nimport { phoenixResolver } from './elixir';\nimport { playResolver } from './scala';\nimport { nuxtResolver, vueResolver } from './vue';\nimport { solidityResolver } from './solidity';\nimport { sstResolver, cdkResolver, serverlessResolver, samResolver } from './iac';\nimport { terraformResolver } from './terraform';\nimport { pulumiResolver } from './pulumi';\nimport { cloudformationResolver } from './cloudformation';\nimport { kubernetesResolver } from './kubernetes';\nimport { dockerComposeResolver } from './docker';\nimport { ansibleResolver } from './ansible';\nimport { angularResolver } from './angular';\nimport { amplifyResolver } from './amplify';\nimport { flutterResolver } from './flutter';\n\nconst FRAMEWORK_RESOLVERS: FrameworkResolver[] = [\n // PHP\n laravelResolver,\n // JavaScript / TypeScript\n expressResolver,\n reactResolver,\n svelteResolver,\n angularResolver,\n // Vue / Nuxt\n nuxtResolver,\n vueResolver,\n // Python\n djangoResolver,\n flaskResolver,\n fastapiResolver,\n // Ruby\n railsResolver,\n // Java\n springResolver,\n // Go\n goResolver,\n // Rust\n rustResolver,\n // C#\n aspnetResolver,\n // Swift\n swiftUIResolver,\n uikitResolver,\n vaporResolver,\n // Elixir\n phoenixResolver,\n // Scala\n playResolver,\n // Solidity\n solidityResolver,\n // Infrastructure as Code\n sstResolver,\n cdkResolver,\n serverlessResolver,\n samResolver,\n terraformResolver,\n pulumiResolver,\n cloudformationResolver,\n amplifyResolver,\n // Containers & Orchestration\n kubernetesResolver,\n dockerComposeResolver,\n // Configuration Management\n ansibleResolver,\n // Dart / Flutter\n flutterResolver,\n];\n\nexport function getAllFrameworkResolvers(): FrameworkResolver[] {\n return FRAMEWORK_RESOLVERS;\n}\n\nexport function getFrameworkResolver(name: string): FrameworkResolver | undefined {\n return FRAMEWORK_RESOLVERS.find(r => r.name === name);\n}\n\nexport function registerFrameworkResolver(resolver: FrameworkResolver): void {\n const index = FRAMEWORK_RESOLVERS.findIndex(r => r.name === resolver.name);\n if (index !== -1) FRAMEWORK_RESOLVERS.splice(index, 1);\n FRAMEWORK_RESOLVERS.push(resolver);\n}\n\n// \u2500\u2500 Context builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 buildResolutionContext(projectRoot: string, db: GraphDatabase): ResolutionContext {\n const fileCache = new Map<string, string | null>();\n const nodeCache = new Map<string, Node[]>();\n\n return {\n getNodesInFile(filePath: string): Node[] {\n if (!nodeCache.has(filePath)) {\n nodeCache.set(filePath, db.getNodesByFile(filePath));\n }\n return nodeCache.get(filePath)!;\n },\n getNodesByName(name: string): Node[] {\n return db.findNodesByExactName(name);\n },\n getNodesByKind(kind: Node['kind']): Node[] {\n return db.getNodesByKind(kind);\n },\n fileExists(filePath: string): boolean {\n return fs.existsSync(path.join(projectRoot, filePath));\n },\n readFile(filePath: string): string | null {\n if (fileCache.has(filePath)) return fileCache.get(filePath)!;\n try {\n const content = fs.readFileSync(path.join(projectRoot, filePath), 'utf8');\n fileCache.set(filePath, content);\n return content;\n } catch {\n fileCache.set(filePath, null);\n return null;\n }\n },\n getProjectRoot(): string {\n return projectRoot;\n },\n getAllFiles(): string[] {\n return db.getAllFiles().map(f => f.path);\n },\n };\n}\n\n// \u2500\u2500 detectFrameworks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 * Detect which frameworks are used in a project.\n * Mirrors CodeGraph detectFrameworks() \u2014 uses ResolutionContext for file access.\n * Records detected framework names in config via updateConfig().\n */\nexport async function detectFrameworks(projectRoot: string, db?: GraphDatabase): Promise<FrameworkResolver[]> {\n // If no db provided, fall back to package.json-only detection\n if (!db) {\n return detectFromPackageJson(projectRoot);\n }\n\n const context = buildResolutionContext(projectRoot, db);\n const detected: FrameworkResolver[] = [];\n\n for (const resolver of FRAMEWORK_RESOLVERS) {\n try {\n if (resolver.detect(context)) {\n logDebug(`detectFrameworks: detected ${resolver.name}`);\n detected.push(resolver);\n }\n } catch (err) {\n logWarn(`detectFrameworks: error detecting ${resolver.name}`, { error: String(err) });\n }\n }\n\n if (detected.length > 0) {\n await updateConfig(projectRoot, { frameworkHints: detected.map(f => f.name) });\n }\n\n return detected;\n}\n\n/**\n * Lightweight fallback: detect frameworks from package.json only (no DB needed).\n */\nasync function detectFromPackageJson(projectRoot: string): Promise<FrameworkResolver[]> {\n const pkgPath = path.join(projectRoot, 'package.json');\n if (!fs.existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as Record<string, unknown>;\n } catch {\n return [];\n }\n\n const deps: Record<string, string> = {\n ...((pkg.dependencies as Record<string, string>) ?? {}),\n ...((pkg.devDependencies as Record<string, string>) ?? {}),\n };\n\n const PACKAGE_TO_RESOLVER: Record<string, string> = {\n react: 'react', next: 'react', 'react-native': 'react',\n svelte: 'svelte', '@sveltejs/kit': 'svelte',\n express: 'express', fastify: 'express', koa: 'express',\n };\n\n const detectedNames = new Set<string>();\n const detected: FrameworkResolver[] = [];\n\n for (const [pkg, resolverName] of Object.entries(PACKAGE_TO_RESOLVER)) {\n if (Object.prototype.hasOwnProperty.call(deps, pkg) && !detectedNames.has(resolverName)) {\n const resolver = getFrameworkResolver(resolverName);\n if (resolver) {\n detectedNames.add(resolverName);\n detected.push(resolver);\n }\n }\n }\n\n if (detected.length > 0) {\n await updateConfig(projectRoot, { frameworkHints: detected.map(f => f.name) });\n }\n\n return detected;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Framework Resolver Registry\n *\n * Mirrors CodeGraph src/resolution/frameworks/index.ts\n * Manages framework-specific resolvers and detection.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { logDebug, logWarn } from '../errors';\nimport { updateConfig } from '../config';\nimport type { Node } from '../types';\nimport type { GraphDatabase } from '../db/database';\nimport type { FrameworkResolver, ResolutionContext } from './types';\n\n// Re-export types\nexport type { FrameworkResolver, ResolutionContext, UnresolvedRef, ResolvedRef } from './types';\n\n// Re-export individual resolvers\nexport { reactResolver } from './react';\nexport { svelteResolver } from './svelte';\nexport { expressResolver } from './express';\nexport { djangoResolver, flaskResolver, fastapiResolver } from './python';\nexport { railsResolver } from './ruby';\nexport { springResolver } from './java';\nexport { goResolver } from './go';\nexport { rustResolver } from './rust';\nexport { aspnetResolver } from './csharp';\nexport { swiftUIResolver, uikitResolver, vaporResolver } from './swift';\nexport { laravelResolver, FACADE_MAPPINGS } from './laravel';\nexport { phoenixResolver } from './elixir';\nexport { playResolver } from './scala';\nexport { nuxtResolver, vueResolver } from './vue';\nexport { solidityResolver } from './solidity';\nexport { sstResolver, cdkResolver, serverlessResolver, samResolver } from './iac';\nexport { terraformResolver } from './terraform';\nexport { pulumiResolver } from './pulumi';\nexport { cloudformationResolver } from './cloudformation';\nexport { kubernetesResolver } from './kubernetes';\nexport { dockerComposeResolver } from './docker';\nexport { ansibleResolver } from './ansible';\nexport { angularResolver } from './angular';\nexport { amplifyResolver } from './amplify';\nexport { flutterResolver } from './flutter';\n\n// \u2500\u2500 Registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nimport { reactResolver } from './react';\nimport { svelteResolver } from './svelte';\nimport { expressResolver } from './express';\nimport { djangoResolver, flaskResolver, fastapiResolver } from './python';\nimport { railsResolver } from './ruby';\nimport { springResolver } from './java';\nimport { goResolver } from './go';\nimport { rustResolver } from './rust';\nimport { aspnetResolver } from './csharp';\nimport { swiftUIResolver, uikitResolver, vaporResolver } from './swift';\nimport { laravelResolver } from './laravel';\nimport { phoenixResolver } from './elixir';\nimport { playResolver } from './scala';\nimport { nuxtResolver, vueResolver } from './vue';\nimport { solidityResolver } from './solidity';\nimport { sstResolver, cdkResolver, serverlessResolver, samResolver } from './iac';\nimport { terraformResolver } from './terraform';\nimport { pulumiResolver } from './pulumi';\nimport { cloudformationResolver } from './cloudformation';\nimport { kubernetesResolver } from './kubernetes';\nimport { dockerComposeResolver } from './docker';\nimport { ansibleResolver } from './ansible';\nimport { angularResolver } from './angular';\nimport { amplifyResolver } from './amplify';\nimport { flutterResolver } from './flutter';\n\nconst FRAMEWORK_RESOLVERS: FrameworkResolver[] = [\n // PHP\n laravelResolver,\n // JavaScript / TypeScript\n expressResolver,\n reactResolver,\n svelteResolver,\n angularResolver,\n // Vue / Nuxt\n nuxtResolver,\n vueResolver,\n // Python\n djangoResolver,\n flaskResolver,\n fastapiResolver,\n // Ruby\n railsResolver,\n // Java\n springResolver,\n // Go\n goResolver,\n // Rust\n rustResolver,\n // C#\n aspnetResolver,\n // Swift\n swiftUIResolver,\n uikitResolver,\n vaporResolver,\n // Elixir\n phoenixResolver,\n // Scala\n playResolver,\n // Solidity\n solidityResolver,\n // Infrastructure as Code\n sstResolver,\n cdkResolver,\n serverlessResolver,\n samResolver,\n terraformResolver,\n pulumiResolver,\n cloudformationResolver,\n amplifyResolver,\n // Containers & Orchestration\n kubernetesResolver,\n dockerComposeResolver,\n // Configuration Management\n ansibleResolver,\n // Dart / Flutter\n flutterResolver,\n];\n\nexport function getAllFrameworkResolvers(): FrameworkResolver[] {\n return FRAMEWORK_RESOLVERS;\n}\n\nexport function getFrameworkResolver(name: string): FrameworkResolver | undefined {\n return FRAMEWORK_RESOLVERS.find(r => r.name === name);\n}\n\nexport function registerFrameworkResolver(resolver: FrameworkResolver): void {\n const index = FRAMEWORK_RESOLVERS.findIndex(r => r.name === resolver.name);\n if (index !== -1) FRAMEWORK_RESOLVERS.splice(index, 1);\n FRAMEWORK_RESOLVERS.push(resolver);\n}\n\n// \u2500\u2500 Context builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 buildResolutionContext(projectRoot: string, db: GraphDatabase): ResolutionContext {\n const fileCache = new Map<string, string | null>();\n const nodeCache = new Map<string, Node[]>();\n const allIndexedFiles = db.getAllFiles().map(f => f.path);\n\n return {\n getNodesInFile(filePath: string): Node[] {\n if (!nodeCache.has(filePath)) {\n nodeCache.set(filePath, db.getNodesByFile(filePath));\n }\n return nodeCache.get(filePath)!;\n },\n getNodesByName(name: string): Node[] {\n return db.findNodesByExactName(name);\n },\n getNodesByKind(kind: Node['kind']): Node[] {\n return db.getNodesByKind(kind);\n },\n fileExists(filePath: string): boolean {\n // Check root first\n if (fs.existsSync(path.join(projectRoot, filePath))) return true;\n // Multi-root workspace: check if any indexed file matches the filename\n const basename = path.basename(filePath);\n if (allIndexedFiles.some(f => f.endsWith('/' + basename) || f === basename)) return true;\n // Note: basename matching is intentionally broad \u2014 config files like angular.json,\n // docker-compose.yaml are highly specific names unlikely to cause false positives.\n // Fallback: scan top-level subdirectories for non-indexed config files (e.g., angular.json)\n try {\n const entries = fs.readdirSync(projectRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name === 'node_modules' || entry.name === '.git') continue;\n const candidate = path.join(projectRoot, entry.name, filePath);\n if (fs.existsSync(candidate)) return true;\n // Check one level deeper (module/app/file pattern)\n try {\n const subEntries = fs.readdirSync(path.join(projectRoot, entry.name), { withFileTypes: true });\n for (const sub of subEntries) {\n if (!sub.isDirectory() || sub.name === 'node_modules' || sub.name === '.git') continue;\n if (fs.existsSync(path.join(projectRoot, entry.name, sub.name, filePath))) return true;\n }\n } catch { /* skip unreadable */ }\n }\n } catch { /* fall through */ }\n return false;\n },\n readFile(filePath: string): string | null {\n if (fileCache.has(filePath)) return fileCache.get(filePath)!;\n // Try root first\n const rootPath = path.join(projectRoot, filePath);\n if (fs.existsSync(rootPath)) {\n try {\n const content = fs.readFileSync(rootPath, 'utf8');\n fileCache.set(filePath, content);\n return content;\n } catch { /* fall through */ }\n }\n // Multi-root workspace: find first matching file in indexed paths\n const basename = path.basename(filePath);\n const match = allIndexedFiles.find(f => f.endsWith('/' + basename) || f === basename);\n if (match) {\n try {\n const content = fs.readFileSync(path.join(projectRoot, match), 'utf8');\n fileCache.set(filePath, content);\n return content;\n } catch { /* fall through */ }\n }\n // Fallback: find first matching file in subdirectories (for non-indexed config files)\n try {\n const entries = fs.readdirSync(projectRoot, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name === 'node_modules' || entry.name === '.git') continue;\n const candidate = path.join(projectRoot, entry.name, filePath);\n if (fs.existsSync(candidate)) {\n const content = fs.readFileSync(candidate, 'utf8');\n fileCache.set(filePath, content);\n return content;\n }\n // Check one level deeper\n try {\n const subEntries = fs.readdirSync(path.join(projectRoot, entry.name), { withFileTypes: true });\n for (const sub of subEntries) {\n if (!sub.isDirectory() || sub.name === 'node_modules' || sub.name === '.git') continue;\n const deepCandidate = path.join(projectRoot, entry.name, sub.name, filePath);\n if (fs.existsSync(deepCandidate)) {\n const content = fs.readFileSync(deepCandidate, 'utf8');\n fileCache.set(filePath, content);\n return content;\n }\n }\n } catch { /* skip */ }\n }\n } catch { /* fall through */ }\n fileCache.set(filePath, null);\n return null;\n },\n getProjectRoot(): string {\n return projectRoot;\n },\n getAllFiles(): string[] {\n return allIndexedFiles;\n },\n };\n}\n\n// \u2500\u2500 detectFrameworks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 * Detect which frameworks are used in a project.\n * Mirrors CodeGraph detectFrameworks() \u2014 uses ResolutionContext for file access.\n * Records detected framework names in config via updateConfig().\n */\nexport async function detectFrameworks(projectRoot: string, db?: GraphDatabase): Promise<FrameworkResolver[]> {\n // If no db provided, fall back to package.json-only detection\n if (!db) {\n return detectFromPackageJson(projectRoot);\n }\n\n const context = buildResolutionContext(projectRoot, db);\n const detected: FrameworkResolver[] = [];\n\n for (const resolver of FRAMEWORK_RESOLVERS) {\n try {\n if (resolver.detect(context)) {\n logDebug(`detectFrameworks: detected ${resolver.name}`);\n detected.push(resolver);\n }\n } catch (err) {\n logWarn(`detectFrameworks: error detecting ${resolver.name}`, { error: String(err) });\n }\n }\n\n if (detected.length > 0) {\n await updateConfig(projectRoot, { frameworkHints: detected.map(f => f.name) });\n }\n\n return detected;\n}\n\n/**\n * Lightweight fallback: detect frameworks from package.json only (no DB needed).\n */\nasync function detectFromPackageJson(projectRoot: string): Promise<FrameworkResolver[]> {\n const pkgPath = path.join(projectRoot, 'package.json');\n if (!fs.existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as Record<string, unknown>;\n } catch {\n return [];\n }\n\n const deps: Record<string, string> = {\n ...((pkg.dependencies as Record<string, string>) ?? {}),\n ...((pkg.devDependencies as Record<string, string>) ?? {}),\n };\n\n const PACKAGE_TO_RESOLVER: Record<string, string> = {\n react: 'react', next: 'react', 'react-native': 'react',\n svelte: 'svelte', '@sveltejs/kit': 'svelte',\n express: 'express', fastify: 'express', koa: 'express',\n };\n\n const detectedNames = new Set<string>();\n const detected: FrameworkResolver[] = [];\n\n for (const [pkg, resolverName] of Object.entries(PACKAGE_TO_RESOLVER)) {\n if (Object.prototype.hasOwnProperty.call(deps, pkg) && !detectedNames.has(resolverName)) {\n const resolver = getFrameworkResolver(resolverName);\n if (resolver) {\n detectedNames.add(resolverName);\n detected.push(resolver);\n }\n }\n }\n\n if (detected.length > 0) {\n await updateConfig(projectRoot, { frameworkHints: detected.map(f => f.name) });\n }\n\n return detected;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_react", "import_svelte", "import_express", "import_python", "import_ruby", "import_java", "import_go", "import_rust", "import_csharp", "import_swift", "import_laravel", "import_elixir", "import_scala", "import_vue", "import_solidity", "import_iac", "import_terraform", "import_pulumi", "import_cloudformation", "import_kubernetes", "import_docker", "import_ansible", "import_angular", "import_amplify", "import_flutter", "pkg"]
7
7
  }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var client_exports = {};
20
+ __export(client_exports, {
21
+ JevClient: () => JevClient,
22
+ JevError: () => JevError,
23
+ createJevClientFromConfig: () => createJevClientFromConfig
24
+ });
25
+ module.exports = __toCommonJS(client_exports);
26
+ const DEFAULT_BASE_URL = "https://api.typesafe.ai";
27
+ const DEFAULT_MODEL = "jev-latest";
28
+ const DEFAULT_TIMEOUT_MS = 15e3;
29
+ class JevError extends Error {
30
+ constructor(message, status) {
31
+ super(message);
32
+ this.status = status;
33
+ this.name = "JevError";
34
+ }
35
+ }
36
+ class JevClient {
37
+ constructor(opts) {
38
+ if (!opts.apiKey) {
39
+ throw new JevError("jev API key is required");
40
+ }
41
+ this.apiKey = opts.apiKey;
42
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
43
+ this.model = opts.model ?? DEFAULT_MODEL;
44
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
45
+ }
46
+ /**
47
+ * Ask one or more typed questions about a piece of state in a single
48
+ * request — the API evaluates them in parallel, so callers should batch
49
+ * multiple questions about the same state rather than issuing separate calls.
50
+ */
51
+ async ask(state, questions) {
52
+ const controller = new AbortController();
53
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
54
+ let res;
55
+ try {
56
+ res = await fetch(`${this.baseUrl}/v1/systemone`, {
57
+ method: "POST",
58
+ headers: {
59
+ "Authorization": `Bearer ${this.apiKey}`,
60
+ "Content-Type": "application/json"
61
+ },
62
+ body: JSON.stringify({ state, model: this.model, questions }),
63
+ signal: controller.signal
64
+ });
65
+ } catch (err) {
66
+ if (err instanceof Error && err.name === "AbortError") {
67
+ throw new JevError(`jev request timed out after ${this.timeoutMs}ms`);
68
+ }
69
+ throw new JevError(`jev request failed: ${err instanceof Error ? err.message : String(err)}`);
70
+ } finally {
71
+ clearTimeout(timeout);
72
+ }
73
+ if (!res.ok) {
74
+ let detail = "";
75
+ try {
76
+ detail = await res.text();
77
+ } catch {
78
+ }
79
+ throw new JevError(`jev request failed (${res.status})${detail ? `: ${detail}` : ""}`, res.status);
80
+ }
81
+ return await res.json();
82
+ }
83
+ }
84
+ function createJevClientFromConfig(config) {
85
+ const apiKey = config.jevApiKey || process.env.JEV_API_KEY;
86
+ if (!apiKey) {
87
+ throw new JevError(
88
+ 'jev API key not set \u2014 set the JEV_API_KEY environment variable, or "jevApiKey" in .kirograph/config.json'
89
+ );
90
+ }
91
+ return new JevClient({ apiKey, baseUrl: config.jevBaseUrl, model: config.jevModel });
92
+ }
93
+ // Annotate the CommonJS export names for ESM import in node:
94
+ 0 && (module.exports = {
95
+ JevClient,
96
+ JevError,
97
+ createJevClientFromConfig
98
+ });
99
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/jev/client.ts"],
4
+ "sourcesContent": ["/**\n * KiroGraph \u2014 jev (TypeSafe System One) client\n *\n * Thin HTTP client for https://docs.typesafe.ai's fast typed-classification\n * model. Used by opt-in \"jev mode\" toggles across the codebase (memory\n * relation judging, wiki contradiction detection, attack-surface auth\n * detection) as a cheaper/faster alternative to delegating a narrow\n * classification decision to a full agent turn.\n */\nimport type { JevQuestion, JevResponse } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.typesafe.ai';\nconst DEFAULT_MODEL = 'jev-latest';\nconst DEFAULT_TIMEOUT_MS = 15000;\n\nexport class JevError extends Error {\n constructor(message: string, public readonly status?: number) {\n super(message);\n this.name = 'JevError';\n }\n}\n\nexport interface JevClientOptions {\n apiKey: string;\n /** Override for testing / self-hosted deployments. Default: https://api.typesafe.ai */\n baseUrl?: string;\n /** Default: jev-latest */\n model?: string;\n timeoutMs?: number;\n}\n\nexport class JevClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly model: string;\n private readonly timeoutMs: number;\n\n constructor(opts: JevClientOptions) {\n if (!opts.apiKey) {\n throw new JevError('jev API key is required');\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.model = opts.model ?? DEFAULT_MODEL;\n this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n /**\n * Ask one or more typed questions about a piece of state in a single\n * request \u2014 the API evaluates them in parallel, so callers should batch\n * multiple questions about the same state rather than issuing separate calls.\n */\n async ask(state: string, questions: Record<string, JevQuestion>): Promise<JevResponse> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/v1/systemone`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ state, model: this.model, questions }),\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new JevError(`jev request timed out after ${this.timeoutMs}ms`);\n }\n throw new JevError(`jev request failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n let detail = '';\n try { detail = await res.text(); } catch { /* ignore */ }\n throw new JevError(`jev request failed (${res.status})${detail ? `: ${detail}` : ''}`, res.status);\n }\n\n return await res.json() as JevResponse;\n }\n}\n\n/** Minimal shape of the config fields JevClient needs \u2014 avoids importing the full KiroGraphConfig type. */\nexport interface JevConfigLike {\n jevApiKey?: string;\n jevBaseUrl?: string;\n jevModel?: string;\n}\n\n/**\n * Build a JevClient from KiroGraph config, falling back to the JEV_API_KEY\n * env var when `jevApiKey` isn't set in .kirograph/config.json (config.json\n * is often committed to source control \u2014 a config field is provided mainly\n * for pointing at a local mock server in tests).\n */\nexport function createJevClientFromConfig(config: JevConfigLike): JevClient {\n const apiKey = config.jevApiKey || process.env.JEV_API_KEY;\n if (!apiKey) {\n throw new JevError(\n 'jev API key not set \u2014 set the JEV_API_KEY environment variable, or \"jevApiKey\" in .kirograph/config.json',\n );\n }\n return new JevClient({ apiKey, baseUrl: config.jevBaseUrl, model: config.jevModel });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAEpB,MAAM,iBAAiB,MAAM;AAAA,EAClC,YAAY,SAAiC,QAAiB;AAC5D,UAAM,OAAO;AAD8B;AAE3C,SAAK,OAAO;AAAA,EACd;AACF;AAWO,MAAM,UAAU;AAAA,EAMrB,YAAY,MAAwB;AAClC,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,SAAS,yBAAyB;AAAA,IAC9C;AACA,SAAK,SAAS,KAAK;AACnB,SAAK,WAAW,KAAK,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACpE,SAAK,QAAQ,KAAK,SAAS;AAC3B,SAAK,YAAY,KAAK,aAAa;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAI,OAAe,WAA8D;AACrF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEnE,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,GAAG,KAAK,OAAO,iBAAiB;AAAA,QAChD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,iBAAiB,UAAU,KAAK,MAAM;AAAA,UACtC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,OAAO,OAAO,KAAK,OAAO,UAAU,CAAC;AAAA,QAC5D,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,SAAS,+BAA+B,KAAK,SAAS,IAAI;AAAA,MACtE;AACA,YAAM,IAAI,SAAS,uBAAuB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IAC9F,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,UAAI,SAAS;AACb,UAAI;AAAE,iBAAS,MAAM,IAAI,KAAK;AAAA,MAAG,QAAQ;AAAA,MAAe;AACxD,YAAM,IAAI,SAAS,uBAAuB,IAAI,MAAM,IAAI,SAAS,KAAK,MAAM,KAAK,EAAE,IAAI,IAAI,MAAM;AAAA,IACnG;AAEA,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB;AACF;AAeO,SAAS,0BAA0B,QAAkC;AAC1E,QAAM,SAAS,OAAO,aAAa,QAAQ,IAAI;AAC/C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,IAAI,UAAU,EAAE,QAAQ,SAAS,OAAO,YAAY,OAAO,OAAO,SAAS,CAAC;AACrF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/jev/types.ts"],
4
+ "sourcesContent": ["/**\n * KiroGraph \u2014 jev (TypeSafe System One) client types\n *\n * Mirrors the real API shape at https://docs.typesafe.ai:\n * POST /v1/systemone\n * { state: string, model: string, questions: { [id]: JevQuestion } }\n * \u2192 { model: string, answers: { [id]: JevAnswer }, usage?: {...} }\n */\n\n/** Choice: selects one of a fixed set of named categories. Max 255 options. */\nexport interface JevChoiceQuestion {\n type: 'choice';\n instructions: string;\n /** category name \u2192 description shown to the model */\n criteria: Record<string, string>;\n}\n\n/** Score: a calibrated numeric level chosen from an ordered set of 2\u201310 descriptions. */\nexport interface JevScoreQuestion {\n type: 'score';\n instructions: string;\n /** ordered level descriptions, low \u2192 high */\n criteria: string[];\n}\n\n/** Noul: a boolean-shaped judgment returned as a 0.0\u20131.0 value. */\nexport interface JevNoulQuestion {\n type: 'noul';\n instructions: string;\n}\n\nexport type JevQuestion = JevChoiceQuestion | JevScoreQuestion | JevNoulQuestion;\n\nexport interface JevChoiceAnswer {\n type: 'choice';\n choice: string;\n confidence?: number;\n probabilities?: Record<string, number>;\n}\n\nexport interface JevScoreAnswer {\n type: 'score';\n score: number;\n confidence?: number;\n legend?: Record<string, string>;\n}\n\nexport interface JevNoulAnswer {\n type: 'noul';\n noul: number;\n confidence?: number;\n}\n\nexport type JevAnswer = JevChoiceAnswer | JevScoreAnswer | JevNoulAnswer;\n\nexport interface JevUsage {\n input_tokens: number;\n output_tokens: number;\n}\n\nexport interface JevResponse {\n model: string;\n answers: Record<string, JevAnswer>;\n usage?: JevUsage;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -59,18 +59,24 @@ async function handleMemory(toolName, args, cg) {
59
59
  const projectRoot = cg.getProjectRoot();
60
60
  const config = await loadConfig(projectRoot);
61
61
  if (!config.enableMemory) return "Memory is not enabled. Set enableMemory: true in .kirograph/config.json";
62
- const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
62
+ const { MemoryManager, MemorySchemaError } = await Promise.resolve().then(() => require("../../memory/index.js"));
63
63
  const db = cg.getDatabase();
64
64
  db.applyMemorySchema();
65
65
  const mem = new MemoryManager(config, db.getRawDb());
66
66
  mem.initialize();
67
- const id = await mem.store({
68
- content: args.content,
69
- kind: args.kind ?? "note",
70
- source: "agent",
71
- topicKey: args.topicKey,
72
- reviewAfter: args.reviewAfter
73
- });
67
+ let id;
68
+ try {
69
+ id = await mem.store({
70
+ content: args.content,
71
+ kind: args.kind ?? "note",
72
+ source: "agent",
73
+ topicKey: args.topicKey,
74
+ reviewAfter: args.reviewAfter
75
+ });
76
+ } catch (err) {
77
+ if (err instanceof MemorySchemaError) return `Rejected: ${err.message}`;
78
+ throw err;
79
+ }
74
80
  if (!id) return "Observation already exists (duplicate content).";
75
81
  return `Stored observation ${id} [${args.kind ?? "note"}]`;
76
82
  }