pi-lens 3.8.65 → 3.8.67

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 (96) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +0 -1
  3. package/dist/clients/actionable-warnings-logger.js +11 -36
  4. package/dist/clients/ast-grep-tool-logger.js +11 -36
  5. package/dist/clients/biome-client.js +10 -2
  6. package/dist/clients/bootstrap.js +1 -3
  7. package/dist/clients/cascade-logger.js +7 -12
  8. package/dist/clients/complexity-client.js +397 -623
  9. package/dist/clients/dead-code-logger.js +11 -22
  10. package/dist/clients/dependency-checker.js +15 -2
  11. package/dist/clients/deps/vscode-jsonrpc.js +1 -1
  12. package/dist/clients/diagnostic-logger.js +7 -31
  13. package/dist/clients/dispatch/auxiliary-lsp.js +38 -0
  14. package/dist/clients/dispatch/dispatcher.js +4 -39
  15. package/dist/clients/dispatch/fact-runner.js +9 -57
  16. package/dist/clients/dispatch/facts/comment-facts.js +12 -25
  17. package/dist/clients/dispatch/facts/function-facts.js +161 -132
  18. package/dist/clients/dispatch/facts/import-facts.js +164 -117
  19. package/dist/clients/dispatch/facts/tree-sitter-facts.js +34 -0
  20. package/dist/clients/dispatch/facts/try-catch-facts.js +62 -68
  21. package/dist/clients/dispatch/inline-suppressions.js +61 -0
  22. package/dist/clients/dispatch/integration.js +94 -32
  23. package/dist/clients/dispatch/plan.js +0 -49
  24. package/dist/clients/dispatch/priorities.js +0 -1
  25. package/dist/clients/dispatch/rules/cors-wildcard.js +54 -0
  26. package/dist/clients/dispatch/rules/high-import-coupling.js +37 -0
  27. package/dist/clients/dispatch/rules/no-commented-credentials.js +52 -0
  28. package/dist/clients/dispatch/runners/index.js +2 -7
  29. package/dist/clients/dispatch/runners/lsp.js +18 -6
  30. package/dist/clients/dispatch/runners/tree-sitter.js +11 -64
  31. package/dist/clients/dispatch/runners/utils/runner-helpers.js +28 -33
  32. package/dist/clients/dispatch/tool-profile.js +0 -1
  33. package/dist/clients/dispatch/types.js +1 -1
  34. package/dist/clients/formatters.js +10 -0
  35. package/dist/clients/git-guard.js +1 -1
  36. package/dist/clients/grammar-source.js +65 -2
  37. package/dist/clients/jscpd-client.js +8 -2
  38. package/dist/clients/language-policy.js +2 -2
  39. package/dist/clients/latency-logger.js +10 -15
  40. package/dist/clients/lsp/client.js +74 -11
  41. package/dist/clients/lsp/config.js +61 -3
  42. package/dist/clients/lsp/index.js +43 -3
  43. package/dist/clients/lsp/launch.js +3 -32
  44. package/dist/clients/mcp/session.js +0 -1
  45. package/dist/clients/module-report.js +16 -5
  46. package/dist/clients/ndjson-logger.js +150 -0
  47. package/dist/clients/package-manager.js +65 -0
  48. package/dist/clients/pipeline.js +25 -41
  49. package/dist/clients/project-diagnostics/extractors.js +85 -0
  50. package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +44 -0
  51. package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +25 -0
  52. package/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +34 -0
  53. package/dist/clients/project-diagnostics/runner-adapters/jscpd.js +38 -0
  54. package/dist/clients/project-diagnostics/runner-adapters/madge.js +47 -0
  55. package/dist/clients/project-diagnostics/runner-adapters/trivy.js +31 -0
  56. package/dist/clients/project-diagnostics/scanner.js +11 -10
  57. package/dist/clients/read-guard-logger.js +11 -36
  58. package/dist/clients/review-graph/builder.js +337 -27
  59. package/dist/clients/review-graph/git-identity.js +150 -0
  60. package/dist/clients/review-graph/service.js +3 -3
  61. package/dist/clients/runtime-coordinator.js +83 -0
  62. package/dist/clients/runtime-session.js +28 -2
  63. package/dist/clients/runtime-tool-result.js +10 -2
  64. package/dist/clients/runtime-turn.js +25 -7
  65. package/dist/clients/sg-runner.js +15 -0
  66. package/dist/clients/test-runner-client.js +12 -6
  67. package/dist/clients/tree-sitter-cache.js +47 -14
  68. package/dist/clients/tree-sitter-client.js +9 -1
  69. package/dist/clients/tree-sitter-logger.js +7 -12
  70. package/dist/clients/tree-sitter-query-loader.js +1 -0
  71. package/dist/clients/tree-sitter-shared.js +113 -0
  72. package/dist/clients/tree-sitter-symbol-extractor.js +19 -12
  73. package/dist/index.js +4 -22
  74. package/dist/tools/lens-diagnostics.js +254 -20
  75. package/grammars/tree-sitter-yaml.wasm +0 -0
  76. package/grammars/tree-sitter-yaml.wasm.json +3 -3
  77. package/package.json +4 -3
  78. package/rules/ast-grep-rules/rule-tests/no-init-return-test.yml +15 -0
  79. package/rules/ast-grep-rules/rules/no-init-return.yml +11 -5
  80. package/rules/tree-sitter-queries/python/python-assert-production.yml +4 -0
  81. package/scripts/download-grammars.js +29 -4
  82. package/scripts/grammars.lock.json +14 -2
  83. package/scripts/install-selftest.mjs +8 -7
  84. package/dist/clients/deps/typescript.js +0 -15
  85. package/dist/clients/dispatch/rules/quality-rules.js +0 -297
  86. package/dist/clients/dispatch/rules/sonar-rules.js +0 -493
  87. package/dist/clients/dispatch/runners/biome.js +0 -69
  88. package/dist/clients/dispatch/runners/python-slop.js +0 -106
  89. package/dist/clients/dispatch/runners/ts-lsp.js +0 -109
  90. package/dist/clients/runner-tracker.js +0 -153
  91. package/dist/clients/ts-service.js +0 -130
  92. package/dist/clients/type-coverage-client.js +0 -128
  93. package/dist/clients/typescript-client.js +0 -509
  94. package/dist/commands/booboo.js +0 -1412
  95. package/rules/python-slop-rules/.sgconfig.yml +0 -4
  96. package/rules/python-slop-rules/rules/slop-rules.yml +0 -647
@@ -1,4 +1,4 @@
1
- import { ts } from "../../deps/typescript.js";
1
+ import { extractFactsFromTree, firstChildOfType, walk, } from "./tree-sitter-facts.js";
2
2
  const BOUNDARY_PREFIXES = [
3
3
  "fetch",
4
4
  "fs.",
@@ -9,139 +9,177 @@ const BOUNDARY_PREFIXES = [
9
9
  "req.",
10
10
  "res.",
11
11
  ];
12
+ const FUNCTION_TYPES = new Set([
13
+ "function_declaration",
14
+ "method_definition",
15
+ "function_expression",
16
+ "arrow_function",
17
+ ]);
18
+ // Decision points for McCabe complexity (matches the old ts.SyntaxKind set).
19
+ const COMPLEXITY_TYPES = new Set([
20
+ "if_statement",
21
+ "for_statement",
22
+ "for_in_statement", // for…of and for…in both parse to for_in_statement
23
+ "while_statement",
24
+ "do_statement",
25
+ "switch_case", // `case` only — `default` (switch_default) is not counted
26
+ "catch_clause",
27
+ "ternary_expression",
28
+ ]);
29
+ // Control structures that increase nesting depth (matches the old set).
30
+ const NESTING_TYPES = new Set([
31
+ "if_statement",
32
+ "for_statement",
33
+ "for_in_statement",
34
+ "while_statement",
35
+ "do_statement",
36
+ "switch_statement",
37
+ "try_statement",
38
+ ]);
39
+ const LOGICAL_OPERATORS = new Set(["&&", "||", "??"]);
40
+ /** Named children excluding comments — i.e. the "real" statements/args/expressions. */
41
+ function namedChildren(node) {
42
+ return (node.children ?? []).filter((c) => c && c.isNamed && c.type !== "comment");
43
+ }
44
+ /** First named, non-comment child (e.g. a return statement's returned expression). */
45
+ function firstNamedChild(node) {
46
+ return namedChildren(node)[0];
47
+ }
48
+ function getBody(node) {
49
+ // Only block-bodied functions get a summary (expression-bodied arrows have no
50
+ // statement_block — matching the old `!ts.isBlock(body)` skip).
51
+ return firstChildOfType(node, "statement_block");
52
+ }
53
+ function getParameters(node) {
54
+ const fp = firstChildOfType(node, "formal_parameters");
55
+ if (!fp)
56
+ return [];
57
+ return (fp.children ?? []).filter((c) => c &&
58
+ (c.type === "required_parameter" ||
59
+ c.type === "optional_parameter" ||
60
+ c.type === "rest_pattern"));
61
+ }
62
+ function parameterName(param) {
63
+ return firstNamedChild(param)?.text ?? "";
64
+ }
12
65
  function getFunctionName(node) {
13
- if (ts.isFunctionDeclaration(node)) {
14
- return node.name?.text ?? "<anonymous>";
66
+ if (node.type === "function_declaration") {
67
+ return firstChildOfType(node, "identifier")?.text ?? "<anonymous>";
15
68
  }
16
- if (ts.isMethodDeclaration(node)) {
17
- if (ts.isIdentifier(node.name))
18
- return node.name.text;
19
- return node.name.getText();
69
+ if (node.type === "method_definition") {
70
+ // The name is the child just before formal_parameters (property_identifier /
71
+ // private / computed / string / number); skip async/get/set modifiers.
72
+ const kids = node.children ?? [];
73
+ const paramsIdx = kids.findIndex((c) => c?.type === "formal_parameters");
74
+ for (let i = paramsIdx - 1; i >= 0; i -= 1) {
75
+ const t = kids[i]?.type;
76
+ if (t === "property_identifier" ||
77
+ t === "private_property_identifier" ||
78
+ t === "computed_property_name" ||
79
+ t === "string" ||
80
+ t === "number") {
81
+ return kids[i].text;
82
+ }
83
+ }
84
+ return "<anonymous>";
20
85
  }
21
- if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
86
+ if (node.type === "arrow_function" || node.type === "function_expression") {
87
+ // Name comes from the binding site (like the old parent-based lookup):
88
+ // `const f = () => …` / `{ prop: () => … }`.
22
89
  const parent = node.parent;
23
- if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
24
- return parent.name.text;
90
+ if (parent?.type === "variable_declarator") {
91
+ const id = firstChildOfType(parent, "identifier");
92
+ if (id)
93
+ return id.text;
25
94
  }
26
- if (ts.isPropertyAssignment(parent)) {
27
- return parent.name.getText();
95
+ else if (parent?.type === "pair") {
96
+ const key = (parent.children ?? [])[0];
97
+ if (key)
98
+ return key.text;
28
99
  }
29
100
  return "<anonymous>";
30
101
  }
31
102
  return "<unknown>";
32
103
  }
33
104
  function isCallPassThrough(stmt, paramNames) {
34
- if (!ts.isReturnStatement(stmt) || !stmt.expression)
105
+ if (stmt.type !== "return_statement")
35
106
  return { pass: false };
36
- const expr = stmt.expression;
37
- if (!ts.isCallExpression(expr))
107
+ const expr = firstNamedChild(stmt);
108
+ if (!expr || expr.type !== "call_expression")
38
109
  return { pass: false };
39
- const args = expr.arguments.map((a) => a.getText());
110
+ const argsNode = firstChildOfType(expr, "arguments");
111
+ const args = argsNode ? namedChildren(argsNode).map((a) => a.text) : [];
40
112
  if (args.length !== paramNames.length)
41
113
  return { pass: false };
42
114
  for (let i = 0; i < args.length; i += 1) {
43
115
  if (args[i] !== paramNames[i])
44
116
  return { pass: false };
45
117
  }
46
- return { pass: true, target: expr.expression.getText() };
118
+ return { pass: true, target: (expr.children ?? [])[0]?.text };
47
119
  }
48
120
  function calcCyclomaticComplexity(body) {
49
121
  let cc = 1;
50
- const walk = (node) => {
51
- switch (node.kind) {
52
- case ts.SyntaxKind.IfStatement:
53
- case ts.SyntaxKind.ForStatement:
54
- case ts.SyntaxKind.ForInStatement:
55
- case ts.SyntaxKind.ForOfStatement:
56
- case ts.SyntaxKind.WhileStatement:
57
- case ts.SyntaxKind.DoStatement:
58
- case ts.SyntaxKind.CaseClause:
59
- case ts.SyntaxKind.CatchClause:
60
- case ts.SyntaxKind.ConditionalExpression:
122
+ walk(body, (node) => {
123
+ if (COMPLEXITY_TYPES.has(node.type)) {
124
+ cc++;
125
+ }
126
+ else if (node.type === "binary_expression") {
127
+ if ((node.children ?? []).some((c) => LOGICAL_OPERATORS.has(c?.type))) {
61
128
  cc++;
62
- break;
63
- case ts.SyntaxKind.BinaryExpression: {
64
- const op = node.operatorToken.kind;
65
- if (op === ts.SyntaxKind.AmpersandAmpersandToken ||
66
- op === ts.SyntaxKind.BarBarToken ||
67
- op === ts.SyntaxKind.QuestionQuestionToken)
68
- cc++;
69
- break;
70
129
  }
71
130
  }
72
- ts.forEachChild(node, walk);
73
- };
74
- walk(body);
131
+ });
75
132
  return cc;
76
133
  }
77
134
  function calcMaxNestingDepth(body) {
78
135
  let maxDepth = 0;
79
- const isNestingNode = (node) => {
80
- switch (node.kind) {
81
- case ts.SyntaxKind.IfStatement:
82
- case ts.SyntaxKind.ForStatement:
83
- case ts.SyntaxKind.ForInStatement:
84
- case ts.SyntaxKind.ForOfStatement:
85
- case ts.SyntaxKind.WhileStatement:
86
- case ts.SyntaxKind.DoStatement:
87
- case ts.SyntaxKind.SwitchStatement:
88
- case ts.SyntaxKind.TryStatement:
89
- return true;
90
- default:
91
- return false;
92
- }
93
- };
94
- const walk = (node, depth) => {
136
+ const walkDepth = (node, depth) => {
95
137
  if (depth > maxDepth)
96
138
  maxDepth = depth;
97
- const next = isNestingNode(node) ? depth + 1 : depth;
98
- ts.forEachChild(node, (child) => walk(child, next));
139
+ const next = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
140
+ for (const child of node.children ?? []) {
141
+ if (child)
142
+ walkDepth(child, next);
143
+ }
99
144
  };
100
- ts.forEachChild(body, (child) => walk(child, 0));
145
+ // Start at the body's children at depth 0 (the body block itself isn't counted).
146
+ for (const child of body.children ?? []) {
147
+ if (child)
148
+ walkDepth(child, 0);
149
+ }
101
150
  return maxDepth;
102
151
  }
103
152
  function collectOutgoingCalls(body) {
104
153
  const calls = new Set();
105
- const walk = (node) => {
106
- if (ts.isCallExpression(node)) {
107
- const callee = node.expression.getText();
154
+ walk(body, (node) => {
155
+ if (node.type === "call_expression") {
156
+ const callee = (node.children ?? [])[0]?.text ?? "";
108
157
  if (callee.length < 80)
109
158
  calls.add(callee);
110
159
  }
111
- ts.forEachChild(node, walk);
112
- };
113
- walk(body);
160
+ });
114
161
  return [...calls];
115
162
  }
116
163
  function hasAwaitInNode(node) {
117
164
  let found = false;
118
- const walk = (n) => {
119
- if (found)
120
- return;
121
- if (ts.isAwaitExpression(n)) {
165
+ walk(node, (n) => {
166
+ if (n.type === "await_expression")
122
167
  found = true;
123
- return;
124
- }
125
- ts.forEachChild(n, walk);
126
- };
127
- walk(node);
168
+ });
128
169
  return found;
129
170
  }
130
171
  function hasReturnAwaitCall(node) {
131
172
  let found = false;
132
- const walk = (n) => {
133
- if (found)
173
+ walk(node, (n) => {
174
+ if (n.type !== "return_statement")
134
175
  return;
135
- if (ts.isReturnStatement(n) &&
136
- n.expression &&
137
- ts.isAwaitExpression(n.expression) &&
138
- ts.isCallExpression(n.expression.expression)) {
139
- found = true;
176
+ const ret = firstNamedChild(n);
177
+ if (ret?.type !== "await_expression")
140
178
  return;
141
- }
142
- ts.forEachChild(n, walk);
143
- };
144
- walk(node);
179
+ const awaited = firstNamedChild(ret);
180
+ if (awaited?.type === "call_expression")
181
+ found = true;
182
+ });
145
183
  return found;
146
184
  }
147
185
  export const functionFactProvider = {
@@ -151,54 +189,45 @@ export const functionFactProvider = {
151
189
  appliesTo(ctx) {
152
190
  return /\.tsx?$/.test(ctx.filePath);
153
191
  },
154
- run(ctx, store) {
155
- const content = store.getFileFact(ctx.filePath, "file.content");
156
- if (!content) {
157
- store.setFileFact(ctx.filePath, "file.functionSummaries", []);
158
- return;
159
- }
160
- const sourceFile = ts.createSourceFile(ctx.filePath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
161
- const summaries = [];
162
- const addSummary = (node) => {
163
- const body = node.body;
164
- if (!body || !ts.isBlock(body))
165
- return;
166
- const lc = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
167
- const paramNames = node.parameters.map((p) => p.name.getText(sourceFile));
168
- const statementCount = body.statements.length;
169
- const passThrough = statementCount === 1
170
- ? isCallPassThrough(body.statements[0], paramNames)
171
- : { pass: false };
172
- const target = passThrough.target ?? "";
173
- const lowerTarget = target.toLowerCase();
174
- const isBoundaryWrapper = BOUNDARY_PREFIXES.some((prefix) => lowerTarget.startsWith(prefix));
175
- summaries.push({
176
- name: getFunctionName(node),
177
- line: lc.line + 1,
178
- column: lc.character + 1,
179
- isAsync: !!node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword),
180
- hasAwait: hasAwaitInNode(body),
181
- hasReturnAwaitCall: hasReturnAwaitCall(body),
182
- statementCount,
183
- parameterCount: node.parameters.length,
184
- isPassThroughWrapper: passThrough.pass,
185
- passThroughTarget: passThrough.target,
186
- isBoundaryWrapper,
187
- cyclomaticComplexity: calcCyclomaticComplexity(body),
188
- maxNestingDepth: calcMaxNestingDepth(body),
189
- outgoingCalls: collectOutgoingCalls(body),
192
+ async run(ctx, store) {
193
+ await extractFactsFromTree(ctx, store, { "file.functionSummaries": [] }, (root) => {
194
+ const summaries = [];
195
+ const addSummary = (node) => {
196
+ const body = getBody(node);
197
+ if (!body)
198
+ return;
199
+ const params = getParameters(node);
200
+ const paramNames = params.map(parameterName);
201
+ const statements = namedChildren(body);
202
+ const statementCount = statements.length;
203
+ const passThrough = statementCount === 1
204
+ ? isCallPassThrough(statements[0], paramNames)
205
+ : { pass: false };
206
+ const target = passThrough.target ?? "";
207
+ const lowerTarget = target.toLowerCase();
208
+ const isBoundaryWrapper = BOUNDARY_PREFIXES.some((prefix) => lowerTarget.startsWith(prefix));
209
+ summaries.push({
210
+ name: getFunctionName(node),
211
+ line: node.startPosition.row + 1,
212
+ column: node.startPosition.column + 1,
213
+ isAsync: Boolean(firstChildOfType(node, "async")),
214
+ hasAwait: hasAwaitInNode(body),
215
+ hasReturnAwaitCall: hasReturnAwaitCall(body),
216
+ statementCount,
217
+ parameterCount: params.length,
218
+ isPassThroughWrapper: passThrough.pass,
219
+ passThroughTarget: passThrough.target,
220
+ isBoundaryWrapper,
221
+ cyclomaticComplexity: calcCyclomaticComplexity(body),
222
+ maxNestingDepth: calcMaxNestingDepth(body),
223
+ outgoingCalls: collectOutgoingCalls(body),
224
+ });
225
+ };
226
+ walk(root, (node) => {
227
+ if (FUNCTION_TYPES.has(node.type))
228
+ addSummary(node);
190
229
  });
191
- };
192
- const visit = (node) => {
193
- if (ts.isFunctionDeclaration(node) ||
194
- ts.isMethodDeclaration(node) ||
195
- ts.isFunctionExpression(node) ||
196
- ts.isArrowFunction(node)) {
197
- addSummary(node);
198
- }
199
- ts.forEachChild(node, visit);
200
- };
201
- visit(sourceFile);
202
- store.setFileFact(ctx.filePath, "file.functionSummaries", summaries);
230
+ return { "file.functionSummaries": summaries };
231
+ });
203
232
  },
204
233
  };