pi-lens 3.6.2 → 3.6.4
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/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- package/commands/refactor.js +0 -130
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tree-sitter Query Loader
|
|
3
|
-
*
|
|
4
|
-
* Loads tree-sitter queries from YAML files in rules/tree-sitter-queries/
|
|
5
|
-
* and provides them to the TreeSitterClient.
|
|
6
|
-
*/
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
import * as path from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
export class TreeSitterQueryLoader {
|
|
12
|
-
constructor(verbose = false) {
|
|
13
|
-
this.queries = new Map();
|
|
14
|
-
this.loaded = false;
|
|
15
|
-
this.verbose = verbose;
|
|
16
|
-
}
|
|
17
|
-
/** Debug logging helper */
|
|
18
|
-
dbg(msg) {
|
|
19
|
-
if (this.verbose) {
|
|
20
|
-
console.error(`[query-loader] ${msg}`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Load all queries from the rules/tree-sitter-queries directory
|
|
25
|
-
*/
|
|
26
|
-
async loadQueries() {
|
|
27
|
-
if (this.loaded)
|
|
28
|
-
return this.queries;
|
|
29
|
-
const queriesDir = path.join(process.cwd(), "rules", "tree-sitter-queries");
|
|
30
|
-
if (!fs.existsSync(queriesDir)) {
|
|
31
|
-
this.dbg(`Queries directory not found: ${queriesDir}`);
|
|
32
|
-
return this.queries;
|
|
33
|
-
}
|
|
34
|
-
// Load queries from each language subdirectory
|
|
35
|
-
const languageDirs = fs
|
|
36
|
-
.readdirSync(queriesDir, { withFileTypes: true })
|
|
37
|
-
.filter((d) => d.isDirectory())
|
|
38
|
-
.map((d) => d.name);
|
|
39
|
-
for (const lang of languageDirs) {
|
|
40
|
-
const langDir = path.join(queriesDir, lang);
|
|
41
|
-
const queryFiles = fs
|
|
42
|
-
.readdirSync(langDir)
|
|
43
|
-
.filter((f) => f.endsWith(".yml"));
|
|
44
|
-
const langQueries = [];
|
|
45
|
-
for (const file of queryFiles) {
|
|
46
|
-
const filePath = path.join(langDir, file);
|
|
47
|
-
const query = this.parseQueryFile(filePath, lang);
|
|
48
|
-
if (query) {
|
|
49
|
-
langQueries.push(query);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (langQueries.length > 0) {
|
|
53
|
-
this.queries.set(lang, langQueries);
|
|
54
|
-
this.dbg(`Loaded ${langQueries.length} queries for ${lang}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
this.loaded = true;
|
|
58
|
-
return this.queries;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Parse a single YAML query file
|
|
62
|
-
*/
|
|
63
|
-
parseQueryFile(filePath, language) {
|
|
64
|
-
try {
|
|
65
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
66
|
-
// Simple YAML parsing (extract key: value pairs)
|
|
67
|
-
const parsed = this.parseYaml(content);
|
|
68
|
-
if (!parsed.id || !parsed.query) {
|
|
69
|
-
this.dbg(`Invalid query file: ${filePath}`);
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
id: String(parsed.id),
|
|
74
|
-
name: String(parsed.name || parsed.id),
|
|
75
|
-
severity: this.parseSeverity(parsed.severity),
|
|
76
|
-
category: String(parsed.category || "general"),
|
|
77
|
-
language: String(parsed.language || language),
|
|
78
|
-
message: String(parsed.message || `Pattern: ${parsed.id}`),
|
|
79
|
-
description: parsed.description
|
|
80
|
-
? String(parsed.description)
|
|
81
|
-
: undefined,
|
|
82
|
-
query: this.extractMultilineValue(content, "query") || String(parsed.query),
|
|
83
|
-
metavars: Array.isArray(parsed.metavars)
|
|
84
|
-
? parsed.metavars.map(String)
|
|
85
|
-
: this.extractMetavars(String(parsed.query)),
|
|
86
|
-
post_filter: parsed.post_filter
|
|
87
|
-
? String(parsed.post_filter)
|
|
88
|
-
: undefined,
|
|
89
|
-
// biome-ignore lint/suspicious/noExplicitAny: Post filter params
|
|
90
|
-
post_filter_params: parsed.post_filter_params,
|
|
91
|
-
tags: Array.isArray(parsed.tags) ? parsed.tags.map(String) : undefined,
|
|
92
|
-
has_fix: parsed.has_fix === true || parsed.has_fix === "true",
|
|
93
|
-
fix_action: parsed.fix_action ? String(parsed.fix_action) : undefined,
|
|
94
|
-
filePath,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
this.dbg(`Failed to parse ${filePath}: ${err}`);
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Simple YAML parser for our query files
|
|
104
|
-
*/
|
|
105
|
-
parseYaml(content) {
|
|
106
|
-
const result = {};
|
|
107
|
-
const lines = content.split("\n");
|
|
108
|
-
for (let i = 0; i < lines.length; i++) {
|
|
109
|
-
const line = lines[i];
|
|
110
|
-
const match = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
111
|
-
if (match) {
|
|
112
|
-
const key = match[1];
|
|
113
|
-
let value = match[2].trim();
|
|
114
|
-
// Handle arrays inline: metavars: [A, B, C]
|
|
115
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
116
|
-
value = value
|
|
117
|
-
.slice(1, -1)
|
|
118
|
-
.split(",")
|
|
119
|
-
.map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
120
|
-
}
|
|
121
|
-
// Handle multi-line arrays: metavars:\n - A\n - B
|
|
122
|
-
else if (value === "") {
|
|
123
|
-
// Check if next lines are array items ( - item)
|
|
124
|
-
const arrayItems = [];
|
|
125
|
-
const baseIndent = line.match(/^(\s*)/)?.[0].length || 0;
|
|
126
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
127
|
-
const nextLine = lines[j];
|
|
128
|
-
const nextIndent = nextLine.match(/^(\s*)/)?.[0].length || 0;
|
|
129
|
-
// Stop if we hit a line with same or less indent (new key)
|
|
130
|
-
if (nextIndent <= baseIndent && nextLine.match(/^[a-z_]+:/)) {
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
// Check if it's an array item
|
|
134
|
-
const itemMatch = nextLine.match(/^\s+-\s*(.+)$/);
|
|
135
|
-
if (itemMatch) {
|
|
136
|
-
// Strip inline comments and trim
|
|
137
|
-
const item = itemMatch[1].trim().replace(/\s*#.*$/, "");
|
|
138
|
-
if (item)
|
|
139
|
-
arrayItems.push(item);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (arrayItems.length > 0) {
|
|
143
|
-
value = arrayItems;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// Handle booleans
|
|
147
|
-
else if (value === "true")
|
|
148
|
-
value = true;
|
|
149
|
-
else if (value === "false")
|
|
150
|
-
value = false;
|
|
151
|
-
// Strip quotes from strings
|
|
152
|
-
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
153
|
-
value = value.slice(1, -1);
|
|
154
|
-
}
|
|
155
|
-
result[key] = value;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return result;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Extract a multiline value (like query) from YAML
|
|
162
|
-
*/
|
|
163
|
-
extractMultilineValue(content, key) {
|
|
164
|
-
const lines = content.split("\n");
|
|
165
|
-
let startLine = -1;
|
|
166
|
-
let startIndent = 0;
|
|
167
|
-
const keyPrefix = `${key}:`;
|
|
168
|
-
// Find the key line
|
|
169
|
-
for (let i = 0; i < lines.length; i++) {
|
|
170
|
-
const trimmed = lines[i].trimStart();
|
|
171
|
-
if (trimmed.startsWith(keyPrefix)) {
|
|
172
|
-
startLine = i;
|
|
173
|
-
startIndent = lines[i].length - trimmed.length;
|
|
174
|
-
const afterKey = trimmed.slice(keyPrefix.length).trim();
|
|
175
|
-
// If there's content on the same line (not just |), return it
|
|
176
|
-
if (afterKey && afterKey !== "|")
|
|
177
|
-
return afterKey;
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (startLine === -1)
|
|
182
|
-
return null;
|
|
183
|
-
// Collect all lines until we hit a new key with same or less indent
|
|
184
|
-
const valueLines = [];
|
|
185
|
-
for (let i = startLine + 1; i < lines.length; i++) {
|
|
186
|
-
const line = lines[i];
|
|
187
|
-
// Track empty lines
|
|
188
|
-
if (!line.trim()) {
|
|
189
|
-
valueLines.push("");
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
// Check indent
|
|
193
|
-
const indentMatch = line.match(/^(\s*)/);
|
|
194
|
-
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
195
|
-
const trimmed = line.trim();
|
|
196
|
-
// Stop at new key with same or less indent (but not at comments)
|
|
197
|
-
if (indent <= startIndent &&
|
|
198
|
-
trimmed.match(/^[a-z_]+:/) &&
|
|
199
|
-
!trimmed.startsWith("#")) {
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
// Skip comment lines (they're not part of the value)
|
|
203
|
-
if (trimmed.startsWith("#"))
|
|
204
|
-
continue;
|
|
205
|
-
// This is part of the multiline value
|
|
206
|
-
valueLines.push(line.slice(startIndent));
|
|
207
|
-
}
|
|
208
|
-
// Clean up - remove trailing empty lines
|
|
209
|
-
while (valueLines.length > 0 && !valueLines[valueLines.length - 1].trim()) {
|
|
210
|
-
valueLines.pop();
|
|
211
|
-
}
|
|
212
|
-
return valueLines.length > 0 ? valueLines.join("\n") : null;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Parse severity string to valid type
|
|
216
|
-
*/
|
|
217
|
-
parseSeverity(value) {
|
|
218
|
-
if (value === "error")
|
|
219
|
-
return "error";
|
|
220
|
-
if (value === "warning")
|
|
221
|
-
return "warning";
|
|
222
|
-
if (value === "info")
|
|
223
|
-
return "info";
|
|
224
|
-
return "warning"; // default
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Extract @VAR patterns from query string
|
|
228
|
-
*/
|
|
229
|
-
extractMetavars(query) {
|
|
230
|
-
const matches = query.match(/@([A-Z_][A-Z0-9_]*)/g);
|
|
231
|
-
if (!matches)
|
|
232
|
-
return [];
|
|
233
|
-
return [...new Set(matches.map((m) => m.slice(1)))];
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Get queries for a specific language
|
|
237
|
-
*/
|
|
238
|
-
getQueriesForLanguage(language) {
|
|
239
|
-
return this.queries.get(language) || [];
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Get a specific query by ID
|
|
243
|
-
*/
|
|
244
|
-
getQueryById(id) {
|
|
245
|
-
for (const langQueries of this.queries.values()) {
|
|
246
|
-
const query = langQueries.find((q) => q.id === id);
|
|
247
|
-
if (query)
|
|
248
|
-
return query;
|
|
249
|
-
}
|
|
250
|
-
return undefined;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Find matching query for a pattern string
|
|
254
|
-
*/
|
|
255
|
-
findMatchingQuery(pattern, language) {
|
|
256
|
-
const langQueries = this.getQueriesForLanguage(language);
|
|
257
|
-
// Check for pattern keywords
|
|
258
|
-
for (const query of langQueries) {
|
|
259
|
-
// Match by ID
|
|
260
|
-
if (pattern.includes(query.id))
|
|
261
|
-
return query;
|
|
262
|
-
// Match by keywords in pattern
|
|
263
|
-
switch (query.id) {
|
|
264
|
-
case "empty-catch":
|
|
265
|
-
if (pattern.includes("empty-catch") || pattern.includes("catch {}"))
|
|
266
|
-
return query;
|
|
267
|
-
break;
|
|
268
|
-
case "debugger-statement":
|
|
269
|
-
if (pattern.includes("debugger"))
|
|
270
|
-
return query;
|
|
271
|
-
break;
|
|
272
|
-
case "await-in-loop":
|
|
273
|
-
if (pattern.includes("await-in-loop") || pattern.includes("await"))
|
|
274
|
-
return query;
|
|
275
|
-
break;
|
|
276
|
-
case "hardcoded-secrets":
|
|
277
|
-
if (pattern.includes("hardcoded") ||
|
|
278
|
-
pattern.includes("api_key") ||
|
|
279
|
-
pattern.includes("password"))
|
|
280
|
-
return query;
|
|
281
|
-
break;
|
|
282
|
-
case "dangerously-set-inner-html":
|
|
283
|
-
if (pattern.includes("dangerously") || pattern.includes("innerHTML"))
|
|
284
|
-
return query;
|
|
285
|
-
break;
|
|
286
|
-
case "nested-ternary":
|
|
287
|
-
if (pattern.includes("ternary") || pattern.includes("? :"))
|
|
288
|
-
return query;
|
|
289
|
-
break;
|
|
290
|
-
case "no-eval":
|
|
291
|
-
if (pattern.includes("eval") && !pattern.includes("console"))
|
|
292
|
-
return query;
|
|
293
|
-
break;
|
|
294
|
-
case "deep-promise-chain":
|
|
295
|
-
if (pattern.includes(".then") && pattern.includes(".catch"))
|
|
296
|
-
return query;
|
|
297
|
-
break;
|
|
298
|
-
case "console-statement":
|
|
299
|
-
if (pattern.includes("console"))
|
|
300
|
-
return query;
|
|
301
|
-
break;
|
|
302
|
-
case "long-parameter-list":
|
|
303
|
-
if (pattern.includes("PARAMS"))
|
|
304
|
-
return query;
|
|
305
|
-
break;
|
|
306
|
-
// Python queries
|
|
307
|
-
case "bare-except":
|
|
308
|
-
if (pattern.includes("bare-except") || pattern.includes("except:"))
|
|
309
|
-
return query;
|
|
310
|
-
break;
|
|
311
|
-
case "mutable-default-arg":
|
|
312
|
-
if (pattern.includes("mutable") || pattern.includes("default"))
|
|
313
|
-
return query;
|
|
314
|
-
break;
|
|
315
|
-
case "wildcard-import":
|
|
316
|
-
if (pattern.includes("wildcard") || pattern.includes("import *"))
|
|
317
|
-
return query;
|
|
318
|
-
break;
|
|
319
|
-
case "eval-exec":
|
|
320
|
-
if (pattern.includes("eval") || pattern.includes("exec"))
|
|
321
|
-
return query;
|
|
322
|
-
break;
|
|
323
|
-
case "is-vs-equals":
|
|
324
|
-
if (pattern.includes("is") || pattern.includes("equals"))
|
|
325
|
-
return query;
|
|
326
|
-
break;
|
|
327
|
-
case "unreachable-except":
|
|
328
|
-
if (pattern.includes("unreachable") || pattern.includes("except"))
|
|
329
|
-
return query;
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return undefined;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Get all loaded queries
|
|
337
|
-
*/
|
|
338
|
-
getAllQueries() {
|
|
339
|
-
const all = [];
|
|
340
|
-
for (const queries of this.queries.values()) {
|
|
341
|
-
all.push(...queries);
|
|
342
|
-
}
|
|
343
|
-
return all;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Reload queries from disk
|
|
347
|
-
*/
|
|
348
|
-
async reload() {
|
|
349
|
-
this.queries.clear();
|
|
350
|
-
this.loaded = false;
|
|
351
|
-
await this.loadQueries();
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
// Singleton instance
|
|
355
|
-
export const queryLoader = new TreeSitterQueryLoader();
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Symbol extraction via tree-sitter queries
|
|
3
|
-
* Extracts definitions and references from source files
|
|
4
|
-
*/
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
// Tree-sitter query patterns for symbol extraction
|
|
7
|
-
const SYMBOL_QUERIES = {
|
|
8
|
-
typescript: {
|
|
9
|
-
defs: `
|
|
10
|
-
;; Function declarations: function foo(params) { }
|
|
11
|
-
(function_declaration
|
|
12
|
-
name: (identifier) @funcName
|
|
13
|
-
parameters: (formal_parameters) @funcParams
|
|
14
|
-
body: (statement_block) @funcBody) @funcDef
|
|
15
|
-
|
|
16
|
-
;; Arrow functions: const foo = (params) => { }
|
|
17
|
-
(variable_declarator
|
|
18
|
-
name: (identifier) @arrowName
|
|
19
|
-
value: (arrow_function
|
|
20
|
-
parameters: (formal_parameters) @arrowParams
|
|
21
|
-
body: (_) @arrowBody)) @arrowDef
|
|
22
|
-
|
|
23
|
-
;; Class declarations: class Foo { }
|
|
24
|
-
(class_declaration
|
|
25
|
-
name: (type_identifier) @className) @classDef
|
|
26
|
-
|
|
27
|
-
;; Method definitions: class Foo { bar() { } }
|
|
28
|
-
(method_definition
|
|
29
|
-
name: (property_identifier) @methodName
|
|
30
|
-
parameters: (formal_parameters) @methodParams) @methodDef
|
|
31
|
-
|
|
32
|
-
;; Interface declarations: interface Foo { }
|
|
33
|
-
(interface_declaration
|
|
34
|
-
name: (type_identifier) @interfaceName) @interfaceDef
|
|
35
|
-
|
|
36
|
-
;; Type alias: type Foo = ...
|
|
37
|
-
(type_alias_declaration
|
|
38
|
-
name: (type_identifier) @typeName) @typeDef
|
|
39
|
-
`,
|
|
40
|
-
refs: `
|
|
41
|
-
;; Function/method calls: foo() or obj.bar()
|
|
42
|
-
(call_expression
|
|
43
|
-
function: (identifier) @callIdent) @callRef
|
|
44
|
-
|
|
45
|
-
(call_expression
|
|
46
|
-
function: (member_expression
|
|
47
|
-
object: (_)
|
|
48
|
-
property: (property_identifier) @callMethod)) @callMethodRef
|
|
49
|
-
|
|
50
|
-
;; New expressions: new Foo()
|
|
51
|
-
(new_expression
|
|
52
|
-
constructor: (identifier) @newIdent) @newRef
|
|
53
|
-
|
|
54
|
-
;; Type references: type T = Foo
|
|
55
|
-
(type_identifier) @typeIdent
|
|
56
|
-
`,
|
|
57
|
-
},
|
|
58
|
-
python: {
|
|
59
|
-
defs: `
|
|
60
|
-
;; Function definitions: def foo(params):
|
|
61
|
-
(function_definition
|
|
62
|
-
name: (identifier) @funcName
|
|
63
|
-
parameters: (parameters) @funcParams) @funcDef
|
|
64
|
-
|
|
65
|
-
;; Class definitions: class Foo:
|
|
66
|
-
(class_definition
|
|
67
|
-
name: (identifier) @className) @classDef
|
|
68
|
-
|
|
69
|
-
;; Method definitions (within class)
|
|
70
|
-
(class_definition
|
|
71
|
-
body: (block
|
|
72
|
-
(function_definition
|
|
73
|
-
name: (identifier) @methodName
|
|
74
|
-
parameters: (parameters) @methodParams) @methodDef))
|
|
75
|
-
`,
|
|
76
|
-
refs: `
|
|
77
|
-
;; Function calls: foo() or obj.bar()
|
|
78
|
-
(call
|
|
79
|
-
function: (identifier) @callIdent) @callRef
|
|
80
|
-
|
|
81
|
-
(call
|
|
82
|
-
function: (attribute
|
|
83
|
-
object: (_)
|
|
84
|
-
attribute: (identifier) @callMethod)) @callMethodRef
|
|
85
|
-
`,
|
|
86
|
-
},
|
|
87
|
-
rust: {
|
|
88
|
-
defs: `
|
|
89
|
-
;; Function definitions: fn foo(params) { }
|
|
90
|
-
(function_item
|
|
91
|
-
name: (identifier) @funcName
|
|
92
|
-
parameters: (parameters) @funcParams) @funcDef
|
|
93
|
-
|
|
94
|
-
;; Struct definitions: struct Foo { }
|
|
95
|
-
(struct_item
|
|
96
|
-
name: (type_identifier) @structName) @structDef
|
|
97
|
-
|
|
98
|
-
;; Impl blocks: impl Foo { fn bar() { } }
|
|
99
|
-
(impl_item
|
|
100
|
-
type: (type_identifier) @implType
|
|
101
|
-
body: (declaration_list
|
|
102
|
-
(function_item
|
|
103
|
-
name: (identifier) @implMethodName) @implMethodDef))
|
|
104
|
-
`,
|
|
105
|
-
refs: `
|
|
106
|
-
;; Function calls: foo() or obj.bar()
|
|
107
|
-
(call_expression
|
|
108
|
-
function: (identifier) @callIdent) @callRef
|
|
109
|
-
|
|
110
|
-
(call_expression
|
|
111
|
-
function: (field_expression
|
|
112
|
-
value: (_)
|
|
113
|
-
field: (field_identifier) @callField)) @callFieldRef
|
|
114
|
-
`,
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
export class TreeSitterSymbolExtractor {
|
|
118
|
-
constructor(languageId, client) {
|
|
119
|
-
this.languageId = languageId;
|
|
120
|
-
this.client = client;
|
|
121
|
-
}
|
|
122
|
-
async init() {
|
|
123
|
-
try {
|
|
124
|
-
// Get language from client
|
|
125
|
-
const language = this.client.getLanguage(this.languageId);
|
|
126
|
-
if (!language)
|
|
127
|
-
return false;
|
|
128
|
-
const { Query } = await import("web-tree-sitter");
|
|
129
|
-
const queries = SYMBOL_QUERIES[this.languageId];
|
|
130
|
-
if (!queries)
|
|
131
|
-
return false;
|
|
132
|
-
// biome-ignore lint/suspicious/noExplicitAny: Language type
|
|
133
|
-
this.defQuery = new Query(language, queries.defs);
|
|
134
|
-
// biome-ignore lint/suspicious/noExplicitAny: Language type
|
|
135
|
-
this.refQuery = new Query(language, queries.refs);
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
console.error(`[symbol-extractor] Failed to init ${this.languageId}:`, err);
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Extract symbols from a parsed tree-sitter tree
|
|
145
|
-
*/
|
|
146
|
-
extract(
|
|
147
|
-
// biome-ignore lint/suspicious/noExplicitAny: Tree type
|
|
148
|
-
tree, filePath, content) {
|
|
149
|
-
const symbols = [];
|
|
150
|
-
const refs = [];
|
|
151
|
-
const relativePath = path.relative(process.cwd(), filePath);
|
|
152
|
-
// Extract definitions
|
|
153
|
-
const defMatches = this.defQuery.matches(tree.rootNode);
|
|
154
|
-
for (const match of defMatches) {
|
|
155
|
-
const symbol = this.parseDefMatch(match, relativePath, content);
|
|
156
|
-
if (symbol)
|
|
157
|
-
symbols.push(symbol);
|
|
158
|
-
}
|
|
159
|
-
// Extract references
|
|
160
|
-
const refMatches = this.refQuery.matches(tree.rootNode);
|
|
161
|
-
for (const match of refMatches) {
|
|
162
|
-
const ref = this.parseRefMatch(match, relativePath);
|
|
163
|
-
if (ref)
|
|
164
|
-
refs.push(ref);
|
|
165
|
-
}
|
|
166
|
-
return { symbols, refs };
|
|
167
|
-
}
|
|
168
|
-
// biome-ignore lint/suspicious/noExplicitAny: Match type
|
|
169
|
-
parseDefMatch(match, filePath, content) {
|
|
170
|
-
const captures = {};
|
|
171
|
-
for (const capture of match.captures) {
|
|
172
|
-
captures[capture.name] = {
|
|
173
|
-
text: capture.node.text,
|
|
174
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
175
|
-
node: capture.node,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
// Determine kind and name
|
|
179
|
-
let name;
|
|
180
|
-
let kind;
|
|
181
|
-
let params;
|
|
182
|
-
let defNode;
|
|
183
|
-
if (captures.funcName) {
|
|
184
|
-
name = captures.funcName.text;
|
|
185
|
-
kind = "function";
|
|
186
|
-
params = captures.funcParams?.text;
|
|
187
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
188
|
-
defNode = captures.funcDef?.node;
|
|
189
|
-
}
|
|
190
|
-
else if (captures.arrowName) {
|
|
191
|
-
name = captures.arrowName.text;
|
|
192
|
-
kind = "function";
|
|
193
|
-
params = captures.arrowParams?.text;
|
|
194
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
195
|
-
defNode = captures.arrowDef?.node;
|
|
196
|
-
}
|
|
197
|
-
else if (captures.className) {
|
|
198
|
-
name = captures.className.text;
|
|
199
|
-
kind = "class";
|
|
200
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
201
|
-
defNode = captures.classDef?.node;
|
|
202
|
-
}
|
|
203
|
-
else if (captures.methodName) {
|
|
204
|
-
name = captures.methodName.text;
|
|
205
|
-
kind = "method";
|
|
206
|
-
params = captures.methodParams?.text;
|
|
207
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
208
|
-
defNode = captures.methodDef?.node;
|
|
209
|
-
}
|
|
210
|
-
else if (captures.interfaceName) {
|
|
211
|
-
name = captures.interfaceName.text;
|
|
212
|
-
kind = "interface";
|
|
213
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
214
|
-
defNode = captures.interfaceDef?.node;
|
|
215
|
-
}
|
|
216
|
-
else if (captures.typeName) {
|
|
217
|
-
name = captures.typeName.text;
|
|
218
|
-
kind = "type";
|
|
219
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
220
|
-
defNode = captures.typeDef?.node;
|
|
221
|
-
}
|
|
222
|
-
if (!name || !kind || !defNode)
|
|
223
|
-
return null;
|
|
224
|
-
// Check if exported (basic heuristic: has export keyword before it)
|
|
225
|
-
const isExported = this.isExported(defNode, content);
|
|
226
|
-
const signature = params ? this.extractSignature(params, kind) : undefined;
|
|
227
|
-
return {
|
|
228
|
-
id: `${filePath}:${name}`,
|
|
229
|
-
name,
|
|
230
|
-
kind,
|
|
231
|
-
filePath,
|
|
232
|
-
line: defNode.startPosition.row + 1,
|
|
233
|
-
column: defNode.startPosition.column + 1,
|
|
234
|
-
signature,
|
|
235
|
-
isExported,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
// biome-ignore lint/suspicious/noExplicitAny: Match type
|
|
239
|
-
parseRefMatch(match, filePath) {
|
|
240
|
-
let name;
|
|
241
|
-
let refNode;
|
|
242
|
-
for (const capture of match.captures) {
|
|
243
|
-
if (capture.name.endsWith("Ident") ||
|
|
244
|
-
capture.name.endsWith("Method") ||
|
|
245
|
-
capture.name.endsWith("Field")) {
|
|
246
|
-
name = capture.node.text;
|
|
247
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
248
|
-
refNode = capture.node;
|
|
249
|
-
}
|
|
250
|
-
if (capture.name.endsWith("Ref") && !refNode) {
|
|
251
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
252
|
-
refNode = capture.node;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (!name || !refNode)
|
|
256
|
-
return null;
|
|
257
|
-
return {
|
|
258
|
-
symbolId: `${filePath}:${name}`, // Will be resolved later
|
|
259
|
-
filePath,
|
|
260
|
-
line: refNode.startPosition.row + 1,
|
|
261
|
-
column: refNode.startPosition.column + 1,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
265
|
-
isExported(node, content) {
|
|
266
|
-
// Simple heuristic: check for "export" keyword before the node
|
|
267
|
-
const lines = content.split("\n");
|
|
268
|
-
const lineIdx = node.startPosition.row;
|
|
269
|
-
const line = lines[lineIdx] || "";
|
|
270
|
-
return line.includes("export") || this.hasExportModifier(node, content);
|
|
271
|
-
}
|
|
272
|
-
// biome-ignore lint/suspicious/noExplicitAny: Node type
|
|
273
|
-
hasExportModifier(_node, _content) {
|
|
274
|
-
// TODO: Implement proper export modifier detection
|
|
275
|
-
// For now, use simple line-based check
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
extractSignature(paramsText, kind) {
|
|
279
|
-
if (kind === "function" || kind === "method") {
|
|
280
|
-
// Clean up params: remove comments, normalize whitespace
|
|
281
|
-
return paramsText
|
|
282
|
-
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
283
|
-
.replace(/\/\/.*$/gm, "")
|
|
284
|
-
.replace(/\s+/g, " ")
|
|
285
|
-
.trim();
|
|
286
|
-
}
|
|
287
|
-
return undefined;
|
|
288
|
-
}
|
|
289
|
-
}
|