@herbertgao/pi-extensions 2026.9.2 → 2026.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +4 -2
  2. package/THIRD_PARTY_NOTICES.md +49 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +3 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +16 -11
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +20 -6
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +12 -7
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +98 -0
  8. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  9. package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
  10. package/node_modules/@herbertgao/sol-pi/README.md +159 -0
  11. package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
  12. package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
  13. package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
  14. package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
  15. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
  16. package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
  17. package/node_modules/@herbertgao/sol-pi/package.json +76 -0
  18. package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
  19. package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
  20. package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
  21. package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
  22. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
  23. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
  24. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
  25. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
  26. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
  27. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
  28. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
  29. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
  30. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
  31. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
  32. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
  33. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
  34. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
  35. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
  36. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
  37. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
  38. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
  39. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
  40. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
  41. package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
  42. package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
  43. package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
  44. package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -2
  45. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
  46. package/node_modules/@narumitw/pi-btw/package.json +1 -1
  47. package/node_modules/@narumitw/pi-btw/src/btw.ts +13 -2
  48. package/node_modules/pi-antigravity/LICENSE +21 -0
  49. package/node_modules/pi-antigravity/README.md +194 -0
  50. package/node_modules/pi-antigravity/package.json +67 -0
  51. package/node_modules/pi-antigravity/src/auth/index.ts +14 -0
  52. package/node_modules/pi-antigravity/src/auth/oauth.ts +442 -0
  53. package/node_modules/pi-antigravity/src/client/client.ts +561 -0
  54. package/node_modules/pi-antigravity/src/client/index.ts +1 -0
  55. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
  56. package/node_modules/pi-antigravity/src/diagnostics/index.ts +1 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +336 -0
  58. package/node_modules/pi-antigravity/src/image/index.ts +1 -0
  59. package/node_modules/pi-antigravity/src/index.ts +280 -0
  60. package/node_modules/pi-antigravity/src/models/discovery.ts +154 -0
  61. package/node_modules/pi-antigravity/src/models/grouping.ts +424 -0
  62. package/node_modules/pi-antigravity/src/models/index.ts +3 -0
  63. package/node_modules/pi-antigravity/src/models/models.ts +500 -0
  64. package/node_modules/pi-antigravity/src/stream/index.ts +1 -0
  65. package/node_modules/pi-antigravity/src/stream/stream.ts +1460 -0
  66. package/node_modules/pi-antigravity/src/types/enums.ts +42 -0
  67. package/node_modules/pi-antigravity/src/types/index.ts +2 -0
  68. package/node_modules/pi-antigravity/src/types/types.ts +292 -0
  69. package/node_modules/pi-antigravity/src/usage/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/usage/usage.ts +371 -0
  71. package/node_modules/pi-antigravity/src/utils/http.ts +91 -0
  72. package/node_modules/pi-antigravity/src/utils/index.ts +3 -0
  73. package/node_modules/pi-antigravity/src/utils/security.ts +73 -0
  74. package/node_modules/pi-antigravity/src/utils/util.ts +132 -0
  75. package/node_modules/pi-antigravity/tsconfig.json +21 -0
  76. package/node_modules/pi-lens/CHANGELOG.md +176 -0
  77. package/node_modules/pi-lens/README.md +13 -8
  78. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
  79. package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
  80. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -1
  81. package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
  82. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -0
  83. package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -1
  84. package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
  85. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
  86. package/node_modules/pi-lens/dist/clients/config-resolve.js +6 -3
  87. package/node_modules/pi-lens/dist/clients/config-schema.js +17 -0
  88. package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
  89. package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
  90. package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -15
  92. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -3
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +2 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +2 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +2 -2
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +2 -2
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +4 -3
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
  143. package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
  144. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
  145. package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
  146. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
  147. package/node_modules/pi-lens/dist/clients/formatters.js +54 -188
  148. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
  149. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
  150. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
  151. package/node_modules/pi-lens/dist/clients/installer/index.js +196 -52
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
  154. package/node_modules/pi-lens/dist/clients/language-profile.js +12 -2
  155. package/node_modules/pi-lens/dist/clients/latency-logger.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/lens-config.js +4 -0
  157. package/node_modules/pi-lens/dist/clients/lsp/config.js +36 -7
  158. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +78 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/index.js +281 -45
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
  163. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
  164. package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
  165. package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
  166. package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
  167. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +8 -3
  168. package/node_modules/pi-lens/dist/clients/pipeline.js +3 -2
  169. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
  170. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
  171. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  172. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +2 -0
  173. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
  174. package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
  175. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +9 -0
  176. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
  177. package/node_modules/pi-lens/dist/clients/runtime-session.js +13 -4
  178. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
  179. package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
  180. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
  181. package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
  182. package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
  183. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
  184. package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
  185. package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
  186. package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
  187. package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
  188. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
  189. package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
  190. package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
  191. package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
  192. package/node_modules/pi-lens/dist/index.js +21969 -20005
  193. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
  194. package/node_modules/pi-lens/dist/mcp/server.js +135 -159
  195. package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
  196. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
  197. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
  198. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
  199. package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
  200. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
  201. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +353 -85
  202. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +21 -110
  203. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
  204. package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
  205. package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
  206. package/node_modules/pi-lens/dist/tools/render-compact.js +280 -1
  207. package/node_modules/pi-lens/dist/tools/symbol-search.js +2 -2
  208. package/node_modules/pi-lens/docs/agent-guide.md +3 -4
  209. package/node_modules/pi-lens/docs/agent-tools.md +46 -22
  210. package/node_modules/pi-lens/docs/configuration.md +9 -0
  211. package/node_modules/pi-lens/docs/environment-variables.md +0 -5
  212. package/node_modules/pi-lens/docs/features.md +5 -5
  213. package/node_modules/pi-lens/docs/globalconfig.md +11 -1
  214. package/node_modules/pi-lens/docs/mcp.md +13 -1
  215. package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
  216. package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
  217. package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
  218. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +35 -0
  219. package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -4
  220. package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
  221. package/node_modules/pi-lens/docs/real-harness.md +46 -0
  222. package/node_modules/pi-lens/docs/release-qa-baseline.md +5 -1
  223. package/node_modules/pi-lens/docs/servercapabilities.md +1 -6
  224. package/node_modules/pi-lens/docs/settings.md +37 -2
  225. package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
  226. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  227. package/node_modules/pi-lens/docs/usage.md +13 -3
  228. package/node_modules/pi-lens/package.json +10 -1
  229. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
  230. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
  231. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
  232. package/node_modules/pi-web-access/CHANGELOG.md +29 -0
  233. package/node_modules/pi-web-access/README.md +33 -22
  234. package/node_modules/pi-web-access/abortable.ts +17 -0
  235. package/node_modules/pi-web-access/crawl4ai.ts +204 -0
  236. package/node_modules/pi-web-access/credential-source.ts +1 -0
  237. package/node_modules/pi-web-access/curator-page.ts +20 -2
  238. package/node_modules/pi-web-access/curator-run.ts +44 -0
  239. package/node_modules/pi-web-access/curator-server.ts +3 -1
  240. package/node_modules/pi-web-access/duckduckgo.ts +1 -1
  241. package/node_modules/pi-web-access/extract.ts +57 -12
  242. package/node_modules/pi-web-access/fetch-params.ts +1 -1
  243. package/node_modules/pi-web-access/gemini-search.ts +9 -5
  244. package/node_modules/pi-web-access/index.ts +25 -18
  245. package/node_modules/pi-web-access/openai-search.ts +35 -13
  246. package/node_modules/pi-web-access/package.json +2 -2
  247. package/node_modules/pi-web-access/page-query.ts +5 -2
  248. package/node_modules/pi-web-access/query-rewrite.ts +5 -2
  249. package/node_modules/pi-web-access/serpapi.ts +220 -0
  250. package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
  251. package/node_modules/pi-web-access/storage.ts +10 -0
  252. package/node_modules/pi-web-access/summary-review.ts +34 -21
  253. package/node_modules/pi-web-access/utils.ts +6 -4
  254. package/package.json +11 -5
  255. package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
@@ -6,7 +6,6 @@
6
6
  */
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
- import { Type } from "../clients/deps/typebox.js";
10
9
  import { getProjectIgnoreMatcher, isExcludedDirName, } from "../clients/file-utils.js";
11
10
  import { getLSPService, groupFilesByPrimaryServer, runPerServerGroups, } from "../clients/lsp/index.js";
12
11
  import { buildScopeKey, createWorkspaceDiagnosticsCacheContext, } from "../clients/lsp/workspace-diagnostics-cache.js";
@@ -20,23 +19,26 @@ import { attemptTsserverSyncDiagnostics } from "../clients/lsp/tsserver-sync.js"
20
19
  import { convertLspDiagnostics } from "../clients/dispatch/utils/lsp-diagnostics.js";
21
20
  import { demoteInferredProjectDiagnostics } from "../clients/lsp/inferred-project.js";
22
21
  import { isBlocking, reconcileScanDiagnostics, } from "../clients/widget-state.js";
23
- import { baseName, compactRenderResult } from "./render-compact.js";
24
- import { makeProgressReporter, scanningSummaryLine } from "./scan-progress.js";
22
+ import { makeProgressReporter } from "./scan-progress.js";
25
23
  import { isWarmAttached, tryWarmAttachedDiagnostics, } from "../clients/warm-attach.js";
26
24
  import { extensionsForLanguage, SCAN_LANGUAGE_PRIORITY, } from "../clients/language-registry.js";
27
25
  const MAX_FILES = 100;
28
- const MAX_BATCH_FILES = 100;
26
+ export const MAX_BATCH_FILES = 100;
29
27
  const MAX_DIAGNOSTICS = 200;
30
28
  const DEFAULT_BATCH_CONCURRENCY = 8;
31
29
  const MAX_BATCH_CONCURRENCY = 16;
32
30
  const DEFAULT_BATCH_FILE_DEADLINE_MS = 15_000;
33
31
  // LSP severities: 1=Error, 2=Warning, 3=Information, 4=Hint
34
- const SEVERITY_NAMES = {
32
+ export const SEVERITY_NAMES = {
35
33
  1: "error",
36
34
  2: "warning",
37
35
  3: "information",
38
36
  4: "hint",
39
37
  };
38
+ export const LSP_SEVERITY_FILTERS = [
39
+ ...Object.values(SEVERITY_NAMES),
40
+ "all",
41
+ ];
40
42
  function batchFileDeadlineMs() {
41
43
  const raw = Number(process.env.PI_LENS_LSP_BATCH_FILE_MS);
42
44
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_BATCH_FILE_DEADLINE_MS;
@@ -216,102 +218,8 @@ nextWriteIndex,
216
218
  // injecting "Unresolved from this turn" for three more turns after answering
217
219
  // "confirmed clean" for the same file. index.ts injects
218
220
  // `runtime.retireInlineBlockerOnConfirmedClean`. Optional/undefined in tests.
219
- onConfirmedNoBlockers) {
221
+ onConfirmedNoBlockers, getService = getLSPService) {
220
222
  return {
221
- name: "lsp_diagnostics",
222
- label: "LSP Diagnostics",
223
- description: "Get errors, warnings, and hints from language servers for a file or directory. " +
224
- "Use BEFORE running builds to proactively check for issues. " +
225
- "Works on directories by auto-detecting file extensions and scanning all matching files.",
226
- promptSnippet: "Get LSP diagnostics for a file or directory (use before builds)",
227
- renderResult: compactRenderResult(({ details, args, isError, text }) => {
228
- // Streaming progress partials render the live bar (see scanningSummaryLine).
229
- const scanning = scanningSummaryLine(details, text);
230
- if (scanning)
231
- return scanning;
232
- if (isError) {
233
- return `lsp_diagnostics — ${text.split("\n")[0] ?? "error"}`;
234
- }
235
- const count = details?.totalDiagnostics ?? details?.diagnostics?.length ?? 0;
236
- const target = baseName(details?.filePath ?? args.path) || "workspace";
237
- const files = details?.filesChecked ?? details?.filesScanned;
238
- const scope = typeof files === "number" && files > 1
239
- ? ` across ${files} files`
240
- : target
241
- ? ` ${target}`
242
- : "";
243
- const noun = count === 1 ? "diagnostic" : "diagnostics";
244
- // #533: a batch/directory result with any unconfirmed files must NEVER
245
- // compact-render as a bare "N diagnostics" — that erases the fact some
246
- // files' clean status was never actually confirmed by the server.
247
- const unconfirmedFiles = details?.unconfirmedFiles ?? 0;
248
- const navigationOnlyFiles = details?.navigationOnlyFiles ?? 0;
249
- if (navigationOnlyFiles > 0) {
250
- return `lsp_diagnostics${scope} — ${count} ${noun} · ${details?.cleanFiles ?? 0} clean · ${navigationOnlyFiles} navigation-only`;
251
- }
252
- if (unconfirmedFiles > 0) {
253
- const cleanFiles = details?.cleanFiles ?? 0;
254
- const timedOutFiles = details?.timedOutFiles ?? 0;
255
- const suffix = timedOutFiles > 0 ? ` (${timedOutFiles} timed out)` : "";
256
- return `lsp_diagnostics${scope} — ${count} ${noun} · ${cleanFiles} clean · ${unconfirmedFiles} unconfirmed${suffix}`;
257
- }
258
- const outcomeCounts = details?.outcomeCounts;
259
- const notConfirmed = outcomeCounts
260
- ? (outcomeCounts.inconclusive ?? 0) +
261
- (outcomeCounts.unavailable ?? 0) +
262
- (outcomeCounts.unsupported ?? 0) +
263
- (outcomeCounts.failed ?? 0)
264
- : 0;
265
- if (notConfirmed > 0) {
266
- return `lsp_diagnostics${scope} — ${count} ${noun} · ${notConfirmed} checks not confirmed`;
267
- }
268
- if ((details?.incompleteFiles ?? 0) > 0) {
269
- return `lsp_diagnostics${scope} — incomplete (${details?.incompleteFiles} files not confirmed)`;
270
- }
271
- // Single-file mode: 0 diagnostics from an unconfirmed result — either a
272
- // silent-on-clean server or (#570) a timed-out check — is not a clean
273
- // render either.
274
- if (count === 0 && details?.unconfirmed) {
275
- return details?.timedOut
276
- ? `lsp_diagnostics${scope} — timed out (result may be incomplete)`
277
- : `lsp_diagnostics${scope} — unconfirmed (server cannot confirm clean)`;
278
- }
279
- return `lsp_diagnostics${scope} — ${count} ${noun}`;
280
- }),
281
- parameters: Type.Object({
282
- path: Type.Optional(Type.String({
283
- description: "File or directory path to check. For directories, all matching source files are scanned.",
284
- })),
285
- paths: Type.Optional(Type.Array(Type.String(), {
286
- minItems: 1,
287
- maxItems: MAX_BATCH_FILES,
288
- description: "Explicit files to check as a bounded-concurrency batch. When provided, path is ignored.",
289
- })),
290
- severity: Type.Optional(Type.String({
291
- enum: ["error", "warning", "information", "hint", "all"],
292
- description: "Filter by severity level (default: all)",
293
- })),
294
- concurrency: Type.Optional(Type.Number({
295
- description: "Batch/directory concurrency, in distinct LSP server groups run in parallel " +
296
- "(default 8, max 16) — not individual files. Files sharing one server " +
297
- "(e.g. a same-language batch) are always processed one at a time against " +
298
- "that server regardless of this value; this caps how many DIFFERENT " +
299
- "servers run concurrently.",
300
- })),
301
- waitMs: Type.Optional(Type.Number({
302
- description: "Optional per-file LSP wait budget for batch diagnostics. Uses server defaults when omitted.",
303
- })),
304
- serverScope: Type.Optional(Type.String({
305
- enum: ["primary", "all"],
306
- description: "'primary' (fast, low-noise): only the file's actual language " +
307
- "server (e.g. typescript) — for 'does this have real type " +
308
- "errors'. 'all' (default): also touches cross-cutting auxiliary " +
309
- "scanners (ast-grep, opengrep, zizmor, typos, marksman) attached " +
310
- "to this file, including findings for files not yet dispatched " +
311
- "this session. Primary confirmation is always reported " +
312
- "separately from auxiliary findings regardless of this setting.",
313
- })),
314
- }),
315
223
  async execute(_toolCallId, params, _signal, onUpdate, ctx) {
316
224
  // Escape aborts the turn via ctx.signal; honor both it and the tool-call
317
225
  // signal so a batch/directory scan cancels rather than grinding on.
@@ -327,7 +235,7 @@ onConfirmedNoBlockers) {
327
235
  ? Math.floor(typedParams.waitMs)
328
236
  : undefined;
329
237
  const serverScope = typedParams.serverScope === "primary" ? "primary" : "all";
330
- const lspService = getLSPService();
238
+ const lspService = getService();
331
239
  if (!lspService) {
332
240
  return {
333
241
  content: [
@@ -450,7 +358,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
450
358
  const attached = await tryWarmAttachedDiagnostics(absPath, content, attachedBudgetMs, "sweep");
451
359
  if (attached?.available) {
452
360
  const scopedDiagnostics = serverScope === "primary"
453
- ? attached.response.diagnostics.filter((item) => item.source === primaryServerId(absPath))
361
+ ? attached.response.diagnostics.filter((item) => item.serverId === primaryServerId(absPath))
454
362
  : attached.response.diagnostics;
455
363
  const filtered = applyAuxiliarySuppressions(scopedDiagnostics, content, { fileRole: detectFileRole(absPath, content) });
456
364
  return {
@@ -546,6 +454,7 @@ function diagnosticsToFileDiags(file, diagnostics) {
546
454
  severity: d.severity,
547
455
  message: d.message,
548
456
  source: d.source,
457
+ serverId: d.serverId,
549
458
  code: d.code,
550
459
  }));
551
460
  }
@@ -704,7 +613,9 @@ observedAt) {
704
613
  scanOrigin: "lsp_diagnostics",
705
614
  });
706
615
  const retagged = retagAuxiliaryDiagnostics(diagnostics, rawDiags, content ?? "", { cwd, fileRole: detectFileRole(file, content) });
707
- reconcileScanDiagnostics(file, retagged, true, writeIndex, observedAt);
616
+ const reconciled = reconcileScanDiagnostics(file, retagged, true, writeIndex, observedAt);
617
+ if (!reconciled)
618
+ return { confirmed: false, blocking: true };
708
619
  // #1561: the blocking tally comes from the SAME `isBlocking` predicate the
709
620
  // footer counts with — no second severity rule for the blocker-retire
710
621
  // decision to drift away from.
@@ -965,8 +876,8 @@ onConfirmedNoBlockers) {
965
876
  });
966
877
  }
967
878
  const primaryId = primaryServerId(absPath);
968
- const primaryDiags = limited.filter((d) => d.source === primaryId);
969
- const auxiliaryDiags = limited.filter((d) => d.source !== primaryId);
879
+ const primaryDiags = limited.filter((d) => d.serverId === primaryId);
880
+ const auxiliaryDiags = limited.filter((d) => d.serverId !== primaryId);
970
881
  // Primary confirmation is always its own line, independent of how many
971
882
  // auxiliary findings exist — a wall of ast-grep/opengrep noise must never
972
883
  // bury whether the actual language server confirmed the file clean.
@@ -999,7 +910,7 @@ onConfirmedNoBlockers) {
999
910
  // this result does NOT speak for, so "no auxiliary findings" can never be read
1000
911
  // as "the security scanners found nothing".
1001
912
  const coverageLine = unconfirmedServerIds.length > 0
1002
- ? `Auxiliary coverage INCOMPLETE — ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit, or use waitMs to wait longer.`
913
+ ? `Auxiliary coverage INCOMPLETE — ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit. waitMs can extend an ordinary wait, but it cannot override a session-demoted scanner.`
1003
914
  : undefined;
1004
915
  let text;
1005
916
  if (total === 0) {
@@ -1228,8 +1139,8 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
1228
1139
  // `clean`/`unconfirmed` above already reflect ONLY the primary server's
1229
1140
  // confirmation; this split does the same job for the listed diagnostics.
1230
1141
  const primaryIdByFile = new Map(results.map((r) => [r.file, r.primaryServerId]));
1231
- const primaryDisplay = display.filter((d) => d.source === primaryIdByFile.get(d.file));
1232
- const auxiliaryDisplay = display.filter((d) => d.source !== primaryIdByFile.get(d.file));
1142
+ const primaryDisplay = display.filter((d) => d.serverId === primaryIdByFile.get(d.file));
1143
+ const auxiliaryDisplay = display.filter((d) => d.serverId !== primaryIdByFile.get(d.file));
1233
1144
  return {
1234
1145
  results,
1235
1146
  fileErrors,
@@ -1332,8 +1243,8 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1332
1243
  file: result.file,
1333
1244
  outcome: result.outcome,
1334
1245
  reason: result.inconclusiveReason ?? result.error ?? result.unavailable,
1335
- primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.source === result.primaryServerId).length,
1336
- auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.source !== result.primaryServerId).length,
1246
+ primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId === result.primaryServerId).length,
1247
+ auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId !== result.primaryServerId).length,
1337
1248
  })),
1338
1249
  outcomeCounts,
1339
1250
  incompleteFiles: incompleteFiles > 0 ? incompleteFiles : undefined,
@@ -557,29 +557,8 @@ getFlag, mutationDeps) {
557
557
  return {
558
558
  name: "lsp_navigation",
559
559
  label: "LSP Navigate",
560
- description: "Navigate code using LSP (Language Server Protocol). LSP is enabled by default; disable with --no-lsp.\n" +
561
- "Operations:\n" +
562
- "- definition: Jump to where a symbol is defined\n" +
563
- "- typeDefinition: Jump to the definition of a symbol's TYPE (e.g. the class/interface of a variable)\n" +
564
- "- declaration: Jump to a symbol's declaration (e.g. an extern/forward decl, distinct from its definition)\n" +
565
- "- references: Find all usages of a symbol\n" +
566
- "- hover: Get type/doc info at a position\n" +
567
- "- signatureHelp: Show callable signatures at cursor\n" +
568
- "- documentSymbol: List all symbols (functions/classes/vars) in a file\n" +
569
- "- findSymbol: Search document symbols in a file by name/detail with optional kind/top-level/exact filters\n" +
570
- "- workspaceSymbol: Search symbols across the whole project (best with path context)\n" +
571
- "- codeAction: Find available quick fixes/refactors at a range\n" +
572
- "- rename: Compute or apply workspace edits for renaming a symbol\n" +
573
- "- rename_file: Preview/apply LSP-aware source file rename notifications\n" +
574
- "- implementation: Jump to interface implementations\n" +
575
- "- prepareCallHierarchy: Get callable item at position (for incoming/outgoing)\n" +
576
- "- incomingCalls: Find all functions/methods that CALL this function\n" +
577
- "- outgoingCalls: Find all functions/methods CALLED by this function\n" +
578
- "- executeCommand: Run a server-advertised command via workspace/executeCommand. HARDENED: allowlisted to commands the server advertised; dry-run by default (reports whether advertised) — set apply:true to actually run. Pass command (+ optional commandArguments).\n" +
579
- "- workspaceDiagnostics: List all diagnostics tracked by active LSP clients\n" +
580
- "- capabilities: Show cached operation support for active LSP servers\n\n" +
581
- "Line and character are 1-based (as shown in editors). For position-based operations, prefer passing symbol when you know the line but not the exact character; character can be omitted or -1 and pi-lens will resolve the symbol column. Use symbol#N for repeated symbols on the same line (1-based occurrence).",
582
- promptSnippet: "Find definitions, references, and hover info via LSP",
560
+ description: 'Navigate source with language-server operations such as definition, references, hover, and rename. Example: use `{operation: "references", path: "src/app.ts", line: 12}`.',
561
+ promptSnippet: "Navigate definitions and references with LSP",
583
562
  renderResult: compactRenderResult(({ details, args, isError, text }) => {
584
563
  const op = details?.operation ??
585
564
  (typeof args.operation === "string" ? args.operation : "lsp");
@@ -594,56 +573,55 @@ getFlag, mutationDeps) {
594
573
  }),
595
574
  parameters: Type.Object({
596
575
  operation: Type.String({
597
- description: "LSP operation to perform. Valid values: " +
598
- VALID_OPERATIONS.join(", "),
576
+ description: "LSP operation to perform. Valid values: definition, typeDefinition, declaration, references, hover, signatureHelp, documentSymbol, findSymbol, workspaceSymbol, codeAction, rename, rename_file, implementation, prepareCallHierarchy, incomingCalls, outgoingCalls, executeCommand, workspaceDiagnostics, capabilities.",
599
577
  }),
600
578
  path: Type.Optional(Type.String({
601
- description: "Absolute or relative file path. Required for file-scoped operations; optional for workspaceSymbol/workspaceDiagnostics.",
579
+ description: "Target file path.",
602
580
  })),
603
581
  line: Type.Optional(Type.Number({
604
- description: "Line number (1-based). Required for definition/references/hover/implementation",
582
+ description: "1-based line number.",
605
583
  })),
606
584
  character: Type.Optional(Type.Number({
607
- description: "Character offset (1-based). Optional when symbol is provided; use -1 to force symbol-column resolution.",
585
+ description: "1-based character offset.",
608
586
  })),
609
587
  symbol: Type.Optional(Type.String({
610
- description: "Symbol name on the target line for automatic character resolution. Use symbol#N to select the Nth occurrence on the line.",
588
+ description: "Symbol for automatic character resolution.",
611
589
  })),
612
590
  endLine: Type.Optional(Type.Number({
613
- description: "End line (1-based). Optional; used by codeAction range.",
591
+ description: "1-based range end line.",
614
592
  })),
615
593
  endCharacter: Type.Optional(Type.Number({
616
- description: "End character (1-based). Optional; used by codeAction range.",
594
+ description: "1-based range end character.",
617
595
  })),
618
596
  newName: Type.Optional(Type.String({
619
- description: "Required for rename operation.",
597
+ description: "New symbol name.",
620
598
  })),
621
599
  newFilePath: Type.Optional(Type.String({
622
- description: "Required for rename_file operation.",
600
+ description: "New file path.",
623
601
  })),
624
602
  apply: Type.Optional(Type.Boolean({
625
- description: "rename/executeCommand: apply for real. rename defaults to preview; executeCommand defaults to a dry-run that only reports whether the command is advertised — set apply:true to actually run it.",
603
+ description: "Apply a mutation instead of previewing it.",
626
604
  })),
627
605
  command: Type.Optional(Type.String({
628
- description: "executeCommand only: the server command id to run. Must be one the server advertised (see the capabilities operation).",
606
+ description: "Server command identifier.",
629
607
  })),
630
608
  commandArguments: Type.Optional(Type.Array(Type.Unknown(), {
631
- description: "executeCommand only: arguments array passed to workspace/executeCommand.",
609
+ description: "Arguments for the server command.",
632
610
  })),
633
611
  query: Type.Optional(Type.String({
634
- description: "Symbol name to search. Used by workspaceSymbol and findSymbol.",
612
+ description: "Symbol search query.",
635
613
  })),
636
614
  kinds: Type.Optional(Type.Array(Type.String(), {
637
- description: "findSymbol only: restrict matches to symbol kind labels such as function, class, method, variable, interface.",
615
+ description: "Symbol-kind filters.",
638
616
  })),
639
617
  exactMatch: Type.Optional(Type.Boolean({
640
- description: "findSymbol only: match whole symbol names/details exactly instead of substring matching.",
618
+ description: "Require an exact symbol match.",
641
619
  })),
642
620
  topLevelOnly: Type.Optional(Type.Boolean({
643
- description: "findSymbol only: do not search nested child symbols.",
621
+ description: "Exclude nested symbols.",
644
622
  })),
645
623
  maxResults: Type.Optional(Type.Number({
646
- description: "findSymbol only: maximum matches to return. Default 20.",
624
+ description: "Maximum symbol matches.",
647
625
  })),
648
626
  callHierarchyItem: Type.Optional(Type.Object({
649
627
  name: Type.String(),
@@ -669,9 +647,7 @@ getFlag, mutationDeps) {
669
647
  character: Type.Number(),
670
648
  }),
671
649
  }),
672
- }, {
673
- description: "Call hierarchy item. Required for incomingCalls/outgoingCalls",
674
- })),
650
+ }, {})),
675
651
  }),
676
652
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
677
653
  const startedAt = Date.now();
@@ -26,12 +26,8 @@ export function createModuleReportTool(getProjectRoot) {
26
26
  return {
27
27
  name: "module_report",
28
28
  label: "Module Report",
29
- description: "Structured, navigable overview of a source module a token-efficient substitute for reading the whole file. Returns each symbol's name/kind/signature/line-range (plus a first-line `doc` summary when a doc comment is attached), important inline callbacks/closures/lambdas with stable handles, plus who-uses-this, risk flags, and ranked recommendedReads. To read a symbol's body: call read/read_symbol with offset=startLine, limit=endLine-startLine+1 on THIS report's `path` those aren't repeated per symbol. Prefer this before a full read; then use read_symbol (or read) for the exact body you need.\n" +
30
- "Single mode: language-uniform tree-sitter outline + review-graph who-uses-this + inline executable extraction; degrades to outline-only when no cached graph is available. `semantic.source` reports whether graph data was used.\n" +
31
- 'Pass `blastRadius: true` to also get the cross-file blast radius — the transitive dependents of this module aggregated to ranked file `read` args ("if you change this, verify these files"). Read-only over the cached graph; omitted on a cold cache. Supersedes the standalone impact query.\n' +
32
- "Pass `callGraph: true` to include bounded derived callers/callees from the cached FunctionCallGraph; unavailable cache state is explicit and never reported as zero calls.\n" +
33
- '`view: "compact"` returns a line-oriented text rendering (one line per symbol/callback, cheapest option) instead of JSON — same data, roughly a quarter of the token cost; use it for a quick skim. Default view returns JSON. An outline shows shape, not bodies — it does NOT count as having read a symbol\'s body for editing; use read_symbol for that.',
34
- promptSnippet: "Navigable file outline — a cheap substitute for reading a whole file",
29
+ description: "Return a navigable source-module outline with symbols, references, and read handles. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. On a cold cache, project_report and symbol_search return available: false with a retry hint and start a non-blocking background build; module_report degrades to outline-only with cache freshness explicit. Example: use module_report on `src/app.ts` before read_symbol.",
30
+ promptSnippet: "Outline a source module before reading a body",
35
31
  renderResult: compactRenderResult(({ details, args, isError }) => {
36
32
  const base = baseName(args.path) || "module";
37
33
  if (isError || details?.available === false) {
@@ -48,7 +44,7 @@ export function createModuleReportTool(getProjectRoot) {
48
44
  }),
49
45
  parameters: Type.Object({
50
46
  path: Type.String({
51
- description: "Absolute or workspace-relative path to the source file.",
47
+ description: "Source file, e.g. `src/app.ts`.",
52
48
  }),
53
49
  maxRefsPerSymbol: Type.Optional(Type.Number({
54
50
  description: "Cap on who-uses-this entries per symbol (default 10).",
@@ -157,7 +153,7 @@ export function createReadSymbolTool(getProjectRoot, recordSymbolRead) {
157
153
  return {
158
154
  name: "read_symbol",
159
155
  label: "Read Symbol",
160
- description: "Return the verbatim source of a single named symbol or module_report callback handle in a file — a targeted, cheap alternative to reading the whole file. Pair with module_report: module_report finds the symbol/callback handle, read_symbol shows its body. Unlike an outline, this delivers the actual lines, so it counts as having read that symbol for the read-before-edit guard. The returned body includes an attached doc comment when one exists. Accepts a dotted `Class.method` name to resolve a member directly, falling back to a plain top-level lookup when the qualifier doesn't resolve. A miss embeds the ~3 nearest symbol names in the file so a typo self-corrects without a second call. When multiple same-file symbols share a name (overloads, a type and a value sharing a name), the first is returned with an `ambiguous` note; pass `kind` to pick a specific one.",
156
+ description: "Return one symbol's verbatim source. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. Example: use read_symbol after module_report identifies `parseConfig`.",
161
157
  promptSnippet: "Read one symbol's body instead of the whole file",
162
158
  renderResult: compactRenderResult(({ details, args, isError, lineCount }) => {
163
159
  const base = baseName(args.path);
@@ -259,7 +255,7 @@ export function createReadEnclosingTool(getProjectRoot, recordSymbolRead) {
259
255
  return {
260
256
  name: "read_enclosing",
261
257
  label: "Read Enclosing",
262
- description: "Return the verbatim source for the smallest useful symbol/callback enclosing a line in a file. Use after ast_grep_search, diagnostics, or LSP locations when you need exact body text without reading the whole file. Uses tree-sitter only no LSP or graph build — and records read-guard coverage for the returned range.",
258
+ description: "Return the smallest symbol or callback enclosing a line. An outline shows shape, not bodies, and does not satisfy read-before-edit; `read_symbol` and `read_enclosing` return body text and record read coverage. Example: use read_enclosing after a diagnostic points to line 42.",
263
259
  promptSnippet: "Read the enclosing symbol or callback body for a line",
264
260
  renderResult: compactRenderResult(({ details, args, isError }) => {
265
261
  const base = baseName(args.path);
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { Type } from "../clients/deps/typebox.js";
11
11
  import { projectReport, renderCompactProjectReport, } from "../clients/lens-engine.js";
12
- import { compactRenderResult } from "./render-compact.js";
12
+ import { compactRenderResult, renderToolText, } from "./render-compact.js";
13
13
  function errorMessage(err) {
14
14
  return err instanceof Error ? err.message : String(err);
15
15
  }
@@ -17,9 +17,8 @@ export function createProjectReportTool(getProjectRoot) {
17
17
  return {
18
18
  name: "project_report",
19
19
  label: "Project Report",
20
- description: "Project-level orientation from the review graph — 'orient me in this project' before drilling into any one file. First step of a wider discovery funnel: project_report orients, module_report explains a file, read_symbol reads a body. Six capped, ranked sections: a trust header (graph freshness, file coverage, edge-resolution-quality mix), hubs (top fan-in files — the repo's contract surface), entry points (near-zero fan-in / high fan-out files — activation/CLI/mains), a directory-level subsystem map (import cycles + layering violations, e.g. a forbidden clients/ -> tools/ edge), risk hotspots (fan-in × max per-symbol cyclomatic complexity), and suspected dead weight (zero-importer files, shipped with a low-confidence disclaimer dynamic imports/runtime registration/test-only reachability all produce false positives). Every file line carries a `suggestedNext` module_report call. No per-symbol detail and no prose summary — structural facts only. Read-only over the cached graph: returns `available: false` with a retry hint on a cold cache and kicks off a background build (never blocks this call).\n" +
21
- '`view: "compact"` returns a line-oriented text rendering instead of JSON (cheapest option); default view returns JSON. Pass `focus` to re-rank every section toward a task hint (does not expand scope).',
22
- promptSnippet: "Project-level orientation from the review graph",
20
+ description: "Orient in a project from its review graph, with ranked hubs and entry points. On a cold cache, project_report and symbol_search return available: false with a retry hint and start a non-blocking background build; module_report degrades to outline-only with cache freshness explicit. Example: use project_report before module_report when the target file is unknown.",
21
+ promptSnippet: "Orient in a project before choosing a file",
23
22
  renderResult: compactRenderResult(({ details, isError }) => {
24
23
  if (isError || details?.available === false) {
25
24
  return `project_report — unavailable${details?.hint ? `: ${details.hint}` : ""}`;
@@ -28,7 +27,9 @@ export function createProjectReportTool(getProjectRoot) {
28
27
  `${details?.hubs ?? 0} hub(s)`,
29
28
  `${details?.entryPoints ?? 0} entry point(s)`,
30
29
  ];
31
- const view = details?.view && details.view !== "default" ? ` [${details.view}]` : "";
30
+ const view = details?.view && details.view !== "default"
31
+ ? ` [${details.view}]`
32
+ : "";
32
33
  return `project_report ${parts.join(" · ")}${view}`;
33
34
  }),
34
35
  parameters: Type.Object({
@@ -55,24 +56,14 @@ export function createProjectReportTool(getProjectRoot) {
55
56
  }
56
57
  catch (err) {
57
58
  return {
58
- content: [
59
- {
60
- type: "text",
61
- text: `Project report failed: ${errorMessage(err)}`,
62
- },
63
- ],
59
+ ...renderToolText(`Project report failed: ${errorMessage(err)}`),
64
60
  isError: true,
65
61
  details: { available: false },
66
62
  };
67
63
  }
68
64
  if (!report.available) {
69
65
  return {
70
- content: [
71
- {
72
- type: "text",
73
- text: report.hint ?? "No review graph cached for this workspace yet.",
74
- },
75
- ],
66
+ ...renderToolText(report.hint ?? "No review graph cached for this workspace yet.", report),
76
67
  isError: true,
77
68
  details: { available: false, hint: report.hint },
78
69
  };
@@ -82,6 +73,7 @@ export function createProjectReportTool(getProjectRoot) {
82
73
  : JSON.stringify(report);
83
74
  return {
84
75
  content: [{ type: "text", text }],
76
+ isError: false,
85
77
  details: {
86
78
  available: true,
87
79
  hubs: report.hubs?.length ?? 0,