@liendev/lien 0.14.0 → 0.15.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.
- package/CURSOR_RULES_TEMPLATE.md +63 -517
- package/dist/index.js +175 -23
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -161,7 +161,7 @@ function migrateConfig(oldConfig) {
|
|
|
161
161
|
path: ".",
|
|
162
162
|
enabled: true,
|
|
163
163
|
config: {
|
|
164
|
-
include: oldConfig.indexing.include ?? ["**/*.{ts,tsx,js,jsx,py,go,rs,java,c,cpp,cs}"],
|
|
164
|
+
include: oldConfig.indexing.include ?? ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,c,cpp,cs}"],
|
|
165
165
|
exclude: oldConfig.indexing.exclude ?? [
|
|
166
166
|
"**/node_modules/**",
|
|
167
167
|
"**/dist/**",
|
|
@@ -181,7 +181,7 @@ function migrateConfig(oldConfig) {
|
|
|
181
181
|
path: ".",
|
|
182
182
|
enabled: true,
|
|
183
183
|
config: {
|
|
184
|
-
include: ["**/*.{ts,tsx,js,jsx,py,go,rs,java,c,cpp,cs}"],
|
|
184
|
+
include: ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,c,cpp,cs}"],
|
|
185
185
|
exclude: [
|
|
186
186
|
"**/node_modules/**",
|
|
187
187
|
"**/dist/**",
|
|
@@ -1030,7 +1030,7 @@ async function scanCodebase(options) {
|
|
|
1030
1030
|
".lien/**",
|
|
1031
1031
|
...excludePatterns
|
|
1032
1032
|
]);
|
|
1033
|
-
const patterns = includePatterns.length > 0 ? includePatterns : ["**/*.{ts,tsx,js,jsx,py,go,rs,java,cpp,c,h,md,mdx}"];
|
|
1033
|
+
const patterns = includePatterns.length > 0 ? includePatterns : ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,cpp,c,cs,h,md,mdx}"];
|
|
1034
1034
|
const allFiles = [];
|
|
1035
1035
|
for (const pattern of patterns) {
|
|
1036
1036
|
const files = await glob(pattern, {
|
|
@@ -1388,6 +1388,7 @@ import Parser from "tree-sitter";
|
|
|
1388
1388
|
import TypeScript from "tree-sitter-typescript";
|
|
1389
1389
|
import JavaScript from "tree-sitter-javascript";
|
|
1390
1390
|
import PHPParser from "tree-sitter-php";
|
|
1391
|
+
import Python from "tree-sitter-python";
|
|
1391
1392
|
import { extname } from "path";
|
|
1392
1393
|
function getParser(language) {
|
|
1393
1394
|
if (!parserCache.has(language)) {
|
|
@@ -1414,6 +1415,8 @@ function detectLanguage2(filePath) {
|
|
|
1414
1415
|
return "javascript";
|
|
1415
1416
|
case "php":
|
|
1416
1417
|
return "php";
|
|
1418
|
+
case "py":
|
|
1419
|
+
return "python";
|
|
1417
1420
|
default:
|
|
1418
1421
|
return null;
|
|
1419
1422
|
}
|
|
@@ -1447,8 +1450,9 @@ var init_parser = __esm({
|
|
|
1447
1450
|
languageConfig = {
|
|
1448
1451
|
typescript: TypeScript.typescript,
|
|
1449
1452
|
javascript: JavaScript,
|
|
1450
|
-
php: PHPParser.php
|
|
1453
|
+
php: PHPParser.php,
|
|
1451
1454
|
// Note: tree-sitter-php exports both 'php' (mixed HTML/PHP) and 'php_only'
|
|
1455
|
+
python: Python
|
|
1452
1456
|
};
|
|
1453
1457
|
}
|
|
1454
1458
|
});
|
|
@@ -1524,7 +1528,35 @@ function extractInterfaceInfo(node, _content, _parentClass) {
|
|
|
1524
1528
|
signature: `interface ${nameNode.text}`
|
|
1525
1529
|
};
|
|
1526
1530
|
}
|
|
1527
|
-
function
|
|
1531
|
+
function extractPythonFunctionInfo(node, content, parentClass) {
|
|
1532
|
+
const nameNode = node.childForFieldName("name");
|
|
1533
|
+
if (!nameNode) return null;
|
|
1534
|
+
return {
|
|
1535
|
+
name: nameNode.text,
|
|
1536
|
+
type: parentClass ? "method" : "function",
|
|
1537
|
+
startLine: node.startPosition.row + 1,
|
|
1538
|
+
endLine: node.endPosition.row + 1,
|
|
1539
|
+
parentClass,
|
|
1540
|
+
signature: extractSignature(node, content),
|
|
1541
|
+
parameters: extractParameters(node, content),
|
|
1542
|
+
complexity: calculateComplexity(node)
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
function extractPythonClassInfo(node, _content, _parentClass) {
|
|
1546
|
+
const nameNode = node.childForFieldName("name");
|
|
1547
|
+
if (!nameNode) return null;
|
|
1548
|
+
return {
|
|
1549
|
+
name: nameNode.text,
|
|
1550
|
+
type: "class",
|
|
1551
|
+
startLine: node.startPosition.row + 1,
|
|
1552
|
+
endLine: node.endPosition.row + 1,
|
|
1553
|
+
signature: `class ${nameNode.text}`
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function extractSymbolInfo(node, content, parentClass, language) {
|
|
1557
|
+
if (node.type === "function_definition" && language === "python") {
|
|
1558
|
+
return extractPythonFunctionInfo(node, content, parentClass);
|
|
1559
|
+
}
|
|
1528
1560
|
const extractor = symbolExtractors[node.type];
|
|
1529
1561
|
return extractor ? extractor(node, content, parentClass) : null;
|
|
1530
1562
|
}
|
|
@@ -1563,23 +1595,39 @@ function extractReturnType(node, _content) {
|
|
|
1563
1595
|
function calculateComplexity(node) {
|
|
1564
1596
|
let complexity = 1;
|
|
1565
1597
|
const decisionPoints = [
|
|
1566
|
-
// TypeScript/JavaScript
|
|
1598
|
+
// Common across languages (TypeScript/JavaScript/Python/PHP)
|
|
1567
1599
|
"if_statement",
|
|
1600
|
+
// if conditions
|
|
1568
1601
|
"while_statement",
|
|
1569
|
-
|
|
1570
|
-
// do...while loops
|
|
1602
|
+
// while loops
|
|
1571
1603
|
"for_statement",
|
|
1572
|
-
|
|
1573
|
-
"for_of_statement",
|
|
1574
|
-
// for...of loops
|
|
1604
|
+
// for loops
|
|
1575
1605
|
"switch_case",
|
|
1606
|
+
// switch/case statements
|
|
1576
1607
|
"catch_clause",
|
|
1608
|
+
// try/catch error handling
|
|
1577
1609
|
"ternary_expression",
|
|
1610
|
+
// Ternary operator (a ? b : c)
|
|
1578
1611
|
"binary_expression",
|
|
1579
|
-
// For && and ||
|
|
1580
|
-
//
|
|
1581
|
-
"
|
|
1612
|
+
// For && and || logical operators
|
|
1613
|
+
// TypeScript/JavaScript specific
|
|
1614
|
+
"do_statement",
|
|
1615
|
+
// do...while loops
|
|
1616
|
+
"for_in_statement",
|
|
1617
|
+
// for...in loops
|
|
1618
|
+
"for_of_statement",
|
|
1619
|
+
// for...of loops
|
|
1620
|
+
// PHP specific
|
|
1621
|
+
"foreach_statement",
|
|
1582
1622
|
// PHP foreach loops
|
|
1623
|
+
// Python specific
|
|
1624
|
+
"elif_clause",
|
|
1625
|
+
// Python elif (adds decision point)
|
|
1626
|
+
// Note: 'else_clause' is NOT a decision point (it's the default path)
|
|
1627
|
+
"except_clause",
|
|
1628
|
+
// Python except (try/except)
|
|
1629
|
+
"conditional_expression"
|
|
1630
|
+
// Python ternary (x if cond else y)
|
|
1583
1631
|
];
|
|
1584
1632
|
function traverse(n) {
|
|
1585
1633
|
if (decisionPoints.includes(n.type)) {
|
|
@@ -1608,7 +1656,13 @@ function extractImports(rootNode) {
|
|
|
1608
1656
|
if (sourceNode) {
|
|
1609
1657
|
const importPath = sourceNode.text.replace(/['"]/g, "");
|
|
1610
1658
|
imports.push(importPath);
|
|
1659
|
+
} else {
|
|
1660
|
+
const importText = node.text.split("\n")[0];
|
|
1661
|
+
imports.push(importText);
|
|
1611
1662
|
}
|
|
1663
|
+
} else if (node.type === "import_from_statement") {
|
|
1664
|
+
const importText = node.text.split("\n")[0];
|
|
1665
|
+
imports.push(importText);
|
|
1612
1666
|
}
|
|
1613
1667
|
if (node === rootNode) {
|
|
1614
1668
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -1635,9 +1689,16 @@ var init_symbols = __esm({
|
|
|
1635
1689
|
"interface_declaration": extractInterfaceInfo,
|
|
1636
1690
|
// PHP
|
|
1637
1691
|
"function_definition": extractFunctionInfo,
|
|
1638
|
-
// PHP functions
|
|
1639
|
-
"method_declaration": extractMethodInfo
|
|
1692
|
+
// PHP functions (Python handled via language check in extractSymbolInfo)
|
|
1693
|
+
"method_declaration": extractMethodInfo,
|
|
1640
1694
|
// PHP methods
|
|
1695
|
+
// Python
|
|
1696
|
+
"async_function_definition": extractPythonFunctionInfo,
|
|
1697
|
+
// Python async functions
|
|
1698
|
+
"class_definition": extractPythonClassInfo
|
|
1699
|
+
// Python classes
|
|
1700
|
+
// Note: Python regular functions use 'function_definition' (same as PHP)
|
|
1701
|
+
// They are dispatched to extractPythonFunctionInfo via language check in extractSymbolInfo()
|
|
1641
1702
|
};
|
|
1642
1703
|
}
|
|
1643
1704
|
});
|
|
@@ -1795,6 +1856,69 @@ var init_php = __esm({
|
|
|
1795
1856
|
}
|
|
1796
1857
|
});
|
|
1797
1858
|
|
|
1859
|
+
// src/indexer/ast/traversers/python.ts
|
|
1860
|
+
var PythonTraverser;
|
|
1861
|
+
var init_python = __esm({
|
|
1862
|
+
"src/indexer/ast/traversers/python.ts"() {
|
|
1863
|
+
"use strict";
|
|
1864
|
+
PythonTraverser = class {
|
|
1865
|
+
targetNodeTypes = [
|
|
1866
|
+
"function_definition",
|
|
1867
|
+
"async_function_definition"
|
|
1868
|
+
];
|
|
1869
|
+
containerTypes = [
|
|
1870
|
+
"class_definition"
|
|
1871
|
+
// We extract methods, not the class itself
|
|
1872
|
+
];
|
|
1873
|
+
declarationTypes = [
|
|
1874
|
+
// Python doesn't have const/let/var declarations like JS/TS
|
|
1875
|
+
// Functions are always defined with 'def' or 'async def'
|
|
1876
|
+
];
|
|
1877
|
+
functionTypes = [
|
|
1878
|
+
"function_definition",
|
|
1879
|
+
"async_function_definition"
|
|
1880
|
+
];
|
|
1881
|
+
shouldExtractChildren(node) {
|
|
1882
|
+
return this.containerTypes.includes(node.type);
|
|
1883
|
+
}
|
|
1884
|
+
isDeclarationWithFunction(_node) {
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1887
|
+
getContainerBody(node) {
|
|
1888
|
+
if (node.type === "class_definition") {
|
|
1889
|
+
return node.childForFieldName("body");
|
|
1890
|
+
}
|
|
1891
|
+
return null;
|
|
1892
|
+
}
|
|
1893
|
+
shouldTraverseChildren(node) {
|
|
1894
|
+
return node.type === "module" || // Top-level Python file
|
|
1895
|
+
node.type === "block";
|
|
1896
|
+
}
|
|
1897
|
+
findParentContainerName(node) {
|
|
1898
|
+
let current = node.parent;
|
|
1899
|
+
while (current) {
|
|
1900
|
+
if (current.type === "class_definition") {
|
|
1901
|
+
const nameNode = current.childForFieldName("name");
|
|
1902
|
+
return nameNode?.text;
|
|
1903
|
+
}
|
|
1904
|
+
current = current.parent;
|
|
1905
|
+
}
|
|
1906
|
+
return void 0;
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Python doesn't have this pattern (const x = () => {})
|
|
1910
|
+
* Functions are always defined with 'def' or 'async def'
|
|
1911
|
+
*/
|
|
1912
|
+
findFunctionInDeclaration(_node) {
|
|
1913
|
+
return {
|
|
1914
|
+
hasFunction: false,
|
|
1915
|
+
functionNode: null
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1798
1922
|
// src/indexer/ast/traversers/index.ts
|
|
1799
1923
|
function getTraverser(language) {
|
|
1800
1924
|
const traverser = traverserRegistry[language];
|
|
@@ -1809,10 +1933,12 @@ var init_traversers = __esm({
|
|
|
1809
1933
|
"use strict";
|
|
1810
1934
|
init_typescript();
|
|
1811
1935
|
init_php();
|
|
1936
|
+
init_python();
|
|
1812
1937
|
traverserRegistry = {
|
|
1813
1938
|
typescript: new TypeScriptTraverser(),
|
|
1814
1939
|
javascript: new JavaScriptTraverser(),
|
|
1815
|
-
php: new PHPTraverser()
|
|
1940
|
+
php: new PHPTraverser(),
|
|
1941
|
+
python: new PythonTraverser()
|
|
1816
1942
|
};
|
|
1817
1943
|
}
|
|
1818
1944
|
});
|
|
@@ -1843,7 +1969,7 @@ function chunkByAST(filepath, content, options = {}) {
|
|
|
1843
1969
|
}
|
|
1844
1970
|
}
|
|
1845
1971
|
const parentClassName = traverser.findParentContainerName(actualNode);
|
|
1846
|
-
const symbolInfo = extractSymbolInfo(actualNode, content, parentClassName);
|
|
1972
|
+
const symbolInfo = extractSymbolInfo(actualNode, content, parentClassName, language);
|
|
1847
1973
|
const nodeContent = getNodeContent(node, lines);
|
|
1848
1974
|
chunks.push(createChunk(filepath, node, nodeContent, symbolInfo, fileImports, language));
|
|
1849
1975
|
}
|
|
@@ -4926,7 +5052,7 @@ async function createNewConfig(rootDir, options) {
|
|
|
4926
5052
|
path: ".",
|
|
4927
5053
|
enabled: true,
|
|
4928
5054
|
config: {
|
|
4929
|
-
include: ["**/*.{ts,tsx,js,jsx,py,go,rs,java,c,cpp,cs}"],
|
|
5055
|
+
include: ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,c,cpp,cs}"],
|
|
4930
5056
|
exclude: [
|
|
4931
5057
|
"**/node_modules/**",
|
|
4932
5058
|
"**/dist/**",
|
|
@@ -5304,22 +5430,48 @@ var tools = [
|
|
|
5304
5430
|
toMCPToolSchema(
|
|
5305
5431
|
SemanticSearchSchema,
|
|
5306
5432
|
"semantic_search",
|
|
5307
|
-
|
|
5433
|
+
`Search codebase by MEANING, not text. USE THIS INSTEAD OF grep/ripgrep for finding implementations, features, or understanding how code works.
|
|
5434
|
+
|
|
5435
|
+
Examples:
|
|
5436
|
+
- "Where is authentication handled?" \u2192 semantic_search({ query: "handles user authentication" })
|
|
5437
|
+
- "How does payment work?" \u2192 semantic_search({ query: "processes payment transactions" })
|
|
5438
|
+
|
|
5439
|
+
Use natural language describing what the code DOES, not function names. For exact string matching, use grep instead.
|
|
5440
|
+
|
|
5441
|
+
Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) for each match.`
|
|
5308
5442
|
),
|
|
5309
5443
|
toMCPToolSchema(
|
|
5310
5444
|
FindSimilarSchema,
|
|
5311
5445
|
"find_similar",
|
|
5312
|
-
|
|
5446
|
+
`Find code structurally similar to a given snippet. Use for:
|
|
5447
|
+
- Ensuring consistency when adding new code
|
|
5448
|
+
- Finding duplicate implementations
|
|
5449
|
+
- Refactoring similar patterns together
|
|
5450
|
+
|
|
5451
|
+
Provide at least 10 characters of code to match against. Results include a relevance category for each match.`
|
|
5313
5452
|
),
|
|
5314
5453
|
toMCPToolSchema(
|
|
5315
5454
|
GetFileContextSchema,
|
|
5316
5455
|
"get_file_context",
|
|
5317
|
-
|
|
5456
|
+
`Get full context for a file including related code and dependencies.
|
|
5457
|
+
|
|
5458
|
+
IMPORTANT: Call this BEFORE editing any file to understand:
|
|
5459
|
+
- What the file does
|
|
5460
|
+
- What depends on it
|
|
5461
|
+
- Related test files (via testAssociations)
|
|
5462
|
+
|
|
5463
|
+
Results include a relevance category for each related chunk. Typical flow: semantic_search \u2192 find file \u2192 get_file_context \u2192 make changes.`
|
|
5318
5464
|
),
|
|
5319
5465
|
toMCPToolSchema(
|
|
5320
5466
|
ListFunctionsSchema,
|
|
5321
5467
|
"list_functions",
|
|
5322
|
-
|
|
5468
|
+
`Fast symbol lookup by naming pattern. Use when searching by NAME, not behavior.
|
|
5469
|
+
|
|
5470
|
+
Examples:
|
|
5471
|
+
- "Show all controllers" \u2192 list_functions({ pattern: ".*Controller.*" })
|
|
5472
|
+
- "Find service classes" \u2192 list_functions({ pattern: ".*Service$" })
|
|
5473
|
+
|
|
5474
|
+
10x faster than semantic_search for structural/architectural queries. Use semantic_search instead when searching by what code DOES.`
|
|
5323
5475
|
)
|
|
5324
5476
|
];
|
|
5325
5477
|
|