pi-lens 3.8.74 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +2440 -954
  2. package/README.md +13 -0
  3. package/dist/clients/actionable-warnings.js +73 -18
  4. package/dist/clients/agent-behavior-client.js +21 -4
  5. package/dist/clients/ast-grep-client.js +68 -16
  6. package/dist/clients/ast-grep-types.js +0 -1
  7. package/dist/clients/ast-grep-yaml-synth.js +37 -10
  8. package/dist/clients/atomic-write-staging.js +63 -0
  9. package/dist/clients/atomic-write.js +114 -14
  10. package/dist/clients/bash-file-access.js +193 -23
  11. package/dist/clients/biome-client.js +29 -25
  12. package/dist/clients/bootstrap.js +11 -2
  13. package/dist/clients/bounded-cache.js +34 -0
  14. package/dist/clients/bounded-pid-file-lock.js +192 -0
  15. package/dist/clients/bus-publish.js +10 -3
  16. package/dist/clients/cache/rule-cache.js +47 -5
  17. package/dist/clients/cache-manager.js +97 -9
  18. package/dist/clients/cache-observability.js +301 -14
  19. package/dist/clients/call-graph.js +449 -117
  20. package/dist/clients/child-unref.js +85 -0
  21. package/dist/clients/codebase-model.js +52 -23
  22. package/dist/clients/collateral-test-role.js +40 -0
  23. package/dist/clients/complexity-client.js +2 -1
  24. package/dist/clients/console-guard-install.js +14 -0
  25. package/dist/clients/cooperative-budget.js +42 -0
  26. package/dist/clients/dead-code-client.js +2 -1
  27. package/dist/clients/debug-handles.js +206 -0
  28. package/dist/clients/debug-heap.js +167 -0
  29. package/dist/clients/degradation-ledger.js +153 -0
  30. package/dist/clients/dependency-checker.js +347 -75
  31. package/dist/clients/diagnostic-dispositions.js +61 -11
  32. package/dist/clients/diagnostics-publish.js +10 -3
  33. package/dist/clients/dispatch/dispatcher.js +63 -20
  34. package/dist/clients/dispatch/facts/function-facts.js +23 -7
  35. package/dist/clients/dispatch/facts/import-facts.js +8 -18
  36. package/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  37. package/dist/clients/dispatch/indent-detect.js +35 -0
  38. package/dist/clients/dispatch/inline-suppressions.js +17 -8
  39. package/dist/clients/dispatch/integration.js +1085 -681
  40. package/dist/clients/dispatch/lazy.js +14 -0
  41. package/dist/clients/dispatch/plan.js +11 -1
  42. package/dist/clients/dispatch/rule-id-normalize.js +50 -0
  43. package/dist/clients/dispatch/rule-policy.js +135 -0
  44. package/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  45. package/dist/clients/dispatch/runners/biome-check.js +5 -9
  46. package/dist/clients/dispatch/runners/detekt.js +3 -2
  47. package/dist/clients/dispatch/runners/hadolint.js +2 -1
  48. package/dist/clients/dispatch/runners/helm-lint.js +154 -0
  49. package/dist/clients/dispatch/runners/htmlhint.js +3 -2
  50. package/dist/clients/dispatch/runners/index.js +5 -1
  51. package/dist/clients/dispatch/runners/lsp.js +14 -6
  52. package/dist/clients/dispatch/runners/markdownlint.js +4 -9
  53. package/dist/clients/dispatch/runners/pyright.js +9 -5
  54. package/dist/clients/dispatch/runners/ruff.js +4 -8
  55. package/dist/clients/dispatch/runners/shellcheck.js +2 -3
  56. package/dist/clients/dispatch/runners/shfmt.js +2 -3
  57. package/dist/clients/dispatch/runners/spotbugs.js +2 -1
  58. package/dist/clients/dispatch/runners/taplo.js +2 -1
  59. package/dist/clients/dispatch/runners/terragrunt.js +163 -0
  60. package/dist/clients/dispatch/runners/tflint.js +37 -5
  61. package/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  62. package/dist/clients/dispatch/runners/trivy-config.js +13 -7
  63. package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  64. package/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
  65. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  66. package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  67. package/dist/clients/disposition-publish.js +10 -3
  68. package/dist/clients/durable-store.js +97 -0
  69. package/dist/clients/event-loop-monitor.js +94 -3
  70. package/dist/clients/extension-log.js +139 -0
  71. package/dist/clients/extension-mode.js +73 -0
  72. package/dist/clients/file-kinds.js +57 -0
  73. package/dist/clients/file-role.js +18 -3
  74. package/dist/clients/file-utils.js +56 -34
  75. package/dist/clients/format-events-publish.js +16 -3
  76. package/dist/clients/format-service.js +24 -8
  77. package/dist/clients/formatters-lazy.js +8 -0
  78. package/dist/clients/formatters.js +358 -61
  79. package/dist/clients/generated-artifacts.js +148 -15
  80. package/dist/clients/git-guard.js +681 -11
  81. package/dist/clients/git-tracked-ignore.js +4 -0
  82. package/dist/clients/go-client.js +2 -1
  83. package/dist/clients/govulncheck-client.js +8 -0
  84. package/dist/clients/grammar-source.js +12 -4
  85. package/dist/clients/gzip-stage-write.js +21 -3
  86. package/dist/clients/host-ports.js +31 -0
  87. package/dist/clients/installer/index.js +390 -99
  88. package/dist/clients/instance-reaper.js +142 -172
  89. package/dist/clients/jscpd-client.js +31 -54
  90. package/dist/clients/knip-client.js +15 -36
  91. package/dist/clients/language-policy.js +14 -2
  92. package/dist/clients/language-profile.js +13 -7
  93. package/dist/clients/latency-logger.js +23 -1
  94. package/dist/clients/lens-config.js +12 -1
  95. package/dist/clients/lens-engine.js +86 -17
  96. package/dist/clients/lens-events.js +27 -4
  97. package/dist/clients/lens-flag-registry.js +8 -0
  98. package/dist/clients/lens-map.js +94 -19
  99. package/dist/clients/live-bus-emitter.js +30 -0
  100. package/dist/clients/log-cleanup.js +2 -1
  101. package/dist/clients/lsp/cascade-tier.js +1 -1
  102. package/dist/clients/lsp/client.js +493 -48
  103. package/dist/clients/lsp/config.js +13 -2
  104. package/dist/clients/lsp/diagnostic-binding.js +86 -0
  105. package/dist/clients/lsp/edits.js +1012 -121
  106. package/dist/clients/lsp/index.js +1807 -780
  107. package/dist/clients/lsp/launch.js +32 -26
  108. package/dist/clients/lsp/path-utils.js +1 -1
  109. package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  110. package/dist/clients/lsp/server.js +196 -32
  111. package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  112. package/dist/clients/lsp-lazy.js +8 -0
  113. package/dist/clients/lsp-mutation.js +248 -0
  114. package/dist/clients/mcp/analyze.js +123 -32
  115. package/dist/clients/mcp/ipc.js +157 -24
  116. package/dist/clients/mcp/session.js +216 -8
  117. package/dist/clients/memory-sampler.js +115 -0
  118. package/dist/clients/metrics-client.js +2 -1
  119. package/dist/clients/metrics-history.js +2 -1
  120. package/dist/clients/module-report-lsp.js +5 -0
  121. package/dist/clients/module-report.js +168 -3
  122. package/dist/clients/ndjson-logger.js +319 -118
  123. package/dist/clients/opengrep-client.js +1 -1
  124. package/dist/clients/partial-edit-apply.js +120 -5
  125. package/dist/clients/path-utils.js +110 -9
  126. package/dist/clients/pipeline.js +47 -13
  127. package/dist/clients/print-mode.js +21 -0
  128. package/dist/clients/project-changes.js +40 -0
  129. package/dist/clients/project-diagnostics/cache.js +3 -2
  130. package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  131. package/dist/clients/project-diagnostics/scanner.js +21 -1
  132. package/dist/clients/project-lens-config.js +86 -9
  133. package/dist/clients/project-report.js +27 -13
  134. package/dist/clients/project-snapshot.js +175 -20
  135. package/dist/clients/project-trust.js +181 -0
  136. package/dist/clients/quiet-window.js +18 -4
  137. package/dist/clients/read-bridge.js +147 -0
  138. package/dist/clients/read-guard-logger.js +159 -1
  139. package/dist/clients/read-guard-tool-lines.js +150 -29
  140. package/dist/clients/read-guard.js +185 -4
  141. package/dist/clients/resource-sampler.js +70 -84
  142. package/dist/clients/review-graph/builder.js +1122 -221
  143. package/dist/clients/review-graph/git-identity.js +2 -1
  144. package/dist/clients/review-graph/import-resolvers.js +21 -10
  145. package/dist/clients/review-graph/symbol-id.js +46 -0
  146. package/dist/clients/review-graph/tsconfig-paths.js +57 -6
  147. package/dist/clients/review-graph/workspace-modules.js +51 -3
  148. package/dist/clients/review-graph-logger.js +30 -1
  149. package/dist/clients/ruff-client.js +27 -44
  150. package/dist/clients/runtime-agent-end.js +107 -73
  151. package/dist/clients/runtime-context.js +36 -12
  152. package/dist/clients/runtime-coordinator.js +74 -6
  153. package/dist/clients/runtime-session.js +405 -117
  154. package/dist/clients/runtime-tool-call.js +71 -48
  155. package/dist/clients/runtime-tool-result.js +218 -23
  156. package/dist/clients/runtime-turn.js +237 -64
  157. package/dist/clients/rust-client.js +2 -1
  158. package/dist/clients/safe-spawn.js +777 -62
  159. package/dist/clients/security-scan-client.js +2 -1
  160. package/dist/clients/session-state-store.js +3 -2
  161. package/dist/clients/sg-runner.js +284 -174
  162. package/dist/clients/slow-fs.js +2 -1
  163. package/dist/clients/smells-rollup.js +224 -0
  164. package/dist/clients/source-filter.js +184 -20
  165. package/dist/clients/source-walker.js +66 -10
  166. package/dist/clients/startup-scan.js +15 -16
  167. package/dist/clients/startup-timing.js +1 -1
  168. package/dist/clients/test-runner-client.js +38 -8
  169. package/dist/clients/tool-event.js +35 -0
  170. package/dist/clients/tool-policy.js +388 -39
  171. package/dist/clients/tool-render.js +155 -0
  172. package/dist/clients/tree-sitter-cache.js +6 -1
  173. package/dist/clients/tree-sitter-client.js +419 -50
  174. package/dist/clients/tree-sitter-logger.js +21 -0
  175. package/dist/clients/tree-sitter-query-loader.js +7 -1
  176. package/dist/clients/tree-sitter-shared.js +7 -2
  177. package/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  178. package/dist/clients/user-notify.js +51 -0
  179. package/dist/clients/vanished-instance-marker.js +57 -0
  180. package/dist/clients/warm-attach.js +33 -13
  181. package/dist/clients/widget-state.js +300 -43
  182. package/dist/clients/word-index.js +391 -64
  183. package/dist/clients/workspace-topology.js +89 -6
  184. package/dist/index.js +49361 -36888
  185. package/dist/mcp/analyze-cli.js +182 -29
  186. package/dist/mcp/server.js +211 -44
  187. package/dist/tools/ast-grep-search.js +93 -27
  188. package/dist/tools/lens-diagnostics.js +200 -36
  189. package/dist/tools/lsp-diagnostics.js +153 -40
  190. package/dist/tools/lsp-navigation.js +46 -3
  191. package/dist/tools/module-report.js +9 -0
  192. package/docs/agent-guide.md +2 -2
  193. package/docs/agent-tools.md +21 -10
  194. package/docs/analysisall.md +192 -0
  195. package/docs/api-ports-inventory.md +165 -0
  196. package/docs/ast-grep_rules_catalog.md +5 -2
  197. package/docs/durable-store-audit-1202.md +32 -0
  198. package/docs/environment-variables.md +44 -0
  199. package/docs/fable.md +27 -0
  200. package/docs/features.md +31 -2
  201. package/docs/globalconfig.md +45 -1
  202. package/docs/language-coverage.md +4 -3
  203. package/docs/lsp-capability-matrix.md +2 -2
  204. package/docs/mcp.md +37 -4
  205. package/docs/servercapabilities.md +3 -3
  206. package/docs/settings.md +12 -1
  207. package/docs/tools.md +4 -1
  208. package/docs/usage.md +18 -0
  209. package/package.json +10 -10
  210. package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  211. package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  212. package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  213. package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  214. package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  215. package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  216. package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  217. package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  218. package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  219. package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  220. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  221. package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  222. package/scripts/analyze-pi-lens-logs.mjs +55 -2
  223. package/dist/clients/ast-grep-parser.js +0 -86
  224. package/dist/clients/lsp/interactive-install.js +0 -367
  225. package/dist/clients/lsp/lsp-index.js +0 -10
  226. package/dist/clients/lsp/server-strategies.js +0 -5
  227. package/dist/clients/project-metadata.js +0 -690
  228. package/dist/clients/source-groups.js +0 -96
  229. package/dist/clients/startup-marker.js +0 -6
  230. package/dist/clients/types.js +0 -11
@@ -0,0 +1,25 @@
1
+ id: no-bare-host-path-in-win32-branch
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use win32 path operations inside a branch committed to Windows path semantics"
5
+ note: |
6
+ A branch guarded by isWindowsPath/isFullyQualifiedWin32 has committed to
7
+ Windows path parsing. Host-default dirname/basename/join/relative/sep
8
+ follows the process OS and is wrong for a Windows-shaped path on POSIX.
9
+
10
+ SCOPE (review-verified, #1339): same-function consequence only. The
11
+ original #1150 defect — a bare dirname inside an UNGUARDED helper that
12
+ the guarded branch calls (cross-function) — is NOT reachable by this
13
+ engine and stays review-enforced via AGENTS.md defect-shape 2. The
14
+ rule's fixture file encodes that miss as a documented negative; flip it
15
+ to a positive only if cross-function reach is ever added. What this
16
+ rule does catch is the in-branch shape (#1152 class).
17
+ rule:
18
+ all:
19
+ - kind: if_statement
20
+ - has:
21
+ field: condition
22
+ regex: "\\b(isWindowsPath|isFullyQualifiedWin32)\\s*\\("
23
+ - has:
24
+ field: consequence
25
+ regex: "(?s)\\bpath\\.(dirname|basename|join|relative)\\s*\\(|\\bpath\\.sep\\b|(^|[^.])\\b(dirname|basename|join|relative)\\s*\\("
@@ -8,6 +8,12 @@ note: |
8
8
  rule:
9
9
  all:
10
10
  - kind: jsx_element
11
+ - has:
12
+ field: open_tag
13
+ kind: jsx_opening_element
14
+ has:
15
+ field: name
16
+ regex: "^a$"
11
17
  - has:
12
18
  kind: jsx_element
13
19
  stopBy: end
@@ -20,6 +26,7 @@ rule:
20
26
  not:
21
27
  inside:
22
28
  kind: jsx_element
29
+ stopBy: end
23
30
  has:
24
31
  field: open_tag
25
32
  kind: jsx_opening_element
@@ -0,0 +1,30 @@
1
+ id: no-raw-json-store-write
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use the atomic write seam for JSON stores instead of writeFileSync"
5
+ note: |
6
+ JSON-backed durable state must be committed through clients/durable-store.ts
7
+ when read-modify-write isolation is required; that seam publishes through
8
+ clients/atomic-write.ts. This rule covers the direct
9
+ writeFileSync(path, JSON.stringify(data)) shape. Build scripts and the
10
+ installer's wx lock writes are intentionally outside this rule's scope.
11
+ ignores:
12
+ - "**/clients/atomic-write.ts"
13
+ - "**/clients/installer/index.ts"
14
+ - "**/scripts/**"
15
+ rule:
16
+ any:
17
+ - pattern: writeFileSync($PATH, JSON.stringify($$$ARGS))
18
+ - pattern: $FS.writeFileSync($PATH, JSON.stringify($$$ARGS))
19
+ - pattern: writeFileSync($PATH, JSON.stringify($$$ARGS), $OPTS)
20
+ - pattern: $FS.writeFileSync($PATH, JSON.stringify($$$ARGS), $OPTS)
21
+ - kind: expression_statement
22
+ has:
23
+ pattern: writeFileSync($PATH, $PAYLOAD)
24
+ follows:
25
+ pattern: const $PAYLOAD = JSON.stringify($DATA)
26
+ - kind: expression_statement
27
+ has:
28
+ pattern: $FS.writeFileSync($PATH, $PAYLOAD)
29
+ follows:
30
+ pattern: const $PAYLOAD = JSON.stringify($DATA)
@@ -0,0 +1,23 @@
1
+ id: no-win32-isabsolute-for-qualification
2
+ language: TypeScript
3
+ severity: warning
4
+ message: "Use isFullyQualified for path qualification instead of win32.isAbsolute"
5
+ note: |
6
+ Qualification must follow the input path's shape. Use clients/path-utils.ts
7
+ isFullyQualified(), which distinguishes drive-absolute, UNC, rooted-relative,
8
+ POSIX-absolute, and relative paths across host operating systems. The
9
+ implementation of isFullyQualified itself is excluded because it is the
10
+ compatibility seam that owns the win32 parser call.
11
+
12
+ SCOPE (review-verified, #1349): direct call forms only --
13
+ path.win32.isAbsolute(...) and a destructured win32.isAbsolute(...). An
14
+ ALIASED parser (const w = path.win32; w.isAbsolute(p) -- the shape the
15
+ actual pre-#1330 defect used) is beyond the engine's reach and stays
16
+ review-enforced; the fixture file encodes that miss as a documented
17
+ negative, matching the #1339 precedent.
18
+ ignores:
19
+ - "**/clients/path-utils.ts"
20
+ rule:
21
+ any:
22
+ - pattern: path.win32.isAbsolute($P)
23
+ - pattern: win32.isAbsolute($P)
@@ -18,15 +18,13 @@ query: |
18
18
  (jsx_element
19
19
  open_tag: (jsx_opening_element
20
20
  (identifier) @OUTER
21
- (#eq? @OUTER "a"))
22
- (jsx_element
23
- open_tag: (jsx_opening_element
24
- (identifier) @INNER
25
- (#eq? @INNER "a"))))
21
+ (#eq? @OUTER "a"))) @OUTER_ELEMENT
26
22
 
27
23
  metavars:
28
24
  - OUTER
29
- - INNER
25
+ - OUTER_ELEMENT
26
+
27
+ post_filter: no_nested_anchor_chain
30
28
 
31
29
  tags:
32
30
  - correctness
@@ -22,8 +22,8 @@ description: |
22
22
 
23
23
  query: |
24
24
  (formal_parameters
25
- (required_parameter (identifier) @PARAM1)
26
- (required_parameter (identifier) @NAME))
25
+ (_ pattern: (_) @PARAM1)
26
+ (_ pattern: (_) @NAME))
27
27
 
28
28
  metavars:
29
29
  - PARAM1
@@ -8,6 +8,7 @@
8
8
  * ~/.pi-lens/cascade*.log JSONL impact-cascade logs
9
9
  * ~/.pi-lens/read-guard*.log JSONL read-guard friction logs
10
10
  * ~/.pi-lens/tree-sitter*.log JSONL structural runner logs
11
+ * ~/.pi-lens/extension*.log JSONL extension diagnostics and console-guard telemetry
11
12
  * ~/.pi-lens/actionable-warnings*.log JSONL advisory pipeline (inject/suppress)
12
13
  * ~/.pi-lens/ast-grep-tools*.log JSONL MCP ast-grep search/replace telemetry
13
14
  * ~/.pi-lens/logs/*.jsonl JSONL diagnostic findings
@@ -18,10 +19,12 @@ import os from "node:os";
18
19
  import path from "node:path";
19
20
  import readline from "node:readline";
20
21
  import { fileURLToPath } from "node:url";
22
+ import { minimatch } from "minimatch";
21
23
 
22
24
  const DEFAULT_ROOT = path.join(os.homedir(), ".pi-lens");
23
25
  const DEFAULT_SINCE = "2d";
24
26
  const DEFAULT_LIMIT = 12;
27
+ const DEFAULT_EXCLUDE_GLOBS = ["**/AppData/Local/Temp/claude/**", "**/heap-corpus*/**", "**/.plegma/work/**"];
25
28
 
26
29
  const args = parseArgs(process.argv.slice(2));
27
30
  const root = path.resolve(expandHome(args.root ?? DEFAULT_ROOT));
@@ -30,6 +33,10 @@ const limit =
30
33
  Number.parseInt(args.limit ?? `${DEFAULT_LIMIT}`, 10) || DEFAULT_LIMIT;
31
34
  const outputJson = Boolean(args.json);
32
35
  const includeArchived = Boolean(args.archived);
36
+ const excludeGlobs = [
37
+ ...DEFAULT_EXCLUDE_GLOBS,
38
+ ...(Array.isArray(args.exclude) ? args.exclude : args.exclude ? [args.exclude] : []),
39
+ ];
33
40
 
34
41
  const thresholds = {
35
42
  startupSlowMs: Number.parseInt(args.startupSlowMs ?? "500", 10),
@@ -83,7 +90,14 @@ function parseArgs(argv) {
83
90
  const key = arg.slice(2);
84
91
  const next = argv[i + 1];
85
92
  if (!next || next.startsWith("--")) out[key] = "true";
86
- else out[key] = argv[++i];
93
+ else if (key === "exclude") {
94
+ const value = argv[++i];
95
+ out.exclude = out.exclude
96
+ ? Array.isArray(out.exclude)
97
+ ? [...out.exclude, value]
98
+ : [out.exclude, value]
99
+ : value;
100
+ } else out[key] = argv[++i];
87
101
  }
88
102
  }
89
103
  return out;
@@ -158,6 +172,7 @@ function createState(files) {
158
172
  window: { since: since?.toISOString() ?? "all", root },
159
173
  files,
160
174
  parseErrors: counter(),
175
+ excludedRows: 0,
161
176
  seen: counter(),
162
177
  projects: counter(),
163
178
  smellTotals: counter(),
@@ -557,6 +572,10 @@ async function analyzeSessionStart(files, state) {
557
572
  const ts = dateOf(match[1]);
558
573
  if (!inWindow(ts)) return;
559
574
  const message = match[2];
575
+ if (isExcludedText(message)) {
576
+ state.excludedRows++;
577
+ return;
578
+ }
560
579
  state.seen.inc("sessionstart");
561
580
 
562
581
  const cwd = /session_start cwd:\s*(.*)$/.exec(message)?.[1];
@@ -722,13 +741,45 @@ async function forEachJsonLine(file, bucket, state, visitor) {
722
741
  await forEachLine(file, async (line) => {
723
742
  if (!line.trim()) return;
724
743
  try {
725
- visitor(JSON.parse(line));
744
+ const entry = JSON.parse(line);
745
+ if (isExcludedEntry(entry)) {
746
+ state.excludedRows++;
747
+ return;
748
+ }
749
+ visitor(entry);
726
750
  } catch {
727
751
  state.parseErrors.inc(`${bucket}:${path.basename(file)}`);
728
752
  }
729
753
  });
730
754
  }
731
755
 
756
+ function isExcludedEntry(entry) {
757
+ return pathValues(entry).some((value) =>
758
+ excludeGlobs.some((glob) =>
759
+ minimatch(value, glob, { nocase: true, dot: true }),
760
+ ),
761
+ );
762
+ }
763
+
764
+ function pathValues(value, key = "") {
765
+ if (typeof value === "string") {
766
+ return /(file|path|cwd|root|project)/i.test(key) ? [normalizePath(value)] : [];
767
+ }
768
+ if (!value || typeof value !== "object") return [];
769
+ return Object.entries(value).flatMap(([childKey, child]) =>
770
+ pathValues(child, childKey),
771
+ );
772
+ }
773
+
774
+ function isExcludedText(text) {
775
+ const cwd = /session_start cwd:\s*(.*)$/.exec(text)?.[1];
776
+ return cwd
777
+ ? excludeGlobs.some((glob) =>
778
+ minimatch(normalizePath(cwd), glob, { nocase: true, dot: true }),
779
+ )
780
+ : false;
781
+ }
782
+
732
783
  async function forEachLine(file, visitor) {
733
784
  if (!fs.existsSync(file)) return;
734
785
  const rl = readline.createInterface({
@@ -1129,6 +1180,7 @@ function buildReport(state) {
1129
1180
  Object.entries(state.files).map(([key, list]) => [key, list.length]),
1130
1181
  ),
1131
1182
  rowsSeen: state.seen.toJSON(),
1183
+ rowsExcluded: state.excludedRows,
1132
1184
  parseErrors: state.parseErrors.toJSON(),
1133
1185
  projects: state.projects.top(limit),
1134
1186
  smells,
@@ -1232,6 +1284,7 @@ function printReport(report) {
1232
1284
  .map(([k, v]) => `${k}=${v}`)
1233
1285
  .join(", ")}`,
1234
1286
  );
1287
+ console.log(`${report.rowsExcluded} rows excluded (synthetic corpora)`);
1235
1288
  if (Object.keys(report.parseErrors).length)
1236
1289
  console.log(`parse errors: ${JSON.stringify(report.parseErrors)}`);
1237
1290
 
@@ -1,86 +0,0 @@
1
- import * as path from "node:path";
2
- export class AstGrepParser {
3
- getRuleDescription;
4
- mapSeverity;
5
- constructor(getRuleDescription, mapSeverity) {
6
- this.getRuleDescription = getRuleDescription;
7
- this.mapSeverity = mapSeverity;
8
- }
9
- parseOutput(output, filterFile) {
10
- const resolvedFilterFile = path.resolve(filterFile);
11
- try {
12
- const items = JSON.parse(output);
13
- if (Array.isArray(items)) {
14
- return items
15
- .map((item) => this.parseDiagnostic(item, resolvedFilterFile))
16
- .filter((d) => d !== null);
17
- }
18
- }
19
- catch (err) {
20
- void err;
21
- }
22
- return output
23
- .split(/\r?\n/)
24
- .filter((l) => l.trim())
25
- .map((line) => {
26
- try {
27
- return this.parseDiagnostic(JSON.parse(line), resolvedFilterFile);
28
- }
29
- catch (err) {
30
- void err;
31
- return null;
32
- }
33
- })
34
- .filter((d) => d !== null);
35
- }
36
- parseDiagnostic(item, filterFile) {
37
- if (item.labels?.length) {
38
- return this.parseNewFormat(item, filterFile);
39
- }
40
- if (item.spans?.length) {
41
- return this.parseLegacyFormat(item, filterFile);
42
- }
43
- return null;
44
- }
45
- parseNewFormat(item, filterFile) {
46
- const label = item.labels.find((l) => l.style === "primary") || item.labels[0];
47
- const filePath = path.resolve(label.file || filterFile);
48
- if (filePath !== filterFile)
49
- return null;
50
- const start = label.range?.start || { line: 0, column: 0 };
51
- const end = label.range?.end || start;
52
- return {
53
- line: start.line + 1,
54
- column: start.column,
55
- endLine: end.line + 1,
56
- endColumn: end.column,
57
- severity: this.mapSeverity(item.severity),
58
- message: item.message || "Unknown issue",
59
- rule: item.ruleId || "unknown",
60
- ruleDescription: this.getRuleDescription(item.ruleId || "unknown"),
61
- file: filePath,
62
- };
63
- }
64
- parseLegacyFormat(item, filterFile) {
65
- const span = item.spans?.[0];
66
- if (!span)
67
- return null;
68
- const filePath = path.resolve(span.file || filterFile);
69
- if (filePath !== filterFile)
70
- return null;
71
- const start = span.range?.start || { line: 0, column: 0 };
72
- const end = span.range?.end || start;
73
- const ruleId = item.name || item.ruleId || "unknown";
74
- return {
75
- line: start.line + 1,
76
- column: start.column,
77
- endLine: end.line + 1,
78
- endColumn: end.column,
79
- severity: this.mapSeverity(item.severity || item.Severity || "warning"),
80
- message: item.Message?.text || item.message || "Unknown issue",
81
- rule: ruleId,
82
- ruleDescription: this.getRuleDescription(ruleId),
83
- file: filePath,
84
- };
85
- }
86
- }