pi-lens 2.2.9 → 3.0.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.
Files changed (304) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +709 -519
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/file-time.test.ts +276 -0
  5. package/clients/__tests__/format-service.test.js +245 -0
  6. package/clients/__tests__/format-service.test.ts +339 -0
  7. package/clients/__tests__/formatters.test.js +271 -0
  8. package/clients/__tests__/formatters.test.ts +401 -0
  9. package/clients/amain-types.js +164 -0
  10. package/clients/amain-types.ts +165 -0
  11. package/clients/architect-client.js +56 -12
  12. package/clients/architect-client.ts +81 -16
  13. package/clients/ast-grep-client.js +2 -2
  14. package/clients/ast-grep-client.ts +14 -39
  15. package/clients/ast-grep-parser.ts +1 -1
  16. package/clients/ast-grep-rule-manager.js +8 -0
  17. package/clients/ast-grep-rule-manager.ts +10 -1
  18. package/clients/ast-grep-types.js +9 -0
  19. package/clients/ast-grep-types.ts +106 -0
  20. package/clients/auto-loop.js +10 -0
  21. package/clients/auto-loop.ts +14 -1
  22. package/clients/biome-client.js +81 -19
  23. package/clients/biome-client.ts +103 -22
  24. package/clients/bus/bus.js +191 -0
  25. package/clients/bus/bus.ts +251 -0
  26. package/clients/bus/events.js +214 -0
  27. package/clients/bus/events.ts +279 -0
  28. package/clients/bus/index.js +8 -0
  29. package/clients/bus/index.ts +9 -0
  30. package/clients/bus/integration.js +158 -0
  31. package/clients/bus/integration.ts +214 -0
  32. package/clients/complexity-client.js +13 -7
  33. package/clients/complexity-client.ts +13 -7
  34. package/clients/config-validator.js +465 -0
  35. package/clients/config-validator.ts +558 -0
  36. package/clients/dependency-checker.js +4 -10
  37. package/clients/dependency-checker.ts +4 -10
  38. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  39. package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
  40. package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
  41. package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
  42. package/clients/dispatch/bus-dispatcher.js +177 -0
  43. package/clients/dispatch/bus-dispatcher.ts +251 -0
  44. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  45. package/clients/dispatch/dispatcher.edge.test.ts +100 -0
  46. package/clients/dispatch/dispatcher.format.test.js +46 -0
  47. package/clients/dispatch/dispatcher.format.test.ts +58 -0
  48. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  49. package/clients/dispatch/dispatcher.inline.test.ts +93 -0
  50. package/clients/dispatch/dispatcher.js +19 -53
  51. package/clients/dispatch/dispatcher.ts +20 -67
  52. package/clients/dispatch/plan.js +9 -4
  53. package/clients/dispatch/plan.ts +9 -4
  54. package/clients/dispatch/runners/architect.js +21 -7
  55. package/clients/dispatch/runners/architect.test.js +138 -0
  56. package/clients/dispatch/runners/architect.test.ts +162 -0
  57. package/clients/dispatch/runners/architect.ts +22 -7
  58. package/clients/dispatch/runners/ast-grep-napi.js +462 -0
  59. package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
  60. package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
  61. package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
  62. package/clients/dispatch/runners/ast-grep.js +62 -19
  63. package/clients/dispatch/runners/ast-grep.ts +70 -18
  64. package/clients/dispatch/runners/biome.js +29 -53
  65. package/clients/dispatch/runners/biome.ts +29 -63
  66. package/clients/dispatch/runners/config-validation.js +67 -0
  67. package/clients/dispatch/runners/config-validation.ts +82 -0
  68. package/clients/dispatch/runners/go-vet.js +4 -28
  69. package/clients/dispatch/runners/go-vet.ts +4 -32
  70. package/clients/dispatch/runners/index.js +30 -10
  71. package/clients/dispatch/runners/index.ts +30 -10
  72. package/clients/dispatch/runners/oxlint.js +141 -0
  73. package/clients/dispatch/runners/oxlint.test.js +230 -0
  74. package/clients/dispatch/runners/oxlint.test.ts +303 -0
  75. package/clients/dispatch/runners/oxlint.ts +175 -0
  76. package/clients/dispatch/runners/pyright.js +40 -70
  77. package/clients/dispatch/runners/pyright.test.js +16 -2
  78. package/clients/dispatch/runners/pyright.test.ts +14 -2
  79. package/clients/dispatch/runners/pyright.ts +48 -91
  80. package/clients/dispatch/runners/python-slop.js +97 -0
  81. package/clients/dispatch/runners/python-slop.test.js +203 -0
  82. package/clients/dispatch/runners/python-slop.test.ts +298 -0
  83. package/clients/dispatch/runners/python-slop.ts +124 -0
  84. package/clients/dispatch/runners/ruff.js +18 -71
  85. package/clients/dispatch/runners/ruff.ts +19 -79
  86. package/clients/dispatch/runners/rust-clippy.js +28 -32
  87. package/clients/dispatch/runners/rust-clippy.ts +29 -31
  88. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  89. package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
  90. package/clients/dispatch/runners/shellcheck.js +147 -0
  91. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  92. package/clients/dispatch/runners/shellcheck.test.ts +129 -0
  93. package/clients/dispatch/runners/shellcheck.ts +188 -0
  94. package/clients/dispatch/runners/similarity.js +230 -0
  95. package/clients/dispatch/runners/similarity.ts +339 -0
  96. package/clients/dispatch/runners/spellcheck.js +106 -0
  97. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  98. package/clients/dispatch/runners/spellcheck.test.ts +214 -0
  99. package/clients/dispatch/runners/spellcheck.ts +136 -0
  100. package/clients/dispatch/runners/tree-sitter.js +107 -0
  101. package/clients/dispatch/runners/tree-sitter.ts +135 -0
  102. package/clients/dispatch/runners/ts-lsp.js +104 -33
  103. package/clients/dispatch/runners/ts-lsp.ts +120 -38
  104. package/clients/dispatch/runners/ts-slop.js +113 -0
  105. package/clients/dispatch/runners/ts-slop.test.js +180 -0
  106. package/clients/dispatch/runners/ts-slop.test.ts +230 -0
  107. package/clients/dispatch/runners/ts-slop.ts +142 -0
  108. package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
  109. package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
  110. package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
  111. package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
  112. package/clients/dispatch/runners/utils.js +2 -4
  113. package/clients/dispatch/runners/utils.ts +2 -4
  114. package/clients/dispatch/types.ts +1 -1
  115. package/clients/dispatch/utils/format-utils.js +49 -0
  116. package/clients/dispatch/utils/format-utils.ts +60 -0
  117. package/clients/dogfood.test.js +201 -0
  118. package/clients/dogfood.test.ts +269 -0
  119. package/clients/file-time.js +152 -0
  120. package/clients/file-time.ts +208 -0
  121. package/clients/file-utils.js +40 -0
  122. package/clients/file-utils.ts +44 -0
  123. package/clients/fix-scanners.js +10 -20
  124. package/clients/fix-scanners.ts +10 -22
  125. package/clients/format-service.js +172 -0
  126. package/clients/format-service.ts +254 -0
  127. package/clients/formatters.js +435 -0
  128. package/clients/formatters.ts +508 -0
  129. package/clients/go-client.js +5 -14
  130. package/clients/go-client.ts +5 -13
  131. package/clients/installer/index.js +356 -0
  132. package/clients/installer/index.ts +426 -0
  133. package/clients/jscpd-client.js +11 -9
  134. package/clients/jscpd-client.ts +12 -8
  135. package/clients/knip-client.js +3 -7
  136. package/clients/knip-client.ts +3 -6
  137. package/clients/lsp/__tests__/client.test.js +325 -0
  138. package/clients/lsp/__tests__/client.test.ts +434 -0
  139. package/clients/lsp/__tests__/config.test.js +166 -0
  140. package/clients/lsp/__tests__/config.test.ts +209 -0
  141. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  142. package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
  143. package/clients/lsp/__tests__/integration.test.js +127 -0
  144. package/clients/lsp/__tests__/integration.test.ts +160 -0
  145. package/clients/lsp/__tests__/launch.test.js +260 -0
  146. package/clients/lsp/__tests__/launch.test.ts +329 -0
  147. package/clients/lsp/__tests__/server.test.js +259 -0
  148. package/clients/lsp/__tests__/server.test.ts +332 -0
  149. package/clients/lsp/__tests__/service.test.js +417 -0
  150. package/clients/lsp/__tests__/service.test.ts +499 -0
  151. package/clients/lsp/client.js +235 -0
  152. package/clients/lsp/client.ts +328 -0
  153. package/clients/lsp/config.js +115 -0
  154. package/clients/lsp/config.ts +149 -0
  155. package/clients/lsp/index.js +222 -0
  156. package/clients/lsp/index.ts +280 -0
  157. package/clients/lsp/installer/index.js +391 -0
  158. package/clients/lsp/interactive-install.js +210 -0
  159. package/clients/lsp/interactive-install.ts +251 -0
  160. package/clients/lsp/language.js +170 -0
  161. package/clients/lsp/language.ts +216 -0
  162. package/clients/lsp/launch.js +174 -0
  163. package/clients/lsp/launch.ts +240 -0
  164. package/clients/lsp/lsp/launch.js +116 -0
  165. package/clients/lsp/lsp/server.js +532 -0
  166. package/clients/lsp/lsp-index.js +10 -0
  167. package/clients/lsp/lsp-index.ts +11 -0
  168. package/clients/lsp/path-utils.js +48 -0
  169. package/clients/lsp/path-utils.ts +52 -0
  170. package/clients/lsp/server.js +615 -0
  171. package/clients/lsp/server.ts +800 -0
  172. package/clients/lsp/test-py-spawn/requirements.txt +1 -0
  173. package/clients/lsp/test-py-spawn/test.py +3 -0
  174. package/clients/lsp/test-py-svc/requirements.txt +1 -0
  175. package/clients/lsp/test-py-svc/test.py +3 -0
  176. package/clients/lsp/test-python-project/requirements.txt +1 -0
  177. package/clients/lsp/test-python-project/test.py +5 -0
  178. package/clients/metrics-history.js +2 -2
  179. package/clients/metrics-history.ts +2 -2
  180. package/clients/production-readiness.js +522 -0
  181. package/clients/production-readiness.ts +556 -0
  182. package/clients/project-index.js +255 -0
  183. package/clients/project-index.ts +383 -0
  184. package/clients/project-metadata.js +531 -0
  185. package/clients/project-metadata.ts +624 -0
  186. package/clients/ruff-client.js +56 -16
  187. package/clients/ruff-client.ts +72 -15
  188. package/clients/runner-tracker.js +152 -0
  189. package/clients/runner-tracker.ts +213 -0
  190. package/clients/rust-client.js +4 -11
  191. package/clients/rust-client.ts +5 -11
  192. package/clients/safe-spawn.js +96 -0
  193. package/clients/safe-spawn.ts +128 -0
  194. package/clients/scan-architectural-debt.js +3 -6
  195. package/clients/scan-architectural-debt.ts +3 -6
  196. package/clients/scan-utils.js +5 -20
  197. package/clients/scan-utils.ts +5 -29
  198. package/clients/secrets-scanner.js +3 -17
  199. package/clients/secrets-scanner.ts +4 -20
  200. package/clients/services/__tests__/effect-integration.test.js +86 -0
  201. package/clients/services/__tests__/effect-integration.test.ts +111 -0
  202. package/clients/services/effect-integration.js +194 -0
  203. package/clients/services/effect-integration.ts +268 -0
  204. package/clients/services/index.js +7 -0
  205. package/clients/services/index.ts +8 -0
  206. package/clients/services/runner-service.js +105 -0
  207. package/clients/services/runner-service.ts +179 -0
  208. package/clients/sg-runner.js +87 -13
  209. package/clients/sg-runner.ts +97 -13
  210. package/clients/state-matrix.js +160 -0
  211. package/clients/state-matrix.ts +202 -0
  212. package/clients/subprocess-client.js +10 -9
  213. package/clients/subprocess-client.ts +10 -8
  214. package/clients/test-runner-client.js +3 -7
  215. package/clients/test-runner-client.ts +3 -6
  216. package/clients/tool-availability.js +4 -10
  217. package/clients/tool-availability.ts +4 -9
  218. package/clients/tree-sitter-client.js +564 -0
  219. package/clients/tree-sitter-client.ts +797 -0
  220. package/clients/tree-sitter-query-loader.js +355 -0
  221. package/clients/tree-sitter-query-loader.ts +425 -0
  222. package/clients/type-coverage-client.js +3 -7
  223. package/clients/type-coverage-client.ts +3 -6
  224. package/clients/typescript-client.codefix.test.js +157 -0
  225. package/clients/typescript-client.codefix.test.ts +186 -0
  226. package/clients/typescript-client.js +43 -0
  227. package/clients/typescript-client.ts +98 -0
  228. package/commands/booboo.js +799 -219
  229. package/commands/booboo.ts +1004 -225
  230. package/commands/clients/ast-grep-client.js +250 -0
  231. package/commands/clients/ast-grep-parser.js +86 -0
  232. package/commands/clients/ast-grep-rule-manager.js +91 -0
  233. package/commands/clients/ast-grep-types.js +9 -0
  234. package/commands/clients/biome-client.js +380 -0
  235. package/commands/clients/complexity-client.js +667 -0
  236. package/commands/clients/file-kinds.js +177 -0
  237. package/commands/clients/file-utils.js +40 -0
  238. package/commands/clients/jscpd-client.js +169 -0
  239. package/commands/clients/knip-client.js +211 -0
  240. package/commands/clients/ruff-client.js +297 -0
  241. package/commands/clients/safe-spawn.js +88 -0
  242. package/commands/clients/scan-utils.js +83 -0
  243. package/commands/clients/sg-runner.js +190 -0
  244. package/commands/clients/types.js +11 -0
  245. package/commands/clients/typescript-client.js +505 -0
  246. package/commands/fix-from-booboo.js +398 -0
  247. package/commands/fix-from-booboo.ts +485 -0
  248. package/commands/fix-simplified.js +618 -0
  249. package/commands/fix-simplified.ts +768 -0
  250. package/commands/rate.js +10 -14
  251. package/commands/rate.ts +9 -16
  252. package/default-architect.yaml +59 -15
  253. package/index.ts +342 -429
  254. package/package.json +16 -3
  255. package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
  256. package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
  257. package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
  258. package/rules/python-slop-rules/.sgconfig.yml +4 -0
  259. package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
  260. package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
  261. package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
  262. package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
  263. package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
  264. package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
  265. package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
  266. package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
  267. package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
  268. package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
  269. package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
  270. package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
  271. package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
  272. package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
  273. package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
  274. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
  275. package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
  276. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
  277. package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
  278. package/rules/ts-slop-rules/.sgconfig.yml +4 -0
  279. package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
  280. package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
  281. package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
  282. package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
  283. package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
  284. package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
  285. package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
  286. package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
  287. package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
  288. package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
  289. package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
  290. package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
  291. package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
  292. package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
  293. package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
  294. package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
  295. package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
  296. package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
  297. package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
  298. package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
  299. package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
  300. package/skills/ast-grep/SKILL.md +182 -0
  301. package/clients/dispatch/runners/secrets.js +0 -109
  302. package/commands/fix.js +0 -244
  303. package/commands/fix.ts +0 -373
  304. package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
@@ -0,0 +1,355 @@
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();