codemap-ai 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  FlowStorage
3
- } from "./chunk-VB74K47A.js";
3
+ } from "./chunk-EEMILSZ4.js";
4
4
 
5
5
  // src/flow/builder.ts
6
- import { readFileSync, statSync } from "fs";
6
+ import { readFileSync as readFileSync2, statSync } from "fs";
7
7
  import { createHash } from "crypto";
8
8
  import { glob } from "glob";
9
9
  import { resolve, relative, extname } from "path";
@@ -473,10 +473,13 @@ var TypeScriptParser = class extends BaseParser {
473
473
  language = "typescript";
474
474
  extensions = [".ts", ".tsx"];
475
475
  parser;
476
+ treeSitterParser;
477
+ // Expose for Phase 3
476
478
  constructor() {
477
479
  super();
478
480
  this.parser = new Parser();
479
481
  this.parser.setLanguage(TypeScript.typescript);
482
+ this.treeSitterParser = this.parser;
480
483
  }
481
484
  parse(filePath, content) {
482
485
  const analysis = this.createEmptyAnalysis(filePath);
@@ -764,6 +767,7 @@ var JavaScriptParser = class extends TypeScriptParser {
764
767
  super();
765
768
  this.parser = new Parser();
766
769
  this.parser.setLanguage(JavaScript);
770
+ this.treeSitterParser = this.parser;
767
771
  }
768
772
  parse(filePath, content) {
769
773
  const sourceCode = typeof content === "string" ? content : String(content);
@@ -788,10 +792,13 @@ var PythonParser = class extends BaseParser {
788
792
  language = "python";
789
793
  extensions = [".py"];
790
794
  parser;
795
+ treeSitterParser;
796
+ // Expose for Phase 3
791
797
  constructor() {
792
798
  super();
793
799
  this.parser = new Parser2();
794
800
  this.parser.setLanguage(Python);
801
+ this.treeSitterParser = this.parser;
795
802
  }
796
803
  parse(filePath, content) {
797
804
  const analysis = this.createEmptyAnalysis(filePath);
@@ -1352,6 +1359,614 @@ function registerAllParsers() {
1352
1359
  }
1353
1360
  registerAllParsers();
1354
1361
 
1362
+ // src/flow/phase3-analyzer.ts
1363
+ import { readFileSync } from "fs";
1364
+
1365
+ // src/analysis/property-tracker.ts
1366
+ function extractPropertyAccesses(functionNode, scopeId, scopeName, filePath) {
1367
+ const accesses = [];
1368
+ const isJS = /\.(js|ts|jsx|tsx)$/.test(filePath);
1369
+ const isPython = /\.py$/.test(filePath);
1370
+ if (isJS) {
1371
+ extractJavaScriptPropertyAccesses(functionNode, scopeId, scopeName, accesses);
1372
+ } else if (isPython) {
1373
+ extractPythonPropertyAccesses(functionNode, scopeId, scopeName, accesses);
1374
+ }
1375
+ return accesses;
1376
+ }
1377
+ function extractJavaScriptPropertyAccesses(node, scopeId, scopeName, accesses) {
1378
+ traverseNode(node, (current) => {
1379
+ if (current.type === "member_expression") {
1380
+ const access = parseJavaScriptMemberExpression(current, scopeId);
1381
+ if (access) {
1382
+ accesses.push(access);
1383
+ }
1384
+ }
1385
+ if (current.type === "optional_chain") {
1386
+ const memberExpr = current.children.find((c) => c.type === "member_expression");
1387
+ if (memberExpr) {
1388
+ const access = parseJavaScriptMemberExpression(memberExpr, scopeId);
1389
+ if (access) {
1390
+ access.accessType = "optional";
1391
+ accesses.push(access);
1392
+ }
1393
+ }
1394
+ }
1395
+ if (current.type === "subscript_expression") {
1396
+ const access = parseJavaScriptSubscript(current, scopeId);
1397
+ if (access) {
1398
+ accesses.push(access);
1399
+ }
1400
+ }
1401
+ });
1402
+ }
1403
+ function parseJavaScriptMemberExpression(node, scopeId) {
1404
+ const propertyPath = [];
1405
+ let objectName = "";
1406
+ let currentNode = node;
1407
+ while (currentNode && currentNode.type === "member_expression") {
1408
+ const propertyNode = currentNode.childForFieldName("property");
1409
+ if (propertyNode) {
1410
+ propertyPath.unshift(propertyNode.text);
1411
+ }
1412
+ const objectNode = currentNode.childForFieldName("object");
1413
+ if (!objectNode) break;
1414
+ if (objectNode.type === "identifier") {
1415
+ objectName = objectNode.text;
1416
+ break;
1417
+ } else if (objectNode.type === "member_expression") {
1418
+ currentNode = objectNode;
1419
+ } else {
1420
+ return null;
1421
+ }
1422
+ }
1423
+ if (!objectName || isCommonJavaScriptObject(objectName)) {
1424
+ return null;
1425
+ }
1426
+ if (propertyPath.length === 0) {
1427
+ return null;
1428
+ }
1429
+ return {
1430
+ objectName,
1431
+ propertyPath,
1432
+ fullPath: `${objectName}.${propertyPath.join(".")}`,
1433
+ line: node.startPosition.row + 1,
1434
+ scopeId,
1435
+ accessType: "dot"
1436
+ };
1437
+ }
1438
+ function parseJavaScriptSubscript(node, scopeId) {
1439
+ const objectNode = node.childForFieldName("object");
1440
+ const indexNode = node.childForFieldName("index");
1441
+ if (!objectNode || !indexNode) return null;
1442
+ if (indexNode.type !== "string") return null;
1443
+ const objectName = objectNode.text;
1444
+ const propertyName = indexNode.text.replace(/['"]/g, "");
1445
+ if (isCommonJavaScriptObject(objectName)) {
1446
+ return null;
1447
+ }
1448
+ return {
1449
+ objectName,
1450
+ propertyPath: [propertyName],
1451
+ fullPath: `${objectName}.${propertyName}`,
1452
+ line: node.startPosition.row + 1,
1453
+ scopeId,
1454
+ accessType: "bracket"
1455
+ };
1456
+ }
1457
+ function extractPythonPropertyAccesses(node, scopeId, scopeName, accesses) {
1458
+ traverseNode(node, (current) => {
1459
+ if (current.type === "attribute") {
1460
+ const access = parsePythonAttribute(current, scopeId);
1461
+ if (access) {
1462
+ accesses.push(access);
1463
+ }
1464
+ }
1465
+ if (current.type === "subscript") {
1466
+ const access = parsePythonSubscript(current, scopeId);
1467
+ if (access) {
1468
+ accesses.push(access);
1469
+ }
1470
+ }
1471
+ });
1472
+ }
1473
+ function parsePythonAttribute(node, scopeId) {
1474
+ const propertyPath = [];
1475
+ let objectName = "";
1476
+ let currentNode = node;
1477
+ while (currentNode && currentNode.type === "attribute") {
1478
+ const attrNode = currentNode.childForFieldName("attribute");
1479
+ if (attrNode) {
1480
+ propertyPath.unshift(attrNode.text);
1481
+ }
1482
+ const objectNode = currentNode.childForFieldName("object");
1483
+ if (!objectNode) break;
1484
+ if (objectNode.type === "identifier") {
1485
+ objectName = objectNode.text;
1486
+ break;
1487
+ } else if (objectNode.type === "attribute") {
1488
+ currentNode = objectNode;
1489
+ } else {
1490
+ return null;
1491
+ }
1492
+ }
1493
+ if (!objectName || isCommonPythonObject(objectName)) {
1494
+ return null;
1495
+ }
1496
+ if (propertyPath.length === 0) {
1497
+ return null;
1498
+ }
1499
+ return {
1500
+ objectName,
1501
+ propertyPath,
1502
+ fullPath: `${objectName}.${propertyPath.join(".")}`,
1503
+ line: node.startPosition.row + 1,
1504
+ scopeId,
1505
+ accessType: "dot"
1506
+ };
1507
+ }
1508
+ function parsePythonSubscript(node, scopeId) {
1509
+ const valueNode = node.childForFieldName("value");
1510
+ const subscriptChildren = node.children.filter(
1511
+ (c) => c.type === "string" || c.type === "identifier"
1512
+ );
1513
+ if (!valueNode || subscriptChildren.length === 0) return null;
1514
+ const keyNode = subscriptChildren.find((c) => c.type === "string");
1515
+ if (!keyNode) return null;
1516
+ const objectName = valueNode.text;
1517
+ const stringContent = keyNode.children.find((c) => c.type === "string_content");
1518
+ const propertyName = stringContent ? stringContent.text : keyNode.text.replace(/['"]/g, "");
1519
+ if (isCommonPythonObject(objectName)) {
1520
+ return null;
1521
+ }
1522
+ return {
1523
+ objectName,
1524
+ propertyPath: [propertyName],
1525
+ fullPath: `${objectName}.${propertyName}`,
1526
+ line: node.startPosition.row + 1,
1527
+ scopeId,
1528
+ accessType: "bracket"
1529
+ };
1530
+ }
1531
+ function traverseNode(node, visitor) {
1532
+ visitor(node);
1533
+ for (let i = 0; i < node.childCount; i++) {
1534
+ const child = node.child(i);
1535
+ if (child) {
1536
+ traverseNode(child, visitor);
1537
+ }
1538
+ }
1539
+ }
1540
+ function isCommonJavaScriptObject(name) {
1541
+ const commonObjects = /* @__PURE__ */ new Set([
1542
+ // Global objects
1543
+ "console",
1544
+ "window",
1545
+ "document",
1546
+ "process",
1547
+ "global",
1548
+ // Common libraries
1549
+ "Math",
1550
+ "JSON",
1551
+ "Date",
1552
+ "Array",
1553
+ "Object",
1554
+ "String",
1555
+ "Number",
1556
+ // Keywords
1557
+ "this",
1558
+ "super",
1559
+ // Common module patterns
1560
+ "exports",
1561
+ "module",
1562
+ "require"
1563
+ ]);
1564
+ return commonObjects.has(name);
1565
+ }
1566
+ function isCommonPythonObject(name) {
1567
+ const commonObjects = /* @__PURE__ */ new Set([
1568
+ // Keywords
1569
+ "self",
1570
+ "cls",
1571
+ // Built-in types
1572
+ "str",
1573
+ "int",
1574
+ "float",
1575
+ "dict",
1576
+ "list",
1577
+ "tuple",
1578
+ "set",
1579
+ // Common modules
1580
+ "os",
1581
+ "sys",
1582
+ "json",
1583
+ "math",
1584
+ "datetime"
1585
+ ]);
1586
+ return commonObjects.has(name);
1587
+ }
1588
+
1589
+ // src/analysis/return-analyzer.ts
1590
+ function analyzeReturnStatements(functionNode, functionId, functionName, filePath) {
1591
+ const returns = [];
1592
+ const isJS = /\.(js|ts|jsx|tsx)$/.test(filePath);
1593
+ const isPython = /\.py$/.test(filePath);
1594
+ if (isJS) {
1595
+ extractJavaScriptReturns(functionNode, functionId, functionName, returns);
1596
+ } else if (isPython) {
1597
+ extractPythonReturns(functionNode, functionId, functionName, returns);
1598
+ }
1599
+ return returns;
1600
+ }
1601
+ function extractJavaScriptReturns(node, functionId, functionName, returns) {
1602
+ traverseNode2(node, (current) => {
1603
+ if (current.type === "return_statement") {
1604
+ const returnNode = current.children.find(
1605
+ (c) => c.type !== "return" && c.type !== ";"
1606
+ );
1607
+ if (returnNode) {
1608
+ const returnInfo = parseJavaScriptReturnValue(
1609
+ returnNode,
1610
+ functionId,
1611
+ functionName
1612
+ );
1613
+ if (returnInfo) {
1614
+ returns.push(returnInfo);
1615
+ }
1616
+ }
1617
+ }
1618
+ });
1619
+ }
1620
+ function parseJavaScriptReturnValue(returnNode, functionId, functionName) {
1621
+ const line = returnNode.startPosition.row + 1;
1622
+ const returnExpression = returnNode.text;
1623
+ let returnType = null;
1624
+ let objectShape = null;
1625
+ if (returnNode.type === "object") {
1626
+ objectShape = extractJavaScriptObjectShape(returnNode);
1627
+ returnType = "object";
1628
+ } else if (returnNode.type === "array") {
1629
+ returnType = "array";
1630
+ objectShape = {
1631
+ properties: [],
1632
+ propertyTypes: /* @__PURE__ */ new Map(),
1633
+ nestedShapes: /* @__PURE__ */ new Map(),
1634
+ isArray: true,
1635
+ isDictionary: false,
1636
+ isOptional: false
1637
+ };
1638
+ } else if (returnNode.type === "new_expression") {
1639
+ const constructorNode = returnNode.childForFieldName("constructor");
1640
+ if (constructorNode) {
1641
+ returnType = constructorNode.text;
1642
+ }
1643
+ } else if (returnNode.type === "identifier") {
1644
+ returnType = `@var:${returnNode.text}`;
1645
+ } else if (returnNode.type === "call_expression") {
1646
+ const funcNode = returnNode.childForFieldName("function");
1647
+ if (funcNode) {
1648
+ returnType = `@call:${funcNode.text}`;
1649
+ }
1650
+ }
1651
+ return {
1652
+ functionId,
1653
+ functionName,
1654
+ returnType,
1655
+ objectShape,
1656
+ line,
1657
+ returnExpression
1658
+ };
1659
+ }
1660
+ function extractJavaScriptObjectShape(objectNode) {
1661
+ const properties = [];
1662
+ const propertyTypes = /* @__PURE__ */ new Map();
1663
+ const nestedShapes = /* @__PURE__ */ new Map();
1664
+ for (const child of objectNode.children) {
1665
+ if (child.type === "pair") {
1666
+ const keyNode = child.childForFieldName("key");
1667
+ const valueNode = child.childForFieldName("value");
1668
+ if (keyNode) {
1669
+ const propertyName = keyNode.text.replace(/['"]/g, "");
1670
+ properties.push(propertyName);
1671
+ if (valueNode) {
1672
+ const propertyType = inferJavaScriptType(valueNode);
1673
+ propertyTypes.set(propertyName, propertyType);
1674
+ if (valueNode.type === "object") {
1675
+ const nestedShape = extractJavaScriptObjectShape(valueNode);
1676
+ nestedShapes.set(propertyName, nestedShape);
1677
+ }
1678
+ }
1679
+ }
1680
+ }
1681
+ }
1682
+ return {
1683
+ properties,
1684
+ propertyTypes,
1685
+ nestedShapes,
1686
+ isArray: false,
1687
+ isDictionary: false,
1688
+ isOptional: false
1689
+ };
1690
+ }
1691
+ function inferJavaScriptType(valueNode) {
1692
+ switch (valueNode.type) {
1693
+ case "number":
1694
+ return "number";
1695
+ case "string":
1696
+ case "template_string":
1697
+ return "string";
1698
+ case "true":
1699
+ case "false":
1700
+ return "boolean";
1701
+ case "null":
1702
+ return "null";
1703
+ case "undefined":
1704
+ return "undefined";
1705
+ case "array":
1706
+ return "array";
1707
+ case "object":
1708
+ return "object";
1709
+ case "arrow_function":
1710
+ case "function_expression":
1711
+ return "function";
1712
+ case "identifier":
1713
+ return `@var:${valueNode.text}`;
1714
+ case "call_expression":
1715
+ return "@call";
1716
+ default:
1717
+ return "unknown";
1718
+ }
1719
+ }
1720
+ function extractPythonReturns(node, functionId, functionName, returns) {
1721
+ traverseNode2(node, (current) => {
1722
+ if (current.type === "return_statement") {
1723
+ const returnNode = current.children.find((c) => c.type !== "return");
1724
+ if (returnNode) {
1725
+ const returnInfo = parsePythonReturnValue(
1726
+ returnNode,
1727
+ functionId,
1728
+ functionName
1729
+ );
1730
+ if (returnInfo) {
1731
+ returns.push(returnInfo);
1732
+ }
1733
+ }
1734
+ }
1735
+ });
1736
+ }
1737
+ function parsePythonReturnValue(returnNode, functionId, functionName) {
1738
+ const line = returnNode.startPosition.row + 1;
1739
+ const returnExpression = returnNode.text;
1740
+ let returnType = null;
1741
+ let objectShape = null;
1742
+ if (returnNode.type === "dictionary") {
1743
+ objectShape = extractPythonDictionaryShape(returnNode);
1744
+ returnType = "dict";
1745
+ } else if (returnNode.type === "list") {
1746
+ returnType = "list";
1747
+ objectShape = {
1748
+ properties: [],
1749
+ propertyTypes: /* @__PURE__ */ new Map(),
1750
+ nestedShapes: /* @__PURE__ */ new Map(),
1751
+ isArray: true,
1752
+ isDictionary: false,
1753
+ isOptional: false
1754
+ };
1755
+ } else if (returnNode.type === "call") {
1756
+ const funcNode = returnNode.childForFieldName("function");
1757
+ if (funcNode) {
1758
+ const funcName = funcNode.text;
1759
+ if (funcName[0] === funcName[0].toUpperCase()) {
1760
+ returnType = funcName;
1761
+ } else {
1762
+ returnType = `@call:${funcName}`;
1763
+ }
1764
+ }
1765
+ } else if (returnNode.type === "identifier") {
1766
+ returnType = `@var:${returnNode.text}`;
1767
+ } else if (returnNode.type === "list_comprehension") {
1768
+ returnType = "list";
1769
+ objectShape = {
1770
+ properties: [],
1771
+ propertyTypes: /* @__PURE__ */ new Map(),
1772
+ nestedShapes: /* @__PURE__ */ new Map(),
1773
+ isArray: true,
1774
+ isDictionary: false,
1775
+ isOptional: false
1776
+ };
1777
+ }
1778
+ return {
1779
+ functionId,
1780
+ functionName,
1781
+ returnType,
1782
+ objectShape,
1783
+ line,
1784
+ returnExpression
1785
+ };
1786
+ }
1787
+ function extractPythonDictionaryShape(dictNode) {
1788
+ const properties = [];
1789
+ const propertyTypes = /* @__PURE__ */ new Map();
1790
+ const nestedShapes = /* @__PURE__ */ new Map();
1791
+ for (const child of dictNode.children) {
1792
+ if (child.type === "pair") {
1793
+ const keyNode = child.children.find((c) => c.type === "string");
1794
+ const valueNode = child.children.find(
1795
+ (c) => c.type !== "string" && c.type !== ":"
1796
+ );
1797
+ if (keyNode) {
1798
+ const stringContent = keyNode.children.find(
1799
+ (c) => c.type === "string_content"
1800
+ );
1801
+ const propertyName = stringContent ? stringContent.text : keyNode.text.replace(/['"]/g, "");
1802
+ properties.push(propertyName);
1803
+ if (valueNode) {
1804
+ const propertyType = inferPythonType(valueNode);
1805
+ propertyTypes.set(propertyName, propertyType);
1806
+ if (valueNode.type === "dictionary") {
1807
+ const nestedShape = extractPythonDictionaryShape(valueNode);
1808
+ nestedShapes.set(propertyName, nestedShape);
1809
+ }
1810
+ }
1811
+ }
1812
+ }
1813
+ }
1814
+ return {
1815
+ properties,
1816
+ propertyTypes,
1817
+ nestedShapes,
1818
+ isArray: false,
1819
+ isDictionary: true,
1820
+ isOptional: false
1821
+ };
1822
+ }
1823
+ function inferPythonType(valueNode) {
1824
+ switch (valueNode.type) {
1825
+ case "integer":
1826
+ case "float":
1827
+ return "number";
1828
+ case "string":
1829
+ return "string";
1830
+ case "true":
1831
+ case "false":
1832
+ return "boolean";
1833
+ case "none":
1834
+ return "None";
1835
+ case "list":
1836
+ return "list";
1837
+ case "dictionary":
1838
+ return "dict";
1839
+ case "lambda":
1840
+ return "function";
1841
+ case "identifier":
1842
+ return `@var:${valueNode.text}`;
1843
+ case "call":
1844
+ return "@call";
1845
+ default:
1846
+ return "unknown";
1847
+ }
1848
+ }
1849
+ function traverseNode2(node, visitor) {
1850
+ visitor(node);
1851
+ for (let i = 0; i < node.childCount; i++) {
1852
+ const child = node.child(i);
1853
+ if (child) {
1854
+ traverseNode2(child, visitor);
1855
+ }
1856
+ }
1857
+ }
1858
+
1859
+ // src/flow/storage-queries-phase3.ts
1860
+ import { randomUUID } from "crypto";
1861
+ function storePropertyAccesses(db, accesses) {
1862
+ const stmt = db.prepare(`
1863
+ INSERT OR REPLACE INTO property_accesses
1864
+ (id, scope_node_id, object_name, property_path, full_path, access_type, file_path, line)
1865
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1866
+ `);
1867
+ const insert = db.transaction((accesses2) => {
1868
+ for (const access of accesses2) {
1869
+ stmt.run(
1870
+ randomUUID(),
1871
+ access.scopeId,
1872
+ access.objectName,
1873
+ JSON.stringify(access.propertyPath),
1874
+ access.fullPath,
1875
+ access.accessType,
1876
+ access.filePath || "",
1877
+ access.line
1878
+ );
1879
+ }
1880
+ });
1881
+ insert(accesses);
1882
+ }
1883
+ function storeReturnInfo(db, returns) {
1884
+ const stmt = db.prepare(`
1885
+ INSERT OR REPLACE INTO return_shapes
1886
+ (id, function_id, return_type, object_shape, line, return_expression)
1887
+ VALUES (?, ?, ?, ?, ?, ?)
1888
+ `);
1889
+ const insert = db.transaction((returns2) => {
1890
+ for (const ret of returns2) {
1891
+ stmt.run(
1892
+ randomUUID(),
1893
+ ret.functionId,
1894
+ ret.returnType,
1895
+ ret.objectShape ? JSON.stringify(serializeObjectShape(ret.objectShape)) : null,
1896
+ ret.line,
1897
+ ret.returnExpression
1898
+ );
1899
+ }
1900
+ });
1901
+ insert(returns);
1902
+ }
1903
+ function serializeObjectShape(shape) {
1904
+ return {
1905
+ properties: shape.properties,
1906
+ propertyTypes: Array.from(shape.propertyTypes.entries()),
1907
+ nestedShapes: Array.from(shape.nestedShapes.entries()).map(([key, nested]) => [
1908
+ key,
1909
+ serializeObjectShape(nested)
1910
+ ]),
1911
+ isArray: shape.isArray,
1912
+ isDictionary: shape.isDictionary,
1913
+ isOptional: shape.isOptional
1914
+ };
1915
+ }
1916
+
1917
+ // src/flow/phase3-analyzer.ts
1918
+ function analyzeFileForPhase3(filePath, functionNodes, db) {
1919
+ try {
1920
+ const language = getLanguageFromPath(filePath);
1921
+ if (!language) return;
1922
+ const parserInstance = parserRegistry.getByLanguage(language);
1923
+ if (!parserInstance || !parserInstance.treeSitterParser) return;
1924
+ const content = readFileSync(filePath, "utf-8");
1925
+ const tree = parserInstance.treeSitterParser.parse(content);
1926
+ const rootNode = tree.rootNode;
1927
+ for (const func of functionNodes) {
1928
+ const funcNode = findNodeAtLine(rootNode, func.startLine, func.endLine);
1929
+ if (!funcNode) continue;
1930
+ const accesses = extractPropertyAccesses(funcNode, func.id, func.name, filePath);
1931
+ if (accesses.length > 0) {
1932
+ const accessesWithFile = accesses.map((a) => ({ ...a, filePath }));
1933
+ storePropertyAccesses(db, accessesWithFile);
1934
+ }
1935
+ const returns = analyzeReturnStatements(funcNode, func.id, func.name, filePath);
1936
+ if (returns.length > 0) {
1937
+ storeReturnInfo(db, returns);
1938
+ }
1939
+ }
1940
+ } catch (error) {
1941
+ }
1942
+ }
1943
+ function getLanguageFromPath(filePath) {
1944
+ if (filePath.endsWith(".py")) return "python";
1945
+ if (filePath.endsWith(".js") || filePath.endsWith(".jsx")) return "javascript";
1946
+ if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
1947
+ return null;
1948
+ }
1949
+ function findNodeAtLine(node, startLine, endLine) {
1950
+ const nodeStart = node.startPosition.row + 1;
1951
+ const nodeEnd = node.endPosition.row + 1;
1952
+ if (nodeStart === startLine && nodeEnd === endLine) {
1953
+ return node;
1954
+ }
1955
+ for (let i = 0; i < node.childCount; i++) {
1956
+ const child = node.child(i);
1957
+ if (child) {
1958
+ const result = findNodeAtLine(child, startLine, endLine);
1959
+ if (result) return result;
1960
+ }
1961
+ }
1962
+ if (nodeStart <= startLine && nodeEnd >= endLine) {
1963
+ if (node.type === "function_definition" || node.type === "function_declaration" || node.type === "method_definition" || node.type === "arrow_function" || node.type === "function_expression") {
1964
+ return node;
1965
+ }
1966
+ }
1967
+ return null;
1968
+ }
1969
+
1355
1970
  // src/flow/builder.ts
1356
1971
  var FlowBuilder = class {
1357
1972
  storage;
@@ -1379,7 +1994,7 @@ var FlowBuilder = class {
1379
1994
  const filesToIndex = [];
1380
1995
  let skipped = 0;
1381
1996
  for (const filePath of allFiles) {
1382
- const content = readFileSync(filePath, "utf-8");
1997
+ const content = readFileSync2(filePath, "utf-8");
1383
1998
  const hash = this.hashContent(content);
1384
1999
  if (!this.config.forceReindex) {
1385
2000
  const existingHash = this.storage.getFileHash(filePath);
@@ -1463,7 +2078,7 @@ var FlowBuilder = class {
1463
2078
  }
1464
2079
  async parseAndStore(filePath, hash, rootPath) {
1465
2080
  const language = this.getLanguage(filePath);
1466
- const content = readFileSync(filePath, "utf-8");
2081
+ const content = readFileSync2(filePath, "utf-8");
1467
2082
  const parser = parserRegistry.getByLanguage(language);
1468
2083
  if (!parser) {
1469
2084
  throw new Error(`No parser found for language: ${language}`);
@@ -1500,6 +2115,15 @@ var FlowBuilder = class {
1500
2115
  );
1501
2116
  }
1502
2117
  }
2118
+ const functionNodes = analysis.nodes.filter((n) => n.type === "function" || n.type === "method").map((n) => ({
2119
+ id: n.id,
2120
+ name: n.name,
2121
+ startLine: n.startLine,
2122
+ endLine: n.endLine
2123
+ }));
2124
+ if (functionNodes.length > 0) {
2125
+ analyzeFileForPhase3(filePath, functionNodes, this.storage.db);
2126
+ }
1503
2127
  if (analysis.databaseOperations) {
1504
2128
  for (const dbOp of analysis.databaseOperations) {
1505
2129
  this.storage.insertDatabaseOperation({