pi-lens 3.7.0 → 3.8.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.
Files changed (140) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/README.md +145 -58
  3. package/clients/architect-client.ts +7 -2
  4. package/clients/ast-grep-client.ts +7 -1
  5. package/clients/dispatch/plan.ts +26 -5
  6. package/clients/dispatch/runners/architect.ts +20 -7
  7. package/clients/dispatch/runners/ast-grep-napi.ts +5 -2
  8. package/clients/dispatch/runners/ast-grep.ts +29 -18
  9. package/clients/dispatch/runners/biome.ts +4 -4
  10. package/clients/dispatch/runners/eslint.ts +157 -0
  11. package/clients/dispatch/runners/golangci-lint.ts +133 -0
  12. package/clients/dispatch/runners/index.ts +6 -0
  13. package/clients/dispatch/runners/python-slop.ts +17 -7
  14. package/clients/dispatch/runners/rubocop.ts +141 -0
  15. package/clients/dispatch/runners/ruff.ts +4 -4
  16. package/clients/dispatch/runners/tree-sitter.ts +30 -19
  17. package/clients/dispatch/runners/ts-slop.ts +17 -7
  18. package/clients/dispatch/runners/utils/runner-helpers.ts +76 -8
  19. package/clients/dispatch/utils/format-utils.ts +2 -1
  20. package/clients/file-kinds.ts +5 -1
  21. package/clients/fix-scanners.ts +8 -8
  22. package/clients/installer/index.ts +19 -1
  23. package/clients/lsp/index.ts +0 -40
  24. package/clients/lsp/launch.ts +5 -2
  25. package/clients/package-root.ts +44 -0
  26. package/clients/pipeline.ts +179 -8
  27. package/clients/scan-utils.ts +20 -32
  28. package/clients/sg-runner.ts +7 -5
  29. package/clients/source-filter.ts +222 -0
  30. package/clients/startup-scan.ts +142 -0
  31. package/clients/todo-scanner.ts +44 -55
  32. package/clients/tree-sitter-cache.ts +315 -0
  33. package/clients/tree-sitter-client.ts +208 -52
  34. package/clients/tree-sitter-fixer.ts +217 -0
  35. package/clients/tree-sitter-navigator.ts +329 -0
  36. package/clients/tree-sitter-query-loader.ts +55 -32
  37. package/commands/booboo.ts +47 -35
  38. package/default-architect.yaml +76 -87
  39. package/docs/ARCHITECTURE.md +74 -0
  40. package/docs/AST_GREP_RULES.md +266 -0
  41. package/docs/COMPLEXITY_METRICS.md +120 -0
  42. package/docs/EXCLUSIONS.md +83 -0
  43. package/docs/LSP_CONFIG.md +240 -0
  44. package/docs/TREE_SITTER_RULES.md +340 -0
  45. package/docs/WRITING_NEW_AST_GREP_RULES.md +200 -0
  46. package/index.ts +209 -86
  47. package/package.json +13 -4
  48. package/rules/ast-grep-rules/rules/array-callback-return-js.yml +33 -0
  49. package/rules/ast-grep-rules/rules/array-callback-return.yml +1 -1
  50. package/rules/ast-grep-rules/rules/constructor-super-js.yml +22 -0
  51. package/rules/ast-grep-rules/rules/empty-catch-js.yml +45 -0
  52. package/rules/ast-grep-rules/rules/empty-catch.yml +1 -1
  53. package/rules/ast-grep-rules/rules/getter-return-js.yml +59 -0
  54. package/rules/ast-grep-rules/rules/getter-return.yml +1 -1
  55. package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +12 -0
  56. package/rules/ast-grep-rules/rules/jsx-boolean-short-circuit.yml +1 -1
  57. package/rules/ast-grep-rules/rules/jwt-no-verify-js.yml +14 -0
  58. package/rules/ast-grep-rules/rules/missed-concurrency-js.yml +25 -0
  59. package/rules/ast-grep-rules/rules/nested-ternary-js.yml +10 -0
  60. package/rules/ast-grep-rules/rules/no-alert-js.yml +6 -0
  61. package/rules/ast-grep-rules/rules/no-architecture-violation.yml +21 -18
  62. package/rules/ast-grep-rules/rules/no-array-constructor-js.yml +10 -0
  63. package/rules/ast-grep-rules/rules/no-async-promise-executor-js.yml +15 -0
  64. package/rules/ast-grep-rules/rules/no-async-promise-executor.yml +1 -1
  65. package/rules/ast-grep-rules/rules/no-await-in-loop-js.yml +30 -0
  66. package/rules/ast-grep-rules/rules/no-await-in-promise-all-js.yml +20 -0
  67. package/rules/ast-grep-rules/rules/no-await-in-promise-all.yml +1 -1
  68. package/rules/ast-grep-rules/rules/no-bare-except.yml +1 -1
  69. package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +16 -0
  70. package/rules/ast-grep-rules/rules/no-compare-neg-zero-js.yml +13 -0
  71. package/rules/ast-grep-rules/rules/no-compare-neg-zero.yml +1 -1
  72. package/rules/ast-grep-rules/rules/no-comparison-to-none.yml +1 -1
  73. package/rules/ast-grep-rules/rules/no-cond-assign-js.yml +36 -0
  74. package/rules/ast-grep-rules/rules/no-cond-assign.yml +1 -1
  75. package/rules/ast-grep-rules/rules/no-constant-condition-js.yml +25 -0
  76. package/rules/ast-grep-rules/rules/no-constant-condition.yml +1 -1
  77. package/rules/ast-grep-rules/rules/no-constructor-return-js.yml +28 -0
  78. package/rules/ast-grep-rules/rules/no-constructor-return.yml +1 -1
  79. package/rules/ast-grep-rules/rules/no-discarded-error-js.yml +25 -0
  80. package/rules/ast-grep-rules/rules/no-discarded-error.yml +25 -0
  81. package/rules/ast-grep-rules/rules/no-dupe-args-js.yml +15 -0
  82. package/rules/ast-grep-rules/rules/no-dupe-keys-js.yml +73 -0
  83. package/rules/ast-grep-rules/rules/no-extra-boolean-cast-js.yml +25 -0
  84. package/rules/ast-grep-rules/rules/no-hardcoded-secrets-js.yml +17 -0
  85. package/rules/ast-grep-rules/rules/no-implied-eval-js.yml +15 -0
  86. package/rules/ast-grep-rules/rules/no-inner-html-js.yml +13 -0
  87. package/rules/ast-grep-rules/rules/no-insecure-randomness-js.yml +20 -0
  88. package/rules/ast-grep-rules/rules/no-insecure-randomness.yml +1 -1
  89. package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +11 -0
  90. package/rules/ast-grep-rules/rules/no-nan-comparison-js.yml +22 -0
  91. package/rules/ast-grep-rules/rules/no-nan-comparison.yml +22 -0
  92. package/rules/ast-grep-rules/rules/no-new-symbol-js.yml +8 -0
  93. package/rules/ast-grep-rules/rules/no-new-wrappers-js.yml +13 -0
  94. package/rules/ast-grep-rules/rules/no-open-redirect-js.yml +15 -0
  95. package/rules/ast-grep-rules/rules/no-prototype-builtins-js.yml +15 -0
  96. package/rules/ast-grep-rules/rules/no-prototype-builtins.yml +1 -1
  97. package/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +13 -0
  98. package/rules/ast-grep-rules/rules/no-sql-in-code.yml +1 -1
  99. package/rules/ast-grep-rules/rules/no-throw-string-js.yml +12 -0
  100. package/rules/ast-grep-rules/rules/no-throw-string.yml +1 -1
  101. package/rules/ast-grep-rules/rules/strict-equality-js.yml +10 -0
  102. package/rules/ast-grep-rules/rules/strict-inequality-js.yml +10 -0
  103. package/rules/ast-grep-rules/rules/toctou-js.yml +112 -0
  104. package/rules/ast-grep-rules/rules/toctou.yml +1 -1
  105. package/rules/ast-grep-rules/rules/unchecked-sync-fs-js.yml +44 -0
  106. package/rules/ast-grep-rules/rules/unchecked-sync-fs.yml +44 -0
  107. package/rules/ast-grep-rules/rules/unchecked-throwing-call-js.yml +31 -0
  108. package/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +48 -0
  109. package/rules/ast-grep-rules/rules/unchecked-throwing-call-ruby.yml +47 -0
  110. package/rules/ast-grep-rules/rules/unchecked-throwing-call.yml +31 -0
  111. package/rules/ast-grep-rules/rules/weak-rsa-key-js.yml +15 -0
  112. package/rules/tree-sitter-queries/go/go-bare-error.yml +47 -0
  113. package/rules/tree-sitter-queries/go/go-defer-in-loop.yml +47 -0
  114. package/rules/tree-sitter-queries/go/go-hardcoded-secrets.yml +54 -0
  115. package/rules/tree-sitter-queries/python/is-vs-equals.yml +1 -1
  116. package/rules/tree-sitter-queries/python/python-debugger.yml +46 -0
  117. package/rules/tree-sitter-queries/python/python-empty-except.yml +48 -0
  118. package/rules/tree-sitter-queries/python/python-hardcoded-secrets.yml +44 -0
  119. package/rules/tree-sitter-queries/python/python-mutable-class-attr.yml +57 -0
  120. package/rules/tree-sitter-queries/python/python-print-statement.yml +53 -0
  121. package/rules/tree-sitter-queries/python/python-raise-string.yml +38 -0
  122. package/rules/tree-sitter-queries/python/python-unsafe-regex.yml +58 -0
  123. package/rules/tree-sitter-queries/ruby/ruby-debugger.yml +44 -0
  124. package/rules/tree-sitter-queries/ruby/ruby-empty-rescue.yml +47 -0
  125. package/rules/tree-sitter-queries/ruby/ruby-eval.yml +43 -0
  126. package/rules/tree-sitter-queries/ruby/ruby-hardcoded-secrets.yml +40 -0
  127. package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +48 -0
  128. package/rules/tree-sitter-queries/ruby/ruby-puts-statement.yml +52 -0
  129. package/rules/tree-sitter-queries/ruby/ruby-rescue-exception.yml +51 -0
  130. package/rules/tree-sitter-queries/ruby/ruby-unsafe-regex.yml +49 -0
  131. package/rules/tree-sitter-queries/rust/rust-clone-in-loop.yml +49 -0
  132. package/rules/tree-sitter-queries/rust/rust-unwrap.yml +45 -0
  133. package/rules/tree-sitter-queries/typescript/console-statement.yml +3 -3
  134. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +13 -27
  135. package/rules/tree-sitter-queries/typescript/injections.scm +40 -0
  136. package/rules/tree-sitter-queries/typescript/no-console-in-tests.yml +52 -0
  137. package/rules/tree-sitter-queries/typescript/sql-injection.yml +55 -0
  138. package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +71 -0
  139. package/rules/tree-sitter-queries/typescript/variable-shadowing.yml +51 -0
  140. package/scripts/download-grammars.ts +155 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,235 @@
2
2
 
3
3
  All notable changes to pi-lens will be documented in this file.
4
4
 
5
+ ## [3.8.1] - 2026-04-05
6
+
7
+ ### Fixed
8
+ - **`console-statement` hijacking `no-console-in-tests`** — The keyword match for
9
+ `console-statement` (`pattern.includes("console")`) was catching `no-console-in-tests`
10
+ because both contain "console". The simpler rule always won, so both fired on every
11
+ console call. Fixed by excluding test-related patterns: `!pattern.includes("test")`.
12
+ - **`hardcoded-secrets` malformed tree-sitter query** — Had two top-level S-expression
13
+ patterns instead of a single union pattern `[...]`. Replaced with valid union syntax
14
+ and added `post_filter: check_secret_pattern` so variable names are actually filtered
15
+ against credential patterns. Reduced false positives from 58 → 0 on the codebase.
16
+
17
+ ## [3.8.0] - 2026-04-05
18
+
19
+ ### Added — Tree-sitter Expansion
20
+
21
+ - **Go, Rust, Ruby grammar support** — WASM grammars for 3 new languages downloaded at
22
+ install time via `scripts/download-grammars.ts`. Grammar download script added with
23
+ npm `download-grammars` script and postinstall hook. Tree-sitter structural analysis
24
+ now covers all 7 dispatch languages: TypeScript, TSX, JavaScript, Python, Go, Rust, Ruby.
25
+
26
+ - **Tree-sitter dispatch for Go/Rust/Ruby** — Dispatch runner `appliesTo` extended;
27
+ extension→language map replaces the brittle `endsWith` chain. Tree-sitter runner
28
+ added to Go, Rust, and Ruby dispatch plans.
29
+
30
+ - **Incremental parse cache (`TreeCache`)** — AST trees are cached by SHA-256 content
31
+ hash and mtime. Subsequent queries on the same file (same turn) skip re-parsing.
32
+ Cache stores up to 50 files with LRU eviction. `calculateEdit()` + `incrementalUpdate()`
33
+ infrastructure ready for full incremental parsing when old content is tracked.
34
+
35
+ - **AST navigator (`TreeSitterNavigator`)** — Scope-aware traversal utilities: `findParent()`,
36
+ `isInTryCatch()`, `isInTestBlock()`, `isInLoop()`, `getScopeChain()`, `isShadowed()`,
37
+ `getSiblings()`. Used by post-filters for context-aware rule evaluation.
38
+
39
+ - **Native predicate support in queries** — Query YAML files now support a `predicates:`
40
+ array field. Rules with inline `#eq?` / `#match?` / `#not-eq?` predicates run filtering
41
+ inside WASM rather than in JavaScript post-filters.
42
+
43
+ - **Inline fix hints** — Tree-sitter diagnostics now carry `fixable: true` and
44
+ `fixSuggestion: "remove this statement"` when `has_fix: true` in the rule. Displayed
45
+ as `💡 Fix: remove this statement` inline in the diagnostic output. Tree-sitter runner
46
+ is read-only — linters (Biome/Ruff/ESLint) own the autofix phase.
47
+
48
+ - **New post-filters** — `not_in_try_catch`, `in_try_catch`, `not_in_test_block`,
49
+ `not_in_function`, `check_secret_pattern`, `python_empty_except`, `ruby_empty_rescue`,
50
+ `name_matches_param`.
51
+
52
+ ### Added — New Rules (50+)
53
+
54
+ **Structural safety (ast-grep, TypeScript + JavaScript):**
55
+ - `unchecked-sync-fs` — `fs.statSync/readFileSync/writeFileSync/...` outside try/catch (error)
56
+ - `unchecked-throwing-call` — `JSON.parse`, `new URL()`, `execSync` outside try/catch (error)
57
+ - `no-nan-comparison` — `x === NaN` always false, use `Number.isNaN()` (error)
58
+ - `no-discarded-error` — `new Error()` as standalone statement without throw (error)
59
+
60
+ **Structural safety (ast-grep, Python):**
61
+ - `unchecked-throwing-call-python` — `open()`, `json.loads()`, `os.stat()` etc. outside
62
+ try/except (error)
63
+
64
+ **Structural safety (ast-grep, Ruby):**
65
+ - `unchecked-throwing-call-ruby` — `File.read`, `JSON.parse`, `Integer()` etc. outside
66
+ begin/rescue (error)
67
+
68
+ **Tree-sitter Python rules (new):**
69
+ - `python-mutable-class-attr` — class-level `list`/`dict`/`set` shared across all instances (error)
70
+ - `python-debugger` — `breakpoint()`, `pdb.set_trace()` left in code (error)
71
+ - `python-print-statement` — `print()` debug output in production code (warning)
72
+ - `python-hardcoded-secrets` — hardcoded credential assignments (error)
73
+ - `python-empty-except` — except block that only does `pass` (error)
74
+ - `python-unsafe-regex` — `re.compile(variable)` ReDoS risk (error)
75
+ - `python-raise-string` — `raise "string"` is TypeError in Python 3 (error)
76
+
77
+ **Tree-sitter Ruby rules (new):**
78
+ - `ruby-rescue-exception` — `rescue Exception` catches SystemExit and signals (error)
79
+ - `ruby-empty-rescue` — rescue with no body silently swallows errors (error)
80
+ - `ruby-debugger` — `binding.pry` / `binding.irb` left in code (error)
81
+ - `ruby-puts-statement` — `puts`/`p`/`pp` debug output in production (warning)
82
+ - `ruby-hardcoded-secrets` — hardcoded credential assignments (error)
83
+ - `ruby-unsafe-regex` — `Regexp.new(variable)` ReDoS risk (error)
84
+
85
+ **Tree-sitter Go rules (new):**
86
+ - `go-hardcoded-secrets` — hardcoded credentials in short/var/const declarations (error)
87
+
88
+ **JavaScript coverage (38 new rules):**
89
+ All runtime-applicable TypeScript ast-grep rules now have JavaScript equivalents:
90
+ `strict-equality`, `empty-catch`, `no-throw-string`, `no-cond-assign`,
91
+ `no-async-promise-executor`, `toctou`, `no-hardcoded-secrets`, `no-inner-html`,
92
+ `no-insecure-randomness`, `no-sql-in-code`, `jwt-no-verify`, `weak-rsa-key`, and 26 more.
93
+
94
+ ### Changed — Severity Upgrades
95
+
96
+ **17 ast-grep rules upgraded from `warning` to `error`** (will crash / produce wrong output):
97
+ `empty-catch`, `array-callback-return`, `getter-return`, `jsx-boolean-short-circuit`,
98
+ `no-async-promise-executor`, `no-await-in-promise-all`, `no-bare-except`,
99
+ `no-compare-neg-zero`, `no-cond-assign`, `no-constant-condition`,
100
+ `no-constructor-return`, `no-insecure-randomness`, `no-prototype-builtins`,
101
+ `no-sql-in-code`, `no-throw-string`, `toctou`, `no-comparison-to-none`.
102
+
103
+ **4 tree-sitter rules upgraded from `warning` to `error`**:
104
+ `go-defer-in-loop`, `is-vs-equals`, `rust-unwrap`, `unsafe-regex`.
105
+
106
+ ### Fixed
107
+
108
+ - **`console-statement` duplicating `no-console-in-tests`** — `console-statement` now
109
+ uses `post_filter: not_in_test_block` so production and test console detection are
110
+ mutually exclusive.
111
+
112
+ - **`variable-shadowing` never detecting actual shadowing** — Rule now captures both
113
+ `@PARAM` and `@NAME`; `name_matches_param` post-filter only flags when names are
114
+ identical. Previously the rule fired on any variable in a nested function.
115
+
116
+ - **`isInLoop()` false positives** — `call_expression` removed from loop node type list.
117
+ Previously `isInLoop()` returned `true` inside any function call.
118
+
119
+ - **`injectPredicates()` inserting at wrong AST position** — Broken predicate injection
120
+ machinery removed. Predicates already work inline in query S-expressions.
121
+
122
+ - **`sql-injection` rule not matching `db.query()`** — Query now uses union
123
+ `[identifier | member_expression]` to catch both bare `query()` and `db.query()`.
124
+
125
+ - **`contains_sql_keywords` post-filter inverted logic** — Rule was skipping `sql`
126
+ tagged templates (the primary SQL injection vector). Post-filter removed entirely;
127
+ rule relies on inline `#match?` predicate.
128
+
129
+ - **`no-discarded-error` ast-grep `not: inside:` not traversing ancestors** — Required
130
+ `stopBy: end` in ast-grep's `inside` predicate to check all ancestors, not just the
131
+ direct parent. Applied to all `not: inside:` rules.
132
+
133
+ - **Go/Rust/Ruby rules silently skipped** — Runner `appliesTo` was `["jsts", "python"]`
134
+ only. Extended to include `go`, `rust`, `ruby`.
135
+
136
+ ### Fixed (from PR #1 — alexx-ftw)
137
+
138
+ - **`process.cwd()` wrong for global npm installs** — All asset resolution (WASM grammars,
139
+ tree-sitter query YAMLs, ast-grep rule directories, `default-architect.yaml`) now uses
140
+ `resolvePackagePath(import.meta.url, ...)` which walks up from the module file to the
141
+ package root. Previously, running pi-lens as a globally installed extension would fail
142
+ to find built-in rules and grammars.
143
+
144
+ - **Session start scanning `$HOME` or generic directories** — `resolveStartupScanContext()`
145
+ gates all heavy startup scans (knip, jscpd, exports index, project index) behind project
146
+ root detection (`.git`, `package.json`, `go.mod`, etc.) and a 2000-source-file budget.
147
+ Pi-lens stays responsive when opened outside a real project.
148
+
149
+ - **`cachedExports` not cleared on session reset** — Export cache from the previous
150
+ session persisted into new sessions, causing false duplicate-export warnings.
151
+
152
+ - **`biomeClient.ensureAvailable()` at session start** — Changed to `isAvailable()` so
153
+ session start no longer blocks on a Biome auto-install. Installs happen lazily on
154
+ first file write.
155
+
156
+ - **Project index not persisted across sessions** — Index now saved to disk after build
157
+ via `saveIndex()`, and `isIndexFresh()` check skips rebuild when the saved index is
158
+ still current.
159
+
160
+ - **`tree-sitter-query-loader` only loading from `process.cwd()`** — Now loads from
161
+ both the user's project rules directory AND the package's built-in rules, merging
162
+ both sets. Project-specific rules coexist with built-in rules.
163
+
164
+ ---
165
+
166
+ ## [3.7.2] - 2026-04-05
167
+
168
+ ### Added
169
+ - **All-clear signal** — When the pipeline runs clean (no blockers, no test failures),
170
+ the agent now receives a confirmation one-liner instead of silence:
171
+ `✓ TypeScript clean · 12/12 tests · 847ms`
172
+ When non-blocking warnings exist: `✓ no blockers · 3 warning(s) -> /lens-booboo · 847ms`
173
+ Agents can now distinguish "checks ran clean" from "checks didn't run".
174
+
175
+ ### Fixed
176
+ - **Auto-fix message now names the tool** — `✅ Auto-fixed 3 issue(s) (eslint:2, biome:1)`
177
+ instead of the vague `Auto-fixed 3 issue(s)`. Agents know exactly what was corrected.
178
+
179
+ ### Security
180
+ - **Remove `effect` dependency** — Used for 5 trivial `tryPromise` wrappers in one file,
181
+ never consumed via Effect's runtime. Dead dependency removed.
182
+ - **`--ignore-scripts` in auto-installer** — `npm install` for auto-installed tools now
183
+ passes `--ignore-scripts` by default. Only packages that legitimately need postinstall
184
+ scripts to download native binaries (`@biomejs/biome`, `@ast-grep/napi`, `esbuild`) are
185
+ allowlisted.
186
+ - **`npx -y` replaced with `npx --no`** — LSP server launch via npx no longer silently
187
+ downloads uncached packages. `--no` fails fast if the package isn't cached; the
188
+ interactive-install flow is the correct path for first-time installs.
189
+ - **Local-first `sg` (ast-grep) resolution** — All `sg` callers now check
190
+ `node_modules/.bin/sg` → global `sg` → `npx --no sg` (cache-only). No silent
191
+ network downloads of the ast-grep CLI.
192
+
193
+ ---
194
+
195
+ ## [3.7.2] - 2026-04-05 (previous)
196
+
197
+ ### Added
198
+ - **ESLint `--fix` in autofix phase** — Projects with an ESLint config now have fixable
199
+ issues auto-corrected (import ordering, jsx style, etc.) before dispatch runs, using
200
+ `--fix-dry-run` to get the accurate fixed count then `--fix` to apply. Availability
201
+ is cached per session. Only fires on JS/TS files with an ESLint config present.
202
+
203
+ ### Fixed
204
+ - **Misleading infinite-loop comment in biome/ruff runners** — The comment incorrectly
205
+ stated that writing files from runners would trigger infinite loops (formatters already
206
+ prove this isn't true). Updated to explain the real reason: dispatch runners report
207
+ issues for agent understanding; silently rewriting would leave the agent's context
208
+ window stale.
209
+
210
+ ---
211
+
212
+ ## [3.7.1] - 2026-04-05
213
+
214
+ ### Added
215
+ - **ESLint dispatch runner** — Projects with `.eslintrc` / `eslint.config.js` (any variant)
216
+ now run ESLint automatically on every JS/TS file write. Prefers local
217
+ `node_modules/.bin/eslint` over global. Skips silently on projects using Biome/OxLint
218
+ (no ESLint config). ESLint errors (severity 2) are blocking; warnings are non-blocking.
219
+
220
+ - **golangci-lint dispatch runner** — Go projects with `.golangci.yml` / `.golangci.yaml`
221
+ now run golangci-lint on every `.go` file write (in addition to `go-vet`). Parses JSON
222
+ output. Skips when no config is present (avoids default-rule noise on non-opted-in
223
+ projects). 60s timeout.
224
+
225
+ - **RuboCop dispatch runner** — Ruby files (`.rb`, `.rake`, `.gemspec`, `.ru`) now run
226
+ RuboCop in lint-only mode on every write. Prefers `bundle exec rubocop` when a Gemfile
227
+ references rubocop. Fatal/error offenses are blocking; convention/refactor are warnings.
228
+
229
+ - **`ruby` file kind** — `.rb`, `.rake`, `.gemspec`, `.ru` files are now recognised as
230
+ `ruby` kind, enabling file-kind-gated runners and formatter detection.
231
+
232
+ ---
233
+
5
234
  ## [3.7.0] - 2026-04-05
6
235
 
7
236
  ### Added
package/README.md CHANGED
@@ -1,21 +1,26 @@
1
1
  # pi-lens
2
2
 
3
- **pi extension for real-time code quality.** 31 LSP servers, tree-sitter structural analysis, AST pattern matching, auto-install for TypeScript/Python tooling, duplicate detection, complexity metrics, and inline blockers with comprehensive `/lens-booboo` reports.
3
+ **pi extension for real-time code quality.** 31 LSP servers, tree-sitter structural analysis (7 languages), 112 AST pattern matching rules, auto-install for TypeScript/Python/Go/Rust/Ruby tooling, duplicate detection, complexity metrics, and inline blockers with comprehensive `/lens-booboo` reports.
4
4
 
5
5
  ## What pi-lens Does
6
6
 
7
7
  **For every file you edit:**
8
8
  1. **Auto-formats** — Detects and runs formatters (Biome, Prettier, Ruff, gofmt, rustfmt, etc.)
9
- 2. **Type-checks** — TypeScript, Python, Go, Rust (31 languages with `--lens-lsp`)
9
+ 2. **Type-checks** — TypeScript, Python, Go, Rust, Ruby (31 languages with `--lens-lsp`)
10
10
  3. **Scans for secrets** — Blocks on hardcoded API keys, tokens, passwords
11
- 4. **Runs linters** — Biome (TS/JS), Ruff (Python), plus structural analysis
12
- 5. **Tree-sitter analysis** — Deep structural patterns (empty catch, eval, deep nesting, mixed async styles)
13
- 6. **Auto-installs** — TypeScript, Python, Biome, Ruff, and analysis tools auto-install on first use
14
- 7. **Only shows NEW issues** — Delta-mode tracks baselines and filters pre-existing problems
15
-
16
- **Blockers** (type errors, secrets, empty catches) appear inline and stop the agent until fixed.
11
+ 4. **Runs linters** — Biome (TS/JS), Ruff (Python), ESLint (opt-in), plus structural analysis
12
+ 5. **Auto-fixes** — Biome, Ruff, and ESLint safe fixes applied automatically (named per tool)
13
+ 6. **Tree-sitter analysis** — Deep structural patterns across 7 languages (45+ patterns)
14
+ 7. **Pre-write duplicate detection** — Blocks export redefinitions and structural similarity clones
15
+ 8. **Auto-installs** — TypeScript, Python, Biome, Ruff, Go, Rust, Ruby tools auto-install on first use
16
+ 9. **Only shows NEW issues** Delta-mode tracks baselines and filters pre-existing problems
17
+ 10. **Build artifact filtering** — Skips compiled `.js` when `.ts` sibling exists (no duplicate findings)
18
+
19
+ **Blockers** (type errors, secrets, empty catches, duplicate exports) appear inline and stop the agent until fixed.
17
20
  **Warnings** (complexity, code smells) go to `/lens-booboo` — run it to see them all.
18
21
 
22
+ **All-clear signals:** When checks pass, pi-lens confirms with `✓ TypeScript clean · 12/12 tests · 847ms` so you know checks ran (not just skipped).
23
+
19
24
  ## Quick Start
20
25
 
21
26
  ```bash
@@ -92,26 +97,28 @@ pi-lens **automatically lints** every file you write or edit. Linters are auto-d
92
97
  |--------|-----------|--------------|------|----------|
93
98
  | **Biome** | TS/JS/JSON/CSS | Automatic | **Default** | 10 |
94
99
  | **Ruff** | Python | Automatic | **Default** | 10 |
100
+ | **ESLint** | TS/JS/Vue/Svelte | Automatic (with config) | Project-configured | 11 |
95
101
  | **oxlint** | TS/JS | Manual (`npm i -g oxlint`) | Fast alternative | 12 |
96
- | **ESLint** | JS/Vue/Svelte | `npx` via `--lens-lsp` | LSP only | - |
102
+ | **golangci-lint** | Go | Manual (with config) | Go meta-linter | 13 |
103
+ | **RuboCop** | Ruby | Manual / Bundler | Ruby standard | 15 |
97
104
  | **shellcheck** | Bash/sh/zsh/fish | Manual (`apt install shellcheck`) | Shell scripts | 20 |
98
105
 
99
106
  (*) = Auto-installed (no manual setup required)
100
107
 
101
- **Priority:** Lower numbers = run earlier. Biome/Ruff run first, followed by specialized linters.
102
-
103
108
  **How it works:**
104
109
  1. Agent writes a file
105
110
  2. pi-lens detects linters based on config files and file type
106
- 3. Biome takes priority for TS/JS; Ruff takes priority for Python
107
- 4. Multiple linters can run on the same file (e.g., Biome + oxlint)
111
+ 3. Biome takes priority for TS/JS; Ruff takes priority for Python; ESLint only runs when `.eslintrc` or `eslint.config.js` is present
112
+ 4. Multiple linters can run on the same file (e.g., Biome + oxlint + ESLint)
108
113
  5. Issues are delta-tracked (only new issues shown after first write)
114
+ 6. **Named autofix**: Auto-fix messages show tool breakdown: `✅ Auto-fixed 3 issue(s) (eslint:2, biome:1)`
109
115
 
110
116
  **Notes:**
111
- - Biome and Ruff are **dual-purpose** (lint + format)
117
+ - Biome and Ruff are **dual-purpose** (lint + format + auto-fix)
118
+ - ESLint requires a config file (project opts in); skipped on Biome/OxLint projects
119
+ - golangci-lint requires `.golangci.yml` (project opts in)
112
120
  - oxlint is a faster Rust-based alternative to ESLint
113
- - ESLint only runs when `--lens-lsp` is enabled
114
- - shellcheck requires manual installation on most systems
121
+ - ESLint LSP only runs when `--lens-lsp` is enabled; dispatch runner runs in standard mode too
115
122
 
116
123
  ---
117
124
 
@@ -137,15 +144,16 @@ pi --lens-lsp # Enable LSP
137
144
 
138
145
  ### `pi` vs `pi --lens-lsp`
139
146
 
140
- | Feature | `pi` (Default) | `pi --lens-lsp` |
147
+ | `pi` (Default) | `pi --lens-lsp` |
141
148
  |---------|----------------|-----------------|
142
- | **Type Checking** | Built-in TypeScriptClient | Full LSP (31 language servers) |
143
- | **Auto-format** | Biome, Prettier, Ruff, etc. | Same |
144
- | **Auto-fix** | Enabled by default | Same |
149
+ | **Type Checking** | Built-in TypeScriptClient, Pyright, go-vet, rust-clippy | Full LSP (31 language servers) |
150
+ | **Auto-format** | Biome, Prettier, Ruff, gofmt, rustfmt, etc. | Same |
151
+ | **Auto-fix** | Biome, Ruff, ESLint (named per tool) | Same |
145
152
  | **Secrets scan** | Blocks on hardcoded secrets | Same |
146
- | **Languages** | TypeScript, Python (built-in) | 31 languages via LSP |
153
+ | **Languages** | TypeScript, Python, Go, Rust, Ruby (built-in) | 31 languages via LSP |
147
154
  | **Python** | Ruff/pyright (built-in) | Pyright LSP |
148
- | **Go, Rust, etc.** | Basic linting | Full LSP support |
155
+ | **Go** | go-vet, golangci-lint | Full gopls |
156
+ | **Rust** | rust-clippy | Full rust-analyzer |
149
157
 
150
158
  **Recommendation:** Use `pi` for TypeScript/Python projects. Use `pi --lens-lsp` for multi-language projects or when you need full language server features.
151
159
 
@@ -160,9 +168,10 @@ Every file write/edit triggers multiple analysis phases:
160
168
  **Execution flow:**
161
169
  1. **Secrets scan** (pre-flight) — Hardcoded secrets block immediately (non-runner check)
162
170
  2. **LSP integration** (Phase 3, with `--lens-lsp`) — Real-time type errors from language servers
163
- 3. **Dispatch system** — Routes file to appropriate runners by `FileKind`
171
+ 3. **Dispatch system** — Routes file to appropriate runners by `FileKind` (TS, Python, Go, Rust, Ruby, etc.)
164
172
  4. **Runners execute** by priority (lower = earlier). See [Runners](#runners) section for full list.
165
173
  5. **Test runner detection** (post-write) — Detects Jest/Vitest/Pytest and runs relevant tests
174
+ 6. **Cascade deferral** — Errors in OTHER files (caused by this edit) are tracked but not shown inline; surfaced at `turn_end` once all edits complete
166
175
 
167
176
  **Delta mode behavior:**
168
177
  - **First write:** All issues tracked and stored in baseline
@@ -175,7 +184,17 @@ STOP — 1 issue(s) must be fixed:
175
184
  L23: var total = sum(items); — use 'let' or 'const'
176
185
  ```
177
186
 
178
- > **Note:** Only **blocking** issues (`ts-lsp`, `pyright` errors, `type-safety` switch errors, secrets) appear inline. Warnings are tracked but not shown inline (noise reduction) — run `/lens-booboo` to see all warnings.
187
+ Or when clean:
188
+ ```
189
+ ✓ TypeScript clean · 12/12 tests · 847ms
190
+ ```
191
+
192
+ Or with warnings:
193
+ ```
194
+ ✓ no blockers · 3 warning(s) -> /lens-booboo · 623ms
195
+ ```
196
+
197
+ > **Note:** Only **blocking** issues (`ts-lsp`, `pyright` errors, `type-safety` switch errors, secrets, duplicate exports) appear inline. Warnings are tracked but not shown inline (noise reduction) — run `/lens-booboo` to see all warnings.
179
198
 
180
199
  ---
181
200
 
@@ -187,19 +206,22 @@ pi-lens uses a **dispatcher-runner architecture** for extensible multi-language
187
206
  |--------|----------|----------|--------|-------------|
188
207
  | **ts-lsp** | TypeScript | 5 | Blocking | TypeScript errors (hard stops) |
189
208
  | **pyright** | Python | 5 | Blocking | Python type errors (hard stops) |
190
- | **biome** | TS/JS | 10 | Warning | Linting issues (delta-tracked) |
191
- | **ruff** | Python | 10 | Warning | Python linting (delta-tracked) |
209
+ | **biome** | TS/JS/JSON/CSS | 10 | Warning | Linting + auto-fix (delta-tracked) |
210
+ | **ruff** | Python | 10 | Warning | Python linting + auto-fix (delta-tracked) |
211
+ | **eslint** | TS/JS | 11 | Warning | ESLint with auto-fix (project must have config) |
192
212
  | **oxlint** | TS/JS | 12 | Warning | Fast Rust-based JS/TS linter |
193
- | **tree-sitter** | TS/JS, Python | 14 | Mixed | AST-based structural analysis (21 patterns) — **singleton WASM client** |
194
- | **ast-grep-napi** | TS/JS | 15 | Blocking | Security rules inline (no-eval, jwt-no-verify, no-hardcoded-secrets, etc.) |
213
+ | **tree-sitter** | TS/JS, Python, Go, Rust, Ruby | 14 | Mixed | AST-based structural analysis (45+ patterns, 7 languages) — **singleton WASM client** |
214
+ | **ast-grep-napi** | TS/JS/Python/Go/Rust | 15 | Blocking | Security rules inline (112 patterns, no-eval, jwt-no-verify, no-hardcoded-secrets, etc.) |
195
215
  | **type-safety** | TS | 20 | Mixed | Switch exhaustiveness (blocking), other (warning) |
196
216
  | **shellcheck** | Shell | 20 | Warning | Bash/sh/zsh/fish linting |
197
217
  | **python-slop** | Python | 25 | Warning | AI slop detection (~40 patterns) |
198
218
  | **spellcheck** | Markdown | 30 | Warning | Typo detection in docs |
199
- | **similarity** | TS | 35 | Warning | Semantic duplicate detection (≥90% structural similarity, Rust-accelerated when available) |
219
+ | **similarity** | TS | 35 | Warning | Semantic duplicate detection (≥90% structural similarity, pre-write check) |
200
220
  | **architect** | All | 40 | Warning | Architectural rule violations |
201
221
  | **go-vet** | Go | 50 | Warning | Go static analysis |
222
+ | **golangci-lint** | Go | 51 | Warning | Go meta-linter (needs .golangci.yml) |
202
223
  | **rust-clippy** | Rust | 50 | Warning | Rust linting |
224
+ | **rubocop** | Ruby | 52 | Warning | Ruby linting (supports bundle exec) |
203
225
 
204
226
  **Priority legend:**
205
227
  - **5** — Type checkers (blocking errors)
@@ -215,25 +237,37 @@ pi-lens uses a **dispatcher-runner architecture** for extensible multi-language
215
237
 
216
238
  **Consolidated runners:** `ts-slop` merged into `ast-grep-napi` — CLI ast-grep used for full linter via `/lens-booboo`
217
239
 
218
- **Tree-sitter runner patterns** (priority 14, AST-based structural analysis):
240
+ **Tree-sitter runner patterns** (priority 14, AST-based structural analysis, 7 languages, 45+ patterns):
219
241
 
220
- TypeScript/JavaScript (13 patterns):
221
- - **Error**: empty-catch, hardcoded-secrets, eval
222
- - **Warning**: debugger, await-in-loop, console-statement, long-parameter-list, nested-ternary, deep-promise-chain, mixed-async-styles, deep-nesting, constructor-super, no-dupe-class-members
242
+ **TypeScript/JavaScript (17 patterns):**
243
+ - **Error**: empty-catch, hardcoded-secrets, eval, sql-injection, unsafe-regex
244
+ - **Warning**: debugger, await-in-loop, console-statement (not in tests), long-parameter-list, nested-ternary, deep-promise-chain, mixed-async-styles, deep-nesting, constructor-super, no-dupe-class-members, variable-shadowing
223
245
 
224
- TSX (2 patterns):
246
+ **TSX (2 patterns):**
225
247
  - **Error**: dangerously-set-inner-html
226
248
  - **Warning**: no-nested-links
227
249
 
228
- Python (6 patterns):
229
- - **Error**: bare-except, mutable-default-arg, eval-exec, unreachable-except
230
- - **Warning**: wildcard-import, is-vs-equals
250
+ **Python (11 patterns):**
251
+ - **Error**: bare-except, mutable-default-arg, eval-exec, unreachable-except, python-empty-except, python-hardcoded-secrets, python-mutable-class-attr, python-unsafe-regex, python-raise-string
252
+ - **Warning**: wildcard-import, is-vs-equals, python-debugger, python-print-statement
253
+
254
+ **Go (3 patterns):**
255
+ - **Error**: go-hardcoded-secrets, go-defer-in-loop
256
+ - **Warning**: go-bare-error
257
+
258
+ **Rust (2 patterns):**
259
+ - **Error**: rust-unwrap
260
+ - **Warning**: rust-clone-in-loop
231
261
 
232
- **Custom tree-sitter queries:** Add `.yml` files to `.pi-lens/rules/tree-sitter-queries/{typescript,python}/`
262
+ **Ruby (8 patterns):**
263
+ - **Error**: ruby-rescue-exception, ruby-empty-rescue, ruby-hardcoded-secrets, ruby-unsafe-regex, ruby-debugger
264
+ - **Warning**: ruby-puts-statement, ruby-eval, ruby-open-struct
233
265
 
234
- **AI Slop Detection:**
266
+ **Custom tree-sitter queries:** Add `.yml` files to `.pi-lens/rules/tree-sitter-queries/{typescript,python,go,rust,ruby}/`
267
+
268
+ **AI Slop Detection:**
235
269
  - `python-slop` runner (priority 25): ~40 patterns for Python code quality
236
- - `ast-grep-napi` runner (priority 15): Security rules fire inline (blocking); slop/architecture warnings via `/lens-booboo` only. Skips 5 rules already covered by tree-sitter.
270
+ - `ast-grep-napi` runner (priority 15): Security rules fire inline (blocking); slop/architecture warnings via `/lens-booboo` only. Skips rules already covered by tree-sitter.
237
271
 
238
272
  ---
239
273
 
@@ -241,6 +275,27 @@ Python (6 patterns):
241
275
 
242
276
  Safeguards that run **before** the dispatch system:
243
277
 
278
+ #### Pre-Write Duplicate Detection
279
+
280
+ Blocks the agent **before** the write/edit is applied if the new content would create problems:
281
+
282
+ **Export Duplicate Detection**
283
+ - **Triggers:** New `export function/class/const/type/interface` matches an existing export in another file
284
+ - **Blocks:** `🔴 STOP — Redefining existing export(s). Import instead`
285
+ - **Why:** Prevents accidental redefinitions when agent creates "helper" functions that already exist
286
+
287
+ **Structural Similarity Detection**
288
+ - **Triggers:** New function body is ≥90% similar to an existing utility function
289
+ - **Warns:** `Function 'parseData' has 94% similarity to existing utility 'parseJSON()'. Consider reusing the existing utility.`
290
+ - **How:** 57×72 state matrix comparison (Amain algorithm), runs via Rust core when available (~50ms)
291
+ - **Why:** Prevents proliferation of nearly-identical helper functions
292
+
293
+ **Build Artifact Filtering**
294
+ - **Problem:** Scanning both `.ts` source and `.js` output produces duplicate findings
295
+ - **Solution:** Source-filter module detects higher-precedence siblings (`.ts` shadows `.js`, `.vue` shadows `.js`)
296
+ - **Applies to:** TypeScript→JavaScript, Vue/Svelte→JavaScript, CoffeeScript→JavaScript
297
+ - **Result:** No duplicate diagnostics from compiled outputs
298
+
244
299
  #### Secrets Scanning (Pre-flight)
245
300
 
246
301
  Runs on every file write/edit **before** any other checks. Scans for:
@@ -292,20 +347,23 @@ See [AST_GREP_RULES.md](AST_GREP_RULES.md) for full guide.
292
347
  When pi starts a new session, pi-lens performs initialization scans to establish baselines and surface existing technical debt:
293
348
 
294
349
  **Initialization sequence:**
295
- 1. **Reset session state** — Clear metrics and complexity baselines
296
- 2. **Initialize LSP** (with `--lens-lsp`) Detect and auto-install language servers
297
- 3. **Pre-install TypeScript LSP** (with `--lens-lsp`) — Warm up cache for instant response
298
- 4. **Detect available tools** Biome, ast-grep, Ruff, Knip, jscpd, Madge, type-coverage, Go, Rust
299
- 5. **Load architect rules** — If `architect.yml` or `.architect.yml` present
300
- 6. **Detect test runner** — Jest, Vitest, Pytest, etc.
301
-
302
- **Cached scans** (with 5-min TTL):
350
+ 1. **Reset session state** — Clear metrics, complexity baselines, and cached exports
351
+ 2. **Startup scan safety** — Detect project root (`.git`, `package.json`, `go.mod`, etc.) and skip heavy scans if not in a real project (prevents scanning `$HOME`)
352
+ 3. **Initialize LSP** (with `--lens-lsp`) — Detect and auto-install language servers
353
+ 4. **Pre-install TypeScript LSP** (with `--lens-lsp`) Warm up cache for instant response
354
+ 5. **Detect available tools** — Biome, ast-grep, Ruff, ESLint, Knip, jscpd, Madge, type-coverage, Go, Rust, Ruby
355
+ 6. **Load architect rules** — If `architect.yml` or `.architect.yml` present
356
+ 7. **Detect test runner** — Jest, Vitest, Pytest, etc.
357
+
358
+ **Cached scans** (with TTL):
303
359
  | Scan | Tool | Cached | Purpose |
304
360
  |------|------|--------|---------|
305
- | **TODOs** | Internal | No | Tech debt markers |
306
- | **Dead code** | Knip | Yes | Unused exports/files/deps |
307
- | **Duplicates** | jscpd | Yes | Copy-paste detection |
308
- | **Exports** | ast-grep | No | Function index for similarity |
361
+ | **TODOs** | Internal | Baseline stored | Tech debt markers (delta reported at turn_end) |
362
+ | **Dead code** | Knip | 5-min TTL | Unused exports/files/deps |
363
+ | **Duplicates** | jscpd | 5-min TTL | Copy-paste detection |
364
+ | **Circular deps** | Madge | Turn-end | Import cycle detection (runs async) |
365
+ | **Exports** | ast-grep | Session | Function index for duplicate detection |
366
+ | **Project index** | Amain | Persisted to disk | Structural similarity for pre-write checks |
309
367
 
310
368
  **Error debt tracking** (with `--error-debt` flag):
311
369
  - If tests passed at end of previous session but fail now → **regression detected**
@@ -325,17 +383,19 @@ Full codebase analysis with **10 tracked runners** producing a comprehensive rep
325
383
 
326
384
  | # | Runner | What it finds |
327
385
  |---|--------|---------------|
328
- | 1 | **ast-grep (design smells)** | Structural issues (empty catch, no-debugger, etc.) |
386
+ | 1 | **ast-grep (design smells)** | Structural issues (empty catch, no-debugger, unchecked throws, etc.) |
329
387
  | 2 | **ast-grep (similar functions)** | Duplicate function patterns across files |
330
388
  | 3 | **semantic similarity (Amain)** | 57×72 matrix semantic clones (≥90% similarity) |
331
389
  | 4 | **complexity metrics** | Low MI, high cognitive complexity, AI slop indicators |
332
- | 5 | **TODO scanner** | TODO/FIXME annotations and tech debt markers |
390
+ | 5 | **TODO scanner** | TODO/FIXME annotations and tech debt markers (delta mode) |
333
391
  | 6 | **dead code (Knip)** | Unused exports, files, dependencies |
334
392
  | 7 | **duplicate code (jscpd)** | Copy-paste blocks with line/token counts |
335
393
  | 8 | **type coverage** | Percentage typed vs `any`, low-coverage files |
336
- | 9 | **circular deps (Madge)** | Import cycles and dependency chains |
394
+ | 9 | **circular deps (Madge)** | Import cycles and dependency chains (turn-end async) |
337
395
  | 10 | **architectural rules** | Layer violations, file size limits, path rules |
338
396
 
397
+ **Turn-end findings** — Some expensive analysis (jscpd, Madge, TODO-delta) runs asynchronously at the end of each turn and surfaces findings via context injection. Results are cached and merged into the next context event.
398
+
339
399
  **Output:**
340
400
  - **Terminal:** Progress `[1/10] runner...` with timing, summary with findings per runner
341
401
  - **JSON:** `.pi-lens/reviews/booboo-{timestamp}.json` (structured data for AI processing)
@@ -407,12 +467,13 @@ pi-lens works out of the box for TypeScript/JavaScript. For full language suppor
407
467
 
408
468
  | Tool | Install | What it does |
409
469
  |------|---------|--------------|
410
- | `@biomejs/biome` | `npm i -D @biomejs/biome` | Linting + formatting |
470
+ | `@biomejs/biome` | `npm i -D @biomejs/biome` | Linting + formatting + auto-fix |
471
+ | `eslint` | `npm i -D eslint` | Linting with project-specific rules (requires config file) |
411
472
  | `oxlint` | `npm i -D oxlint` | Fast Rust-based JS/TS linting |
412
473
  | `knip` | `npm i -D knip` | Dead code / unused exports |
413
474
  | `jscpd` | `npm i -D jscpd` | Copy-paste detection |
414
475
  | `type-coverage` | `npm i -D type-coverage` | TypeScript `any` coverage % |
415
- | `@ast-grep/napi` | `npm i -D @ast-grep/napi` | Fast structural analysis (TS/JS) security rules inline, slop in booboo |
476
+ | `@ast-grep/napi` | `npm i -D @ast-grep/napi` | Fast structural analysis — 112 patterns (TS/JS/Python/Go/Rust security rules, slop detection) |
416
477
  | `@ast-grep/cli` | `npm i -D @ast-grep/cli` | Structural pattern matching (all languages) |
417
478
  | `typos-cli` | `cargo install typos-cli` | Spellcheck for Markdown |
418
479
 
@@ -428,6 +489,7 @@ pi-lens works out of the box for TypeScript/JavaScript. For full language suppor
428
489
  | Tool | Install | What it does |
429
490
  |------|---------|--------------|
430
491
  | `go` | [golang.org](https://golang.org) | Built-in `go vet` for static analysis |
492
+ | `golangci-lint` | [golangci-lint.run](https://golangci-lint.run) | Meta-linter (staticcheck, errcheck, gosimple, etc.) |
431
493
 
432
494
  ### Rust
433
495
 
@@ -435,6 +497,12 @@ pi-lens works out of the box for TypeScript/JavaScript. For full language suppor
435
497
  |------|---------|--------------|
436
498
  | `rust` + `clippy` | [rustup.rs](https://rustup.rs) | Linting via `cargo clippy` |
437
499
 
500
+ ### Ruby
501
+
502
+ | Tool | Install | What it does |
503
+ |------|---------|--------------|
504
+ | `rubocop` | `gem install rubocop` / Bundler | Ruby linting + formatting |
505
+
438
506
  ### Shell
439
507
 
440
508
  | Tool | Install | What it does |
@@ -443,6 +511,19 @@ pi-lens works out of the box for TypeScript/JavaScript. For full language suppor
443
511
 
444
512
  ---
445
513
 
514
+ ## Security Hardening
515
+
516
+ pi-lens follows npm security best practices for tool installation:
517
+
518
+ | Measure | Implementation |
519
+ |---------|---------------|
520
+ | **Post-install scripts** | `--ignore-scripts` by default; only allowlisted packages (`@biomejs/biome`, `@ast-grep/napi`, `esbuild`) can run scripts for native binary downloads |
521
+ | **npx behavior** | `--no` flag prevents silent downloads; fails fast if package not cached |
522
+ | **Tool resolution** | Local `node_modules/.bin/` preferred over global over npx |
523
+ | **Global install safety** | `resolvePackagePath()` uses `import.meta.url` instead of `process.cwd()` to locate built-in rules/grammars when installed globally |
524
+
525
+ ---
526
+
446
527
  ## Commands
447
528
 
448
529
  | Command | Description |
@@ -467,22 +548,28 @@ pi-lens works out of the box for TypeScript/JavaScript. For full language suppor
467
548
  | `--lens-lsp` | Use real Language Server Protocol servers instead of built-in type-checking |
468
549
  | `--lens-verbose` | Enable detailed console logging |
469
550
  | `--no-autoformat` | Disable automatic formatting (formatting is **enabled by default**) |
470
- | `--no-autofix` | Disable all auto-fixing (Biome safe fixes + Ruff autofix **enabled by default**). Unsafe fixes (e.g. removing unused vars) are never applied automatically — use `/lens-booboo` with explicit confirmation. |
551
+ | `--no-autofix` | Disable all auto-fixing (Biome safe fixes + Ruff + ESLint autofix **enabled by default**). Unsafe fixes are never applied automatically. |
471
552
  | `--no-autofix-biome` | Disable Biome auto-fix only |
472
553
  | `--no-autofix-ruff` | Disable Ruff auto-fix only |
554
+ | `--no-autofix-eslint` | Disable ESLint auto-fix only |
555
+ | `--no-eslint` | Skip ESLint linting |
473
556
  | `--no-oxlint` | Skip Oxlint linting |
474
557
  | `--no-shellcheck` | Skip shellcheck for shell scripts |
475
558
  | `--no-tests` | Disable automatic test running on file write |
476
559
  | `--no-madge` | Skip circular dependency checks |
477
560
  | `--no-ast-grep` | Skip ast-grep structural analysis |
561
+ | `--no-tree-sitter` | Skip tree-sitter structural analysis |
478
562
  | `--no-biome` | Skip Biome linting |
563
+ | `--no-ruff` | Skip Ruff linting |
479
564
  | `--no-lsp` | Skip TypeScript/Python type checking |
565
+ | `--no-similarity` | Skip pre-write structural similarity detection |
480
566
  | `--error-debt` | Track test regressions across sessions |
481
567
 
482
568
  **Recommended combinations:**
483
569
  ```bash
484
570
  pi # Default: auto-format, auto-fix, built-in type-checking
485
571
  pi --lens-lsp # LSP type-checking (31 languages)
572
+ pi --no-eslint # Skip ESLint on a Biome/OxLint project
486
573
  ```
487
574
 
488
575
  ---
@@ -12,6 +12,7 @@
12
12
  import * as fs from "node:fs";
13
13
  import * as path from "node:path";
14
14
  import { minimatch } from "minimatch";
15
+ import { resolvePackagePath } from "./package-root.js";
15
16
 
16
17
  // --- Types ---
17
18
 
@@ -91,6 +92,8 @@ export class ArchitectClient {
91
92
  // Try multiple possible locations for the default config
92
93
  const possibleDefaultPaths = [
93
94
  path.join(projectRoot, "default-architect.yaml"),
95
+ path.join(projectRoot, ".pi-lens", "default-architect.yaml"),
96
+ resolvePackagePath(import.meta.url, "default-architect.yaml"),
94
97
  path.join(projectRoot, "..", "default-architect.yaml"),
95
98
  path.join(process.cwd(), "default-architect.yaml"),
96
99
  ];
@@ -170,7 +173,7 @@ export class ArchitectClient {
170
173
 
171
174
  for (const check of rule.must_not) {
172
175
  // We use 'g' to find all occurrences and correctly report line numbers
173
- const regex = new RegExp(check.pattern, "gi");
176
+ const regex = new RegExp(check.pattern, "gim");
174
177
  let match: RegExpExecArray | null;
175
178
 
176
179
  // biome-ignore lint/suspicious/noAssignInExpressions: RegExp.exec iteration
@@ -286,7 +289,9 @@ export class ArchitectClient {
286
289
  ) {
287
290
  // Extract everything after "pattern:" and unquote
288
291
  const raw = trimmed.replace(/^-?\s*pattern:\s*/, "").trim();
289
- const unquoted = raw.replace(/^["']|["']$/g, "");
292
+ let unquoted = raw.replace(/^["']|["']$/g, "");
293
+ // Single-quoted YAML: '' is an escaped single-quote
294
+ if (raw.startsWith("'")) unquoted = unquoted.split("''").join("'");
290
295
  if (unquoted) {
291
296
  violation = { pattern: unquoted, message: "" };
292
297
  }