@the-magic-tower/fixhive-opencode-plugin 0.1.12 → 0.1.14

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.
package/dist/index.js CHANGED
@@ -1,3 +1,22 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = import.meta.require;
19
+
1
20
  // src/plugin/index.ts
2
21
  import { tool as tool2 } from "@opencode-ai/plugin";
3
22
  import { existsSync as existsSync2, readFileSync } from "fs";
@@ -5,10 +24,6 @@ import { join } from "path";
5
24
 
6
25
  // src/core/privacy-filter.ts
7
26
  var DEFAULT_FILTER_RULES = [
8
- // =====================================
9
- // SECRETS (Critical - Always Filter)
10
- // =====================================
11
- // AWS Keys
12
27
  {
13
28
  name: "aws_access_key",
14
29
  category: "secret",
@@ -16,7 +31,6 @@ var DEFAULT_FILTER_RULES = [
16
31
  replacement: "[AWS_KEY_REDACTED]",
17
32
  priority: 100
18
33
  },
19
- // OpenAI API Keys
20
34
  {
21
35
  name: "openai_key",
22
36
  category: "secret",
@@ -24,7 +38,6 @@ var DEFAULT_FILTER_RULES = [
24
38
  replacement: "[OPENAI_KEY_REDACTED]",
25
39
  priority: 100
26
40
  },
27
- // GitHub Tokens
28
41
  {
29
42
  name: "github_token",
30
43
  category: "secret",
@@ -32,7 +45,6 @@ var DEFAULT_FILTER_RULES = [
32
45
  replacement: "[GITHUB_TOKEN_REDACTED]",
33
46
  priority: 100
34
47
  },
35
- // Google API Keys
36
48
  {
37
49
  name: "google_api_key",
38
50
  category: "secret",
@@ -40,7 +52,6 @@ var DEFAULT_FILTER_RULES = [
40
52
  replacement: "[GOOGLE_API_KEY_REDACTED]",
41
53
  priority: 100
42
54
  },
43
- // Stripe Keys
44
55
  {
45
56
  name: "stripe_key",
46
57
  category: "secret",
@@ -48,7 +59,6 @@ var DEFAULT_FILTER_RULES = [
48
59
  replacement: "[STRIPE_KEY_REDACTED]",
49
60
  priority: 100
50
61
  },
51
- // JWT Tokens (limited length to prevent ReDoS)
52
62
  {
53
63
  name: "jwt_token",
54
64
  category: "secret",
@@ -56,7 +66,6 @@ var DEFAULT_FILTER_RULES = [
56
66
  replacement: "[JWT_REDACTED]",
57
67
  priority: 100
58
68
  },
59
- // Bearer Tokens
60
69
  {
61
70
  name: "bearer_token",
62
71
  category: "secret",
@@ -64,7 +73,6 @@ var DEFAULT_FILTER_RULES = [
64
73
  replacement: "$1 [TOKEN_REDACTED]",
65
74
  priority: 100
66
75
  },
67
- // Private Keys
68
76
  {
69
77
  name: "private_key",
70
78
  category: "secret",
@@ -72,7 +80,6 @@ var DEFAULT_FILTER_RULES = [
72
80
  replacement: "[PRIVATE_KEY_REDACTED]",
73
81
  priority: 100
74
82
  },
75
- // Generic API Keys (context-based, limited length to prevent ReDoS)
76
83
  {
77
84
  name: "generic_api_key",
78
85
  category: "secret",
@@ -80,7 +87,6 @@ var DEFAULT_FILTER_RULES = [
80
87
  replacement: "$1=[KEY_REDACTED]",
81
88
  priority: 95
82
89
  },
83
- // Secret/Password assignments (limited length to prevent ReDoS)
84
90
  {
85
91
  name: "secret_assignment",
86
92
  category: "secret",
@@ -88,10 +94,6 @@ var DEFAULT_FILTER_RULES = [
88
94
  replacement: "$1=[REDACTED]",
89
95
  priority: 90
90
96
  },
91
- // =====================================
92
- // IDENTITY (High Risk)
93
- // =====================================
94
- // Email Addresses
95
97
  {
96
98
  name: "email",
97
99
  category: "identity",
@@ -99,10 +101,6 @@ var DEFAULT_FILTER_RULES = [
99
101
  replacement: "[EMAIL_REDACTED]",
100
102
  priority: 80
101
103
  },
102
- // =====================================
103
- // INFRASTRUCTURE (Medium Risk)
104
- // =====================================
105
- // Database Connection Strings
106
104
  {
107
105
  name: "db_connection",
108
106
  category: "infrastructure",
@@ -110,7 +108,6 @@ var DEFAULT_FILTER_RULES = [
110
108
  replacement: "$1://[CONNECTION_REDACTED]",
111
109
  priority: 85
112
110
  },
113
- // Internal URLs
114
111
  {
115
112
  name: "internal_url",
116
113
  category: "infrastructure",
@@ -118,7 +115,6 @@ var DEFAULT_FILTER_RULES = [
118
115
  replacement: "[INTERNAL_URL_REDACTED]",
119
116
  priority: 75
120
117
  },
121
- // IP Addresses (except localhost and common dev IPs)
122
118
  {
123
119
  name: "ipv4",
124
120
  category: "infrastructure",
@@ -131,10 +127,6 @@ var DEFAULT_FILTER_RULES = [
131
127
  },
132
128
  priority: 70
133
129
  },
134
- // =====================================
135
- // FILE PATHS (Context-dependent)
136
- // =====================================
137
- // Home Directory Paths (macOS)
138
130
  {
139
131
  name: "macos_home_path",
140
132
  category: "path",
@@ -142,7 +134,6 @@ var DEFAULT_FILTER_RULES = [
142
134
  replacement: "~",
143
135
  priority: 60
144
136
  },
145
- // Home Directory Paths (Linux)
146
137
  {
147
138
  name: "linux_home_path",
148
139
  category: "path",
@@ -150,7 +141,6 @@ var DEFAULT_FILTER_RULES = [
150
141
  replacement: "~",
151
142
  priority: 60
152
143
  },
153
- // Home Directory Paths (Windows)
154
144
  {
155
145
  name: "windows_home_path",
156
146
  category: "path",
@@ -158,10 +148,6 @@ var DEFAULT_FILTER_RULES = [
158
148
  replacement: "~",
159
149
  priority: 60
160
150
  },
161
- // =====================================
162
- // ENVIRONMENT (Medium Risk)
163
- // =====================================
164
- // Sensitive Environment Variables
165
151
  {
166
152
  name: "env_var_value",
167
153
  category: "environment",
@@ -189,16 +175,12 @@ var DEFAULT_FILTER_RULES = [
189
175
  priority: 65
190
176
  }
191
177
  ];
192
- var PrivacyFilter = class {
178
+
179
+ class PrivacyFilter {
193
180
  rules;
194
181
  constructor(customRules) {
195
- this.rules = [...DEFAULT_FILTER_RULES, ...customRules || []].sort(
196
- (a, b) => b.priority - a.priority
197
- );
182
+ this.rules = [...DEFAULT_FILTER_RULES, ...customRules || []].sort((a, b) => b.priority - a.priority);
198
183
  }
199
- /**
200
- * Sanitize content by applying all filter rules
201
- */
202
184
  sanitize(content, context) {
203
185
  let result = content;
204
186
  const appliedFilters = [];
@@ -228,9 +210,6 @@ var PrivacyFilter = class {
228
210
  appliedFilters: [...new Set(appliedFilters)]
229
211
  };
230
212
  }
231
- /**
232
- * Generalize file paths while keeping meaningful structure
233
- */
234
213
  generalizePaths(content, context) {
235
214
  let result = content;
236
215
  if (context.projectRoot) {
@@ -245,16 +224,10 @@ var PrivacyFilter = class {
245
224
  result = result.replace(/site-packages\/[\w.-]+\//g, "site-packages/<PKG>/");
246
225
  return result;
247
226
  }
248
- /**
249
- * Add a custom filter rule
250
- */
251
227
  addRule(rule) {
252
228
  this.rules.push(rule);
253
229
  this.rules.sort((a, b) => b.priority - a.priority);
254
230
  }
255
- /**
256
- * Remove a filter rule by name
257
- */
258
231
  removeRule(name) {
259
232
  const index = this.rules.findIndex((r) => r.name === name);
260
233
  if (index !== -1) {
@@ -263,16 +236,9 @@ var PrivacyFilter = class {
263
236
  }
264
237
  return false;
265
238
  }
266
- /**
267
- * Get all current rules
268
- */
269
239
  getRules() {
270
240
  return this.rules;
271
241
  }
272
- /**
273
- * Check if content contains sensitive data
274
- * Note: Always reset regex lastIndex BEFORE testing to prevent state pollution
275
- */
276
242
  containsSensitiveData(content) {
277
243
  for (const rule of this.rules) {
278
244
  if (rule.category === "secret") {
@@ -286,13 +252,13 @@ var PrivacyFilter = class {
286
252
  }
287
253
  return false;
288
254
  }
289
- };
255
+ }
290
256
  function createFilterContext(projectDirectory) {
291
257
  const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
292
258
  return {
293
259
  projectRoot: projectDirectory,
294
260
  homeDir,
295
- commonPaths: /* @__PURE__ */ new Map([
261
+ commonPaths: new Map([
296
262
  ["/usr/local/lib/", "<LIB>/"],
297
263
  ["/usr/lib/", "<LIB>/"],
298
264
  ["/var/log/", "<LOG>/"],
@@ -301,11 +267,10 @@ function createFilterContext(projectDirectory) {
301
267
  ])
302
268
  };
303
269
  }
304
- var defaultPrivacyFilter = new PrivacyFilter();
270
+ var defaultPrivacyFilter = new PrivacyFilter;
305
271
 
306
272
  // src/core/error-detector.ts
307
273
  var ERROR_PATTERNS = {
308
- // Universal error indicators
309
274
  universal: [
310
275
  /\b(error|failed|failure|fatal|exception|panic)\b/i,
311
276
  /\b(cannot|could not|unable to|couldn't)\b/i,
@@ -316,36 +281,26 @@ var ERROR_PATTERNS = {
316
281
  /\b(segmentation fault|segfault|core dumped)\b/i,
317
282
  /\b(out of memory|oom|memory allocation failed)\b/i
318
283
  ],
319
- // Error prefixes
320
284
  prefixed: [
321
285
  /^Error:/m,
322
286
  /^ERROR\s/m,
323
287
  /^E\s+\d+:/m,
324
- // Rust errors
325
288
  /^\[ERROR\]/m,
326
289
  /^fatal:/m,
327
290
  /^FATAL:/m,
328
291
  /^panic:/m,
329
292
  /^Traceback \(most recent call last\):/m,
330
- // Python
331
293
  /^Exception in thread/m,
332
- // Java
333
294
  /^Uncaught \w+Error:/m
334
- // JavaScript
335
295
  ],
336
- // Build/compilation errors
337
296
  build: [
338
297
  /^.+:\d+:\d+:\s*error:/m,
339
- // GCC/Clang format
340
298
  /error\[E\d+\]:/m,
341
- // Rust compiler
342
299
  /error TS\d+:/m,
343
- // TypeScript
344
300
  /SyntaxError:/m,
345
301
  /ParseError:/m,
346
302
  /CompileError:/m
347
303
  ],
348
- // Package manager errors
349
304
  package: [
350
305
  /npm ERR!/m,
351
306
  /npm error/m,
@@ -358,9 +313,7 @@ var ERROR_PATTERNS = {
358
313
  /Cannot find module/m,
359
314
  /Module not found/m
360
315
  ],
361
- // Permission errors
362
316
  permission: [/EACCES:/m, /EPERM:/m, /Permission denied/m, /Access denied/m, /Insufficient permissions/m],
363
- // Network errors
364
317
  network: [
365
318
  /ECONNREFUSED/m,
366
319
  /ETIMEDOUT/m,
@@ -369,7 +322,6 @@ var ERROR_PATTERNS = {
369
322
  /Connection refused/m,
370
323
  /Network is unreachable/m
371
324
  ],
372
- // Test failures
373
325
  test: [/FAIL /m, /AssertionError/m, /Expected .+ but got/m, /Test failed/i, /\d+ failing/m]
374
326
  };
375
327
  var STACK_TRACE_PATTERNS = {
@@ -384,37 +336,26 @@ var STACK_TRACE_PATTERNS = {
384
336
  };
385
337
  var EXIT_CODE_SEVERITY = {
386
338
  1: "error",
387
- // General errors
388
339
  2: "error",
389
- // Misuse of shell builtins
390
340
  126: "error",
391
- // Command cannot execute
392
341
  127: "error",
393
- // Command not found
394
342
  128: "critical",
395
- // Invalid exit argument
396
343
  130: "warning",
397
- // Script terminated by Ctrl+C
398
344
  137: "critical",
399
- // SIGKILL (OOM, etc.)
400
345
  139: "critical",
401
- // Segmentation fault
402
346
  143: "warning"
403
- // SIGTERM
404
347
  };
405
- var ErrorDetector = class {
348
+
349
+ class ErrorDetector {
406
350
  privacyFilter;
407
351
  constructor(privacyFilter) {
408
- this.privacyFilter = privacyFilter || new PrivacyFilter();
352
+ this.privacyFilter = privacyFilter || new PrivacyFilter;
409
353
  }
410
- /**
411
- * Detect if output contains an error
412
- */
413
354
  detect(toolOutput) {
414
355
  const signals = [];
415
356
  const combinedOutput = `${toolOutput.output || ""}
416
357
  ${toolOutput.stderr || ""}`;
417
- if (toolOutput.exitCode !== void 0 && toolOutput.exitCode !== 0) {
358
+ if (toolOutput.exitCode !== undefined && toolOutput.exitCode !== 0) {
418
359
  const severity2 = EXIT_CODE_SEVERITY[toolOutput.exitCode] || "error";
419
360
  signals.push({
420
361
  type: "exit_code",
@@ -440,7 +381,8 @@ ${toolOutput.stderr || ""}`;
440
381
  signals.push({
441
382
  type: "stack_trace",
442
383
  weight: 0.95,
443
- value: stackTrace.frames.slice(0, 5).join("\n"),
384
+ value: stackTrace.frames.slice(0, 5).join(`
385
+ `),
444
386
  description: `${stackTrace.language} stack trace detected`
445
387
  });
446
388
  }
@@ -450,8 +392,8 @@ ${toolOutput.stderr || ""}`;
450
392
  const severity = this.determineSeverity(signals, toolOutput.exitCode);
451
393
  const { message, stack } = this.extractErrorDetails(combinedOutput);
452
394
  const sanitizedMessage = this.privacyFilter.sanitize(message);
453
- const sanitizedStack = stack ? this.privacyFilter.sanitize(stack) : void 0;
454
- const sanitizedOutput = this.privacyFilter.sanitize(combinedOutput.substring(0, 5e3));
395
+ const sanitizedStack = stack ? this.privacyFilter.sanitize(stack) : undefined;
396
+ const sanitizedOutput = this.privacyFilter.sanitize(combinedOutput.substring(0, 5000));
455
397
  return {
456
398
  detected,
457
399
  confidence,
@@ -463,15 +405,9 @@ ${toolOutput.stderr || ""}`;
463
405
  rawOutput: sanitizedOutput.sanitized
464
406
  };
465
407
  }
466
- /**
467
- * Check if content contains error keywords
468
- */
469
408
  containsErrorKeywords(content) {
470
409
  return ERROR_PATTERNS.universal.some((p) => p.test(content));
471
410
  }
472
- /**
473
- * Detect error patterns in content
474
- */
475
411
  detectErrorPatterns(content) {
476
412
  const signals = [];
477
413
  const weights = {
@@ -483,7 +419,8 @@ ${toolOutput.stderr || ""}`;
483
419
  test: 0.7
484
420
  };
485
421
  for (const [category, patterns] of Object.entries(ERROR_PATTERNS)) {
486
- if (category === "universal") continue;
422
+ if (category === "universal")
423
+ continue;
487
424
  for (const pattern of patterns) {
488
425
  const match = content.match(pattern);
489
426
  if (match) {
@@ -499,9 +436,6 @@ ${toolOutput.stderr || ""}`;
499
436
  }
500
437
  return signals;
501
438
  }
502
- /**
503
- * Detect stack traces in content
504
- */
505
439
  detectStackTrace(content) {
506
440
  for (const [language, pattern] of Object.entries(STACK_TRACE_PATTERNS)) {
507
441
  const globalPattern = new RegExp(pattern.source, "gm");
@@ -520,49 +454,50 @@ ${toolOutput.stderr || ""}`;
520
454
  frames: []
521
455
  };
522
456
  }
523
- /**
524
- * Calculate confidence score from signals
525
- */
526
457
  calculateConfidence(signals) {
527
- if (signals.length === 0) return 0;
458
+ if (signals.length === 0)
459
+ return 0;
528
460
  const totalWeight = signals.reduce((sum, s) => sum + s.weight, 0);
529
461
  const avgWeight = totalWeight / signals.length;
530
462
  const multiplier = Math.min(1.2, 1 + signals.length * 0.05);
531
463
  return Math.min(1, avgWeight * multiplier);
532
464
  }
533
- /**
534
- * Classify error type based on signals and content
535
- */
536
465
  classifyErrorType(signals, content) {
537
- if (ERROR_PATTERNS.build.some((p) => p.test(content))) return "build";
538
- if (ERROR_PATTERNS.package.some((p) => p.test(content))) return "dependency";
539
- if (ERROR_PATTERNS.permission.some((p) => p.test(content))) return "permission";
540
- if (ERROR_PATTERNS.network.some((p) => p.test(content))) return "network";
541
- if (ERROR_PATTERNS.test.some((p) => p.test(content))) return "test";
542
- if (/TypeError:|type error|Type '[^']+' is not assignable/i.test(content)) return "type_error";
543
- if (/SyntaxError:|syntax error|unexpected token/i.test(content)) return "syntax";
544
- if (/ReferenceError:|RangeError:|runtime error/i.test(content)) return "runtime";
466
+ if (ERROR_PATTERNS.build.some((p) => p.test(content)))
467
+ return "build";
468
+ if (ERROR_PATTERNS.package.some((p) => p.test(content)))
469
+ return "dependency";
470
+ if (ERROR_PATTERNS.permission.some((p) => p.test(content)))
471
+ return "permission";
472
+ if (ERROR_PATTERNS.network.some((p) => p.test(content)))
473
+ return "network";
474
+ if (ERROR_PATTERNS.test.some((p) => p.test(content)))
475
+ return "test";
476
+ if (/TypeError:|type error|Type '[^']+' is not assignable/i.test(content))
477
+ return "type_error";
478
+ if (/SyntaxError:|syntax error|unexpected token/i.test(content))
479
+ return "syntax";
480
+ if (/ReferenceError:|RangeError:|runtime error/i.test(content))
481
+ return "runtime";
545
482
  const hasStackTrace = signals.some((s) => s.type === "stack_trace");
546
- if (hasStackTrace) return "runtime";
483
+ if (hasStackTrace)
484
+ return "runtime";
547
485
  return "unknown";
548
486
  }
549
- /**
550
- * Determine severity from signals and exit code
551
- */
552
487
  determineSeverity(signals, exitCode) {
553
- if (exitCode !== void 0 && EXIT_CODE_SEVERITY[exitCode]) {
488
+ if (exitCode !== undefined && EXIT_CODE_SEVERITY[exitCode]) {
554
489
  return EXIT_CODE_SEVERITY[exitCode];
555
490
  }
556
491
  const maxWeight = Math.max(...signals.map((s) => s.weight));
557
- if (maxWeight >= 0.9) return "error";
558
- if (maxWeight >= 0.7) return "error";
492
+ if (maxWeight >= 0.9)
493
+ return "error";
494
+ if (maxWeight >= 0.7)
495
+ return "error";
559
496
  return "warning";
560
497
  }
561
- /**
562
- * Extract error message and stack from output
563
- */
564
498
  extractErrorDetails(output) {
565
- const lines = output.split("\n");
499
+ const lines = output.split(`
500
+ `);
566
501
  let message = "";
567
502
  let stack = "";
568
503
  let inStack = false;
@@ -570,11 +505,9 @@ ${toolOutput.stderr || ""}`;
570
505
  if (this.isErrorLine(line) && !message) {
571
506
  message = line.trim();
572
507
  inStack = true;
573
- } else if (inStack && (line.match(/^\s+at\s/) || // JS stack
574
- line.match(/^\s+File\s/) || // Python stack
575
- line.match(/^\s+\d+:\s/) || // Rust stack
576
- line.match(/^\s+from\s/))) {
577
- stack += line + "\n";
508
+ } else if (inStack && (line.match(/^\s+at\s/) || line.match(/^\s+File\s/) || line.match(/^\s+\d+:\s/) || line.match(/^\s+from\s/))) {
509
+ stack += line + `
510
+ `;
578
511
  }
579
512
  }
580
513
  if (!message) {
@@ -587,18 +520,15 @@ ${toolOutput.stderr || ""}`;
587
520
  }
588
521
  return {
589
522
  message: message || output.substring(0, 500),
590
- stack: stack || void 0
523
+ stack: stack || undefined
591
524
  };
592
525
  }
593
- /**
594
- * Check if a line looks like an error message
595
- */
596
526
  isErrorLine(line) {
597
527
  const trimmed = line.trim();
598
528
  return /^(Error|TypeError|ReferenceError|SyntaxError|RangeError):/i.test(trimmed) || /^(error|FAIL|fatal|panic)\b/i.test(trimmed) || /^error\[E\d+\]:/.test(trimmed) || /^error TS\d+:/.test(trimmed);
599
529
  }
600
- };
601
- var defaultErrorDetector = new ErrorDetector();
530
+ }
531
+ var defaultErrorDetector = new ErrorDetector;
602
532
 
603
533
  // src/storage/local-store.ts
604
534
  import Database from "better-sqlite3";
@@ -642,8 +572,9 @@ function calculateStringSimilarity(str1, str2) {
642
572
  const words1 = new Set(str1.toLowerCase().split(/\s+/));
643
573
  const words2 = new Set(str2.toLowerCase().split(/\s+/));
644
574
  const intersection = new Set([...words1].filter((x) => words2.has(x)));
645
- const union = /* @__PURE__ */ new Set([...words1, ...words2]);
646
- if (union.size === 0) return 0;
575
+ const union = new Set([...words1, ...words2]);
576
+ if (union.size === 0)
577
+ return 0;
647
578
  return intersection.size / union.size;
648
579
  }
649
580
 
@@ -656,9 +587,7 @@ function runMigrations(db) {
656
587
  applied_at TEXT DEFAULT CURRENT_TIMESTAMP
657
588
  )
658
589
  `);
659
- const appliedMigrations = new Set(
660
- db.prepare("SELECT name FROM migrations").all().map((r) => r.name)
661
- );
590
+ const appliedMigrations = new Set(db.prepare("SELECT name FROM migrations").all().map((r) => r.name));
662
591
  for (const migration of MIGRATIONS) {
663
592
  if (!appliedMigrations.has(migration.name)) {
664
593
  db.exec(migration.sql);
@@ -740,7 +669,7 @@ var MIGRATIONS = [
740
669
  ];
741
670
 
742
671
  // src/storage/local-store.ts
743
- var LocalStore = class _LocalStore {
672
+ class LocalStore {
744
673
  db;
745
674
  constructor(projectDirectory) {
746
675
  const dbPath = `${projectDirectory}/.fixhive/fixhive.db`;
@@ -753,10 +682,6 @@ var LocalStore = class _LocalStore {
753
682
  this.db.pragma("foreign_keys = ON");
754
683
  runMigrations(this.db);
755
684
  }
756
- // ============ Error Records ============
757
- /**
758
- * Create a new error record
759
- */
760
685
  createErrorRecord(data) {
761
686
  const id = uuidv4();
762
687
  const errorHash = generateErrorFingerprint(data.errorMessage, data.errorStack);
@@ -784,17 +709,11 @@ var LocalStore = class _LocalStore {
784
709
  this.incrementStat("total_errors");
785
710
  return this.getErrorById(id);
786
711
  }
787
- /**
788
- * Get error record by ID
789
- */
790
712
  getErrorById(id) {
791
713
  const stmt = this.db.prepare("SELECT * FROM error_records WHERE id = ?");
792
714
  const row = stmt.get(id);
793
715
  return row ? this.rowToRecord(row) : null;
794
716
  }
795
- /**
796
- * Get errors by session
797
- */
798
717
  getSessionErrors(sessionId, options) {
799
718
  let query = "SELECT * FROM error_records WHERE session_id = ?";
800
719
  const params = [sessionId];
@@ -810,24 +729,13 @@ var LocalStore = class _LocalStore {
810
729
  const stmt = this.db.prepare(query);
811
730
  return stmt.all(...params).map((row) => this.rowToRecord(row));
812
731
  }
813
- /**
814
- * Get unresolved errors for a session
815
- */
816
732
  getUnresolvedErrors(sessionId) {
817
733
  return this.getSessionErrors(sessionId, { status: "unresolved" });
818
734
  }
819
- /**
820
- * Get recent errors across all sessions
821
- */
822
735
  getRecentErrors(limit = 10) {
823
- const stmt = this.db.prepare(
824
- "SELECT * FROM error_records ORDER BY created_at DESC LIMIT ?"
825
- );
736
+ const stmt = this.db.prepare("SELECT * FROM error_records ORDER BY created_at DESC LIMIT ?");
826
737
  return stmt.all(limit).map((row) => this.rowToRecord(row));
827
738
  }
828
- /**
829
- * Mark error as resolved
830
- */
831
739
  markResolved(id, data) {
832
740
  const stmt = this.db.prepare(`
833
741
  UPDATE error_records
@@ -844,9 +752,6 @@ var LocalStore = class _LocalStore {
844
752
  }
845
753
  return null;
846
754
  }
847
- /**
848
- * Mark error as uploaded to cloud
849
- */
850
755
  markUploaded(id, cloudKnowledgeId) {
851
756
  const stmt = this.db.prepare(`
852
757
  UPDATE error_records
@@ -860,19 +765,10 @@ var LocalStore = class _LocalStore {
860
765
  this.incrementStat("uploaded_errors");
861
766
  }
862
767
  }
863
- /**
864
- * Find similar errors by hash
865
- */
866
768
  findSimilarErrors(errorHash) {
867
- const stmt = this.db.prepare(
868
- "SELECT * FROM error_records WHERE error_hash = ? ORDER BY created_at DESC"
869
- );
769
+ const stmt = this.db.prepare("SELECT * FROM error_records WHERE error_hash = ? ORDER BY created_at DESC");
870
770
  return stmt.all(errorHash).map((row) => this.rowToRecord(row));
871
771
  }
872
- // ============ Query Cache ============
873
- /**
874
- * Get cached query results
875
- */
876
772
  getCachedResults(errorHash) {
877
773
  const stmt = this.db.prepare(`
878
774
  SELECT results FROM query_cache
@@ -885,10 +781,7 @@ var LocalStore = class _LocalStore {
885
781
  }
886
782
  return null;
887
783
  }
888
- /**
889
- * Cache query results
890
- */
891
- cacheResults(errorHash, results, expirationMs = 36e5) {
784
+ cacheResults(errorHash, results, expirationMs = 3600000) {
892
785
  const id = uuidv4();
893
786
  const expiresAt = new Date(Date.now() + expirationMs).toISOString();
894
787
  const stmt = this.db.prepare(`
@@ -898,22 +791,13 @@ var LocalStore = class _LocalStore {
898
791
  stmt.run(id, errorHash, JSON.stringify(results), expiresAt);
899
792
  this.incrementStat("queries_made");
900
793
  }
901
- /**
902
- * Clear expired cache entries
903
- */
904
794
  clearExpiredCache() {
905
795
  const stmt = this.db.prepare("DELETE FROM query_cache WHERE expires_at <= datetime('now')");
906
796
  const result = stmt.run();
907
797
  return result.changes;
908
798
  }
909
- // ============ Statistics ============
910
- /**
911
- * Get usage statistics
912
- */
913
799
  getStats() {
914
- const stmt = this.db.prepare(
915
- "SELECT total_errors, resolved_errors, uploaded_errors FROM usage_stats WHERE id = 1"
916
- );
800
+ const stmt = this.db.prepare("SELECT total_errors, resolved_errors, uploaded_errors FROM usage_stats WHERE id = 1");
917
801
  const row = stmt.get();
918
802
  return {
919
803
  totalErrors: row.total_errors,
@@ -921,92 +805,66 @@ var LocalStore = class _LocalStore {
921
805
  uploadedErrors: row.uploaded_errors
922
806
  };
923
807
  }
924
- /**
925
- * Allowed stat column names for incrementStat (whitelist to prevent SQL injection)
926
- */
927
808
  static ALLOWED_STATS = [
928
809
  "total_errors",
929
810
  "resolved_errors",
930
811
  "uploaded_errors",
931
812
  "queries_made"
932
813
  ];
933
- /**
934
- * Increment a stat counter
935
- * @throws Error if stat name is not in the allowed whitelist
936
- */
937
814
  incrementStat(stat) {
938
- if (!_LocalStore.ALLOWED_STATS.includes(stat)) {
939
- throw new Error(`Invalid stat name: ${stat}. Allowed: ${_LocalStore.ALLOWED_STATS.join(", ")}`);
815
+ if (!LocalStore.ALLOWED_STATS.includes(stat)) {
816
+ throw new Error(`Invalid stat name: ${stat}. Allowed: ${LocalStore.ALLOWED_STATS.join(", ")}`);
940
817
  }
941
818
  const stmt = this.db.prepare(`UPDATE usage_stats SET ${stat} = ${stat} + 1 WHERE id = 1`);
942
819
  stmt.run();
943
820
  }
944
- // ============ Preferences ============
945
- /**
946
- * Get preference value
947
- */
948
821
  getPreference(key) {
949
822
  const stmt = this.db.prepare("SELECT value FROM user_preferences WHERE key = ?");
950
823
  const row = stmt.get(key);
951
824
  return row?.value || null;
952
825
  }
953
- /**
954
- * Set preference value
955
- */
956
826
  setPreference(key, value) {
957
827
  const stmt = this.db.prepare(`
958
828
  INSERT OR REPLACE INTO user_preferences (key, value) VALUES (?, ?)
959
829
  `);
960
830
  stmt.run(key, value);
961
831
  }
962
- // ============ Utilities ============
963
- /**
964
- * Convert database row to LocalErrorRecord
965
- */
966
832
  rowToRecord(row) {
967
833
  return {
968
834
  id: row.id,
969
835
  errorHash: row.error_hash,
970
836
  errorType: row.error_type,
971
837
  errorMessage: row.error_message,
972
- errorStack: row.error_stack || void 0,
973
- language: row.language || void 0,
974
- framework: row.framework || void 0,
838
+ errorStack: row.error_stack || undefined,
839
+ language: row.language || undefined,
840
+ framework: row.framework || undefined,
975
841
  toolName: row.tool_name,
976
842
  toolInput: JSON.parse(row.tool_input || "{}"),
977
843
  sessionId: row.session_id,
978
844
  status: row.status,
979
- resolution: row.resolution || void 0,
980
- resolutionCode: row.resolution_code || void 0,
845
+ resolution: row.resolution || undefined,
846
+ resolutionCode: row.resolution_code || undefined,
981
847
  createdAt: row.created_at,
982
- resolvedAt: row.resolved_at || void 0,
983
- uploadedAt: row.uploaded_at || void 0,
984
- cloudKnowledgeId: row.cloud_knowledge_id || void 0
848
+ resolvedAt: row.resolved_at || undefined,
849
+ uploadedAt: row.uploaded_at || undefined,
850
+ cloudKnowledgeId: row.cloud_knowledge_id || undefined
985
851
  };
986
852
  }
987
- /**
988
- * Close database connection
989
- */
990
853
  close() {
991
854
  this.db.close();
992
855
  }
993
- /**
994
- * Get database for advanced queries
995
- */
996
856
  getDatabase() {
997
857
  return this.db;
998
858
  }
999
- };
1000
-
1001
- // src/cloud/client.ts
1002
- import { createClient } from "@supabase/supabase-js";
859
+ }
1003
860
 
1004
861
  // src/cloud/embedding.ts
1005
862
  import OpenAI from "openai";
1006
863
  var DEFAULT_MODEL = "text-embedding-3-small";
1007
864
  var DEFAULT_DIMENSIONS = 1536;
1008
- var MAX_INPUT_LENGTH = 3e4;
1009
- var EmbeddingService = class {
865
+ var MAX_INPUT_LENGTH = 30000;
866
+
867
+ class EmbeddingService {
1010
868
  client;
1011
869
  model;
1012
870
  dimensions;
@@ -1015,9 +873,6 @@ var EmbeddingService = class {
1015
873
  this.model = model || DEFAULT_MODEL;
1016
874
  this.dimensions = dimensions || DEFAULT_DIMENSIONS;
1017
875
  }
1018
- /**
1019
- * Generate embedding for a single text
1020
- */
1021
876
  async generate(text) {
1022
877
  const truncated = this.truncateText(text);
1023
878
  const response = await this.client.embeddings.create({
@@ -1027,9 +882,6 @@ var EmbeddingService = class {
1027
882
  });
1028
883
  return response.data[0].embedding;
1029
884
  }
1030
- /**
1031
- * Generate embeddings for multiple texts
1032
- */
1033
885
  async generateBatch(texts) {
1034
886
  const truncated = texts.map((t) => this.truncateText(t));
1035
887
  const response = await this.client.embeddings.create({
@@ -1039,10 +891,6 @@ var EmbeddingService = class {
1039
891
  });
1040
892
  return response.data.map((d) => d.embedding);
1041
893
  }
1042
- /**
1043
- * Generate embedding for error context
1044
- * Combines error message, stack trace, and context
1045
- */
1046
894
  async generateErrorEmbedding(errorMessage, errorStack, context) {
1047
895
  const parts = [];
1048
896
  if (context?.language) {
@@ -1056,26 +904,22 @@ var EmbeddingService = class {
1056
904
  parts.push(`Stack Trace:
1057
905
  ${errorStack}`);
1058
906
  }
1059
- const text = parts.join("\n");
907
+ const text = parts.join(`
908
+ `);
1060
909
  return this.generate(text);
1061
910
  }
1062
- /**
1063
- * Truncate text to fit within model limits
1064
- */
1065
911
  truncateText(text) {
1066
912
  if (text.length <= MAX_INPUT_LENGTH) {
1067
913
  return text;
1068
914
  }
1069
915
  const truncated = text.substring(0, MAX_INPUT_LENGTH);
1070
- const lastNewline = truncated.lastIndexOf("\n");
916
+ const lastNewline = truncated.lastIndexOf(`
917
+ `);
1071
918
  if (lastNewline > MAX_INPUT_LENGTH * 0.8) {
1072
919
  return truncated.substring(0, lastNewline);
1073
920
  }
1074
921
  return truncated;
1075
922
  }
1076
- /**
1077
- * Calculate cosine similarity between two embeddings
1078
- */
1079
923
  static cosineSimilarity(a, b) {
1080
924
  if (a.length !== b.length) {
1081
925
  throw new Error("Embeddings must have same dimensions");
@@ -1083,56 +927,63 @@ ${errorStack}`);
1083
927
  let dotProduct = 0;
1084
928
  let normA = 0;
1085
929
  let normB = 0;
1086
- for (let i = 0; i < a.length; i++) {
930
+ for (let i = 0;i < a.length; i++) {
1087
931
  dotProduct += a[i] * b[i];
1088
932
  normA += a[i] * a[i];
1089
933
  normB += b[i] * b[i];
1090
934
  }
1091
935
  const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
1092
- if (magnitude === 0) return 0;
936
+ if (magnitude === 0)
937
+ return 0;
1093
938
  return dotProduct / magnitude;
1094
939
  }
1095
- /**
1096
- * Get embedding dimensions
1097
- */
1098
940
  getDimensions() {
1099
941
  return this.dimensions;
1100
942
  }
1101
- /**
1102
- * Get model name
1103
- */
1104
943
  getModel() {
1105
944
  return this.model;
1106
945
  }
1107
- };
946
+ }
1108
947
  function createEmbeddingService(config) {
1109
948
  return new EmbeddingService(config.apiKey, config.model, config.dimensions);
1110
949
  }
1111
950
 
1112
951
  // src/cloud/client.ts
1113
- var CloudClient = class {
952
+ var createClient;
953
+ async function getCreateClient() {
954
+ if (!createClient) {
955
+ const supabase = await import("@supabase/supabase-js");
956
+ createClient = supabase.createClient;
957
+ }
958
+ return createClient;
959
+ }
960
+
961
+ class CloudClient {
1114
962
  supabase;
1115
963
  embedding;
1116
964
  contributorId;
1117
965
  similarityThreshold;
1118
- constructor(config) {
1119
- this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
966
+ constructor(supabase, embedding, contributorId, similarityThreshold) {
967
+ this.supabase = supabase;
968
+ this.embedding = embedding;
969
+ this.contributorId = contributorId;
970
+ this.similarityThreshold = similarityThreshold;
971
+ }
972
+ static async create(config) {
973
+ const createClientFn = await getCreateClient();
974
+ const supabase = createClientFn(config.supabaseUrl, config.supabaseAnonKey);
975
+ let embedding = null;
1120
976
  if (config.openaiApiKey) {
1121
977
  try {
1122
- this.embedding = new EmbeddingService(config.openaiApiKey);
978
+ embedding = new EmbeddingService(config.openaiApiKey);
1123
979
  } catch (err) {
1124
980
  console.warn("[FixHive] Failed to initialize embedding service:", err);
1125
- this.embedding = null;
1126
981
  }
1127
- } else {
1128
- this.embedding = null;
1129
982
  }
1130
- this.contributorId = config.contributorId || generateContributorId();
1131
- this.similarityThreshold = config.similarityThreshold || 0.7;
983
+ const contributorId = config.contributorId || generateContributorId();
984
+ const similarityThreshold = config.similarityThreshold || 0.7;
985
+ return new CloudClient(supabase, embedding, contributorId, similarityThreshold);
1132
986
  }
1133
- /**
1134
- * Search for similar errors in cloud knowledge base
1135
- */
1136
987
  async searchSimilar(request) {
1137
988
  const startTime = Date.now();
1138
989
  if (!this.embedding) {
@@ -1158,9 +1009,6 @@ ${request.errorStack || ""}`;
1158
1009
  cached: false
1159
1010
  };
1160
1011
  }
1161
- /**
1162
- * Fallback text-based search
1163
- */
1164
1012
  async searchByText(request) {
1165
1013
  const startTime = Date.now();
1166
1014
  let query = this.supabase.from("knowledge_entries").select("*").ilike("error_message", `%${request.errorMessage.substring(0, 100)}%`).order("upvotes", { ascending: false }).limit(request.limit || 10);
@@ -1181,9 +1029,6 @@ ${request.errorStack || ""}`;
1181
1029
  cached: false
1182
1030
  };
1183
1031
  }
1184
- /**
1185
- * Upload a resolution to cloud knowledge base
1186
- */
1187
1032
  async uploadResolution(request) {
1188
1033
  const { errorRecord, resolution, resolutionCode, resolutionSteps } = request;
1189
1034
  let embedding = null;
@@ -1233,9 +1078,6 @@ ${errorRecord.errorStack || ""}`;
1233
1078
  message: "Solution uploaded successfully!"
1234
1079
  };
1235
1080
  }
1236
- /**
1237
- * Check for duplicate entries
1238
- */
1239
1081
  async checkDuplicate(errorHash, embedding) {
1240
1082
  const { data: hashMatch } = await this.supabase.from("knowledge_entries").select("id").eq("error_hash", errorHash).limit(1).single();
1241
1083
  if (hashMatch) {
@@ -1260,9 +1102,6 @@ ${errorRecord.errorStack || ""}`;
1260
1102
  similarityScore: result.similarity_score
1261
1103
  };
1262
1104
  }
1263
- /**
1264
- * Vote on a knowledge entry (with duplicate vote prevention)
1265
- */
1266
1105
  async vote(knowledgeId, helpful) {
1267
1106
  const voteType = helpful ? "up" : "down";
1268
1107
  const { data, error } = await this.supabase.rpc("safe_vote", {
@@ -1283,9 +1122,6 @@ ${errorRecord.errorStack || ""}`;
1283
1122
  }
1284
1123
  return result;
1285
1124
  }
1286
- /**
1287
- * Report an entry for review
1288
- */
1289
1125
  async reportEntry(knowledgeId, reason) {
1290
1126
  const { data, error } = await this.supabase.rpc("report_entry", {
1291
1127
  p_entry_id: knowledgeId,
@@ -1297,9 +1133,6 @@ ${errorRecord.errorStack || ""}`;
1297
1133
  }
1298
1134
  return data;
1299
1135
  }
1300
- /**
1301
- * Report helpful usage
1302
- */
1303
1136
  async reportHelpful(knowledgeId) {
1304
1137
  await this.supabase.rpc("increment_usage_count", {
1305
1138
  entry_id: knowledgeId
@@ -1310,9 +1143,6 @@ ${errorRecord.errorStack || ""}`;
1310
1143
  user_hash: this.contributorId
1311
1144
  });
1312
1145
  }
1313
- /**
1314
- * Get contributor statistics
1315
- */
1316
1146
  async getContributorStats() {
1317
1147
  const { data } = await this.supabase.from("knowledge_entries").select("upvotes, usage_count").eq("contributor_id", this.contributorId);
1318
1148
  if (!data || data.length === 0) {
@@ -1324,9 +1154,6 @@ ${errorRecord.errorStack || ""}`;
1324
1154
  totalUpvotes: data.reduce((sum, e) => sum + (e.upvotes || 0), 0)
1325
1155
  };
1326
1156
  }
1327
- /**
1328
- * Get entry by ID
1329
- */
1330
1157
  async getEntry(id) {
1331
1158
  const { data, error } = await this.supabase.from("knowledge_entries").select("*").eq("id", id).single();
1332
1159
  if (error || !data) {
@@ -1334,22 +1161,19 @@ ${errorRecord.errorStack || ""}`;
1334
1161
  }
1335
1162
  return this.mapToKnowledgeEntry(data);
1336
1163
  }
1337
- /**
1338
- * Map database row to CloudKnowledgeEntry
1339
- */
1340
1164
  mapToKnowledgeEntry(row) {
1341
1165
  return {
1342
1166
  id: row.id,
1343
1167
  errorHash: row.error_hash,
1344
1168
  errorType: row.error_type,
1345
1169
  errorMessage: row.error_message,
1346
- errorStack: row.error_stack || void 0,
1170
+ errorStack: row.error_stack || undefined,
1347
1171
  language: row.language,
1348
- framework: row.framework || void 0,
1349
- dependencies: row.dependencies || void 0,
1172
+ framework: row.framework || undefined,
1173
+ dependencies: row.dependencies || undefined,
1350
1174
  resolutionDescription: row.resolution_description,
1351
- resolutionCode: row.resolution_code || void 0,
1352
- resolutionSteps: row.resolution_steps || void 0,
1175
+ resolutionCode: row.resolution_code || undefined,
1176
+ resolutionSteps: row.resolution_steps || undefined,
1353
1177
  contributorId: row.contributor_id,
1354
1178
  upvotes: row.upvotes || 0,
1355
1179
  downvotes: row.downvotes || 0,
@@ -1357,33 +1181,24 @@ ${errorRecord.errorStack || ""}`;
1357
1181
  createdAt: row.created_at,
1358
1182
  updatedAt: row.updated_at,
1359
1183
  isVerified: row.is_verified || false,
1360
- similarity: row.similarity || void 0
1184
+ similarity: row.similarity || undefined
1361
1185
  };
1362
1186
  }
1363
- /**
1364
- * Get contributor ID
1365
- */
1366
1187
  getContributorId() {
1367
1188
  return this.contributorId;
1368
1189
  }
1369
- /**
1370
- * Check if embedding service is available
1371
- */
1372
1190
  hasEmbeddingService() {
1373
1191
  return this.embedding !== null;
1374
1192
  }
1375
- };
1376
- function createCloudClient(config) {
1377
- return new CloudClient(config);
1193
+ }
1194
+ async function createCloudClient(config) {
1195
+ return CloudClient.create(config);
1378
1196
  }
1379
1197
 
1380
1198
  // src/plugin/tools.ts
1381
1199
  import { tool } from "@opencode-ai/plugin";
1382
1200
  function createTools(localStore, cloudClient, privacyFilter, context) {
1383
1201
  return {
1384
- /**
1385
- * Search cloud knowledge base for error solutions
1386
- */
1387
1202
  fixhive_search: tool({
1388
1203
  description: "Search FixHive knowledge base for error solutions. Use when encountering errors to find community solutions.",
1389
1204
  args: {
@@ -1412,9 +1227,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1412
1227
  return formatSearchResults(results.results, false);
1413
1228
  }
1414
1229
  }),
1415
- /**
1416
- * Mark error as resolved and optionally upload solution
1417
- */
1418
1230
  fixhive_resolve: tool({
1419
1231
  description: "Mark an error as resolved and optionally share the solution with the community.",
1420
1232
  args: {
@@ -1450,9 +1262,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1450
1262
  return "Error marked as resolved locally.";
1451
1263
  }
1452
1264
  }),
1453
- /**
1454
- * List errors in current session
1455
- */
1456
1265
  fixhive_list: tool({
1457
1266
  description: "List errors detected in the current session.",
1458
1267
  args: {
@@ -1471,9 +1280,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1471
1280
  return formatErrorList(errors);
1472
1281
  }
1473
1282
  }),
1474
- /**
1475
- * Vote on a solution
1476
- */
1477
1283
  fixhive_vote: tool({
1478
1284
  description: "Upvote or downvote a FixHive solution based on whether it helped.",
1479
1285
  args: {
@@ -1491,9 +1297,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1491
1297
  return args.helpful ? "Thanks for the feedback! Solution upvoted." : "Thanks for the feedback! Solution downvoted.";
1492
1298
  }
1493
1299
  }),
1494
- /**
1495
- * Report inappropriate content
1496
- */
1497
1300
  fixhive_report: tool({
1498
1301
  description: "Report a FixHive solution for inappropriate content, spam, or incorrect information.",
1499
1302
  args: {
@@ -1508,9 +1311,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1508
1311
  return "Report submitted. Thank you for helping keep FixHive clean!";
1509
1312
  }
1510
1313
  }),
1511
- /**
1512
- * Get usage statistics
1513
- */
1514
1314
  fixhive_stats: tool({
1515
1315
  description: "Get FixHive usage statistics.",
1516
1316
  args: {},
@@ -1532,9 +1332,6 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
1532
1332
  `;
1533
1333
  }
1534
1334
  }),
1535
- /**
1536
- * Report that a solution was helpful
1537
- */
1538
1335
  fixhive_helpful: tool({
1539
1336
  description: "Report that a FixHive solution was helpful and resolved your issue.",
1540
1337
  args: {
@@ -1569,7 +1366,8 @@ ${r.resolutionCode}
1569
1366
  if (r.resolutionSteps?.length) {
1570
1367
  entry += `
1571
1368
  **Steps:**
1572
- ${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join("\n")}
1369
+ ${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join(`
1370
+ `)}
1573
1371
  `;
1574
1372
  }
1575
1373
  entry += `
@@ -1577,7 +1375,8 @@ ${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join("\n")}
1577
1375
 
1578
1376
  ---`;
1579
1377
  return entry;
1580
- }).join("\n");
1378
+ }).join(`
1379
+ `);
1581
1380
  return `${header}
1582
1381
  ${entries}
1583
1382
 
@@ -1588,9 +1387,8 @@ function formatErrorList(errors) {
1588
1387
  const table = `
1589
1388
  | ID | Type | Status | Message |
1590
1389
  |----|------|--------|---------|
1591
- ${errors.map(
1592
- (e) => `| ${e.id.slice(0, 8)} | ${e.errorType} | ${e.status} | ${e.errorMessage.slice(0, 50)}... |`
1593
- ).join("\n")}
1390
+ ${errors.map((e) => `| ${e.id.slice(0, 8)} | ${e.errorType} | ${e.status} | ${e.errorMessage.slice(0, 50)}... |`).join(`
1391
+ `)}
1594
1392
  `;
1595
1393
  return `${header}
1596
1394
  ${table}
@@ -1600,8 +1398,7 @@ Use \`fixhive_resolve <id>\` to mark as resolved and share solutions.`;
1600
1398
 
1601
1399
  // src/plugin/index.ts
1602
1400
  var DEFAULT_CONFIG = {
1603
- cacheExpirationMs: 36e5,
1604
- // 1 hour
1401
+ cacheExpirationMs: 3600000,
1605
1402
  embeddingModel: "text-embedding-3-small",
1606
1403
  embeddingDimensions: 1536,
1607
1404
  similarityThreshold: 0.7,
@@ -1612,14 +1409,14 @@ var FixHivePlugin = async (ctx) => {
1612
1409
  console.log("[FixHive] Plugin loaded");
1613
1410
  console.log(`[FixHive] Project: ${ctx.directory}`);
1614
1411
  console.log(`[FixHive] Cloud: ${config.supabaseUrl ? "enabled" : "disabled"}`);
1615
- const privacyFilter = new PrivacyFilter();
1412
+ const privacyFilter = new PrivacyFilter;
1616
1413
  const filterContext = createFilterContext(ctx.directory);
1617
1414
  const errorDetector = new ErrorDetector(privacyFilter);
1618
1415
  const localStore = new LocalStore(ctx.directory);
1619
1416
  let cloudClient = null;
1620
1417
  if (config.supabaseUrl && config.supabaseAnonKey) {
1621
1418
  try {
1622
- cloudClient = new CloudClient({
1419
+ cloudClient = await CloudClient.create({
1623
1420
  supabaseUrl: config.supabaseUrl,
1624
1421
  supabaseAnonKey: config.supabaseAnonKey,
1625
1422
  openaiApiKey: config.openaiApiKey,
@@ -1643,9 +1440,9 @@ var FixHivePlugin = async (ctx) => {
1643
1440
  console.log("[FixHive] Ready - use fixhive_stats to verify");
1644
1441
  const errorProducingTools = ["bash", "edit", "write", "read", "terminal"];
1645
1442
  return {
1646
- // ============ Tool Execution Hook ============
1647
1443
  "tool.execute.after": async (input, output) => {
1648
- if (!errorProducingTools.includes(input.tool)) return;
1444
+ if (!errorProducingTools.includes(input.tool))
1445
+ return;
1649
1446
  const detection = errorDetector.detect({
1650
1447
  tool: input.tool,
1651
1448
  output: output.output,
@@ -1655,7 +1452,7 @@ var FixHivePlugin = async (ctx) => {
1655
1452
  });
1656
1453
  if (detection.detected && detection.confidence >= 0.5) {
1657
1454
  const sanitizedErrorMessage = privacyFilter.sanitize(detection.errorMessage, filterContext).sanitized;
1658
- const sanitizedErrorStack = detection.errorStack ? privacyFilter.sanitize(detection.errorStack, filterContext).sanitized : void 0;
1455
+ const sanitizedErrorStack = detection.errorStack ? privacyFilter.sanitize(detection.errorStack, filterContext).sanitized : undefined;
1659
1456
  localStore.createErrorRecord({
1660
1457
  errorType: detection.errorType,
1661
1458
  errorMessage: sanitizedErrorMessage,
@@ -1664,7 +1461,6 @@ var FixHivePlugin = async (ctx) => {
1664
1461
  framework: pluginContext.framework,
1665
1462
  toolName: input.tool,
1666
1463
  toolInput: {},
1667
- // Tool input is intentionally omitted to avoid storing sensitive data
1668
1464
  sessionId: pluginContext.sessionId || input.sessionID
1669
1465
  });
1670
1466
  if (cloudClient) {
@@ -1677,10 +1473,7 @@ var FixHivePlugin = async (ctx) => {
1677
1473
  limit: 3
1678
1474
  });
1679
1475
  if (solutions.results.length > 0) {
1680
- localStore.cacheResults(
1681
- generateErrorFingerprint(sanitizedErrorMessage, sanitizedErrorStack),
1682
- solutions.results
1683
- );
1476
+ localStore.cacheResults(generateErrorFingerprint(sanitizedErrorMessage, sanitizedErrorStack), solutions.results);
1684
1477
  output.title = `${output.title} [FixHive: ${solutions.results.length} solution(s) found]`;
1685
1478
  }
1686
1479
  } catch (e) {
@@ -1690,24 +1483,22 @@ var FixHivePlugin = async (ctx) => {
1690
1483
  }
1691
1484
  }
1692
1485
  },
1693
- // ============ Session Compaction Hook ============
1694
1486
  "experimental.session.compacting": async (_input, output) => {
1695
1487
  const unresolvedErrors = localStore.getUnresolvedErrors(pluginContext.sessionId);
1696
1488
  if (unresolvedErrors.length > 0) {
1697
1489
  output.context.push(`
1698
1490
  ## FixHive: Unresolved Errors in Session
1699
1491
 
1700
- ${unresolvedErrors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 100)}...`).join("\n")}
1492
+ ${unresolvedErrors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 100)}...`).join(`
1493
+ `)}
1701
1494
 
1702
1495
  Use \`fixhive_mark_resolved\` when errors are fixed to contribute solutions.
1703
1496
  `);
1704
1497
  }
1705
1498
  },
1706
- // ============ Chat Message Hook ============
1707
1499
  "chat.message": async (input, _output) => {
1708
1500
  pluginContext.sessionId = input.sessionID;
1709
1501
  },
1710
- // ============ Custom Tools ============
1711
1502
  tool: cloudClient ? createTools(localStore, cloudClient, privacyFilter, pluginContext) : createOfflineTools(localStore, privacyFilter, pluginContext)
1712
1503
  };
1713
1504
  };
@@ -1730,7 +1521,8 @@ function createOfflineTools(localStore, _privacyFilter, context) {
1730
1521
  }
1731
1522
  return `## Session Errors (${errors.length})
1732
1523
 
1733
- ${errors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 80)}...`).join("\n")}
1524
+ ${errors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 80)}...`).join(`
1525
+ `)}
1734
1526
 
1735
1527
  *Cloud features disabled. Set FIXHIVE_SUPABASE_URL and FIXHIVE_SUPABASE_KEY to enable.*`;
1736
1528
  }
@@ -1789,7 +1581,7 @@ function detectLanguage(directory) {
1789
1581
  return lang;
1790
1582
  }
1791
1583
  }
1792
- return void 0;
1584
+ return;
1793
1585
  }
1794
1586
  function detectFramework(directory) {
1795
1587
  const pkgPath = join(directory, "package.json");
@@ -1797,49 +1589,57 @@ function detectFramework(directory) {
1797
1589
  try {
1798
1590
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1799
1591
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1800
- if (deps["next"]) return "nextjs";
1801
- if (deps["react"]) return "react";
1802
- if (deps["vue"]) return "vue";
1803
- if (deps["@angular/core"]) return "angular";
1804
- if (deps["express"]) return "express";
1805
- if (deps["fastify"]) return "fastify";
1806
- if (deps["hono"]) return "hono";
1807
- } catch {
1808
- }
1592
+ if (deps["next"])
1593
+ return "nextjs";
1594
+ if (deps["react"])
1595
+ return "react";
1596
+ if (deps["vue"])
1597
+ return "vue";
1598
+ if (deps["@angular/core"])
1599
+ return "angular";
1600
+ if (deps["express"])
1601
+ return "express";
1602
+ if (deps["fastify"])
1603
+ return "fastify";
1604
+ if (deps["hono"])
1605
+ return "hono";
1606
+ } catch {}
1809
1607
  }
1810
1608
  const reqPath = join(directory, "requirements.txt");
1811
1609
  if (existsSync2(reqPath)) {
1812
1610
  try {
1813
1611
  const content = readFileSync(reqPath, "utf-8");
1814
- if (content.includes("django")) return "django";
1815
- if (content.includes("flask")) return "flask";
1816
- if (content.includes("fastapi")) return "fastapi";
1817
- } catch {
1818
- }
1819
- }
1820
- return void 0;
1612
+ if (content.includes("django"))
1613
+ return "django";
1614
+ if (content.includes("flask"))
1615
+ return "flask";
1616
+ if (content.includes("fastapi"))
1617
+ return "fastapi";
1618
+ } catch {}
1619
+ }
1620
+ return;
1821
1621
  }
1822
1622
  var plugin_default = FixHivePlugin;
1823
1623
  export {
1824
- CloudClient,
1825
- EmbeddingService,
1826
- ErrorDetector,
1827
- FixHivePlugin,
1828
- LocalStore,
1829
- PrivacyFilter,
1830
- calculateStringSimilarity,
1831
- createCloudClient,
1832
- createEmbeddingService,
1833
- createFilterContext,
1834
- plugin_default as default,
1835
- defaultErrorDetector,
1836
- defaultPrivacyFilter,
1837
- fingerprintsMatch,
1838
- generateContributorId,
1839
- generateErrorFingerprint,
1840
- generateSessionHash,
1841
- normalizeErrorContent,
1842
- runMigrations,
1624
+ shortHash,
1843
1625
  sha256,
1844
- shortHash
1626
+ runMigrations,
1627
+ normalizeErrorContent,
1628
+ generateSessionHash,
1629
+ generateErrorFingerprint,
1630
+ generateContributorId,
1631
+ fingerprintsMatch,
1632
+ defaultPrivacyFilter,
1633
+ defaultErrorDetector,
1634
+ plugin_default as default,
1635
+ createFilterContext,
1636
+ createEmbeddingService,
1637
+ createCloudClient,
1638
+ calculateStringSimilarity,
1639
+ PrivacyFilter,
1640
+ LocalStore,
1641
+ FixHivePlugin,
1642
+ ErrorDetector,
1643
+ EmbeddingService,
1644
+ CloudClient
1845
1645
  };