pi-lens 3.2.0 → 3.3.1

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 (77) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +4 -10
  3. package/clients/__tests__/file-time.test.js +216 -0
  4. package/clients/__tests__/format-service.test.js +245 -0
  5. package/clients/__tests__/formatters.test.js +271 -0
  6. package/clients/agent-behavior-client.test.js +94 -0
  7. package/clients/biome-client.test.js +144 -0
  8. package/clients/cache-manager.test.js +197 -0
  9. package/clients/complexity-client.test.js +234 -0
  10. package/clients/dependency-checker.test.js +60 -0
  11. package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
  12. package/clients/dispatch/__tests__/runner-registration.test.js +234 -0
  13. package/clients/dispatch/__tests__/runner-registration.test.ts +2 -2
  14. package/clients/dispatch/dispatcher.edge.test.js +82 -0
  15. package/clients/dispatch/dispatcher.format.test.js +46 -0
  16. package/clients/dispatch/dispatcher.inline.test.js +74 -0
  17. package/clients/dispatch/dispatcher.test.js +116 -0
  18. package/clients/dispatch/runners/architect.test.js +138 -0
  19. package/clients/dispatch/runners/ast-grep-napi.test.js +106 -0
  20. package/clients/dispatch/runners/lsp.js +42 -5
  21. package/clients/dispatch/runners/oxlint.test.js +230 -0
  22. package/clients/dispatch/runners/pyright.test.js +98 -0
  23. package/clients/dispatch/runners/python-slop.test.js +203 -0
  24. package/clients/dispatch/runners/scan_codebase.test.js +89 -0
  25. package/clients/dispatch/runners/shellcheck.test.js +98 -0
  26. package/clients/dispatch/runners/spellcheck.test.js +158 -0
  27. package/clients/dispatch/utils/format-utils.js +1 -6
  28. package/clients/dispatch/utils/format-utils.ts +1 -6
  29. package/clients/dogfood.test.js +201 -0
  30. package/clients/file-kinds.test.js +169 -0
  31. package/clients/formatters.js +1 -1
  32. package/clients/go-client.test.js +127 -0
  33. package/clients/jscpd-client.test.js +127 -0
  34. package/clients/knip-client.test.js +112 -0
  35. package/clients/lsp/__tests__/client.test.js +310 -0
  36. package/clients/lsp/__tests__/client.test.ts +1 -46
  37. package/clients/lsp/__tests__/config.test.js +167 -0
  38. package/clients/lsp/__tests__/error-recovery.test.js +213 -0
  39. package/clients/lsp/__tests__/integration.test.js +127 -0
  40. package/clients/lsp/__tests__/launch.test.js +313 -0
  41. package/clients/lsp/__tests__/server.test.js +259 -0
  42. package/clients/lsp/__tests__/service.test.js +435 -0
  43. package/clients/lsp/client.js +32 -44
  44. package/clients/lsp/client.ts +36 -45
  45. package/clients/lsp/launch.js +11 -6
  46. package/clients/lsp/launch.ts +11 -6
  47. package/clients/lsp/server.js +27 -2
  48. package/clients/metrics-client.test.js +141 -0
  49. package/clients/ruff-client.test.js +132 -0
  50. package/clients/rust-client.test.js +108 -0
  51. package/clients/sanitize.test.js +177 -0
  52. package/clients/secrets-scanner.test.js +100 -0
  53. package/clients/test-runner-client.test.js +192 -0
  54. package/clients/todo-scanner.test.js +301 -0
  55. package/clients/type-coverage-client.test.js +105 -0
  56. package/clients/typescript-client.codefix.test.js +157 -0
  57. package/clients/typescript-client.test.js +105 -0
  58. package/commands/rate.test.js +119 -0
  59. package/index.ts +66 -72
  60. package/package.json +1 -1
  61. package/clients/bus/bus.js +0 -191
  62. package/clients/bus/bus.ts +0 -251
  63. package/clients/bus/events.js +0 -214
  64. package/clients/bus/events.ts +0 -279
  65. package/clients/bus/index.js +0 -8
  66. package/clients/bus/index.ts +0 -9
  67. package/clients/bus/integration.js +0 -158
  68. package/clients/bus/integration.ts +0 -227
  69. package/clients/dispatch/bus-dispatcher.js +0 -178
  70. package/clients/dispatch/bus-dispatcher.ts +0 -258
  71. package/clients/services/__tests__/effect-integration.test.ts +0 -111
  72. package/clients/services/effect-integration.js +0 -198
  73. package/clients/services/effect-integration.ts +0 -276
  74. package/clients/services/index.js +0 -7
  75. package/clients/services/index.ts +0 -8
  76. package/clients/services/runner-service.js +0 -134
  77. package/clients/services/runner-service.ts +0 -225
@@ -1,251 +0,0 @@
1
- /**
2
- * Event Bus for pi-lens
3
- *
4
- * Decoupled pub/sub system for diagnostic events.
5
- * Enables loose coupling between diagnostic producers (runners, LSP clients)
6
- * and consumers (UI, aggregators, history trackers).
7
- */
8
-
9
- import type { z } from "zod";
10
-
11
- // --- Types ---
12
-
13
- export interface BusEvent<T = unknown> {
14
- type: string;
15
- properties: T;
16
- timestamp: number;
17
- }
18
-
19
- export type EventHandler<T> = (event: BusEvent<T>) => void | Promise<void>;
20
-
21
- // --- Internal State ---
22
-
23
- const subscribers = new Map<string, Set<EventHandler<unknown>>>();
24
- const globalMiddleware: Array<(event: BusEvent) => BusEvent | undefined> = [];
25
-
26
- let eventCount = 0;
27
- let isDebugEnabled = false;
28
-
29
- // --- Core Functions ---
30
-
31
- function debug(type: string, event: BusEvent) {
32
- if (!isDebugEnabled) return;
33
- const timestamp = new Date(event.timestamp).toISOString();
34
- console.error(`[bus] [${timestamp}] ${type}: ${event.type}`);
35
- }
36
-
37
- /**
38
- * Publish an event to all subscribers
39
- */
40
- export function publish<T>(event: BusEvent<T>): void {
41
- eventCount++;
42
- debug("publish", event as BusEvent<unknown>);
43
-
44
- // Run through middleware
45
- let currentEvent: BusEvent | undefined = event as BusEvent;
46
- for (const mw of globalMiddleware) {
47
- currentEvent = mw(currentEvent);
48
- if (!currentEvent) return; // Middleware cancelled the event
49
- }
50
-
51
- const handlers = subscribers.get(event.type);
52
- if (!handlers || handlers.size === 0) return;
53
-
54
- // Notify all subscribers (fire-and-forget for async handlers)
55
- for (const handler of handlers) {
56
- try {
57
- const result = handler(currentEvent as BusEvent<unknown>);
58
- if (result instanceof Promise) {
59
- result.catch((err) => {
60
- console.error(`[bus] async handler error for ${event.type}:`, err);
61
- });
62
- }
63
- } catch (err) {
64
- console.error(`[bus] handler error for ${event.type}:`, err);
65
- }
66
- }
67
- }
68
-
69
- /**
70
- * Subscribe to a specific event type
71
- * @returns Unsubscribe function
72
- */
73
- export function subscribe<T>(
74
- eventType: string,
75
- handler: EventHandler<T>,
76
- ): () => void {
77
- if (!subscribers.has(eventType)) {
78
- subscribers.set(eventType, new Set());
79
- }
80
-
81
- const handlers = subscribers.get(eventType)!;
82
- handlers.add(handler as EventHandler<unknown>);
83
-
84
- debug("subscribe", { type: eventType, properties: {}, timestamp: Date.now() });
85
-
86
- // Return unsubscribe function
87
- return () => {
88
- handlers.delete(handler as EventHandler<unknown>);
89
- if (handlers.size === 0) {
90
- subscribers.delete(eventType);
91
- }
92
- };
93
- }
94
-
95
- /**
96
- * Subscribe to an event type and automatically unsubscribe after first match
97
- */
98
- export function once<T>(
99
- eventType: string,
100
- predicate?: (event: BusEvent<T>) => boolean,
101
- ): Promise<BusEvent<T>> {
102
- return new Promise((resolve) => {
103
- const unsubscribe = subscribe<T>(eventType, (event) => {
104
- if (!predicate || predicate(event)) {
105
- unsubscribe();
106
- resolve(event);
107
- }
108
- });
109
- });
110
- }
111
-
112
- /**
113
- * Wait for an event with timeout
114
- */
115
- export function waitFor<T>(
116
- eventType: string,
117
- timeoutMs: number,
118
- predicate?: (event: BusEvent<T>) => boolean,
119
- ): Promise<BusEvent<T> | undefined> {
120
- return new Promise((resolve) => {
121
- const timer = setTimeout(() => {
122
- unsubscribe();
123
- resolve(undefined);
124
- }, timeoutMs);
125
-
126
- const unsubscribe = subscribe<T>(eventType, (event) => {
127
- if (!predicate || predicate(event)) {
128
- clearTimeout(timer);
129
- unsubscribe();
130
- resolve(event);
131
- }
132
- });
133
- });
134
- }
135
-
136
- // --- Middleware ---
137
-
138
- export function addMiddleware(
139
- mw: (event: BusEvent) => BusEvent | undefined,
140
- ): () => void {
141
- globalMiddleware.push(mw);
142
- return () => {
143
- const idx = globalMiddleware.indexOf(mw);
144
- if (idx !== -1) globalMiddleware.splice(idx, 1);
145
- };
146
- }
147
-
148
- // --- Utilities ---
149
-
150
- export function enableDebug(enabled = true): void {
151
- isDebugEnabled = enabled;
152
- }
153
-
154
- export function getStats(): {
155
- subscriberCount: number;
156
- eventTypes: string[];
157
- totalEvents: number;
158
- } {
159
- return {
160
- subscriberCount: Array.from(subscribers.values()).reduce(
161
- (sum, set) => sum + set.size,
162
- 0,
163
- ),
164
- eventTypes: Array.from(subscribers.keys()),
165
- totalEvents: eventCount,
166
- };
167
- }
168
-
169
- export function clearAllSubscribers(): void {
170
- subscribers.clear();
171
- }
172
-
173
- // --- Event Factory ---
174
-
175
- export interface EventDefinition<T> {
176
- type: string;
177
- create(properties: T): BusEvent<T>;
178
- subscribe(handler: EventHandler<T>): () => void;
179
- publish(properties: T): void;
180
- }
181
-
182
- export namespace BusEvent {
183
- /**
184
- * Define a typed event type with Zod schema validation
185
- */
186
- export function define<T>(
187
- type: string,
188
- _schema: z.ZodType<T>,
189
- ): EventDefinition<T> {
190
- return {
191
- type,
192
- create(properties): BusEvent<T> {
193
- return {
194
- type,
195
- properties,
196
- timestamp: Date.now(),
197
- };
198
- },
199
- subscribe(handler: EventHandler<T>): () => void {
200
- return subscribe(type, handler);
201
- },
202
- publish(properties: T): void {
203
- publish({
204
- type,
205
- properties,
206
- timestamp: Date.now(),
207
- });
208
- },
209
- };
210
- }
211
-
212
- /**
213
- * Create a simple event type without schema (for internal use)
214
- */
215
- export function defineSimple<T>(type: string): EventDefinition<T> {
216
- return {
217
- type,
218
- create(properties: T): BusEvent<T> {
219
- return {
220
- type,
221
- properties,
222
- timestamp: Date.now(),
223
- };
224
- },
225
- subscribe(handler: EventHandler<T>): () => void {
226
- return subscribe(type, handler);
227
- },
228
- publish(properties: T): void {
229
- publish({
230
- type,
231
- properties,
232
- timestamp: Date.now(),
233
- });
234
- },
235
- };
236
- }
237
-
238
- /**
239
- * Create a raw event (helper)
240
- */
241
- export function create<T>(type: string, properties: T): BusEvent<T> {
242
- return {
243
- type,
244
- properties,
245
- timestamp: Date.now(),
246
- };
247
- }
248
- }
249
-
250
- // --- Re-export for convenience ---
251
- export { subscribe as on, publish as emit };
@@ -1,214 +0,0 @@
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
- }