pi-lens 2.2.9 → 3.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 (304) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +709 -519
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/file-time.test.ts +276 -0
  5. package/clients/__tests__/format-service.test.js +245 -0
  6. package/clients/__tests__/format-service.test.ts +339 -0
  7. package/clients/__tests__/formatters.test.js +271 -0
  8. package/clients/__tests__/formatters.test.ts +401 -0
  9. package/clients/amain-types.js +164 -0
  10. package/clients/amain-types.ts +165 -0
  11. package/clients/architect-client.js +56 -12
  12. package/clients/architect-client.ts +81 -16
  13. package/clients/ast-grep-client.js +2 -2
  14. package/clients/ast-grep-client.ts +14 -39
  15. package/clients/ast-grep-parser.ts +1 -1
  16. package/clients/ast-grep-rule-manager.js +8 -0
  17. package/clients/ast-grep-rule-manager.ts +10 -1
  18. package/clients/ast-grep-types.js +9 -0
  19. package/clients/ast-grep-types.ts +106 -0
  20. package/clients/auto-loop.js +10 -0
  21. package/clients/auto-loop.ts +14 -1
  22. package/clients/biome-client.js +81 -19
  23. package/clients/biome-client.ts +103 -22
  24. package/clients/bus/bus.js +191 -0
  25. package/clients/bus/bus.ts +251 -0
  26. package/clients/bus/events.js +214 -0
  27. package/clients/bus/events.ts +279 -0
  28. package/clients/bus/index.js +8 -0
  29. package/clients/bus/index.ts +9 -0
  30. package/clients/bus/integration.js +158 -0
  31. package/clients/bus/integration.ts +214 -0
  32. package/clients/complexity-client.js +13 -7
  33. package/clients/complexity-client.ts +13 -7
  34. package/clients/config-validator.js +465 -0
  35. package/clients/config-validator.ts +558 -0
  36. package/clients/dependency-checker.js +4 -10
  37. package/clients/dependency-checker.ts +4 -10
  38. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  39. package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
  40. package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
  41. package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
  42. package/clients/dispatch/bus-dispatcher.js +177 -0
  43. package/clients/dispatch/bus-dispatcher.ts +251 -0
  44. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  45. package/clients/dispatch/dispatcher.edge.test.ts +100 -0
  46. package/clients/dispatch/dispatcher.format.test.js +46 -0
  47. package/clients/dispatch/dispatcher.format.test.ts +58 -0
  48. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  49. package/clients/dispatch/dispatcher.inline.test.ts +93 -0
  50. package/clients/dispatch/dispatcher.js +19 -53
  51. package/clients/dispatch/dispatcher.ts +20 -67
  52. package/clients/dispatch/plan.js +9 -4
  53. package/clients/dispatch/plan.ts +9 -4
  54. package/clients/dispatch/runners/architect.js +21 -7
  55. package/clients/dispatch/runners/architect.test.js +138 -0
  56. package/clients/dispatch/runners/architect.test.ts +162 -0
  57. package/clients/dispatch/runners/architect.ts +22 -7
  58. package/clients/dispatch/runners/ast-grep-napi.js +462 -0
  59. package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
  60. package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
  61. package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
  62. package/clients/dispatch/runners/ast-grep.js +62 -19
  63. package/clients/dispatch/runners/ast-grep.ts +70 -18
  64. package/clients/dispatch/runners/biome.js +29 -53
  65. package/clients/dispatch/runners/biome.ts +29 -63
  66. package/clients/dispatch/runners/config-validation.js +67 -0
  67. package/clients/dispatch/runners/config-validation.ts +82 -0
  68. package/clients/dispatch/runners/go-vet.js +4 -28
  69. package/clients/dispatch/runners/go-vet.ts +4 -32
  70. package/clients/dispatch/runners/index.js +30 -10
  71. package/clients/dispatch/runners/index.ts +30 -10
  72. package/clients/dispatch/runners/oxlint.js +141 -0
  73. package/clients/dispatch/runners/oxlint.test.js +230 -0
  74. package/clients/dispatch/runners/oxlint.test.ts +303 -0
  75. package/clients/dispatch/runners/oxlint.ts +175 -0
  76. package/clients/dispatch/runners/pyright.js +40 -70
  77. package/clients/dispatch/runners/pyright.test.js +16 -2
  78. package/clients/dispatch/runners/pyright.test.ts +14 -2
  79. package/clients/dispatch/runners/pyright.ts +48 -91
  80. package/clients/dispatch/runners/python-slop.js +97 -0
  81. package/clients/dispatch/runners/python-slop.test.js +203 -0
  82. package/clients/dispatch/runners/python-slop.test.ts +298 -0
  83. package/clients/dispatch/runners/python-slop.ts +124 -0
  84. package/clients/dispatch/runners/ruff.js +18 -71
  85. package/clients/dispatch/runners/ruff.ts +19 -79
  86. package/clients/dispatch/runners/rust-clippy.js +28 -32
  87. package/clients/dispatch/runners/rust-clippy.ts +29 -31
  88. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  89. package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
  90. package/clients/dispatch/runners/shellcheck.js +147 -0
  91. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  92. package/clients/dispatch/runners/shellcheck.test.ts +129 -0
  93. package/clients/dispatch/runners/shellcheck.ts +188 -0
  94. package/clients/dispatch/runners/similarity.js +230 -0
  95. package/clients/dispatch/runners/similarity.ts +339 -0
  96. package/clients/dispatch/runners/spellcheck.js +106 -0
  97. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  98. package/clients/dispatch/runners/spellcheck.test.ts +214 -0
  99. package/clients/dispatch/runners/spellcheck.ts +136 -0
  100. package/clients/dispatch/runners/tree-sitter.js +107 -0
  101. package/clients/dispatch/runners/tree-sitter.ts +135 -0
  102. package/clients/dispatch/runners/ts-lsp.js +104 -33
  103. package/clients/dispatch/runners/ts-lsp.ts +120 -38
  104. package/clients/dispatch/runners/ts-slop.js +113 -0
  105. package/clients/dispatch/runners/ts-slop.test.js +180 -0
  106. package/clients/dispatch/runners/ts-slop.test.ts +230 -0
  107. package/clients/dispatch/runners/ts-slop.ts +142 -0
  108. package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
  109. package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
  110. package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
  111. package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
  112. package/clients/dispatch/runners/utils.js +2 -4
  113. package/clients/dispatch/runners/utils.ts +2 -4
  114. package/clients/dispatch/types.ts +1 -1
  115. package/clients/dispatch/utils/format-utils.js +49 -0
  116. package/clients/dispatch/utils/format-utils.ts +60 -0
  117. package/clients/dogfood.test.js +201 -0
  118. package/clients/dogfood.test.ts +269 -0
  119. package/clients/file-time.js +152 -0
  120. package/clients/file-time.ts +208 -0
  121. package/clients/file-utils.js +40 -0
  122. package/clients/file-utils.ts +44 -0
  123. package/clients/fix-scanners.js +10 -20
  124. package/clients/fix-scanners.ts +10 -22
  125. package/clients/format-service.js +172 -0
  126. package/clients/format-service.ts +254 -0
  127. package/clients/formatters.js +435 -0
  128. package/clients/formatters.ts +508 -0
  129. package/clients/go-client.js +5 -14
  130. package/clients/go-client.ts +5 -13
  131. package/clients/installer/index.js +356 -0
  132. package/clients/installer/index.ts +426 -0
  133. package/clients/jscpd-client.js +11 -9
  134. package/clients/jscpd-client.ts +12 -8
  135. package/clients/knip-client.js +3 -7
  136. package/clients/knip-client.ts +3 -6
  137. package/clients/lsp/__tests__/client.test.js +325 -0
  138. package/clients/lsp/__tests__/client.test.ts +434 -0
  139. package/clients/lsp/__tests__/config.test.js +166 -0
  140. package/clients/lsp/__tests__/config.test.ts +209 -0
  141. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  142. package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
  143. package/clients/lsp/__tests__/integration.test.js +127 -0
  144. package/clients/lsp/__tests__/integration.test.ts +160 -0
  145. package/clients/lsp/__tests__/launch.test.js +260 -0
  146. package/clients/lsp/__tests__/launch.test.ts +329 -0
  147. package/clients/lsp/__tests__/server.test.js +259 -0
  148. package/clients/lsp/__tests__/server.test.ts +332 -0
  149. package/clients/lsp/__tests__/service.test.js +417 -0
  150. package/clients/lsp/__tests__/service.test.ts +499 -0
  151. package/clients/lsp/client.js +235 -0
  152. package/clients/lsp/client.ts +328 -0
  153. package/clients/lsp/config.js +115 -0
  154. package/clients/lsp/config.ts +149 -0
  155. package/clients/lsp/index.js +222 -0
  156. package/clients/lsp/index.ts +280 -0
  157. package/clients/lsp/installer/index.js +391 -0
  158. package/clients/lsp/interactive-install.js +210 -0
  159. package/clients/lsp/interactive-install.ts +251 -0
  160. package/clients/lsp/language.js +170 -0
  161. package/clients/lsp/language.ts +216 -0
  162. package/clients/lsp/launch.js +174 -0
  163. package/clients/lsp/launch.ts +240 -0
  164. package/clients/lsp/lsp/launch.js +116 -0
  165. package/clients/lsp/lsp/server.js +532 -0
  166. package/clients/lsp/lsp-index.js +10 -0
  167. package/clients/lsp/lsp-index.ts +11 -0
  168. package/clients/lsp/path-utils.js +48 -0
  169. package/clients/lsp/path-utils.ts +52 -0
  170. package/clients/lsp/server.js +615 -0
  171. package/clients/lsp/server.ts +800 -0
  172. package/clients/lsp/test-py-spawn/requirements.txt +1 -0
  173. package/clients/lsp/test-py-spawn/test.py +3 -0
  174. package/clients/lsp/test-py-svc/requirements.txt +1 -0
  175. package/clients/lsp/test-py-svc/test.py +3 -0
  176. package/clients/lsp/test-python-project/requirements.txt +1 -0
  177. package/clients/lsp/test-python-project/test.py +5 -0
  178. package/clients/metrics-history.js +2 -2
  179. package/clients/metrics-history.ts +2 -2
  180. package/clients/production-readiness.js +522 -0
  181. package/clients/production-readiness.ts +556 -0
  182. package/clients/project-index.js +255 -0
  183. package/clients/project-index.ts +383 -0
  184. package/clients/project-metadata.js +531 -0
  185. package/clients/project-metadata.ts +624 -0
  186. package/clients/ruff-client.js +56 -16
  187. package/clients/ruff-client.ts +72 -15
  188. package/clients/runner-tracker.js +152 -0
  189. package/clients/runner-tracker.ts +213 -0
  190. package/clients/rust-client.js +4 -11
  191. package/clients/rust-client.ts +5 -11
  192. package/clients/safe-spawn.js +96 -0
  193. package/clients/safe-spawn.ts +128 -0
  194. package/clients/scan-architectural-debt.js +3 -6
  195. package/clients/scan-architectural-debt.ts +3 -6
  196. package/clients/scan-utils.js +5 -20
  197. package/clients/scan-utils.ts +5 -29
  198. package/clients/secrets-scanner.js +3 -17
  199. package/clients/secrets-scanner.ts +4 -20
  200. package/clients/services/__tests__/effect-integration.test.js +86 -0
  201. package/clients/services/__tests__/effect-integration.test.ts +111 -0
  202. package/clients/services/effect-integration.js +194 -0
  203. package/clients/services/effect-integration.ts +268 -0
  204. package/clients/services/index.js +7 -0
  205. package/clients/services/index.ts +8 -0
  206. package/clients/services/runner-service.js +105 -0
  207. package/clients/services/runner-service.ts +179 -0
  208. package/clients/sg-runner.js +87 -13
  209. package/clients/sg-runner.ts +97 -13
  210. package/clients/state-matrix.js +160 -0
  211. package/clients/state-matrix.ts +202 -0
  212. package/clients/subprocess-client.js +10 -9
  213. package/clients/subprocess-client.ts +10 -8
  214. package/clients/test-runner-client.js +3 -7
  215. package/clients/test-runner-client.ts +3 -6
  216. package/clients/tool-availability.js +4 -10
  217. package/clients/tool-availability.ts +4 -9
  218. package/clients/tree-sitter-client.js +564 -0
  219. package/clients/tree-sitter-client.ts +797 -0
  220. package/clients/tree-sitter-query-loader.js +355 -0
  221. package/clients/tree-sitter-query-loader.ts +425 -0
  222. package/clients/type-coverage-client.js +3 -7
  223. package/clients/type-coverage-client.ts +3 -6
  224. package/clients/typescript-client.codefix.test.js +157 -0
  225. package/clients/typescript-client.codefix.test.ts +186 -0
  226. package/clients/typescript-client.js +43 -0
  227. package/clients/typescript-client.ts +98 -0
  228. package/commands/booboo.js +799 -219
  229. package/commands/booboo.ts +1004 -225
  230. package/commands/clients/ast-grep-client.js +250 -0
  231. package/commands/clients/ast-grep-parser.js +86 -0
  232. package/commands/clients/ast-grep-rule-manager.js +91 -0
  233. package/commands/clients/ast-grep-types.js +9 -0
  234. package/commands/clients/biome-client.js +380 -0
  235. package/commands/clients/complexity-client.js +667 -0
  236. package/commands/clients/file-kinds.js +177 -0
  237. package/commands/clients/file-utils.js +40 -0
  238. package/commands/clients/jscpd-client.js +169 -0
  239. package/commands/clients/knip-client.js +211 -0
  240. package/commands/clients/ruff-client.js +297 -0
  241. package/commands/clients/safe-spawn.js +88 -0
  242. package/commands/clients/scan-utils.js +83 -0
  243. package/commands/clients/sg-runner.js +190 -0
  244. package/commands/clients/types.js +11 -0
  245. package/commands/clients/typescript-client.js +505 -0
  246. package/commands/fix-from-booboo.js +398 -0
  247. package/commands/fix-from-booboo.ts +485 -0
  248. package/commands/fix-simplified.js +618 -0
  249. package/commands/fix-simplified.ts +768 -0
  250. package/commands/rate.js +10 -14
  251. package/commands/rate.ts +9 -16
  252. package/default-architect.yaml +59 -15
  253. package/index.ts +342 -429
  254. package/package.json +16 -3
  255. package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
  256. package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
  257. package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
  258. package/rules/python-slop-rules/.sgconfig.yml +4 -0
  259. package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
  260. package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
  261. package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
  262. package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
  263. package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
  264. package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
  265. package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
  266. package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
  267. package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
  268. package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
  269. package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
  270. package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
  271. package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
  272. package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
  273. package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
  274. package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
  275. package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
  276. package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
  277. package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
  278. package/rules/ts-slop-rules/.sgconfig.yml +4 -0
  279. package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
  280. package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
  281. package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
  282. package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
  283. package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
  284. package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
  285. package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
  286. package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
  287. package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
  288. package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
  289. package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
  290. package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
  291. package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
  292. package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
  293. package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
  294. package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
  295. package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
  296. package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
  297. package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
  298. package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
  299. package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
  300. package/skills/ast-grep/SKILL.md +182 -0
  301. package/clients/dispatch/runners/secrets.js +0 -109
  302. package/commands/fix.js +0 -244
  303. package/commands/fix.ts +0 -373
  304. package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Diagnostic Event Types for pi-lens
3
+ *
4
+ * Standardized events for the bus system.
5
+ * These events flow through the system:
6
+ * - Runners publish DiagnosticFound events
7
+ * - LSP clients publish LspDiagnostic events
8
+ * - Aggregators subscribe and build reports
9
+ * - UI subscribes for real-time display
10
+ */
11
+ import { z } from "zod";
12
+ import { BusEvent } from "./bus.js";
13
+ // --- Shared Schemas ---
14
+ export const DiagnosticSeverity = z.enum(["error", "warning", "info", "hint"]);
15
+ export const OutputSemantic = z.enum([
16
+ "blocking", // Hard stop - must fix
17
+ "warning", // Soft stop - should fix
18
+ "fixed", // Auto-fix applied
19
+ "silent", // Track but don't display
20
+ "none", // No action needed
21
+ ]);
22
+ export const DiagnosticSchema = z.object({
23
+ id: z.string(), // Unique for deduplication
24
+ message: z.string(),
25
+ filePath: z.string(),
26
+ line: z.number().optional(),
27
+ column: z.number().optional(),
28
+ severity: DiagnosticSeverity,
29
+ semantic: OutputSemantic,
30
+ tool: z.string(), // Which tool produced this
31
+ rule: z.string().optional(),
32
+ fixable: z.boolean().optional(),
33
+ fixSuggestion: z.string().optional(),
34
+ });
35
+ export const RunnerStatus = z.enum([
36
+ "starting",
37
+ "running",
38
+ "completed",
39
+ "failed",
40
+ "skipped",
41
+ ]);
42
+ // --- Event Definitions ---
43
+ /**
44
+ * Fired when a runner discovers diagnostics
45
+ * Published by: dispatch runners
46
+ * Subscribed by: aggregators, delta tracker, UI
47
+ */
48
+ export const DiagnosticFound = BusEvent.define("diagnostic.found", z.object({
49
+ runnerId: z.string(),
50
+ filePath: z.string(),
51
+ diagnostics: z.array(DiagnosticSchema),
52
+ durationMs: z.number(),
53
+ }));
54
+ /**
55
+ * Fired when a file is modified
56
+ * Published by: tool_result handler, file watcher
57
+ * Subscribed by: runner scheduler, cache invalidator
58
+ */
59
+ export const FileModified = BusEvent.define("file.modified", z.object({
60
+ filePath: z.string(),
61
+ content: z.string().optional(),
62
+ changeType: z.enum(["write", "edit", "external"]),
63
+ }));
64
+ /**
65
+ * Fired when a file is created
66
+ * Published by: file watcher
67
+ * Subscribed by: runner scheduler
68
+ */
69
+ export const FileCreated = BusEvent.define("file.created", z.object({
70
+ filePath: z.string(),
71
+ }));
72
+ /**
73
+ * Fired when a file is deleted
74
+ * Published by: file watcher
75
+ * Subscribed by: cache invalidator
76
+ */
77
+ export const FileDeleted = BusEvent.define("file.deleted", z.object({
78
+ filePath: z.string(),
79
+ }));
80
+ /**
81
+ * Fired when a runner starts execution
82
+ * Published by: dispatcher
83
+ * Subscribed by: progress UI, metrics
84
+ */
85
+ export const RunnerStarted = BusEvent.define("runner.started", z.object({
86
+ runnerId: z.string(),
87
+ filePath: z.string(),
88
+ timestamp: z.number(),
89
+ }));
90
+ /**
91
+ * Fired when a runner completes
92
+ * Published by: dispatcher
93
+ * Subscribed by: progress UI, metrics aggregator
94
+ */
95
+ export const RunnerCompleted = BusEvent.define("runner.completed", z.object({
96
+ runnerId: z.string(),
97
+ filePath: z.string(),
98
+ status: RunnerStatus,
99
+ durationMs: z.number(),
100
+ diagnosticCount: z.number(),
101
+ }));
102
+ /**
103
+ * Fired when LSP publishes diagnostics
104
+ * Published by: LSPClient (via textDocument/publishDiagnostics)
105
+ * Subscribed by: diagnostic aggregator
106
+ */
107
+ export const LspDiagnostic = BusEvent.define("lsp.diagnostic", z.object({
108
+ serverId: z.string(), // e.g., "typescript", "pyright"
109
+ filePath: z.string(),
110
+ diagnostics: z.array(z.object({
111
+ severity: z.number(), // 1=error, 2=warn, 3=info, 4=hint
112
+ message: z.string(),
113
+ range: z.object({
114
+ start: z.object({ line: z.number(), character: z.number() }),
115
+ end: z.object({ line: z.number(), character: z.number() }),
116
+ }),
117
+ code: z.union([z.string(), z.number()]).optional(),
118
+ source: z.string().optional(),
119
+ })),
120
+ version: z.number().optional(), // Document version for debouncing
121
+ }));
122
+ /**
123
+ * Fired when baseline is updated (for delta mode)
124
+ * Published by: delta tracker
125
+ * Subscribed by: diagnostic filter
126
+ */
127
+ export const BaselineUpdated = BusEvent.define("baseline.updated", z.object({
128
+ filePath: z.string(),
129
+ diagnosticIds: z.array(z.string()),
130
+ timestamp: z.number(),
131
+ }));
132
+ /**
133
+ * Fired when aggregated report is ready
134
+ * Published by: report aggregator
135
+ * Subscribed by: UI, commands
136
+ */
137
+ export const ReportReady = BusEvent.define("report.ready", z.object({
138
+ filePath: z.string(),
139
+ report: z.object({
140
+ blockers: z.array(DiagnosticSchema),
141
+ warnings: z.array(DiagnosticSchema),
142
+ fixed: z.array(DiagnosticSchema),
143
+ silent: z.array(DiagnosticSchema),
144
+ }),
145
+ durationMs: z.number(),
146
+ }));
147
+ /**
148
+ * Fired when auto-fix is applied
149
+ * Published by: autofix runner
150
+ * Subscribed by: UI, file watcher
151
+ */
152
+ export const AutoFixApplied = BusEvent.define("autofix.applied", z.object({
153
+ filePath: z.string(),
154
+ runnerId: z.string(),
155
+ fixesApplied: z.number(),
156
+ fixes: z.array(z.object({
157
+ line: z.number(),
158
+ message: z.string(),
159
+ })),
160
+ }));
161
+ /**
162
+ * Fired when session starts
163
+ * Published by: session_start handler
164
+ * Subscribed by: cache manager, file watcher
165
+ */
166
+ export const SessionStarted = BusEvent.define("session.started", z.object({
167
+ cwd: z.string(),
168
+ timestamp: z.number(),
169
+ }));
170
+ /**
171
+ * Fired when turn ends
172
+ * Published by: turn_end handler
173
+ * Subscribed by: batch processor, metrics
174
+ */
175
+ export const TurnEnded = BusEvent.define("turn.ended", z.object({
176
+ cwd: z.string(),
177
+ modifiedFiles: z.array(z.string()),
178
+ timestamp: z.number(),
179
+ }));
180
+ // --- Event Aggregator Helper ---
181
+ export class DiagnosticAggregator {
182
+ constructor() {
183
+ this.diagnostics = new Map();
184
+ this.unsubscribe = null;
185
+ }
186
+ start() {
187
+ this.unsubscribe = DiagnosticFound.subscribe((event) => {
188
+ const { filePath, diagnostics } = event.properties;
189
+ const existing = this.diagnostics.get(filePath) ?? [];
190
+ // Merge and dedupe by id
191
+ const merged = [...existing, ...diagnostics];
192
+ const unique = new Map(merged.map(d => [d.id, d]));
193
+ this.diagnostics.set(filePath, Array.from(unique.values()));
194
+ });
195
+ }
196
+ stop() {
197
+ this.unsubscribe?.();
198
+ this.unsubscribe = null;
199
+ }
200
+ getForFile(filePath) {
201
+ return this.diagnostics.get(filePath) ?? [];
202
+ }
203
+ getAll() {
204
+ return new Map(this.diagnostics);
205
+ }
206
+ clear(filePath) {
207
+ if (filePath) {
208
+ this.diagnostics.delete(filePath);
209
+ }
210
+ else {
211
+ this.diagnostics.clear();
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Diagnostic Event Types for pi-lens
3
+ *
4
+ * Standardized events for the bus system.
5
+ * These events flow through the system:
6
+ * - Runners publish DiagnosticFound events
7
+ * - LSP clients publish LspDiagnostic events
8
+ * - Aggregators subscribe and build reports
9
+ * - UI subscribes for real-time display
10
+ */
11
+
12
+ import { z } from "zod";
13
+ import { BusEvent } from "./bus.js";
14
+
15
+ // --- Shared Schemas ---
16
+
17
+ export const DiagnosticSeverity = z.enum(["error", "warning", "info", "hint"]);
18
+ export type DiagnosticSeverity = z.infer<typeof DiagnosticSeverity>;
19
+
20
+ export const OutputSemantic = z.enum([
21
+ "blocking", // Hard stop - must fix
22
+ "warning", // Soft stop - should fix
23
+ "fixed", // Auto-fix applied
24
+ "silent", // Track but don't display
25
+ "none", // No action needed
26
+ ]);
27
+ export type OutputSemantic = z.infer<typeof OutputSemantic>;
28
+
29
+ export const DiagnosticSchema = z.object({
30
+ id: z.string(), // Unique for deduplication
31
+ message: z.string(),
32
+ filePath: z.string(),
33
+ line: z.number().optional(),
34
+ column: z.number().optional(),
35
+ severity: DiagnosticSeverity,
36
+ semantic: OutputSemantic,
37
+ tool: z.string(), // Which tool produced this
38
+ rule: z.string().optional(),
39
+ fixable: z.boolean().optional(),
40
+ fixSuggestion: z.string().optional(),
41
+ });
42
+ export type Diagnostic = z.infer<typeof DiagnosticSchema>;
43
+
44
+ export const RunnerStatus = z.enum([
45
+ "starting",
46
+ "running",
47
+ "completed",
48
+ "failed",
49
+ "skipped",
50
+ ]);
51
+ export type RunnerStatus = z.infer<typeof RunnerStatus>;
52
+
53
+ // --- Event Definitions ---
54
+
55
+ /**
56
+ * Fired when a runner discovers diagnostics
57
+ * Published by: dispatch runners
58
+ * Subscribed by: aggregators, delta tracker, UI
59
+ */
60
+ export const DiagnosticFound = BusEvent.define(
61
+ "diagnostic.found",
62
+ z.object({
63
+ runnerId: z.string(),
64
+ filePath: z.string(),
65
+ diagnostics: z.array(DiagnosticSchema),
66
+ durationMs: z.number(),
67
+ }),
68
+ );
69
+
70
+ /**
71
+ * Fired when a file is modified
72
+ * Published by: tool_result handler, file watcher
73
+ * Subscribed by: runner scheduler, cache invalidator
74
+ */
75
+ export const FileModified = BusEvent.define(
76
+ "file.modified",
77
+ z.object({
78
+ filePath: z.string(),
79
+ content: z.string().optional(),
80
+ changeType: z.enum(["write", "edit", "external"]),
81
+ }),
82
+ );
83
+
84
+ /**
85
+ * Fired when a file is created
86
+ * Published by: file watcher
87
+ * Subscribed by: runner scheduler
88
+ */
89
+ export const FileCreated = BusEvent.define(
90
+ "file.created",
91
+ z.object({
92
+ filePath: z.string(),
93
+ }),
94
+ );
95
+
96
+ /**
97
+ * Fired when a file is deleted
98
+ * Published by: file watcher
99
+ * Subscribed by: cache invalidator
100
+ */
101
+ export const FileDeleted = BusEvent.define(
102
+ "file.deleted",
103
+ z.object({
104
+ filePath: z.string(),
105
+ }),
106
+ );
107
+
108
+ /**
109
+ * Fired when a runner starts execution
110
+ * Published by: dispatcher
111
+ * Subscribed by: progress UI, metrics
112
+ */
113
+ export const RunnerStarted = BusEvent.define(
114
+ "runner.started",
115
+ z.object({
116
+ runnerId: z.string(),
117
+ filePath: z.string(),
118
+ timestamp: z.number(),
119
+ }),
120
+ );
121
+
122
+ /**
123
+ * Fired when a runner completes
124
+ * Published by: dispatcher
125
+ * Subscribed by: progress UI, metrics aggregator
126
+ */
127
+ export const RunnerCompleted = BusEvent.define(
128
+ "runner.completed",
129
+ z.object({
130
+ runnerId: z.string(),
131
+ filePath: z.string(),
132
+ status: RunnerStatus,
133
+ durationMs: z.number(),
134
+ diagnosticCount: z.number(),
135
+ }),
136
+ );
137
+
138
+ /**
139
+ * Fired when LSP publishes diagnostics
140
+ * Published by: LSPClient (via textDocument/publishDiagnostics)
141
+ * Subscribed by: diagnostic aggregator
142
+ */
143
+ export const LspDiagnostic = BusEvent.define(
144
+ "lsp.diagnostic",
145
+ z.object({
146
+ serverId: z.string(), // e.g., "typescript", "pyright"
147
+ filePath: z.string(),
148
+ diagnostics: z.array(z.object({
149
+ severity: z.number(), // 1=error, 2=warn, 3=info, 4=hint
150
+ message: z.string(),
151
+ range: z.object({
152
+ start: z.object({ line: z.number(), character: z.number() }),
153
+ end: z.object({ line: z.number(), character: z.number() }),
154
+ }),
155
+ code: z.union([z.string(), z.number()]).optional(),
156
+ source: z.string().optional(),
157
+ })),
158
+ version: z.number().optional(), // Document version for debouncing
159
+ }),
160
+ );
161
+
162
+ /**
163
+ * Fired when baseline is updated (for delta mode)
164
+ * Published by: delta tracker
165
+ * Subscribed by: diagnostic filter
166
+ */
167
+ export const BaselineUpdated = BusEvent.define(
168
+ "baseline.updated",
169
+ z.object({
170
+ filePath: z.string(),
171
+ diagnosticIds: z.array(z.string()),
172
+ timestamp: z.number(),
173
+ }),
174
+ );
175
+
176
+ /**
177
+ * Fired when aggregated report is ready
178
+ * Published by: report aggregator
179
+ * Subscribed by: UI, commands
180
+ */
181
+ export const ReportReady = BusEvent.define(
182
+ "report.ready",
183
+ z.object({
184
+ filePath: z.string(),
185
+ report: z.object({
186
+ blockers: z.array(DiagnosticSchema),
187
+ warnings: z.array(DiagnosticSchema),
188
+ fixed: z.array(DiagnosticSchema),
189
+ silent: z.array(DiagnosticSchema),
190
+ }),
191
+ durationMs: z.number(),
192
+ }),
193
+ );
194
+
195
+ /**
196
+ * Fired when auto-fix is applied
197
+ * Published by: autofix runner
198
+ * Subscribed by: UI, file watcher
199
+ */
200
+ export const AutoFixApplied = BusEvent.define(
201
+ "autofix.applied",
202
+ z.object({
203
+ filePath: z.string(),
204
+ runnerId: z.string(),
205
+ fixesApplied: z.number(),
206
+ fixes: z.array(z.object({
207
+ line: z.number(),
208
+ message: z.string(),
209
+ })),
210
+ }),
211
+ );
212
+
213
+ /**
214
+ * Fired when session starts
215
+ * Published by: session_start handler
216
+ * Subscribed by: cache manager, file watcher
217
+ */
218
+ export const SessionStarted = BusEvent.define(
219
+ "session.started",
220
+ z.object({
221
+ cwd: z.string(),
222
+ timestamp: z.number(),
223
+ }),
224
+ );
225
+
226
+ /**
227
+ * Fired when turn ends
228
+ * Published by: turn_end handler
229
+ * Subscribed by: batch processor, metrics
230
+ */
231
+ export const TurnEnded = BusEvent.define(
232
+ "turn.ended",
233
+ z.object({
234
+ cwd: z.string(),
235
+ modifiedFiles: z.array(z.string()),
236
+ timestamp: z.number(),
237
+ }),
238
+ );
239
+
240
+ // --- Event Aggregator Helper ---
241
+
242
+ export class DiagnosticAggregator {
243
+ private diagnostics = new Map<string, Diagnostic[]>();
244
+ private unsubscribe: (() => void) | null = null;
245
+
246
+ start() {
247
+ this.unsubscribe = DiagnosticFound.subscribe((event) => {
248
+ const { filePath, diagnostics } = event.properties;
249
+ const existing = this.diagnostics.get(filePath) ?? [];
250
+
251
+ // Merge and dedupe by id
252
+ const merged = [...existing, ...diagnostics];
253
+ const unique = new Map(merged.map(d => [d.id, d]));
254
+
255
+ this.diagnostics.set(filePath, Array.from(unique.values()));
256
+ });
257
+ }
258
+
259
+ stop() {
260
+ this.unsubscribe?.();
261
+ this.unsubscribe = null;
262
+ }
263
+
264
+ getForFile(filePath: string): Diagnostic[] {
265
+ return this.diagnostics.get(filePath) ?? [];
266
+ }
267
+
268
+ getAll(): Map<string, Diagnostic[]> {
269
+ return new Map(this.diagnostics);
270
+ }
271
+
272
+ clear(filePath?: string) {
273
+ if (filePath) {
274
+ this.diagnostics.delete(filePath);
275
+ } else {
276
+ this.diagnostics.clear();
277
+ }
278
+ }
279
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Bus Module - Event-driven architecture for pi-lens
3
+ *
4
+ * Export all bus-related functionality.
5
+ */
6
+ export * from "./bus.js";
7
+ export * from "./events.js";
8
+ export * from "./integration.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Bus Module - Event-driven architecture for pi-lens
3
+ *
4
+ * Export all bus-related functionality.
5
+ */
6
+
7
+ export * from "./bus.js";
8
+ export * from "./events.js";
9
+ export * from "./integration.js";
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Bus Integration for pi-lens
3
+ *
4
+ * Connects the event bus system to the existing pi-lens architecture.
5
+ * This provides:
6
+ * - Event aggregation for diagnostic collection
7
+ * - Real-time progress tracking
8
+ * - Hook integration for tool_result handler
9
+ */
10
+ import { RunnerStarted, RunnerCompleted, ReportReady, FileModified, DiagnosticAggregator, } from "./events.js";
11
+ import { enableDebug } from "./bus.js";
12
+ const state = {
13
+ aggregator: new DiagnosticAggregator(),
14
+ runnerInProgress: new Set(),
15
+ lastReport: new Map(),
16
+ isEnabled: false,
17
+ };
18
+ // --- Event Subscribers ---
19
+ let unsubscribers = [];
20
+ /**
21
+ * Initialize the bus integration
22
+ * Call this from session_start handler
23
+ */
24
+ export function initBusIntegration(pi, options) {
25
+ if (state.isEnabled)
26
+ return; // Already initialized
27
+ if (options?.debug) {
28
+ enableDebug(true);
29
+ }
30
+ // Start the diagnostic aggregator
31
+ state.aggregator.start();
32
+ // Subscribe to runner progress events for UI feedback
33
+ const unsubRunnerStarted = RunnerStarted.subscribe((event) => {
34
+ const { runnerId, filePath } = event.properties;
35
+ state.runnerInProgress.add(`${runnerId}:${filePath}`);
36
+ });
37
+ const unsubRunnerCompleted = RunnerCompleted.subscribe((event) => {
38
+ const { runnerId, filePath, durationMs, diagnosticCount } = event.properties;
39
+ state.runnerInProgress.delete(`${runnerId}:${filePath}`);
40
+ // Log slow runners in debug mode
41
+ if (options?.debug && durationMs > 5000) {
42
+ console.error(`[bus] Slow runner: ${runnerId} took ${durationMs}ms for ${filePath}`);
43
+ }
44
+ // Log runners that found issues
45
+ if (diagnosticCount > 0) {
46
+ console.error(`[bus] ${runnerId} found ${diagnosticCount} issues in ${filePath}`);
47
+ }
48
+ });
49
+ // Cache reports for quick retrieval
50
+ const unsubReportReady = ReportReady.subscribe((event) => {
51
+ const { filePath, report, durationMs } = event.properties;
52
+ // Store the report
53
+ state.lastReport.set(filePath, {
54
+ output: formatReport(report, durationMs),
55
+ timestamp: Date.now(),
56
+ });
57
+ });
58
+ // Track file modifications to clear stale data
59
+ const unsubFileModified = FileModified.subscribe((event) => {
60
+ const { filePath } = event.properties;
61
+ // Clear cached report for modified file
62
+ state.lastReport.delete(filePath);
63
+ // Clear diagnostics aggregator for this file (will be repopulated)
64
+ state.aggregator.clear(filePath);
65
+ });
66
+ // Store unsubscribers for cleanup
67
+ unsubscribers = [
68
+ unsubRunnerStarted,
69
+ unsubRunnerCompleted,
70
+ unsubReportReady,
71
+ unsubFileModified,
72
+ ];
73
+ state.isEnabled = true;
74
+ console.error("[pi-lens] Bus integration initialized");
75
+ }
76
+ /**
77
+ * Shutdown the bus integration
78
+ * Call this when the extension is disabled
79
+ */
80
+ export function shutdownBusIntegration() {
81
+ if (!state.isEnabled)
82
+ return;
83
+ // Stop all subscribers
84
+ for (const unsub of unsubscribers) {
85
+ unsub();
86
+ }
87
+ unsubscribers = [];
88
+ // Stop the aggregator
89
+ state.aggregator.stop();
90
+ // Clear all state
91
+ state.runnerInProgress.clear();
92
+ state.lastReport.clear();
93
+ state.aggregator.clear();
94
+ state.isEnabled = false;
95
+ }
96
+ // --- Helper Functions ---
97
+ function formatReport(report, durationMs) {
98
+ const lines = [];
99
+ if (report.blockers.length > 0) {
100
+ lines.push(`🔴 STOP — ${report.blockers.length} issue(s) must be fixed:`);
101
+ for (const d of report.blockers.slice(0, 5)) {
102
+ const line = d.line ? `L${d.line}: ` : "";
103
+ lines.push(` ${line}${d.message.split("\n")[0]}`);
104
+ }
105
+ if (report.blockers.length > 5) {
106
+ lines.push(` ... and ${report.blockers.length - 5} more`);
107
+ }
108
+ }
109
+ if (report.fixed.length > 0) {
110
+ lines.push(`✅ Auto-fixed ${report.fixed.length} issue(s)`);
111
+ }
112
+ if (lines.length > 0) {
113
+ lines.push(`(completed in ${durationMs}ms)`);
114
+ }
115
+ return lines.join("\n");
116
+ }
117
+ // --- API for index.ts ---
118
+ /**
119
+ * Get aggregated diagnostics for a file
120
+ */
121
+ export function getDiagnosticsForFile(filePath) {
122
+ return state.aggregator.getForFile(filePath);
123
+ }
124
+ /**
125
+ * Get the last report output for a file
126
+ */
127
+ export function getLastReport(filePath) {
128
+ return state.lastReport.get(filePath)?.output;
129
+ }
130
+ /**
131
+ * Check if any runners are currently in progress for a file
132
+ */
133
+ export function hasRunnersInProgress(filePath) {
134
+ if (filePath) {
135
+ for (const key of state.runnerInProgress) {
136
+ if (key.endsWith(`:${filePath}`))
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ return state.runnerInProgress.size > 0;
142
+ }
143
+ /**
144
+ * Get list of runners in progress
145
+ */
146
+ export function getRunnersInProgress() {
147
+ return Array.from(state.runnerInProgress).map((key) => {
148
+ const [runnerId, filePath] = key.split(":");
149
+ return { runnerId, filePath };
150
+ });
151
+ }
152
+ /**
153
+ * Clear all cached data
154
+ */
155
+ export function clearBusCache() {
156
+ state.lastReport.clear();
157
+ state.aggregator.clear();
158
+ }