pi-lens 3.8.1 → 3.8.2

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/README.md CHANGED
@@ -1,706 +1,706 @@
1
- # pi-lens
2
-
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
-
5
- ## What pi-lens Does
6
-
7
- **For every file you edit:**
8
- 1. **Auto-formats** — Detects and runs formatters (Biome, Prettier, Ruff, gofmt, rustfmt, etc.)
9
- 2. **Type-checks** — TypeScript, Python, Go, Rust, Ruby (31 languages with `--lens-lsp`)
10
- 3. **Scans for secrets** — Blocks on hardcoded API keys, tokens, passwords
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.
20
- **Warnings** (complexity, code smells) go to `/lens-booboo` — run it to see them all.
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
-
24
- ## Quick Start
25
-
26
- ```bash
27
- # Install
28
- pi install npm:pi-lens
29
-
30
- # Standard mode (auto-formatting, type-checking, linting enabled by default)
31
- pi
32
-
33
- # Disable auto-formatting if needed
34
- pi --no-autoformat
35
-
36
- # Full LSP mode (31 language servers) — recommended for large/multi-language projects
37
- pi --lens-lsp
38
- ```
39
-
40
- ## Install
41
-
42
- ```bash
43
- pi install npm:pi-lens
44
- ```
45
-
46
- Or directly from git:
47
-
48
- ```bash
49
- pi install git:github.com/apmantza/pi-lens
50
- ```
51
-
52
- ---
53
-
54
- ## Features
55
-
56
- ### Auto-Formatting (Default Enabled)
57
-
58
- pi-lens **automatically formats** every file you write or edit. Formatters are auto-detected based on your project configuration.
59
-
60
- **Priority:** **Biome** is the default. **Prettier** runs only if Biome is not configured. This prevents race conditions and ensures consistent formatting.
61
-
62
- | Formatter | Languages | Detection | Installation | Role |
63
- |-----------|-----------|-----------|--------------|------|
64
- | **Biome** | TS/JS/JSON/CSS | `biome.json` or `@biomejs/biome` in devDependencies | Automatic | **Default** |
65
- | **Prettier** | TS/JS/JSON/CSS/Markdown | `.prettierrc` or `prettier` in devDependencies | Manual (`npm install -g prettier`) | Fallback |
66
- | **Ruff** | Python | `[tool.ruff]` in `pyproject.toml` | Automatic | **Default** |
67
- | **Black** | Python | `[tool.black]` in `pyproject.toml` | Manual (`pip install black`) | Fallback |
68
- | **gofmt** | Go | `go` binary available | Manual (included with Go SDK) | Default |
69
- | **rustfmt** | Rust | `rustfmt` binary available | Manual (included with Rust toolchain) | Default |
70
- | **zig fmt** | Zig | `zig` binary available | Manual (included with Zig SDK) | Default |
71
- | **dart format** | Dart | `dart` binary available | Manual (included with Dart SDK) | Default |
72
- | **shfmt** | Shell | `shfmt` binary available | Manual (download binary) | Default |
73
- | **mix format** | Elixir | `mix` binary available | Manual (included with Elixir) | Default |
74
-
75
- (*) = Auto-installed (no manual setup required)
76
-
77
- **How it works:**
78
- 1. Agent writes a file
79
- 2. pi-lens detects formatters based on config files/dependencies
80
- 3. Biome takes priority; Prettier runs only if Biome is not configured
81
- 4. FileTime tracking ensures safety (agents re-read if file changes externally)
82
-
83
- **Safety:** If a formatter changes the file, the agent is notified and must re-read before next edit — preventing stale content overwrites.
84
-
85
- **Disable:**
86
- ```bash
87
- pi --no-autoformat # Skip automatic formatting
88
- ```
89
-
90
- ---
91
-
92
- ### Auto-Linting (Default Enabled)
93
-
94
- pi-lens **automatically lints** every file you write or edit. Linters are auto-detected based on your project configuration.
95
-
96
- | Linter | Languages | Installation | Role | Priority |
97
- |--------|-----------|--------------|------|----------|
98
- | **Biome** | TS/JS/JSON/CSS | Automatic | **Default** | 10 |
99
- | **Ruff** | Python | Automatic | **Default** | 10 |
100
- | **ESLint** | TS/JS/Vue/Svelte | Automatic (with config) | Project-configured | 11 |
101
- | **oxlint** | TS/JS | Manual (`npm i -g oxlint`) | Fast alternative | 12 |
102
- | **golangci-lint** | Go | Manual (with config) | Go meta-linter | 13 |
103
- | **RuboCop** | Ruby | Manual / Bundler | Ruby standard | 15 |
104
- | **shellcheck** | Bash/sh/zsh/fish | Manual (`apt install shellcheck`) | Shell scripts | 20 |
105
-
106
- (*) = Auto-installed (no manual setup required)
107
-
108
- **How it works:**
109
- 1. Agent writes a file
110
- 2. pi-lens detects linters based on config files and file type
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)
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)`
115
-
116
- **Notes:**
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)
120
- - oxlint is a faster Rust-based alternative to ESLint
121
- - ESLint LSP only runs when `--lens-lsp` is enabled; dispatch runner runs in standard mode too
122
-
123
- ---
124
-
125
- ### LSP Support (NEW) — 31 Language Servers
126
-
127
- Enable full Language Server Protocol support with `--lens-lsp`:
128
-
129
- | Category | Languages |
130
- |----------|-----------|
131
- | **Core** | TypeScript, Python, Go, Rust, Ruby, PHP, C#, F#, Java, Kotlin |
132
- | **Native** | C/C++, Zig, Swift, Haskell, OCaml, Lua, Dart |
133
- | **Functional** | Elixir, Gleam, Clojure, Haskell |
134
- | **DevOps** | Terraform, Nix, Docker, Bash |
135
- | **Config** | YAML, JSON, Prisma |
136
- | **Web** | Vue, Svelte, CSS/SCSS/Sass/Less |
137
-
138
- **Auto-installation (8 tools):** TypeScript, Python, Biome, Ruff, and analysis tools (Madge, jscpd, ast-grep, Knip) auto-install on first use to `.pi-lens/tools/`. Other LSP servers require manual installation or are launched via `npx` when available.
139
-
140
- **Usage:**
141
- ```bash
142
- pi --lens-lsp # Enable LSP
143
- ```
144
-
145
- ### `pi` vs `pi --lens-lsp`
146
-
147
- | `pi` (Default) | `pi --lens-lsp` |
148
- |---------|----------------|-----------------|
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 |
152
- | **Secrets scan** | Blocks on hardcoded secrets | Same |
153
- | **Languages** | TypeScript, Python, Go, Rust, Ruby (built-in) | 31 languages via LSP |
154
- | **Python** | Ruff/pyright (built-in) | Pyright LSP |
155
- | **Go** | go-vet, golangci-lint | Full gopls |
156
- | **Rust** | rust-clippy | Full rust-analyzer |
157
-
158
- **Recommendation:** Use `pi` for TypeScript/Python projects. Use `pi --lens-lsp` for multi-language projects or when you need full language server features.
159
-
160
- See [docs/LSP_CONFIG.md](docs/LSP_CONFIG.md) for configuration options.
161
-
162
- ---
163
-
164
- ### On every write / edit
165
-
166
- Every file write/edit triggers multiple analysis phases:
167
-
168
- **Execution flow:**
169
- 1. **Secrets scan** (pre-flight) — Hardcoded secrets block immediately (non-runner check)
170
- 2. **LSP integration** (Phase 3, with `--lens-lsp`) — Real-time type errors from language servers
171
- 3. **Dispatch system** — Routes file to appropriate runners by `FileKind` (TS, Python, Go, Rust, Ruby, etc.)
172
- 4. **Runners execute** by priority (lower = earlier). See [Runners](#runners) section for full list.
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
175
-
176
- **Delta mode behavior:**
177
- - **First write:** All issues tracked and stored in baseline
178
- - **Subsequent edits:** Only **NEW** issues shown (pre-existing issues filtered out)
179
- - **Goal:** Don't spam agent with issues they didn't cause
180
-
181
- **Output shown inline:**
182
- ```
183
- STOP — 1 issue(s) must be fixed:
184
- L23: var total = sum(items); — use 'let' or 'const'
185
- ```
186
-
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.
198
-
199
- ---
200
-
201
- ### Runners
202
-
203
- pi-lens uses a **dispatcher-runner architecture** for extensible multi-language support. Runners are executed by priority (lower = earlier).
204
-
205
- | Runner | Language | Priority | Output | Description |
206
- |--------|----------|----------|--------|-------------|
207
- | **ts-lsp** | TypeScript | 5 | Blocking | TypeScript errors (hard stops) |
208
- | **pyright** | Python | 5 | Blocking | Python type errors (hard stops) |
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) |
212
- | **oxlint** | TS/JS | 12 | Warning | Fast Rust-based JS/TS linter |
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.) |
215
- | **type-safety** | TS | 20 | Mixed | Switch exhaustiveness (blocking), other (warning) |
216
- | **shellcheck** | Shell | 20 | Warning | Bash/sh/zsh/fish linting |
217
- | **python-slop** | Python | 25 | Warning | AI slop detection (~40 patterns) |
218
- | **spellcheck** | Markdown | 30 | Warning | Typo detection in docs |
219
- | **similarity** | TS | 35 | Warning | Semantic duplicate detection (≥90% structural similarity, pre-write check) |
220
- | **architect** | All | 40 | Warning | Architectural rule violations |
221
- | **go-vet** | Go | 50 | Warning | Go static analysis |
222
- | **golangci-lint** | Go | 51 | Warning | Go meta-linter (needs .golangci.yml) |
223
- | **rust-clippy** | Rust | 50 | Warning | Rust linting |
224
- | **rubocop** | Ruby | 52 | Warning | Ruby linting (supports bundle exec) |
225
-
226
- **Priority legend:**
227
- - **5** — Type checkers (blocking errors)
228
- - **10-15** — Linters and structural analysis
229
- - **20-30** — Specialized checks (safety, slop, spellcheck)
230
- - **35** — Metrics only (silent)
231
- - **40-50** — Language-specific and architectural
232
-
233
- **Output semantics:**
234
- - **Blocking** — Hard stop, must fix (type errors, secrets)
235
- - **Warning** — Shown in `/lens-booboo`, not inline (noise reduction)
236
- - **Silent** — Tracked in metrics only, never shown
237
-
238
- **Consolidated runners:** `ts-slop` merged into `ast-grep-napi` — CLI ast-grep used for full linter via `/lens-booboo`
239
-
240
- **Tree-sitter runner patterns** (priority 14, AST-based structural analysis, 7 languages, 45+ patterns):
241
-
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
245
-
246
- **TSX (2 patterns):**
247
- - **Error**: dangerously-set-inner-html
248
- - **Warning**: no-nested-links
249
-
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
261
-
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
265
-
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:**
269
- - `python-slop` runner (priority 25): ~40 patterns for Python code quality
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.
271
-
272
- ---
273
-
274
- ### Additional Safeguards
275
-
276
- Safeguards that run **before** the dispatch system:
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
-
299
- #### Secrets Scanning (Pre-flight)
300
-
301
- Runs on every file write/edit **before** any other checks. Scans for:
302
- - Stripe/OpenAI keys (`sk-*`)
303
- - GitHub tokens (`ghp_*`, `github_pat_*`)
304
- - AWS keys (`AKIA*`)
305
- - Slack tokens (`xoxb-*`, `xoxp-*`)
306
- - Private keys (`BEGIN PRIVATE KEY`)
307
- - Hardcoded passwords and API keys
308
-
309
- **Behavior:** Always blocking, always runs on all file types. Cannot be disabled — security takes precedence.
310
-
311
- #### Agent Behavior Warnings
312
-
313
- Inline heuristics to catch anti-patterns in real-time:
314
-
315
- **Blind Write Detection**
316
- - **Triggers:** Agent edits a file without reading it in the last 5 tool calls
317
- - **Warning:** `BLIND WRITE — editing 'file.ts' without reading in the last 5 tool calls.`
318
- - **Why:** Prevents edits based on stale assumptions
319
-
320
- **Thrashing Detection**
321
- - **Triggers:** 3+ consecutive identical tool calls within 30 seconds
322
- - **Warning:** `[!] THRASHING — 3 consecutive 'edit' calls with no other action.`
323
- - **Why:** Catches stuck loops where the agent repeats failed actions
324
-
325
- **Behavior:** Warnings appear inline but do **not** block execution.
326
-
327
- #### Custom ast-grep Rules
328
-
329
- Create your own structural rules in `.pi-lens/rules/`:
330
-
331
- ```yaml
332
- # .pi-lens/rules/no-console-prod.yml
333
- id: no-console-prod
334
- language: javascript
335
- rule:
336
- pattern: console.$METHOD($$$ARGS)
337
- message: "Remove console statements before production"
338
- severity: warning
339
- ```
340
-
341
- See [AST_GREP_RULES.md](AST_GREP_RULES.md) for full guide.
342
-
343
- ---
344
-
345
- ### At Session Start
346
-
347
- When pi starts a new session, pi-lens performs initialization scans to establish baselines and surface existing technical debt:
348
-
349
- **Initialization sequence:**
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):
359
- | Scan | Tool | Cached | Purpose |
360
- |------|------|--------|---------|
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 |
367
-
368
- **Error debt tracking** (with `--error-debt` flag):
369
- - If tests passed at end of previous session but fail now → **regression detected**
370
- - Blocks agent until tests pass again
371
-
372
- **Output:** Scan results appear in session startup notification
373
-
374
- ---
375
-
376
- ### Code Review
377
-
378
- ```
379
- /lens-booboo [path]
380
- ```
381
-
382
- Full codebase analysis with **10 tracked runners** producing a comprehensive report:
383
-
384
- | # | Runner | What it finds |
385
- |---|--------|---------------|
386
- | 1 | **ast-grep (design smells)** | Structural issues (empty catch, no-debugger, unchecked throws, etc.) |
387
- | 2 | **ast-grep (similar functions)** | Duplicate function patterns across files |
388
- | 3 | **semantic similarity (Amain)** | 57×72 matrix semantic clones (≥90% similarity) |
389
- | 4 | **complexity metrics** | Low MI, high cognitive complexity, AI slop indicators |
390
- | 5 | **TODO scanner** | TODO/FIXME annotations and tech debt markers (delta mode) |
391
- | 6 | **dead code (Knip)** | Unused exports, files, dependencies |
392
- | 7 | **duplicate code (jscpd)** | Copy-paste blocks with line/token counts |
393
- | 8 | **type coverage** | Percentage typed vs `any`, low-coverage files |
394
- | 9 | **circular deps (Madge)** | Import cycles and dependency chains (turn-end async) |
395
- | 10 | **architectural rules** | Layer violations, file size limits, path rules |
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
-
399
- **Output:**
400
- - **Terminal:** Progress `[1/10] runner...` with timing, summary with findings per runner
401
- - **JSON:** `.pi-lens/reviews/booboo-{timestamp}.json` (structured data for AI processing)
402
- - **Markdown:** `.pi-lens/reviews/booboo-{timestamp}.md` (human-readable report)
403
-
404
- **Usage:**
405
- ```bash
406
- /lens-booboo # Scan current directory
407
- /lens-booboo ./src # Scan specific path
408
- ```
409
-
410
- ---
411
-
412
- ### Test Runner
413
-
414
- **Auto-detected test runners:**
415
- | Runner | Config Files | Languages |
416
- |--------|--------------|-----------|
417
- | **Vitest** | `vitest.config.ts`, `vitest.config.js` | TypeScript, JavaScript |
418
- | **Jest** | `jest.config.js`, `jest.config.ts`, `package.json` (jest field) | TypeScript, JavaScript |
419
- | **Pytest** | `pytest.ini`, `setup.cfg`, `pyproject.toml` | Python |
420
-
421
- **Behavior:**
422
- - **On file write:** Detects corresponding test file and runs it
423
- - **Pattern matching:** `file.ts` → `file.test.ts` or `__tests__/file.test.ts`
424
- - **Output:** Inline pass/fail with failure details (shown with lint results)
425
- - **Flag:** Use `--no-tests` to disable automatic test running
426
-
427
- **Execution flow:**
428
- 1. Agent writes `src/utils.ts`
429
- 2. pi-lens finds `src/utils.test.ts` (or `__tests__/utils.test.ts`)
430
- 3. Runs only that test file (not full suite)
431
- 4. Results appear inline:
432
- ```
433
- [tests] 3 passed, 1 failed (42ms)
434
- ✓ should calculate total
435
- ✗ should handle empty array (expected 0, got undefined)
436
- ```
437
-
438
- **Why only corresponding tests?**
439
- Running the full suite on every edit would be too slow. Targeted testing gives immediate feedback for the code being edited.
440
-
441
- ---
442
-
443
- ### Complexity Metrics
444
-
445
- pi-lens tracks code quality metrics for every file:
446
-
447
- | Metric | Description | Threshold |
448
- |--------|-------------|-----------|
449
- | **Maintainability Index** | 0-100 composite score | >60 good <20 bad |
450
- | **Cognitive Complexity** | Mental effort to understand | >20 warn >50 bad |
451
- | **Cyclomatic Complexity** | Independent code paths | >10 warn >20 bad |
452
- | **Code Entropy** | Shannon entropy in bits | >4.0 warn >7.0 bad |
453
-
454
- **Commands:**
455
- - `/lens-tdi` — Technical Debt Index (0-100) with grades A-F
456
- - `/lens-booboo` — Full complexity table for all files
457
-
458
- See [docs/COMPLEXITY_METRICS.md](docs/COMPLEXITY_METRICS.md) for formulas and detailed calculations.
459
-
460
- ---
461
-
462
- ## Dependent Tools
463
-
464
- pi-lens works out of the box for TypeScript/JavaScript. For full language support, install these tools — **all are optional and gracefully skip if not installed**:
465
-
466
- ### JavaScript / TypeScript
467
-
468
- | Tool | Install | What it does |
469
- |------|---------|--------------|
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) |
472
- | `oxlint` | `npm i -D oxlint` | Fast Rust-based JS/TS linting |
473
- | `knip` | `npm i -D knip` | Dead code / unused exports |
474
- | `jscpd` | `npm i -D jscpd` | Copy-paste detection |
475
- | `type-coverage` | `npm i -D type-coverage` | TypeScript `any` coverage % |
476
- | `@ast-grep/napi` | `npm i -D @ast-grep/napi` | Fast structural analysis — 112 patterns (TS/JS/Python/Go/Rust security rules, slop detection) |
477
- | `@ast-grep/cli` | `npm i -D @ast-grep/cli` | Structural pattern matching (all languages) |
478
- | `typos-cli` | `cargo install typos-cli` | Spellcheck for Markdown |
479
-
480
- ### Python
481
-
482
- | Tool | Install | What it does |
483
- |------|---------|--------------|
484
- | `ruff` | `pip install ruff` | Linting + formatting |
485
- | `pyright` | `pip install pyright` | Type-checking (catches type errors) |
486
-
487
- ### Go
488
-
489
- | Tool | Install | What it does |
490
- |------|---------|--------------|
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.) |
493
-
494
- ### Rust
495
-
496
- | Tool | Install | What it does |
497
- |------|---------|--------------|
498
- | `rust` + `clippy` | [rustup.rs](https://rustup.rs) | Linting via `cargo clippy` |
499
-
500
- ### Ruby
501
-
502
- | Tool | Install | What it does |
503
- |------|---------|--------------|
504
- | `rubocop` | `gem install rubocop` / Bundler | Ruby linting + formatting |
505
-
506
- ### Shell
507
-
508
- | Tool | Install | What it does |
509
- |------|---------|--------------|
510
- | `shellcheck` | `apt install shellcheck` / `brew install shellcheck` | Shell script linting (bash/sh/zsh/fish) |
511
-
512
- ---
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
-
527
- ## Commands
528
-
529
- | Command | Description |
530
- |---------|-------------|
531
- | `/lens-booboo` | Full codebase review (10 analysis runners) |
532
- | `/lens-tdi` | Technical Debt Index and trends |
533
-
534
- ---
535
-
536
- ## Execution Modes
537
-
538
- | Mode | Command | What happens |
539
- |------|---------|--------------|
540
- | **Standard** (default) | `pi` | Auto-formatting, TS/Python type-checking, sequential execution |
541
- | **Full LSP** | `pi --lens-lsp` | Real LSP servers (31 languages), sequential execution |
542
-
543
-
544
- ### Flag Reference
545
-
546
- | Flag | Description |
547
- |------|-------------|
548
- | `--lens-lsp` | Use real Language Server Protocol servers instead of built-in type-checking |
549
- | `--lens-verbose` | Enable detailed console logging |
550
- | `--no-autoformat` | Disable automatic formatting (formatting is **enabled by default**) |
551
- | `--no-autofix` | Disable all auto-fixing (Biome safe fixes + Ruff + ESLint autofix **enabled by default**). Unsafe fixes are never applied automatically. |
552
- | `--no-autofix-biome` | Disable Biome auto-fix only |
553
- | `--no-autofix-ruff` | Disable Ruff auto-fix only |
554
- | `--no-autofix-eslint` | Disable ESLint auto-fix only |
555
- | `--no-eslint` | Skip ESLint linting |
556
- | `--no-oxlint` | Skip Oxlint linting |
557
- | `--no-shellcheck` | Skip shellcheck for shell scripts |
558
- | `--no-tests` | Disable automatic test running on file write |
559
- | `--no-madge` | Skip circular dependency checks |
560
- | `--no-ast-grep` | Skip ast-grep structural analysis |
561
- | `--no-tree-sitter` | Skip tree-sitter structural analysis |
562
- | `--no-biome` | Skip Biome linting |
563
- | `--no-ruff` | Skip Ruff linting |
564
- | `--no-lsp` | Skip TypeScript/Python type checking |
565
- | `--no-similarity` | Skip pre-write structural similarity detection |
566
- | `--error-debt` | Track test regressions across sessions |
567
-
568
- **Recommended combinations:**
569
- ```bash
570
- pi # Default: auto-format, auto-fix, built-in type-checking
571
- pi --lens-lsp # LSP type-checking (31 languages)
572
- pi --no-eslint # Skip ESLint on a Biome/OxLint project
573
- ```
574
-
575
- ---
576
-
577
- ## TypeScript LSP — tsconfig detection
578
-
579
- The LSP walks up from edited files to find `tsconfig.json`, using its `compilerOptions` (paths, strict settings, etc.). Falls back to sensible defaults if not found.
580
-
581
- ---
582
-
583
- ## Project Structure
584
-
585
- ```
586
- pi-lens/
587
- ├── clients/ # Lint tools, LSP clients, formatters
588
- ├── commands/ # /lens-booboo, /lens-format commands
589
- ├── docs/ # Documentation
590
- ├── rules/ # AST-grep rules
591
- ├── rust/ # Optional Rust core for performance acceleration
592
- │ ├── src/ # Rust source (pi-lens-core binary)
593
- │ └── Cargo.toml
594
- ├── skills/ # Built-in pi skills
595
- ├── index.ts # Main extension entry point
596
- └── package.json
597
- ```
598
-
599
- See source for detailed structure.
600
-
601
- ---
602
-
603
- ## Rust Core (Optional)
604
-
605
- pi-lens includes a **Rust performance core** (`pi-lens-core`) for CPU-intensive operations. It is entirely optional — all features fall back to the TypeScript implementation automatically if the binary is not available.
606
-
607
- **What it accelerates:**
608
- - **File scanning** — Uses ripgrep's `ignore` crate for fast, `.gitignore`-aware project scanning (~10× faster than glob)
609
- - **Similarity detection** — Parallel 57×72 state-matrix computation and index querying
610
- - **Tree-sitter queries** — Runs TypeScript and Rust AST queries directly from the binary
611
-
612
- **Status:** Does not work out of the box after `npm install`. The source is included in the package so you can build it yourself if you have Rust installed.
613
-
614
- **Build the binary (one-time):**
615
- ```bash
616
- # Requires Rust toolchain — https://rustup.rs
617
- npm run rust:build # release build (recommended)
618
- npm run rust:build:debug # debug build
619
- ```
620
-
621
- Once built, pi-lens will automatically use the Rust binary and fall back to TypeScript if it is absent, outdated, or fails.
622
-
623
- **Verify the binary is being used:**
624
- ```bash
625
- node -e "import('./clients/native-rust-client.js').then(m => console.log('available:', m.getNativeRustCoreClient(true).isAvailable()))"
626
- ```
627
-
628
- **Run integration tests** (requires debug binary):
629
- ```bash
630
- npm run rust:build:debug
631
- npm run rust:test:integration # 37 assertions
632
- npm run rust:test # Rust unit tests
633
- ```
634
-
635
- ---
636
-
637
- ## Skills
638
-
639
- pi-lens includes two built-in skills that guide the LLM on when to use specific tools:
640
-
641
- ### ast-grep
642
-
643
- **Purpose:** Guide AST-aware pattern matching for semantic code search/replace.
644
-
645
- **When to load:** Use `/skill:ast-grep` when performing structural code searches (finding function calls, class methods, imports) or replacements across files.
646
-
647
- **Key guidance:**
648
- - Use `$VAR` for single nodes, `$$$` for multiple
649
- - Patterns must be **complete valid code** (not fragments)
650
- - **Workflow:** Search → Dry-run (`apply: false`) → Apply (`apply: true`)
651
- - **Error "Multiple AST nodes":** Use metavariables like `it($TEST)` not raw text like `it"test"`
652
-
653
- ```typescript
654
- // GOOD: Complete code with metavariables
655
- ast_grep_search
656
- pattern: "console.log($MSG)"
657
- lang: typescript
658
- paths: ["src/"]
659
-
660
- // BAD: BAD: Incomplete pattern
661
- pattern: "console.log(" // Missing args/body
662
- ```
663
-
664
- ### lsp-navigation
665
-
666
- **Purpose:** Guide code intelligence via Language Server Protocol.
667
-
668
- **When to load:** Use `/skill:lsp-navigation` for understanding code structure — definitions, references, types, call hierarchy.
669
-
670
- **Key guidance:**
671
- - **LSP is PRIMARY** for code intelligence — NOT grep/glob/ast-grep
672
- - Requires `--lens-lsp` flag
673
- - Call hierarchy: `prepareCallHierarchy` → `incomingCalls`/`outgoingCalls`
674
-
675
- | Task | Use LSP | Use Other |
676
- |------|---------|-----------|
677
- | "Where is this defined?" | `definition` | — |
678
- | "Find all usages" | `references` | — |
679
- | "What type is this?" | `hover` | — |
680
- | "Who calls this function?" | `prepareCallHierarchy` → `incomingCalls` | — |
681
- | Find patterns (console.log) | — | `ast_grep_search` |
682
- | Find TODO comments | — | `grep` |
683
-
684
- ```typescript
685
- // Code intelligence → LSP
686
- lsp_navigation
687
- operation: "references"
688
- filePath: "src/utils.ts"
689
- line: 42
690
- character: 10
691
-
692
- // BAD: Don't use LSP for text patterns
693
- pattern: "TODO" // Use grep instead
694
- ```
695
-
696
- ---
697
-
698
- ## Changelog
699
-
700
- See [CHANGELOG.md](CHANGELOG.md) for full history.
701
-
702
- ---
703
-
704
- ## License
705
-
706
- MIT
1
+ # pi-lens
2
+
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
+
5
+ ## What pi-lens Does
6
+
7
+ **For every file you edit:**
8
+ 1. **Auto-formats** — Detects and runs formatters (Biome, Prettier, Ruff, gofmt, rustfmt, etc.)
9
+ 2. **Type-checks** — TypeScript, Python, Go, Rust, Ruby (31 languages with `--lens-lsp`)
10
+ 3. **Scans for secrets** — Blocks on hardcoded API keys, tokens, passwords
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.
20
+ **Warnings** (complexity, code smells) go to `/lens-booboo` — run it to see them all.
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
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ # Install
28
+ pi install npm:pi-lens
29
+
30
+ # Standard mode (auto-formatting, type-checking, linting enabled by default)
31
+ pi
32
+
33
+ # Disable auto-formatting if needed
34
+ pi --no-autoformat
35
+
36
+ # Full LSP mode (31 language servers) — recommended for large/multi-language projects
37
+ pi --lens-lsp
38
+ ```
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pi install npm:pi-lens
44
+ ```
45
+
46
+ Or directly from git:
47
+
48
+ ```bash
49
+ pi install git:github.com/apmantza/pi-lens
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Features
55
+
56
+ ### Auto-Formatting (Default Enabled)
57
+
58
+ pi-lens **automatically formats** every file you write or edit. Formatters are auto-detected based on your project configuration.
59
+
60
+ **Priority:** **Biome** is the default. **Prettier** runs only if Biome is not configured. This prevents race conditions and ensures consistent formatting.
61
+
62
+ | Formatter | Languages | Detection | Installation | Role |
63
+ |-----------|-----------|-----------|--------------|------|
64
+ | **Biome** | TS/JS/JSON/CSS | `biome.json` or `@biomejs/biome` in devDependencies | Automatic | **Default** |
65
+ | **Prettier** | TS/JS/JSON/CSS/Markdown | `.prettierrc` or `prettier` in devDependencies | Manual (`npm install -g prettier`) | Fallback |
66
+ | **Ruff** | Python | `[tool.ruff]` in `pyproject.toml` | Automatic | **Default** |
67
+ | **Black** | Python | `[tool.black]` in `pyproject.toml` | Manual (`pip install black`) | Fallback |
68
+ | **gofmt** | Go | `go` binary available | Manual (included with Go SDK) | Default |
69
+ | **rustfmt** | Rust | `rustfmt` binary available | Manual (included with Rust toolchain) | Default |
70
+ | **zig fmt** | Zig | `zig` binary available | Manual (included with Zig SDK) | Default |
71
+ | **dart format** | Dart | `dart` binary available | Manual (included with Dart SDK) | Default |
72
+ | **shfmt** | Shell | `shfmt` binary available | Manual (download binary) | Default |
73
+ | **mix format** | Elixir | `mix` binary available | Manual (included with Elixir) | Default |
74
+
75
+ (*) = Auto-installed (no manual setup required)
76
+
77
+ **How it works:**
78
+ 1. Agent writes a file
79
+ 2. pi-lens detects formatters based on config files/dependencies
80
+ 3. Biome takes priority; Prettier runs only if Biome is not configured
81
+ 4. FileTime tracking ensures safety (agents re-read if file changes externally)
82
+
83
+ **Safety:** If a formatter changes the file, the agent is notified and must re-read before next edit — preventing stale content overwrites.
84
+
85
+ **Disable:**
86
+ ```bash
87
+ pi --no-autoformat # Skip automatic formatting
88
+ ```
89
+
90
+ ---
91
+
92
+ ### Auto-Linting (Default Enabled)
93
+
94
+ pi-lens **automatically lints** every file you write or edit. Linters are auto-detected based on your project configuration.
95
+
96
+ | Linter | Languages | Installation | Role | Priority |
97
+ |--------|-----------|--------------|------|----------|
98
+ | **Biome** | TS/JS/JSON/CSS | Automatic | **Default** | 10 |
99
+ | **Ruff** | Python | Automatic | **Default** | 10 |
100
+ | **ESLint** | TS/JS/Vue/Svelte | Automatic (with config) | Project-configured | 11 |
101
+ | **oxlint** | TS/JS | Manual (`npm i -g oxlint`) | Fast alternative | 12 |
102
+ | **golangci-lint** | Go | Manual (with config) | Go meta-linter | 13 |
103
+ | **RuboCop** | Ruby | Manual / Bundler | Ruby standard | 15 |
104
+ | **shellcheck** | Bash/sh/zsh/fish | Manual (`apt install shellcheck`) | Shell scripts | 20 |
105
+
106
+ (*) = Auto-installed (no manual setup required)
107
+
108
+ **How it works:**
109
+ 1. Agent writes a file
110
+ 2. pi-lens detects linters based on config files and file type
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)
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)`
115
+
116
+ **Notes:**
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)
120
+ - oxlint is a faster Rust-based alternative to ESLint
121
+ - ESLint LSP only runs when `--lens-lsp` is enabled; dispatch runner runs in standard mode too
122
+
123
+ ---
124
+
125
+ ### LSP Support (NEW) — 31 Language Servers
126
+
127
+ Enable full Language Server Protocol support with `--lens-lsp`:
128
+
129
+ | Category | Languages |
130
+ |----------|-----------|
131
+ | **Core** | TypeScript, Python, Go, Rust, Ruby, PHP, C#, F#, Java, Kotlin |
132
+ | **Native** | C/C++, Zig, Swift, Haskell, OCaml, Lua, Dart |
133
+ | **Functional** | Elixir, Gleam, Clojure, Haskell |
134
+ | **DevOps** | Terraform, Nix, Docker, Bash |
135
+ | **Config** | YAML, JSON, Prisma |
136
+ | **Web** | Vue, Svelte, CSS/SCSS/Sass/Less |
137
+
138
+ **Auto-installation (8 tools):** TypeScript, Python, Biome, Ruff, and analysis tools (Madge, jscpd, ast-grep, Knip) auto-install on first use to `.pi-lens/tools/`. Other LSP servers require manual installation or are launched via `npx` when available.
139
+
140
+ **Usage:**
141
+ ```bash
142
+ pi --lens-lsp # Enable LSP
143
+ ```
144
+
145
+ ### `pi` vs `pi --lens-lsp`
146
+
147
+ | `pi` (Default) | `pi --lens-lsp` |
148
+ |---------|----------------|-----------------|
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 |
152
+ | **Secrets scan** | Blocks on hardcoded secrets | Same |
153
+ | **Languages** | TypeScript, Python, Go, Rust, Ruby (built-in) | 31 languages via LSP |
154
+ | **Python** | Ruff/pyright (built-in) | Pyright LSP |
155
+ | **Go** | go-vet, golangci-lint | Full gopls |
156
+ | **Rust** | rust-clippy | Full rust-analyzer |
157
+
158
+ **Recommendation:** Use `pi` for TypeScript/Python projects. Use `pi --lens-lsp` for multi-language projects or when you need full language server features.
159
+
160
+ See [docs/LSP_CONFIG.md](docs/LSP_CONFIG.md) for configuration options.
161
+
162
+ ---
163
+
164
+ ### On every write / edit
165
+
166
+ Every file write/edit triggers multiple analysis phases:
167
+
168
+ **Execution flow:**
169
+ 1. **Secrets scan** (pre-flight) — Hardcoded secrets block immediately (non-runner check)
170
+ 2. **LSP integration** (Phase 3, with `--lens-lsp`) — Real-time type errors from language servers
171
+ 3. **Dispatch system** — Routes file to appropriate runners by `FileKind` (TS, Python, Go, Rust, Ruby, etc.)
172
+ 4. **Runners execute** by priority (lower = earlier). See [Runners](#runners) section for full list.
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
175
+
176
+ **Delta mode behavior:**
177
+ - **First write:** All issues tracked and stored in baseline
178
+ - **Subsequent edits:** Only **NEW** issues shown (pre-existing issues filtered out)
179
+ - **Goal:** Don't spam agent with issues they didn't cause
180
+
181
+ **Output shown inline:**
182
+ ```
183
+ STOP — 1 issue(s) must be fixed:
184
+ L23: var total = sum(items); — use 'let' or 'const'
185
+ ```
186
+
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.
198
+
199
+ ---
200
+
201
+ ### Runners
202
+
203
+ pi-lens uses a **dispatcher-runner architecture** for extensible multi-language support. Runners are executed by priority (lower = earlier).
204
+
205
+ | Runner | Language | Priority | Output | Description |
206
+ |--------|----------|----------|--------|-------------|
207
+ | **ts-lsp** | TypeScript | 5 | Blocking | TypeScript errors (hard stops) |
208
+ | **pyright** | Python | 5 | Blocking | Python type errors (hard stops) |
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) |
212
+ | **oxlint** | TS/JS | 12 | Warning | Fast Rust-based JS/TS linter |
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.) |
215
+ | **type-safety** | TS | 20 | Mixed | Switch exhaustiveness (blocking), other (warning) |
216
+ | **shellcheck** | Shell | 20 | Warning | Bash/sh/zsh/fish linting |
217
+ | **python-slop** | Python | 25 | Warning | AI slop detection (~40 patterns) |
218
+ | **spellcheck** | Markdown | 30 | Warning | Typo detection in docs |
219
+ | **similarity** | TS | 35 | Warning | Semantic duplicate detection (≥90% structural similarity, pre-write check) |
220
+ | **architect** | All | 40 | Warning | Architectural rule violations |
221
+ | **go-vet** | Go | 50 | Warning | Go static analysis |
222
+ | **golangci-lint** | Go | 51 | Warning | Go meta-linter (needs .golangci.yml) |
223
+ | **rust-clippy** | Rust | 50 | Warning | Rust linting |
224
+ | **rubocop** | Ruby | 52 | Warning | Ruby linting (supports bundle exec) |
225
+
226
+ **Priority legend:**
227
+ - **5** — Type checkers (blocking errors)
228
+ - **10-15** — Linters and structural analysis
229
+ - **20-30** — Specialized checks (safety, slop, spellcheck)
230
+ - **35** — Metrics only (silent)
231
+ - **40-50** — Language-specific and architectural
232
+
233
+ **Output semantics:**
234
+ - **Blocking** — Hard stop, must fix (type errors, secrets)
235
+ - **Warning** — Shown in `/lens-booboo`, not inline (noise reduction)
236
+ - **Silent** — Tracked in metrics only, never shown
237
+
238
+ **Consolidated runners:** `ts-slop` merged into `ast-grep-napi` — CLI ast-grep used for full linter via `/lens-booboo`
239
+
240
+ **Tree-sitter runner patterns** (priority 14, AST-based structural analysis, 7 languages, 45+ patterns):
241
+
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
245
+
246
+ **TSX (2 patterns):**
247
+ - **Error**: dangerously-set-inner-html
248
+ - **Warning**: no-nested-links
249
+
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
261
+
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
265
+
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:**
269
+ - `python-slop` runner (priority 25): ~40 patterns for Python code quality
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.
271
+
272
+ ---
273
+
274
+ ### Additional Safeguards
275
+
276
+ Safeguards that run **before** the dispatch system:
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
+
299
+ #### Secrets Scanning (Pre-flight)
300
+
301
+ Runs on every file write/edit **before** any other checks. Scans for:
302
+ - Stripe/OpenAI keys (`sk-*`)
303
+ - GitHub tokens (`ghp_*`, `github_pat_*`)
304
+ - AWS keys (`AKIA*`)
305
+ - Slack tokens (`xoxb-*`, `xoxp-*`)
306
+ - Private keys (`BEGIN PRIVATE KEY`)
307
+ - Hardcoded passwords and API keys
308
+
309
+ **Behavior:** Always blocking, always runs on all file types. Cannot be disabled — security takes precedence.
310
+
311
+ #### Agent Behavior Warnings
312
+
313
+ Inline heuristics to catch anti-patterns in real-time:
314
+
315
+ **Blind Write Detection**
316
+ - **Triggers:** Agent edits a file without reading it in the last 5 tool calls
317
+ - **Warning:** `BLIND WRITE — editing 'file.ts' without reading in the last 5 tool calls.`
318
+ - **Why:** Prevents edits based on stale assumptions
319
+
320
+ **Thrashing Detection**
321
+ - **Triggers:** 3+ consecutive identical tool calls within 30 seconds
322
+ - **Warning:** `[!] THRASHING — 3 consecutive 'edit' calls with no other action.`
323
+ - **Why:** Catches stuck loops where the agent repeats failed actions
324
+
325
+ **Behavior:** Warnings appear inline but do **not** block execution.
326
+
327
+ #### Custom ast-grep Rules
328
+
329
+ Create your own structural rules in `.pi-lens/rules/`:
330
+
331
+ ```yaml
332
+ # .pi-lens/rules/no-console-prod.yml
333
+ id: no-console-prod
334
+ language: javascript
335
+ rule:
336
+ pattern: console.$METHOD($$$ARGS)
337
+ message: "Remove console statements before production"
338
+ severity: warning
339
+ ```
340
+
341
+ See [AST_GREP_RULES.md](AST_GREP_RULES.md) for full guide.
342
+
343
+ ---
344
+
345
+ ### At Session Start
346
+
347
+ When pi starts a new session, pi-lens performs initialization scans to establish baselines and surface existing technical debt:
348
+
349
+ **Initialization sequence:**
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):
359
+ | Scan | Tool | Cached | Purpose |
360
+ |------|------|--------|---------|
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 |
367
+
368
+ **Error debt tracking** (with `--error-debt` flag):
369
+ - If tests passed at end of previous session but fail now → **regression detected**
370
+ - Blocks agent until tests pass again
371
+
372
+ **Output:** Scan results appear in session startup notification
373
+
374
+ ---
375
+
376
+ ### Code Review
377
+
378
+ ```
379
+ /lens-booboo [path]
380
+ ```
381
+
382
+ Full codebase analysis with **10 tracked runners** producing a comprehensive report:
383
+
384
+ | # | Runner | What it finds |
385
+ |---|--------|---------------|
386
+ | 1 | **ast-grep (design smells)** | Structural issues (empty catch, no-debugger, unchecked throws, etc.) |
387
+ | 2 | **ast-grep (similar functions)** | Duplicate function patterns across files |
388
+ | 3 | **semantic similarity (Amain)** | 57×72 matrix semantic clones (≥90% similarity) |
389
+ | 4 | **complexity metrics** | Low MI, high cognitive complexity, AI slop indicators |
390
+ | 5 | **TODO scanner** | TODO/FIXME annotations and tech debt markers (delta mode) |
391
+ | 6 | **dead code (Knip)** | Unused exports, files, dependencies |
392
+ | 7 | **duplicate code (jscpd)** | Copy-paste blocks with line/token counts |
393
+ | 8 | **type coverage** | Percentage typed vs `any`, low-coverage files |
394
+ | 9 | **circular deps (Madge)** | Import cycles and dependency chains (turn-end async) |
395
+ | 10 | **architectural rules** | Layer violations, file size limits, path rules |
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
+
399
+ **Output:**
400
+ - **Terminal:** Progress `[1/10] runner...` with timing, summary with findings per runner
401
+ - **JSON:** `.pi-lens/reviews/booboo-{timestamp}.json` (structured data for AI processing)
402
+ - **Markdown:** `.pi-lens/reviews/booboo-{timestamp}.md` (human-readable report)
403
+
404
+ **Usage:**
405
+ ```bash
406
+ /lens-booboo # Scan current directory
407
+ /lens-booboo ./src # Scan specific path
408
+ ```
409
+
410
+ ---
411
+
412
+ ### Test Runner
413
+
414
+ **Auto-detected test runners:**
415
+ | Runner | Config Files | Languages |
416
+ |--------|--------------|-----------|
417
+ | **Vitest** | `vitest.config.ts`, `vitest.config.js` | TypeScript, JavaScript |
418
+ | **Jest** | `jest.config.js`, `jest.config.ts`, `package.json` (jest field) | TypeScript, JavaScript |
419
+ | **Pytest** | `pytest.ini`, `setup.cfg`, `pyproject.toml` | Python |
420
+
421
+ **Behavior:**
422
+ - **On file write:** Detects corresponding test file and runs it
423
+ - **Pattern matching:** `file.ts` → `file.test.ts` or `__tests__/file.test.ts`
424
+ - **Output:** Inline pass/fail with failure details (shown with lint results)
425
+ - **Flag:** Use `--no-tests` to disable automatic test running
426
+
427
+ **Execution flow:**
428
+ 1. Agent writes `src/utils.ts`
429
+ 2. pi-lens finds `src/utils.test.ts` (or `__tests__/utils.test.ts`)
430
+ 3. Runs only that test file (not full suite)
431
+ 4. Results appear inline:
432
+ ```
433
+ [tests] 3 passed, 1 failed (42ms)
434
+ ✓ should calculate total
435
+ ✗ should handle empty array (expected 0, got undefined)
436
+ ```
437
+
438
+ **Why only corresponding tests?**
439
+ Running the full suite on every edit would be too slow. Targeted testing gives immediate feedback for the code being edited.
440
+
441
+ ---
442
+
443
+ ### Complexity Metrics
444
+
445
+ pi-lens tracks code quality metrics for every file:
446
+
447
+ | Metric | Description | Threshold |
448
+ |--------|-------------|-----------|
449
+ | **Maintainability Index** | 0-100 composite score | >60 good <20 bad |
450
+ | **Cognitive Complexity** | Mental effort to understand | >20 warn >50 bad |
451
+ | **Cyclomatic Complexity** | Independent code paths | >10 warn >20 bad |
452
+ | **Code Entropy** | Shannon entropy in bits | >4.0 warn >7.0 bad |
453
+
454
+ **Commands:**
455
+ - `/lens-tdi` — Technical Debt Index (0-100) with grades A-F
456
+ - `/lens-booboo` — Full complexity table for all files
457
+
458
+ See [docs/COMPLEXITY_METRICS.md](docs/COMPLEXITY_METRICS.md) for formulas and detailed calculations.
459
+
460
+ ---
461
+
462
+ ## Dependent Tools
463
+
464
+ pi-lens works out of the box for TypeScript/JavaScript. For full language support, install these tools — **all are optional and gracefully skip if not installed**:
465
+
466
+ ### JavaScript / TypeScript
467
+
468
+ | Tool | Install | What it does |
469
+ |------|---------|--------------|
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) |
472
+ | `oxlint` | `npm i -D oxlint` | Fast Rust-based JS/TS linting |
473
+ | `knip` | `npm i -D knip` | Dead code / unused exports |
474
+ | `jscpd` | `npm i -D jscpd` | Copy-paste detection |
475
+ | `type-coverage` | `npm i -D type-coverage` | TypeScript `any` coverage % |
476
+ | `@ast-grep/napi` | `npm i -D @ast-grep/napi` | Fast structural analysis — 112 patterns (TS/JS/Python/Go/Rust security rules, slop detection) |
477
+ | `@ast-grep/cli` | `npm i -D @ast-grep/cli` | Structural pattern matching (all languages) |
478
+ | `typos-cli` | `cargo install typos-cli` | Spellcheck for Markdown |
479
+
480
+ ### Python
481
+
482
+ | Tool | Install | What it does |
483
+ |------|---------|--------------|
484
+ | `ruff` | `pip install ruff` | Linting + formatting |
485
+ | `pyright` | `pip install pyright` | Type-checking (catches type errors) |
486
+
487
+ ### Go
488
+
489
+ | Tool | Install | What it does |
490
+ |------|---------|--------------|
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.) |
493
+
494
+ ### Rust
495
+
496
+ | Tool | Install | What it does |
497
+ |------|---------|--------------|
498
+ | `rust` + `clippy` | [rustup.rs](https://rustup.rs) | Linting via `cargo clippy` |
499
+
500
+ ### Ruby
501
+
502
+ | Tool | Install | What it does |
503
+ |------|---------|--------------|
504
+ | `rubocop` | `gem install rubocop` / Bundler | Ruby linting + formatting |
505
+
506
+ ### Shell
507
+
508
+ | Tool | Install | What it does |
509
+ |------|---------|--------------|
510
+ | `shellcheck` | `apt install shellcheck` / `brew install shellcheck` | Shell script linting (bash/sh/zsh/fish) |
511
+
512
+ ---
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
+
527
+ ## Commands
528
+
529
+ | Command | Description |
530
+ |---------|-------------|
531
+ | `/lens-booboo` | Full codebase review (10 analysis runners) |
532
+ | `/lens-tdi` | Technical Debt Index and trends |
533
+
534
+ ---
535
+
536
+ ## Execution Modes
537
+
538
+ | Mode | Command | What happens |
539
+ |------|---------|--------------|
540
+ | **Standard** (default) | `pi` | Auto-formatting, TS/Python type-checking, sequential execution |
541
+ | **Full LSP** | `pi --lens-lsp` | Real LSP servers (31 languages), sequential execution |
542
+
543
+
544
+ ### Flag Reference
545
+
546
+ | Flag | Description |
547
+ |------|-------------|
548
+ | `--lens-lsp` | Use real Language Server Protocol servers instead of built-in type-checking |
549
+ | `--lens-verbose` | Enable detailed console logging |
550
+ | `--no-autoformat` | Disable automatic formatting (formatting is **enabled by default**) |
551
+ | `--no-autofix` | Disable all auto-fixing (Biome safe fixes + Ruff + ESLint autofix **enabled by default**). Unsafe fixes are never applied automatically. |
552
+ | `--no-autofix-biome` | Disable Biome auto-fix only |
553
+ | `--no-autofix-ruff` | Disable Ruff auto-fix only |
554
+ | `--no-autofix-eslint` | Disable ESLint auto-fix only |
555
+ | `--no-eslint` | Skip ESLint linting |
556
+ | `--no-oxlint` | Skip Oxlint linting |
557
+ | `--no-shellcheck` | Skip shellcheck for shell scripts |
558
+ | `--no-tests` | Disable automatic test running on file write |
559
+ | `--no-madge` | Skip circular dependency checks |
560
+ | `--no-ast-grep` | Skip ast-grep structural analysis |
561
+ | `--no-tree-sitter` | Skip tree-sitter structural analysis |
562
+ | `--no-biome` | Skip Biome linting |
563
+ | `--no-ruff` | Skip Ruff linting |
564
+ | `--no-lsp` | Skip TypeScript/Python type checking |
565
+ | `--no-similarity` | Skip pre-write structural similarity detection |
566
+ | `--error-debt` | Track test regressions across sessions |
567
+
568
+ **Recommended combinations:**
569
+ ```bash
570
+ pi # Default: auto-format, auto-fix, built-in type-checking
571
+ pi --lens-lsp # LSP type-checking (31 languages)
572
+ pi --no-eslint # Skip ESLint on a Biome/OxLint project
573
+ ```
574
+
575
+ ---
576
+
577
+ ## TypeScript LSP — tsconfig detection
578
+
579
+ The LSP walks up from edited files to find `tsconfig.json`, using its `compilerOptions` (paths, strict settings, etc.). Falls back to sensible defaults if not found.
580
+
581
+ ---
582
+
583
+ ## Project Structure
584
+
585
+ ```
586
+ pi-lens/
587
+ ├── clients/ # Lint tools, LSP clients, formatters
588
+ ├── commands/ # /lens-booboo, /lens-format commands
589
+ ├── docs/ # Documentation
590
+ ├── rules/ # AST-grep rules
591
+ ├── rust/ # Optional Rust core for performance acceleration
592
+ │ ├── src/ # Rust source (pi-lens-core binary)
593
+ │ └── Cargo.toml
594
+ ├── skills/ # Built-in pi skills
595
+ ├── index.ts # Main extension entry point
596
+ └── package.json
597
+ ```
598
+
599
+ See source for detailed structure.
600
+
601
+ ---
602
+
603
+ ## Rust Core (Optional)
604
+
605
+ pi-lens includes a **Rust performance core** (`pi-lens-core`) for CPU-intensive operations. It is entirely optional — all features fall back to the TypeScript implementation automatically if the binary is not available.
606
+
607
+ **What it accelerates:**
608
+ - **File scanning** — Uses ripgrep's `ignore` crate for fast, `.gitignore`-aware project scanning (~10× faster than glob)
609
+ - **Similarity detection** — Parallel 57×72 state-matrix computation and index querying
610
+ - **Tree-sitter queries** — Runs TypeScript and Rust AST queries directly from the binary
611
+
612
+ **Status:** Does not work out of the box after `npm install`. The source is included in the package so you can build it yourself if you have Rust installed.
613
+
614
+ **Build the binary (one-time):**
615
+ ```bash
616
+ # Requires Rust toolchain — https://rustup.rs
617
+ npm run rust:build # release build (recommended)
618
+ npm run rust:build:debug # debug build
619
+ ```
620
+
621
+ Once built, pi-lens will automatically use the Rust binary and fall back to TypeScript if it is absent, outdated, or fails.
622
+
623
+ **Verify the binary is being used:**
624
+ ```bash
625
+ node -e "import('./clients/native-rust-client.js').then(m => console.log('available:', m.getNativeRustCoreClient(true).isAvailable()))"
626
+ ```
627
+
628
+ **Run integration tests** (requires debug binary):
629
+ ```bash
630
+ npm run rust:build:debug
631
+ npm run rust:test:integration # 37 assertions
632
+ npm run rust:test # Rust unit tests
633
+ ```
634
+
635
+ ---
636
+
637
+ ## Skills
638
+
639
+ pi-lens includes two built-in skills that guide the LLM on when to use specific tools:
640
+
641
+ ### ast-grep
642
+
643
+ **Purpose:** Guide AST-aware pattern matching for semantic code search/replace.
644
+
645
+ **When to load:** Use `/skill:ast-grep` when performing structural code searches (finding function calls, class methods, imports) or replacements across files.
646
+
647
+ **Key guidance:**
648
+ - Use `$VAR` for single nodes, `$$$` for multiple
649
+ - Patterns must be **complete valid code** (not fragments)
650
+ - **Workflow:** Search → Dry-run (`apply: false`) → Apply (`apply: true`)
651
+ - **Error "Multiple AST nodes":** Use metavariables like `it($TEST)` not raw text like `it"test"`
652
+
653
+ ```typescript
654
+ // GOOD: Complete code with metavariables
655
+ ast_grep_search
656
+ pattern: "console.log($MSG)"
657
+ lang: typescript
658
+ paths: ["src/"]
659
+
660
+ // BAD: BAD: Incomplete pattern
661
+ pattern: "console.log(" // Missing args/body
662
+ ```
663
+
664
+ ### lsp-navigation
665
+
666
+ **Purpose:** Guide code intelligence via Language Server Protocol.
667
+
668
+ **When to load:** Use `/skill:lsp-navigation` for understanding code structure — definitions, references, types, call hierarchy.
669
+
670
+ **Key guidance:**
671
+ - **LSP is PRIMARY** for code intelligence — NOT grep/glob/ast-grep
672
+ - Requires `--lens-lsp` flag
673
+ - Call hierarchy: `prepareCallHierarchy` → `incomingCalls`/`outgoingCalls`
674
+
675
+ | Task | Use LSP | Use Other |
676
+ |------|---------|-----------|
677
+ | "Where is this defined?" | `definition` | — |
678
+ | "Find all usages" | `references` | — |
679
+ | "What type is this?" | `hover` | — |
680
+ | "Who calls this function?" | `prepareCallHierarchy` → `incomingCalls` | — |
681
+ | Find patterns (console.log) | — | `ast_grep_search` |
682
+ | Find TODO comments | — | `grep` |
683
+
684
+ ```typescript
685
+ // Code intelligence → LSP
686
+ lsp_navigation
687
+ operation: "references"
688
+ filePath: "src/utils.ts"
689
+ line: 42
690
+ character: 10
691
+
692
+ // BAD: Don't use LSP for text patterns
693
+ pattern: "TODO" // Use grep instead
694
+ ```
695
+
696
+ ---
697
+
698
+ ## Changelog
699
+
700
+ See [CHANGELOG.md](CHANGELOG.md) for full history.
701
+
702
+ ---
703
+
704
+ ## License
705
+
706
+ MIT