pi-lens 3.7.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/README.md +706 -619
  3. package/clients/architect-client.ts +7 -2
  4. package/clients/ast-grep-client.ts +7 -1
  5. package/clients/dispatch/plan.ts +10 -4
  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/python-slop.ts +17 -7
  11. package/clients/dispatch/runners/ruff.ts +4 -4
  12. package/clients/dispatch/runners/tree-sitter.ts +30 -19
  13. package/clients/dispatch/runners/ts-slop.ts +17 -7
  14. package/clients/dispatch/runners/utils/runner-helpers.ts +76 -8
  15. package/clients/dispatch/utils/format-utils.ts +2 -1
  16. package/clients/fix-scanners.ts +8 -8
  17. package/clients/installer/index.ts +19 -1
  18. package/clients/lsp/index.ts +0 -40
  19. package/clients/lsp/launch.ts +5 -2
  20. package/clients/package-root.ts +44 -0
  21. package/clients/pipeline.ts +179 -8
  22. package/clients/scan-utils.ts +20 -32
  23. package/clients/sg-runner.ts +7 -5
  24. package/clients/source-filter.ts +222 -0
  25. package/clients/startup-scan.ts +142 -0
  26. package/clients/todo-scanner.ts +44 -55
  27. package/clients/tree-sitter-cache.ts +315 -0
  28. package/clients/tree-sitter-client.ts +208 -52
  29. package/clients/tree-sitter-fixer.ts +217 -0
  30. package/clients/tree-sitter-navigator.ts +329 -0
  31. package/clients/tree-sitter-query-loader.ts +55 -32
  32. package/commands/booboo.ts +47 -35
  33. package/default-architect.yaml +76 -87
  34. package/docs/ARCHITECTURE.md +74 -0
  35. package/docs/AST_GREP_RULES.md +266 -0
  36. package/docs/COMPLEXITY_METRICS.md +120 -0
  37. package/docs/EXCLUSIONS.md +83 -0
  38. package/docs/LSP_CONFIG.md +240 -0
  39. package/docs/TREE_SITTER_RULES.md +340 -0
  40. package/docs/WRITING_NEW_AST_GREP_RULES.md +200 -0
  41. package/index.ts +209 -86
  42. package/package.json +13 -4
  43. package/rules/ast-grep-rules/rules/array-callback-return-js.yml +33 -0
  44. package/rules/ast-grep-rules/rules/array-callback-return.yml +1 -1
  45. package/rules/ast-grep-rules/rules/constructor-super-js.yml +22 -0
  46. package/rules/ast-grep-rules/rules/empty-catch-js.yml +45 -0
  47. package/rules/ast-grep-rules/rules/empty-catch.yml +1 -1
  48. package/rules/ast-grep-rules/rules/getter-return-js.yml +59 -0
  49. package/rules/ast-grep-rules/rules/getter-return.yml +1 -1
  50. package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +12 -0
  51. package/rules/ast-grep-rules/rules/jsx-boolean-short-circuit.yml +1 -1
  52. package/rules/ast-grep-rules/rules/jwt-no-verify-js.yml +14 -0
  53. package/rules/ast-grep-rules/rules/missed-concurrency-js.yml +25 -0
  54. package/rules/ast-grep-rules/rules/nested-ternary-js.yml +10 -0
  55. package/rules/ast-grep-rules/rules/no-alert-js.yml +6 -0
  56. package/rules/ast-grep-rules/rules/no-architecture-violation.yml +21 -18
  57. package/rules/ast-grep-rules/rules/no-array-constructor-js.yml +10 -0
  58. package/rules/ast-grep-rules/rules/no-async-promise-executor-js.yml +15 -0
  59. package/rules/ast-grep-rules/rules/no-async-promise-executor.yml +1 -1
  60. package/rules/ast-grep-rules/rules/no-await-in-loop-js.yml +30 -0
  61. package/rules/ast-grep-rules/rules/no-await-in-promise-all-js.yml +20 -0
  62. package/rules/ast-grep-rules/rules/no-await-in-promise-all.yml +1 -1
  63. package/rules/ast-grep-rules/rules/no-bare-except.yml +1 -1
  64. package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +16 -0
  65. package/rules/ast-grep-rules/rules/no-compare-neg-zero-js.yml +13 -0
  66. package/rules/ast-grep-rules/rules/no-compare-neg-zero.yml +1 -1
  67. package/rules/ast-grep-rules/rules/no-comparison-to-none.yml +1 -1
  68. package/rules/ast-grep-rules/rules/no-cond-assign-js.yml +36 -0
  69. package/rules/ast-grep-rules/rules/no-cond-assign.yml +1 -1
  70. package/rules/ast-grep-rules/rules/no-constant-condition-js.yml +25 -0
  71. package/rules/ast-grep-rules/rules/no-constant-condition.yml +1 -1
  72. package/rules/ast-grep-rules/rules/no-constructor-return-js.yml +28 -0
  73. package/rules/ast-grep-rules/rules/no-constructor-return.yml +1 -1
  74. package/rules/ast-grep-rules/rules/no-discarded-error-js.yml +25 -0
  75. package/rules/ast-grep-rules/rules/no-discarded-error.yml +25 -0
  76. package/rules/ast-grep-rules/rules/no-dupe-args-js.yml +15 -0
  77. package/rules/ast-grep-rules/rules/no-dupe-keys-js.yml +73 -0
  78. package/rules/ast-grep-rules/rules/no-extra-boolean-cast-js.yml +25 -0
  79. package/rules/ast-grep-rules/rules/no-hardcoded-secrets-js.yml +17 -0
  80. package/rules/ast-grep-rules/rules/no-implied-eval-js.yml +15 -0
  81. package/rules/ast-grep-rules/rules/no-inner-html-js.yml +13 -0
  82. package/rules/ast-grep-rules/rules/no-insecure-randomness-js.yml +20 -0
  83. package/rules/ast-grep-rules/rules/no-insecure-randomness.yml +1 -1
  84. package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +11 -0
  85. package/rules/ast-grep-rules/rules/no-nan-comparison-js.yml +22 -0
  86. package/rules/ast-grep-rules/rules/no-nan-comparison.yml +22 -0
  87. package/rules/ast-grep-rules/rules/no-new-symbol-js.yml +8 -0
  88. package/rules/ast-grep-rules/rules/no-new-wrappers-js.yml +13 -0
  89. package/rules/ast-grep-rules/rules/no-open-redirect-js.yml +15 -0
  90. package/rules/ast-grep-rules/rules/no-prototype-builtins-js.yml +15 -0
  91. package/rules/ast-grep-rules/rules/no-prototype-builtins.yml +1 -1
  92. package/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +13 -0
  93. package/rules/ast-grep-rules/rules/no-sql-in-code.yml +1 -1
  94. package/rules/ast-grep-rules/rules/no-throw-string-js.yml +12 -0
  95. package/rules/ast-grep-rules/rules/no-throw-string.yml +1 -1
  96. package/rules/ast-grep-rules/rules/strict-equality-js.yml +10 -0
  97. package/rules/ast-grep-rules/rules/strict-inequality-js.yml +10 -0
  98. package/rules/ast-grep-rules/rules/toctou-js.yml +112 -0
  99. package/rules/ast-grep-rules/rules/toctou.yml +1 -1
  100. package/rules/ast-grep-rules/rules/unchecked-sync-fs-js.yml +44 -0
  101. package/rules/ast-grep-rules/rules/unchecked-sync-fs.yml +44 -0
  102. package/rules/ast-grep-rules/rules/unchecked-throwing-call-js.yml +31 -0
  103. package/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +48 -0
  104. package/rules/ast-grep-rules/rules/unchecked-throwing-call-ruby.yml +47 -0
  105. package/rules/ast-grep-rules/rules/unchecked-throwing-call.yml +31 -0
  106. package/rules/ast-grep-rules/rules/weak-rsa-key-js.yml +15 -0
  107. package/rules/tree-sitter-queries/go/go-bare-error.yml +47 -0
  108. package/rules/tree-sitter-queries/go/go-defer-in-loop.yml +47 -0
  109. package/rules/tree-sitter-queries/go/go-hardcoded-secrets.yml +54 -0
  110. package/rules/tree-sitter-queries/python/is-vs-equals.yml +1 -1
  111. package/rules/tree-sitter-queries/python/python-debugger.yml +46 -0
  112. package/rules/tree-sitter-queries/python/python-empty-except.yml +48 -0
  113. package/rules/tree-sitter-queries/python/python-hardcoded-secrets.yml +44 -0
  114. package/rules/tree-sitter-queries/python/python-mutable-class-attr.yml +57 -0
  115. package/rules/tree-sitter-queries/python/python-print-statement.yml +53 -0
  116. package/rules/tree-sitter-queries/python/python-raise-string.yml +38 -0
  117. package/rules/tree-sitter-queries/python/python-unsafe-regex.yml +58 -0
  118. package/rules/tree-sitter-queries/ruby/ruby-debugger.yml +44 -0
  119. package/rules/tree-sitter-queries/ruby/ruby-empty-rescue.yml +47 -0
  120. package/rules/tree-sitter-queries/ruby/ruby-eval.yml +43 -0
  121. package/rules/tree-sitter-queries/ruby/ruby-hardcoded-secrets.yml +40 -0
  122. package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +48 -0
  123. package/rules/tree-sitter-queries/ruby/ruby-puts-statement.yml +52 -0
  124. package/rules/tree-sitter-queries/ruby/ruby-rescue-exception.yml +51 -0
  125. package/rules/tree-sitter-queries/ruby/ruby-unsafe-regex.yml +49 -0
  126. package/rules/tree-sitter-queries/rust/rust-clone-in-loop.yml +49 -0
  127. package/rules/tree-sitter-queries/rust/rust-unwrap.yml +45 -0
  128. package/rules/tree-sitter-queries/typescript/console-statement.yml +3 -3
  129. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +13 -27
  130. package/rules/tree-sitter-queries/typescript/injections.scm +40 -0
  131. package/rules/tree-sitter-queries/typescript/no-console-in-tests.yml +52 -0
  132. package/rules/tree-sitter-queries/typescript/sql-injection.yml +55 -0
  133. package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +71 -0
  134. package/rules/tree-sitter-queries/typescript/variable-shadowing.yml +51 -0
  135. package/scripts/download-grammars.ts +78 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,223 @@
2
2
 
3
3
  All notable changes to pi-lens will be documented in this file.
4
4
 
5
+ ## [3.8.2] - 2026-04-06
6
+
7
+ ### Fixed
8
+ - **npm publish bump** — 3.8.1 was already published with the broken postinstall; 3.8.2 contains the actual fix.
9
+
10
+ ## [3.8.1] - 2026-04-06
11
+
12
+ ### Fixed
13
+ - **`console-statement` hijacking `no-console-in-tests`** — The keyword match for
14
+ `console-statement` (`pattern.includes("console")`) was catching `no-console-in-tests`
15
+ because both contain "console". The simpler rule always won, so both fired on every
16
+ console call. Fixed by excluding test-related patterns: `!pattern.includes("test")`.
17
+ - **`hardcoded-secrets` malformed tree-sitter query** — Had two top-level S-expression
18
+ patterns instead of a single union pattern `[...]`. Replaced with valid union syntax
19
+ and added `post_filter: check_secret_pattern` so variable names are actually filtered
20
+ against credential patterns. Reduced false positives from 58 → 0 on the codebase.
21
+ - **`postinstall` failing on Windows** — `scripts/` was accidentally in `.gitignore` so
22
+ `scripts/download-grammars.ts` was never committed. Added the script, which downloads
23
+ the 10 tree-sitter WASM grammars from unpkg at install time. Also fixed `|| true`
24
+ which is not valid on Windows cmd.exe — replaced with native Node TS execution via
25
+ `node --experimental-strip-types` (Node 22+, no extra deps).
26
+
27
+ ## [3.8.0] - 2026-04-05
28
+
29
+ ### Added — Tree-sitter Expansion
30
+
31
+ - **Go, Rust, Ruby grammar support** — WASM grammars for 3 new languages downloaded at
32
+ install time via `scripts/download-grammars.ts`. Grammar download script added with
33
+ npm `download-grammars` script and postinstall hook. Tree-sitter structural analysis
34
+ now covers all 7 dispatch languages: TypeScript, TSX, JavaScript, Python, Go, Rust, Ruby.
35
+
36
+ - **Tree-sitter dispatch for Go/Rust/Ruby** — Dispatch runner `appliesTo` extended;
37
+ extension→language map replaces the brittle `endsWith` chain. Tree-sitter runner
38
+ added to Go, Rust, and Ruby dispatch plans.
39
+
40
+ - **Incremental parse cache (`TreeCache`)** — AST trees are cached by SHA-256 content
41
+ hash and mtime. Subsequent queries on the same file (same turn) skip re-parsing.
42
+ Cache stores up to 50 files with LRU eviction. `calculateEdit()` + `incrementalUpdate()`
43
+ infrastructure ready for full incremental parsing when old content is tracked.
44
+
45
+ - **AST navigator (`TreeSitterNavigator`)** — Scope-aware traversal utilities: `findParent()`,
46
+ `isInTryCatch()`, `isInTestBlock()`, `isInLoop()`, `getScopeChain()`, `isShadowed()`,
47
+ `getSiblings()`. Used by post-filters for context-aware rule evaluation.
48
+
49
+ - **Native predicate support in queries** — Query YAML files now support a `predicates:`
50
+ array field. Rules with inline `#eq?` / `#match?` / `#not-eq?` predicates run filtering
51
+ inside WASM rather than in JavaScript post-filters.
52
+
53
+ - **Inline fix hints** — Tree-sitter diagnostics now carry `fixable: true` and
54
+ `fixSuggestion: "remove this statement"` when `has_fix: true` in the rule. Displayed
55
+ as `💡 Fix: remove this statement` inline in the diagnostic output. Tree-sitter runner
56
+ is read-only — linters (Biome/Ruff/ESLint) own the autofix phase.
57
+
58
+ - **New post-filters** — `not_in_try_catch`, `in_try_catch`, `not_in_test_block`,
59
+ `not_in_function`, `check_secret_pattern`, `python_empty_except`, `ruby_empty_rescue`,
60
+ `name_matches_param`.
61
+
62
+ ### Added — New Rules (50+)
63
+
64
+ **Structural safety (ast-grep, TypeScript + JavaScript):**
65
+ - `unchecked-sync-fs` — `fs.statSync/readFileSync/writeFileSync/...` outside try/catch (error)
66
+ - `unchecked-throwing-call` — `JSON.parse`, `new URL()`, `execSync` outside try/catch (error)
67
+ - `no-nan-comparison` — `x === NaN` always false, use `Number.isNaN()` (error)
68
+ - `no-discarded-error` — `new Error()` as standalone statement without throw (error)
69
+
70
+ **Structural safety (ast-grep, Python):**
71
+ - `unchecked-throwing-call-python` — `open()`, `json.loads()`, `os.stat()` etc. outside
72
+ try/except (error)
73
+
74
+ **Structural safety (ast-grep, Ruby):**
75
+ - `unchecked-throwing-call-ruby` — `File.read`, `JSON.parse`, `Integer()` etc. outside
76
+ begin/rescue (error)
77
+
78
+ **Tree-sitter Python rules (new):**
79
+ - `python-mutable-class-attr` — class-level `list`/`dict`/`set` shared across all instances (error)
80
+ - `python-debugger` — `breakpoint()`, `pdb.set_trace()` left in code (error)
81
+ - `python-print-statement` — `print()` debug output in production code (warning)
82
+ - `python-hardcoded-secrets` — hardcoded credential assignments (error)
83
+ - `python-empty-except` — except block that only does `pass` (error)
84
+ - `python-unsafe-regex` — `re.compile(variable)` ReDoS risk (error)
85
+ - `python-raise-string` — `raise "string"` is TypeError in Python 3 (error)
86
+
87
+ **Tree-sitter Ruby rules (new):**
88
+ - `ruby-rescue-exception` — `rescue Exception` catches SystemExit and signals (error)
89
+ - `ruby-empty-rescue` — rescue with no body silently swallows errors (error)
90
+ - `ruby-debugger` — `binding.pry` / `binding.irb` left in code (error)
91
+ - `ruby-puts-statement` — `puts`/`p`/`pp` debug output in production (warning)
92
+ - `ruby-hardcoded-secrets` — hardcoded credential assignments (error)
93
+ - `ruby-unsafe-regex` — `Regexp.new(variable)` ReDoS risk (error)
94
+
95
+ **Tree-sitter Go rules (new):**
96
+ - `go-hardcoded-secrets` — hardcoded credentials in short/var/const declarations (error)
97
+
98
+ **JavaScript coverage (38 new rules):**
99
+ All runtime-applicable TypeScript ast-grep rules now have JavaScript equivalents:
100
+ `strict-equality`, `empty-catch`, `no-throw-string`, `no-cond-assign`,
101
+ `no-async-promise-executor`, `toctou`, `no-hardcoded-secrets`, `no-inner-html`,
102
+ `no-insecure-randomness`, `no-sql-in-code`, `jwt-no-verify`, `weak-rsa-key`, and 26 more.
103
+
104
+ ### Changed — Severity Upgrades
105
+
106
+ **17 ast-grep rules upgraded from `warning` to `error`** (will crash / produce wrong output):
107
+ `empty-catch`, `array-callback-return`, `getter-return`, `jsx-boolean-short-circuit`,
108
+ `no-async-promise-executor`, `no-await-in-promise-all`, `no-bare-except`,
109
+ `no-compare-neg-zero`, `no-cond-assign`, `no-constant-condition`,
110
+ `no-constructor-return`, `no-insecure-randomness`, `no-prototype-builtins`,
111
+ `no-sql-in-code`, `no-throw-string`, `toctou`, `no-comparison-to-none`.
112
+
113
+ **4 tree-sitter rules upgraded from `warning` to `error`**:
114
+ `go-defer-in-loop`, `is-vs-equals`, `rust-unwrap`, `unsafe-regex`.
115
+
116
+ ### Fixed
117
+
118
+ - **`console-statement` duplicating `no-console-in-tests`** — `console-statement` now
119
+ uses `post_filter: not_in_test_block` so production and test console detection are
120
+ mutually exclusive.
121
+
122
+ - **`variable-shadowing` never detecting actual shadowing** — Rule now captures both
123
+ `@PARAM` and `@NAME`; `name_matches_param` post-filter only flags when names are
124
+ identical. Previously the rule fired on any variable in a nested function.
125
+
126
+ - **`isInLoop()` false positives** — `call_expression` removed from loop node type list.
127
+ Previously `isInLoop()` returned `true` inside any function call.
128
+
129
+ - **`injectPredicates()` inserting at wrong AST position** — Broken predicate injection
130
+ machinery removed. Predicates already work inline in query S-expressions.
131
+
132
+ - **`sql-injection` rule not matching `db.query()`** — Query now uses union
133
+ `[identifier | member_expression]` to catch both bare `query()` and `db.query()`.
134
+
135
+ - **`contains_sql_keywords` post-filter inverted logic** — Rule was skipping `sql`
136
+ tagged templates (the primary SQL injection vector). Post-filter removed entirely;
137
+ rule relies on inline `#match?` predicate.
138
+
139
+ - **`no-discarded-error` ast-grep `not: inside:` not traversing ancestors** — Required
140
+ `stopBy: end` in ast-grep's `inside` predicate to check all ancestors, not just the
141
+ direct parent. Applied to all `not: inside:` rules.
142
+
143
+ - **Go/Rust/Ruby rules silently skipped** — Runner `appliesTo` was `["jsts", "python"]`
144
+ only. Extended to include `go`, `rust`, `ruby`.
145
+
146
+ ### Fixed (from PR #1 — alexx-ftw)
147
+
148
+ - **`process.cwd()` wrong for global npm installs** — All asset resolution (WASM grammars,
149
+ tree-sitter query YAMLs, ast-grep rule directories, `default-architect.yaml`) now uses
150
+ `resolvePackagePath(import.meta.url, ...)` which walks up from the module file to the
151
+ package root. Previously, running pi-lens as a globally installed extension would fail
152
+ to find built-in rules and grammars.
153
+
154
+ - **Session start scanning `$HOME` or generic directories** — `resolveStartupScanContext()`
155
+ gates all heavy startup scans (knip, jscpd, exports index, project index) behind project
156
+ root detection (`.git`, `package.json`, `go.mod`, etc.) and a 2000-source-file budget.
157
+ Pi-lens stays responsive when opened outside a real project.
158
+
159
+ - **`cachedExports` not cleared on session reset** — Export cache from the previous
160
+ session persisted into new sessions, causing false duplicate-export warnings.
161
+
162
+ - **`biomeClient.ensureAvailable()` at session start** — Changed to `isAvailable()` so
163
+ session start no longer blocks on a Biome auto-install. Installs happen lazily on
164
+ first file write.
165
+
166
+ - **Project index not persisted across sessions** — Index now saved to disk after build
167
+ via `saveIndex()`, and `isIndexFresh()` check skips rebuild when the saved index is
168
+ still current.
169
+
170
+ - **`tree-sitter-query-loader` only loading from `process.cwd()`** — Now loads from
171
+ both the user's project rules directory AND the package's built-in rules, merging
172
+ both sets. Project-specific rules coexist with built-in rules.
173
+
174
+ ---
175
+
176
+ ## [3.7.2] - 2026-04-05
177
+
178
+ ### Added
179
+ - **All-clear signal** — When the pipeline runs clean (no blockers, no test failures),
180
+ the agent now receives a confirmation one-liner instead of silence:
181
+ `✓ TypeScript clean · 12/12 tests · 847ms`
182
+ When non-blocking warnings exist: `✓ no blockers · 3 warning(s) -> /lens-booboo · 847ms`
183
+ Agents can now distinguish "checks ran clean" from "checks didn't run".
184
+
185
+ ### Fixed
186
+ - **Auto-fix message now names the tool** — `✅ Auto-fixed 3 issue(s) (eslint:2, biome:1)`
187
+ instead of the vague `Auto-fixed 3 issue(s)`. Agents know exactly what was corrected.
188
+
189
+ ### Security
190
+ - **Remove `effect` dependency** — Used for 5 trivial `tryPromise` wrappers in one file,
191
+ never consumed via Effect's runtime. Dead dependency removed.
192
+ - **`--ignore-scripts` in auto-installer** — `npm install` for auto-installed tools now
193
+ passes `--ignore-scripts` by default. Only packages that legitimately need postinstall
194
+ scripts to download native binaries (`@biomejs/biome`, `@ast-grep/napi`, `esbuild`) are
195
+ allowlisted.
196
+ - **`npx -y` replaced with `npx --no`** — LSP server launch via npx no longer silently
197
+ downloads uncached packages. `--no` fails fast if the package isn't cached; the
198
+ interactive-install flow is the correct path for first-time installs.
199
+ - **Local-first `sg` (ast-grep) resolution** — All `sg` callers now check
200
+ `node_modules/.bin/sg` → global `sg` → `npx --no sg` (cache-only). No silent
201
+ network downloads of the ast-grep CLI.
202
+
203
+ ---
204
+
205
+ ## [3.7.2] - 2026-04-05 (previous)
206
+
207
+ ### Added
208
+ - **ESLint `--fix` in autofix phase** — Projects with an ESLint config now have fixable
209
+ issues auto-corrected (import ordering, jsx style, etc.) before dispatch runs, using
210
+ `--fix-dry-run` to get the accurate fixed count then `--fix` to apply. Availability
211
+ is cached per session. Only fires on JS/TS files with an ESLint config present.
212
+
213
+ ### Fixed
214
+ - **Misleading infinite-loop comment in biome/ruff runners** — The comment incorrectly
215
+ stated that writing files from runners would trigger infinite loops (formatters already
216
+ prove this isn't true). Updated to explain the real reason: dispatch runners report
217
+ issues for agent understanding; silently rewriting would leave the agent's context
218
+ window stale.
219
+
220
+ ---
221
+
5
222
  ## [3.7.1] - 2026-04-05
6
223
 
7
224
  ### Added