@the-magic-tower/fixhive-opencode-plugin 0.1.22 → 0.1.24
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.d.ts +472 -44
- package/dist/index.js +312 -145
- package/dist/index.js.map +1 -0
- package/package.json +5 -7
- package/dist/cloud/client.d.ts +0 -44
- package/dist/cloud/client.d.ts.map +0 -1
- package/dist/cloud/embedding.d.ts +0 -43
- package/dist/cloud/embedding.d.ts.map +0 -1
- package/dist/core/error-detector.d.ts +0 -26
- package/dist/core/error-detector.d.ts.map +0 -1
- package/dist/core/hash.d.ts +0 -41
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/privacy-filter.d.ts +0 -33
- package/dist/core/privacy-filter.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/plugin/index.d.ts +0 -11
- package/dist/plugin/index.d.ts.map +0 -1
- package/dist/plugin/tools.d.ts +0 -15
- package/dist/plugin/tools.d.ts.map +0 -1
- package/dist/storage/local-store.d.ts +0 -46
- package/dist/storage/local-store.d.ts.map +0 -1
- package/dist/storage/migrations.d.ts +0 -10
- package/dist/storage/migrations.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -218
- package/dist/types/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// src/plugin/index.ts
|
|
2
2
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
3
|
-
import { existsSync as existsSync2, readFileSync } from "
|
|
4
|
-
import { join } from "
|
|
3
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
5
|
|
|
6
6
|
// src/core/privacy-filter.ts
|
|
7
7
|
var DEFAULT_FILTER_RULES = [
|
|
8
|
+
// =====================================
|
|
9
|
+
// SECRETS (Critical - Always Filter)
|
|
10
|
+
// =====================================
|
|
11
|
+
// AWS Keys
|
|
8
12
|
{
|
|
9
13
|
name: "aws_access_key",
|
|
10
14
|
category: "secret",
|
|
@@ -12,6 +16,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
12
16
|
replacement: "[AWS_KEY_REDACTED]",
|
|
13
17
|
priority: 100
|
|
14
18
|
},
|
|
19
|
+
// OpenAI API Keys
|
|
15
20
|
{
|
|
16
21
|
name: "openai_key",
|
|
17
22
|
category: "secret",
|
|
@@ -19,6 +24,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
19
24
|
replacement: "[OPENAI_KEY_REDACTED]",
|
|
20
25
|
priority: 100
|
|
21
26
|
},
|
|
27
|
+
// GitHub Tokens
|
|
22
28
|
{
|
|
23
29
|
name: "github_token",
|
|
24
30
|
category: "secret",
|
|
@@ -26,6 +32,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
26
32
|
replacement: "[GITHUB_TOKEN_REDACTED]",
|
|
27
33
|
priority: 100
|
|
28
34
|
},
|
|
35
|
+
// Google API Keys
|
|
29
36
|
{
|
|
30
37
|
name: "google_api_key",
|
|
31
38
|
category: "secret",
|
|
@@ -33,6 +40,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
33
40
|
replacement: "[GOOGLE_API_KEY_REDACTED]",
|
|
34
41
|
priority: 100
|
|
35
42
|
},
|
|
43
|
+
// Stripe Keys
|
|
36
44
|
{
|
|
37
45
|
name: "stripe_key",
|
|
38
46
|
category: "secret",
|
|
@@ -40,6 +48,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
40
48
|
replacement: "[STRIPE_KEY_REDACTED]",
|
|
41
49
|
priority: 100
|
|
42
50
|
},
|
|
51
|
+
// JWT Tokens (limited length to prevent ReDoS)
|
|
43
52
|
{
|
|
44
53
|
name: "jwt_token",
|
|
45
54
|
category: "secret",
|
|
@@ -47,6 +56,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
47
56
|
replacement: "[JWT_REDACTED]",
|
|
48
57
|
priority: 100
|
|
49
58
|
},
|
|
59
|
+
// Bearer Tokens
|
|
50
60
|
{
|
|
51
61
|
name: "bearer_token",
|
|
52
62
|
category: "secret",
|
|
@@ -54,6 +64,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
54
64
|
replacement: "$1 [TOKEN_REDACTED]",
|
|
55
65
|
priority: 100
|
|
56
66
|
},
|
|
67
|
+
// Private Keys
|
|
57
68
|
{
|
|
58
69
|
name: "private_key",
|
|
59
70
|
category: "secret",
|
|
@@ -61,6 +72,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
61
72
|
replacement: "[PRIVATE_KEY_REDACTED]",
|
|
62
73
|
priority: 100
|
|
63
74
|
},
|
|
75
|
+
// Generic API Keys (context-based, limited length to prevent ReDoS)
|
|
64
76
|
{
|
|
65
77
|
name: "generic_api_key",
|
|
66
78
|
category: "secret",
|
|
@@ -68,6 +80,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
68
80
|
replacement: "$1=[KEY_REDACTED]",
|
|
69
81
|
priority: 95
|
|
70
82
|
},
|
|
83
|
+
// Secret/Password assignments (limited length to prevent ReDoS)
|
|
71
84
|
{
|
|
72
85
|
name: "secret_assignment",
|
|
73
86
|
category: "secret",
|
|
@@ -75,6 +88,10 @@ var DEFAULT_FILTER_RULES = [
|
|
|
75
88
|
replacement: "$1=[REDACTED]",
|
|
76
89
|
priority: 90
|
|
77
90
|
},
|
|
91
|
+
// =====================================
|
|
92
|
+
// IDENTITY (High Risk)
|
|
93
|
+
// =====================================
|
|
94
|
+
// Email Addresses
|
|
78
95
|
{
|
|
79
96
|
name: "email",
|
|
80
97
|
category: "identity",
|
|
@@ -82,6 +99,10 @@ var DEFAULT_FILTER_RULES = [
|
|
|
82
99
|
replacement: "[EMAIL_REDACTED]",
|
|
83
100
|
priority: 80
|
|
84
101
|
},
|
|
102
|
+
// =====================================
|
|
103
|
+
// INFRASTRUCTURE (Medium Risk)
|
|
104
|
+
// =====================================
|
|
105
|
+
// Database Connection Strings
|
|
85
106
|
{
|
|
86
107
|
name: "db_connection",
|
|
87
108
|
category: "infrastructure",
|
|
@@ -89,6 +110,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
89
110
|
replacement: "$1://[CONNECTION_REDACTED]",
|
|
90
111
|
priority: 85
|
|
91
112
|
},
|
|
113
|
+
// Internal URLs
|
|
92
114
|
{
|
|
93
115
|
name: "internal_url",
|
|
94
116
|
category: "infrastructure",
|
|
@@ -96,6 +118,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
96
118
|
replacement: "[INTERNAL_URL_REDACTED]",
|
|
97
119
|
priority: 75
|
|
98
120
|
},
|
|
121
|
+
// IP Addresses (except localhost and common dev IPs)
|
|
99
122
|
{
|
|
100
123
|
name: "ipv4",
|
|
101
124
|
category: "infrastructure",
|
|
@@ -108,6 +131,10 @@ var DEFAULT_FILTER_RULES = [
|
|
|
108
131
|
},
|
|
109
132
|
priority: 70
|
|
110
133
|
},
|
|
134
|
+
// =====================================
|
|
135
|
+
// FILE PATHS (Context-dependent)
|
|
136
|
+
// =====================================
|
|
137
|
+
// Home Directory Paths (macOS)
|
|
111
138
|
{
|
|
112
139
|
name: "macos_home_path",
|
|
113
140
|
category: "path",
|
|
@@ -115,6 +142,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
115
142
|
replacement: "~",
|
|
116
143
|
priority: 60
|
|
117
144
|
},
|
|
145
|
+
// Home Directory Paths (Linux)
|
|
118
146
|
{
|
|
119
147
|
name: "linux_home_path",
|
|
120
148
|
category: "path",
|
|
@@ -122,6 +150,7 @@ var DEFAULT_FILTER_RULES = [
|
|
|
122
150
|
replacement: "~",
|
|
123
151
|
priority: 60
|
|
124
152
|
},
|
|
153
|
+
// Home Directory Paths (Windows)
|
|
125
154
|
{
|
|
126
155
|
name: "windows_home_path",
|
|
127
156
|
category: "path",
|
|
@@ -129,6 +158,10 @@ var DEFAULT_FILTER_RULES = [
|
|
|
129
158
|
replacement: "~",
|
|
130
159
|
priority: 60
|
|
131
160
|
},
|
|
161
|
+
// =====================================
|
|
162
|
+
// ENVIRONMENT (Medium Risk)
|
|
163
|
+
// =====================================
|
|
164
|
+
// Sensitive Environment Variables
|
|
132
165
|
{
|
|
133
166
|
name: "env_var_value",
|
|
134
167
|
category: "environment",
|
|
@@ -157,7 +190,9 @@ var DEFAULT_FILTER_RULES = [
|
|
|
157
190
|
}
|
|
158
191
|
];
|
|
159
192
|
function createPrivacyFilter(customRules) {
|
|
160
|
-
const rules = [...DEFAULT_FILTER_RULES, ...customRules || []].sort(
|
|
193
|
+
const rules = [...DEFAULT_FILTER_RULES, ...customRules || []].sort(
|
|
194
|
+
(a, b) => b.priority - a.priority
|
|
195
|
+
);
|
|
161
196
|
function generalizePaths(content, context) {
|
|
162
197
|
let result = content;
|
|
163
198
|
if (context.projectRoot) {
|
|
@@ -173,6 +208,9 @@ function createPrivacyFilter(customRules) {
|
|
|
173
208
|
return result;
|
|
174
209
|
}
|
|
175
210
|
return {
|
|
211
|
+
/**
|
|
212
|
+
* Sanitize content by applying all filter rules
|
|
213
|
+
*/
|
|
176
214
|
sanitize(content, context) {
|
|
177
215
|
let result = content;
|
|
178
216
|
const appliedFilters = [];
|
|
@@ -202,10 +240,16 @@ function createPrivacyFilter(customRules) {
|
|
|
202
240
|
appliedFilters: [...new Set(appliedFilters)]
|
|
203
241
|
};
|
|
204
242
|
},
|
|
243
|
+
/**
|
|
244
|
+
* Add a custom filter rule
|
|
245
|
+
*/
|
|
205
246
|
addRule(rule) {
|
|
206
247
|
rules.push(rule);
|
|
207
248
|
rules.sort((a, b) => b.priority - a.priority);
|
|
208
249
|
},
|
|
250
|
+
/**
|
|
251
|
+
* Remove a filter rule by name
|
|
252
|
+
*/
|
|
209
253
|
removeRule(name) {
|
|
210
254
|
const index = rules.findIndex((r) => r.name === name);
|
|
211
255
|
if (index !== -1) {
|
|
@@ -214,9 +258,16 @@ function createPrivacyFilter(customRules) {
|
|
|
214
258
|
}
|
|
215
259
|
return false;
|
|
216
260
|
},
|
|
261
|
+
/**
|
|
262
|
+
* Get all current rules
|
|
263
|
+
*/
|
|
217
264
|
getRules() {
|
|
218
265
|
return rules;
|
|
219
266
|
},
|
|
267
|
+
/**
|
|
268
|
+
* Check if content contains sensitive data
|
|
269
|
+
* Note: Always reset regex lastIndex BEFORE testing to prevent state pollution
|
|
270
|
+
*/
|
|
220
271
|
containsSensitiveData(content) {
|
|
221
272
|
for (const rule of rules) {
|
|
222
273
|
if (rule.category === "secret") {
|
|
@@ -240,7 +291,7 @@ function createFilterContext(projectDirectory) {
|
|
|
240
291
|
return {
|
|
241
292
|
projectRoot: projectDirectory,
|
|
242
293
|
homeDir,
|
|
243
|
-
commonPaths: new Map([
|
|
294
|
+
commonPaths: /* @__PURE__ */ new Map([
|
|
244
295
|
["/usr/local/lib/", "<LIB>/"],
|
|
245
296
|
["/usr/lib/", "<LIB>/"],
|
|
246
297
|
["/var/log/", "<LOG>/"],
|
|
@@ -253,6 +304,7 @@ var defaultPrivacyFilter = createPrivacyFilter();
|
|
|
253
304
|
|
|
254
305
|
// src/core/error-detector.ts
|
|
255
306
|
var ERROR_PATTERNS = {
|
|
307
|
+
// Universal error indicators
|
|
256
308
|
universal: [
|
|
257
309
|
/\b(error|failed|failure|fatal|exception|panic)\b/i,
|
|
258
310
|
/\b(cannot|could not|unable to|couldn't)\b/i,
|
|
@@ -263,26 +315,36 @@ var ERROR_PATTERNS = {
|
|
|
263
315
|
/\b(segmentation fault|segfault|core dumped)\b/i,
|
|
264
316
|
/\b(out of memory|oom|memory allocation failed)\b/i
|
|
265
317
|
],
|
|
318
|
+
// Error prefixes
|
|
266
319
|
prefixed: [
|
|
267
320
|
/^Error:/m,
|
|
268
321
|
/^ERROR\s/m,
|
|
269
322
|
/^E\s+\d+:/m,
|
|
323
|
+
// Rust errors
|
|
270
324
|
/^\[ERROR\]/m,
|
|
271
325
|
/^fatal:/m,
|
|
272
326
|
/^FATAL:/m,
|
|
273
327
|
/^panic:/m,
|
|
274
328
|
/^Traceback \(most recent call last\):/m,
|
|
329
|
+
// Python
|
|
275
330
|
/^Exception in thread/m,
|
|
331
|
+
// Java
|
|
276
332
|
/^Uncaught \w+Error:/m
|
|
333
|
+
// JavaScript
|
|
277
334
|
],
|
|
335
|
+
// Build/compilation errors
|
|
278
336
|
build: [
|
|
279
337
|
/^.+:\d+:\d+:\s*error:/m,
|
|
338
|
+
// GCC/Clang format
|
|
280
339
|
/error\[E\d+\]:/m,
|
|
340
|
+
// Rust compiler
|
|
281
341
|
/error TS\d+:/m,
|
|
342
|
+
// TypeScript
|
|
282
343
|
/SyntaxError:/m,
|
|
283
344
|
/ParseError:/m,
|
|
284
345
|
/CompileError:/m
|
|
285
346
|
],
|
|
347
|
+
// Package manager errors
|
|
286
348
|
package: [
|
|
287
349
|
/npm ERR!/m,
|
|
288
350
|
/npm error/m,
|
|
@@ -295,7 +357,9 @@ var ERROR_PATTERNS = {
|
|
|
295
357
|
/Cannot find module/m,
|
|
296
358
|
/Module not found/m
|
|
297
359
|
],
|
|
360
|
+
// Permission errors
|
|
298
361
|
permission: [/EACCES:/m, /EPERM:/m, /Permission denied/m, /Access denied/m, /Insufficient permissions/m],
|
|
362
|
+
// Network errors
|
|
299
363
|
network: [
|
|
300
364
|
/ECONNREFUSED/m,
|
|
301
365
|
/ETIMEDOUT/m,
|
|
@@ -304,6 +368,7 @@ var ERROR_PATTERNS = {
|
|
|
304
368
|
/Connection refused/m,
|
|
305
369
|
/Network is unreachable/m
|
|
306
370
|
],
|
|
371
|
+
// Test failures
|
|
307
372
|
test: [/FAIL /m, /AssertionError/m, /Expected .+ but got/m, /Test failed/i, /\d+ failing/m]
|
|
308
373
|
};
|
|
309
374
|
var STACK_TRACE_PATTERNS = {
|
|
@@ -318,14 +383,23 @@ var STACK_TRACE_PATTERNS = {
|
|
|
318
383
|
};
|
|
319
384
|
var EXIT_CODE_SEVERITY = {
|
|
320
385
|
1: "error",
|
|
386
|
+
// General errors
|
|
321
387
|
2: "error",
|
|
388
|
+
// Misuse of shell builtins
|
|
322
389
|
126: "error",
|
|
390
|
+
// Command cannot execute
|
|
323
391
|
127: "error",
|
|
392
|
+
// Command not found
|
|
324
393
|
128: "critical",
|
|
394
|
+
// Invalid exit argument
|
|
325
395
|
130: "warning",
|
|
396
|
+
// Script terminated by Ctrl+C
|
|
326
397
|
137: "critical",
|
|
398
|
+
// SIGKILL (OOM, etc.)
|
|
327
399
|
139: "critical",
|
|
400
|
+
// Segmentation fault
|
|
328
401
|
143: "warning"
|
|
402
|
+
// SIGTERM
|
|
329
403
|
};
|
|
330
404
|
function createErrorDetector(privacyFilter) {
|
|
331
405
|
const filter = privacyFilter || createPrivacyFilter();
|
|
@@ -343,8 +417,7 @@ function createErrorDetector(privacyFilter) {
|
|
|
343
417
|
test: 0.7
|
|
344
418
|
};
|
|
345
419
|
for (const [category, patterns] of Object.entries(ERROR_PATTERNS)) {
|
|
346
|
-
if (category === "universal")
|
|
347
|
-
continue;
|
|
420
|
+
if (category === "universal") continue;
|
|
348
421
|
for (const pattern of patterns) {
|
|
349
422
|
const match = content.match(pattern);
|
|
350
423
|
if (match) {
|
|
@@ -379,44 +452,32 @@ function createErrorDetector(privacyFilter) {
|
|
|
379
452
|
};
|
|
380
453
|
}
|
|
381
454
|
function calculateConfidence(signals) {
|
|
382
|
-
if (signals.length === 0)
|
|
383
|
-
return 0;
|
|
455
|
+
if (signals.length === 0) return 0;
|
|
384
456
|
const totalWeight = signals.reduce((sum, s) => sum + s.weight, 0);
|
|
385
457
|
const avgWeight = totalWeight / signals.length;
|
|
386
458
|
const multiplier = Math.min(1.2, 1 + signals.length * 0.05);
|
|
387
459
|
return Math.min(1, avgWeight * multiplier);
|
|
388
460
|
}
|
|
389
461
|
function classifyErrorType(signals, content) {
|
|
390
|
-
if (ERROR_PATTERNS.build.some((p) => p.test(content)))
|
|
391
|
-
|
|
392
|
-
if (ERROR_PATTERNS.
|
|
393
|
-
|
|
394
|
-
if (ERROR_PATTERNS.
|
|
395
|
-
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
if (ERROR_PATTERNS.test.some((p) => p.test(content)))
|
|
399
|
-
return "test";
|
|
400
|
-
if (/TypeError:|type error|Type '[^']+' is not assignable/i.test(content))
|
|
401
|
-
return "type_error";
|
|
402
|
-
if (/SyntaxError:|syntax error|unexpected token/i.test(content))
|
|
403
|
-
return "syntax";
|
|
404
|
-
if (/ReferenceError:|RangeError:|runtime error/i.test(content))
|
|
405
|
-
return "runtime";
|
|
462
|
+
if (ERROR_PATTERNS.build.some((p) => p.test(content))) return "build";
|
|
463
|
+
if (ERROR_PATTERNS.package.some((p) => p.test(content))) return "dependency";
|
|
464
|
+
if (ERROR_PATTERNS.permission.some((p) => p.test(content))) return "permission";
|
|
465
|
+
if (ERROR_PATTERNS.network.some((p) => p.test(content))) return "network";
|
|
466
|
+
if (ERROR_PATTERNS.test.some((p) => p.test(content))) return "test";
|
|
467
|
+
if (/TypeError:|type error|Type '[^']+' is not assignable/i.test(content)) return "type_error";
|
|
468
|
+
if (/SyntaxError:|syntax error|unexpected token/i.test(content)) return "syntax";
|
|
469
|
+
if (/ReferenceError:|RangeError:|runtime error/i.test(content)) return "runtime";
|
|
406
470
|
const hasStackTrace = signals.some((s) => s.type === "stack_trace");
|
|
407
|
-
if (hasStackTrace)
|
|
408
|
-
return "runtime";
|
|
471
|
+
if (hasStackTrace) return "runtime";
|
|
409
472
|
return "unknown";
|
|
410
473
|
}
|
|
411
474
|
function determineSeverity(signals, exitCode) {
|
|
412
|
-
if (exitCode !==
|
|
475
|
+
if (exitCode !== void 0 && EXIT_CODE_SEVERITY[exitCode]) {
|
|
413
476
|
return EXIT_CODE_SEVERITY[exitCode];
|
|
414
477
|
}
|
|
415
478
|
const maxWeight = Math.max(...signals.map((s) => s.weight));
|
|
416
|
-
if (maxWeight >= 0.9)
|
|
417
|
-
|
|
418
|
-
if (maxWeight >= 0.7)
|
|
419
|
-
return "error";
|
|
479
|
+
if (maxWeight >= 0.9) return "error";
|
|
480
|
+
if (maxWeight >= 0.7) return "error";
|
|
420
481
|
return "warning";
|
|
421
482
|
}
|
|
422
483
|
function isErrorLine(line) {
|
|
@@ -424,8 +485,7 @@ function createErrorDetector(privacyFilter) {
|
|
|
424
485
|
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);
|
|
425
486
|
}
|
|
426
487
|
function extractErrorDetails(output) {
|
|
427
|
-
const lines = output.split(
|
|
428
|
-
`);
|
|
488
|
+
const lines = output.split("\n");
|
|
429
489
|
let message = "";
|
|
430
490
|
let stack = "";
|
|
431
491
|
let inStack = false;
|
|
@@ -433,9 +493,11 @@ function createErrorDetector(privacyFilter) {
|
|
|
433
493
|
if (isErrorLine(line) && !message) {
|
|
434
494
|
message = line.trim();
|
|
435
495
|
inStack = true;
|
|
436
|
-
} else if (inStack && (line.match(/^\s+at\s/) ||
|
|
437
|
-
|
|
438
|
-
|
|
496
|
+
} else if (inStack && (line.match(/^\s+at\s/) || // JS stack
|
|
497
|
+
line.match(/^\s+File\s/) || // Python stack
|
|
498
|
+
line.match(/^\s+\d+:\s/) || // Rust stack
|
|
499
|
+
line.match(/^\s+from\s/))) {
|
|
500
|
+
stack += line + "\n";
|
|
439
501
|
}
|
|
440
502
|
}
|
|
441
503
|
if (!message) {
|
|
@@ -448,15 +510,18 @@ function createErrorDetector(privacyFilter) {
|
|
|
448
510
|
}
|
|
449
511
|
return {
|
|
450
512
|
message: message || output.substring(0, 500),
|
|
451
|
-
stack: stack ||
|
|
513
|
+
stack: stack || void 0
|
|
452
514
|
};
|
|
453
515
|
}
|
|
454
516
|
return {
|
|
517
|
+
/**
|
|
518
|
+
* Detect if output contains an error
|
|
519
|
+
*/
|
|
455
520
|
detect(toolOutput) {
|
|
456
521
|
const signals = [];
|
|
457
522
|
const combinedOutput = `${toolOutput.output || ""}
|
|
458
523
|
${toolOutput.stderr || ""}`;
|
|
459
|
-
if (toolOutput.exitCode !==
|
|
524
|
+
if (toolOutput.exitCode !== void 0 && toolOutput.exitCode !== 0) {
|
|
460
525
|
const severity2 = EXIT_CODE_SEVERITY[toolOutput.exitCode] || "error";
|
|
461
526
|
signals.push({
|
|
462
527
|
type: "exit_code",
|
|
@@ -482,8 +547,7 @@ ${toolOutput.stderr || ""}`;
|
|
|
482
547
|
signals.push({
|
|
483
548
|
type: "stack_trace",
|
|
484
549
|
weight: 0.95,
|
|
485
|
-
value: stackTrace.frames.slice(0, 5).join(
|
|
486
|
-
`),
|
|
550
|
+
value: stackTrace.frames.slice(0, 5).join("\n"),
|
|
487
551
|
description: `${stackTrace.language} stack trace detected`
|
|
488
552
|
});
|
|
489
553
|
}
|
|
@@ -493,8 +557,8 @@ ${toolOutput.stderr || ""}`;
|
|
|
493
557
|
const severity = determineSeverity(signals, toolOutput.exitCode);
|
|
494
558
|
const { message, stack } = extractErrorDetails(combinedOutput);
|
|
495
559
|
const sanitizedMessage = filter.sanitize(message);
|
|
496
|
-
const sanitizedStack = stack ? filter.sanitize(stack) :
|
|
497
|
-
const sanitizedOutput = filter.sanitize(combinedOutput.substring(0,
|
|
560
|
+
const sanitizedStack = stack ? filter.sanitize(stack) : void 0;
|
|
561
|
+
const sanitizedOutput = filter.sanitize(combinedOutput.substring(0, 5e3));
|
|
498
562
|
return {
|
|
499
563
|
detected,
|
|
500
564
|
confidence,
|
|
@@ -555,9 +619,8 @@ function calculateStringSimilarity(str1, str2) {
|
|
|
555
619
|
const words1 = new Set(str1.toLowerCase().split(/\s+/));
|
|
556
620
|
const words2 = new Set(str2.toLowerCase().split(/\s+/));
|
|
557
621
|
const intersection = new Set([...words1].filter((x) => words2.has(x)));
|
|
558
|
-
const union = new Set([...words1, ...words2]);
|
|
559
|
-
if (union.size === 0)
|
|
560
|
-
return 0;
|
|
622
|
+
const union = /* @__PURE__ */ new Set([...words1, ...words2]);
|
|
623
|
+
if (union.size === 0) return 0;
|
|
561
624
|
return intersection.size / union.size;
|
|
562
625
|
}
|
|
563
626
|
|
|
@@ -570,7 +633,9 @@ function runMigrations(db) {
|
|
|
570
633
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
571
634
|
)
|
|
572
635
|
`);
|
|
573
|
-
const appliedMigrations = new Set(
|
|
636
|
+
const appliedMigrations = new Set(
|
|
637
|
+
db.prepare("SELECT name FROM migrations").all().map((r) => r.name)
|
|
638
|
+
);
|
|
574
639
|
for (const migration of MIGRATIONS) {
|
|
575
640
|
if (!appliedMigrations.has(migration.name)) {
|
|
576
641
|
db.exec(migration.sql);
|
|
@@ -664,19 +729,19 @@ function rowToRecord(row) {
|
|
|
664
729
|
errorHash: row.error_hash,
|
|
665
730
|
errorType: row.error_type,
|
|
666
731
|
errorMessage: row.error_message,
|
|
667
|
-
errorStack: row.error_stack ||
|
|
668
|
-
language: row.language ||
|
|
669
|
-
framework: row.framework ||
|
|
732
|
+
errorStack: row.error_stack || void 0,
|
|
733
|
+
language: row.language || void 0,
|
|
734
|
+
framework: row.framework || void 0,
|
|
670
735
|
toolName: row.tool_name,
|
|
671
736
|
toolInput: JSON.parse(row.tool_input || "{}"),
|
|
672
737
|
sessionId: row.session_id,
|
|
673
738
|
status: row.status,
|
|
674
|
-
resolution: row.resolution ||
|
|
675
|
-
resolutionCode: row.resolution_code ||
|
|
739
|
+
resolution: row.resolution || void 0,
|
|
740
|
+
resolutionCode: row.resolution_code || void 0,
|
|
676
741
|
createdAt: row.created_at,
|
|
677
|
-
resolvedAt: row.resolved_at ||
|
|
678
|
-
uploadedAt: row.uploaded_at ||
|
|
679
|
-
cloudKnowledgeId: row.cloud_knowledge_id ||
|
|
742
|
+
resolvedAt: row.resolved_at || void 0,
|
|
743
|
+
uploadedAt: row.uploaded_at || void 0,
|
|
744
|
+
cloudKnowledgeId: row.cloud_knowledge_id || void 0
|
|
680
745
|
};
|
|
681
746
|
}
|
|
682
747
|
function createLocalStore(projectDirectory) {
|
|
@@ -697,6 +762,10 @@ function createLocalStore(projectDirectory) {
|
|
|
697
762
|
stmt.run();
|
|
698
763
|
}
|
|
699
764
|
return {
|
|
765
|
+
// ============ Error Records ============
|
|
766
|
+
/**
|
|
767
|
+
* Create a new error record
|
|
768
|
+
*/
|
|
700
769
|
createErrorRecord(data) {
|
|
701
770
|
const id = uuidv4();
|
|
702
771
|
const errorHash = generateErrorFingerprint(data.errorMessage, data.errorStack);
|
|
@@ -724,11 +793,17 @@ function createLocalStore(projectDirectory) {
|
|
|
724
793
|
incrementStat("total_errors");
|
|
725
794
|
return this.getErrorById(id);
|
|
726
795
|
},
|
|
796
|
+
/**
|
|
797
|
+
* Get error record by ID
|
|
798
|
+
*/
|
|
727
799
|
getErrorById(id) {
|
|
728
800
|
const stmt = db.prepare("SELECT * FROM error_records WHERE id = ?");
|
|
729
801
|
const row = stmt.get(id);
|
|
730
802
|
return row ? rowToRecord(row) : null;
|
|
731
803
|
},
|
|
804
|
+
/**
|
|
805
|
+
* Get errors by session
|
|
806
|
+
*/
|
|
732
807
|
getSessionErrors(sessionId, options) {
|
|
733
808
|
let query = "SELECT * FROM error_records WHERE session_id = ?";
|
|
734
809
|
const params = [sessionId];
|
|
@@ -744,13 +819,24 @@ function createLocalStore(projectDirectory) {
|
|
|
744
819
|
const stmt = db.prepare(query);
|
|
745
820
|
return stmt.all(...params).map((row) => rowToRecord(row));
|
|
746
821
|
},
|
|
822
|
+
/**
|
|
823
|
+
* Get unresolved errors for a session
|
|
824
|
+
*/
|
|
747
825
|
getUnresolvedErrors(sessionId) {
|
|
748
826
|
return this.getSessionErrors(sessionId, { status: "unresolved" });
|
|
749
827
|
},
|
|
828
|
+
/**
|
|
829
|
+
* Get recent errors across all sessions
|
|
830
|
+
*/
|
|
750
831
|
getRecentErrors(limit = 10) {
|
|
751
|
-
const stmt = db.prepare(
|
|
832
|
+
const stmt = db.prepare(
|
|
833
|
+
"SELECT * FROM error_records ORDER BY created_at DESC LIMIT ?"
|
|
834
|
+
);
|
|
752
835
|
return stmt.all(limit).map((row) => rowToRecord(row));
|
|
753
836
|
},
|
|
837
|
+
/**
|
|
838
|
+
* Mark error as resolved
|
|
839
|
+
*/
|
|
754
840
|
markResolved(id, data) {
|
|
755
841
|
const stmt = db.prepare(`
|
|
756
842
|
UPDATE error_records
|
|
@@ -767,6 +853,9 @@ function createLocalStore(projectDirectory) {
|
|
|
767
853
|
}
|
|
768
854
|
return null;
|
|
769
855
|
},
|
|
856
|
+
/**
|
|
857
|
+
* Mark error as uploaded to cloud
|
|
858
|
+
*/
|
|
770
859
|
markUploaded(id, cloudKnowledgeId) {
|
|
771
860
|
const stmt = db.prepare(`
|
|
772
861
|
UPDATE error_records
|
|
@@ -780,10 +869,19 @@ function createLocalStore(projectDirectory) {
|
|
|
780
869
|
incrementStat("uploaded_errors");
|
|
781
870
|
}
|
|
782
871
|
},
|
|
872
|
+
/**
|
|
873
|
+
* Find similar errors by hash
|
|
874
|
+
*/
|
|
783
875
|
findSimilarErrors(errorHash) {
|
|
784
|
-
const stmt = db.prepare(
|
|
876
|
+
const stmt = db.prepare(
|
|
877
|
+
"SELECT * FROM error_records WHERE error_hash = ? ORDER BY created_at DESC"
|
|
878
|
+
);
|
|
785
879
|
return stmt.all(errorHash).map((row) => rowToRecord(row));
|
|
786
880
|
},
|
|
881
|
+
// ============ Query Cache ============
|
|
882
|
+
/**
|
|
883
|
+
* Get cached query results
|
|
884
|
+
*/
|
|
787
885
|
getCachedResults(errorHash) {
|
|
788
886
|
const stmt = db.prepare(`
|
|
789
887
|
SELECT results FROM query_cache
|
|
@@ -796,7 +894,10 @@ function createLocalStore(projectDirectory) {
|
|
|
796
894
|
}
|
|
797
895
|
return null;
|
|
798
896
|
},
|
|
799
|
-
|
|
897
|
+
/**
|
|
898
|
+
* Cache query results
|
|
899
|
+
*/
|
|
900
|
+
cacheResults(errorHash, results, expirationMs = 36e5) {
|
|
800
901
|
const id = uuidv4();
|
|
801
902
|
const expiresAt = new Date(Date.now() + expirationMs).toISOString();
|
|
802
903
|
const stmt = db.prepare(`
|
|
@@ -806,13 +907,22 @@ function createLocalStore(projectDirectory) {
|
|
|
806
907
|
stmt.run(id, errorHash, JSON.stringify(results), expiresAt);
|
|
807
908
|
incrementStat("queries_made");
|
|
808
909
|
},
|
|
910
|
+
/**
|
|
911
|
+
* Clear expired cache entries
|
|
912
|
+
*/
|
|
809
913
|
clearExpiredCache() {
|
|
810
914
|
const stmt = db.prepare("DELETE FROM query_cache WHERE expires_at <= datetime('now')");
|
|
811
915
|
const result = stmt.run();
|
|
812
916
|
return result.changes;
|
|
813
917
|
},
|
|
918
|
+
// ============ Statistics ============
|
|
919
|
+
/**
|
|
920
|
+
* Get usage statistics
|
|
921
|
+
*/
|
|
814
922
|
getStats() {
|
|
815
|
-
const stmt = db.prepare(
|
|
923
|
+
const stmt = db.prepare(
|
|
924
|
+
"SELECT total_errors, resolved_errors, uploaded_errors FROM usage_stats WHERE id = 1"
|
|
925
|
+
);
|
|
816
926
|
const row = stmt.get();
|
|
817
927
|
return {
|
|
818
928
|
totalErrors: row.total_errors,
|
|
@@ -820,20 +930,34 @@ function createLocalStore(projectDirectory) {
|
|
|
820
930
|
uploadedErrors: row.uploaded_errors
|
|
821
931
|
};
|
|
822
932
|
},
|
|
933
|
+
// ============ Preferences ============
|
|
934
|
+
/**
|
|
935
|
+
* Get preference value
|
|
936
|
+
*/
|
|
823
937
|
getPreference(key) {
|
|
824
938
|
const stmt = db.prepare("SELECT value FROM user_preferences WHERE key = ?");
|
|
825
939
|
const row = stmt.get(key);
|
|
826
940
|
return row?.value || null;
|
|
827
941
|
},
|
|
942
|
+
/**
|
|
943
|
+
* Set preference value
|
|
944
|
+
*/
|
|
828
945
|
setPreference(key, value) {
|
|
829
946
|
const stmt = db.prepare(`
|
|
830
947
|
INSERT OR REPLACE INTO user_preferences (key, value) VALUES (?, ?)
|
|
831
948
|
`);
|
|
832
949
|
stmt.run(key, value);
|
|
833
950
|
},
|
|
951
|
+
// ============ Utilities ============
|
|
952
|
+
/**
|
|
953
|
+
* Close database connection
|
|
954
|
+
*/
|
|
834
955
|
close() {
|
|
835
956
|
db.close();
|
|
836
957
|
},
|
|
958
|
+
/**
|
|
959
|
+
* Get database for advanced queries
|
|
960
|
+
*/
|
|
837
961
|
getDatabase() {
|
|
838
962
|
return db;
|
|
839
963
|
}
|
|
@@ -843,14 +967,14 @@ var LocalStore = {
|
|
|
843
967
|
create: createLocalStore
|
|
844
968
|
};
|
|
845
969
|
|
|
846
|
-
// src/cloud/client.ts
|
|
847
|
-
import * as supabaseJs from "@supabase/supabase-js";
|
|
848
|
-
|
|
849
970
|
// src/cloud/embedding.ts
|
|
850
|
-
import * as openaiModule from "openai";
|
|
851
971
|
var DEFAULT_MODEL = "text-embedding-3-small";
|
|
852
972
|
var DEFAULT_DIMENSIONS = 1536;
|
|
853
|
-
var MAX_INPUT_LENGTH =
|
|
973
|
+
var MAX_INPUT_LENGTH = 3e4;
|
|
974
|
+
async function getOpenAIClass() {
|
|
975
|
+
const mod = await import("openai");
|
|
976
|
+
return mod.OpenAI || mod.default;
|
|
977
|
+
}
|
|
854
978
|
function cosineSimilarity(a, b) {
|
|
855
979
|
if (a.length !== b.length) {
|
|
856
980
|
throw new Error("Embeddings must have same dimensions");
|
|
@@ -858,19 +982,18 @@ function cosineSimilarity(a, b) {
|
|
|
858
982
|
let dotProduct = 0;
|
|
859
983
|
let normA = 0;
|
|
860
984
|
let normB = 0;
|
|
861
|
-
for (let i = 0;i < a.length; i++) {
|
|
985
|
+
for (let i = 0; i < a.length; i++) {
|
|
862
986
|
dotProduct += a[i] * b[i];
|
|
863
987
|
normA += a[i] * a[i];
|
|
864
988
|
normB += b[i] * b[i];
|
|
865
989
|
}
|
|
866
990
|
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
867
|
-
if (magnitude === 0)
|
|
868
|
-
return 0;
|
|
991
|
+
if (magnitude === 0) return 0;
|
|
869
992
|
return dotProduct / magnitude;
|
|
870
993
|
}
|
|
871
|
-
function createEmbeddingService(config) {
|
|
872
|
-
const
|
|
873
|
-
const client = new
|
|
994
|
+
async function createEmbeddingService(config) {
|
|
995
|
+
const OpenAI = await getOpenAIClass();
|
|
996
|
+
const client = new OpenAI({ apiKey: config.apiKey });
|
|
874
997
|
const model = config.model || DEFAULT_MODEL;
|
|
875
998
|
const dimensions = config.dimensions || DEFAULT_DIMENSIONS;
|
|
876
999
|
function truncateText(text) {
|
|
@@ -878,14 +1001,16 @@ function createEmbeddingService(config) {
|
|
|
878
1001
|
return text;
|
|
879
1002
|
}
|
|
880
1003
|
const truncated = text.substring(0, MAX_INPUT_LENGTH);
|
|
881
|
-
const lastNewline = truncated.lastIndexOf(
|
|
882
|
-
`);
|
|
1004
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
883
1005
|
if (lastNewline > MAX_INPUT_LENGTH * 0.8) {
|
|
884
1006
|
return truncated.substring(0, lastNewline);
|
|
885
1007
|
}
|
|
886
1008
|
return truncated;
|
|
887
1009
|
}
|
|
888
1010
|
return {
|
|
1011
|
+
/**
|
|
1012
|
+
* Generate embedding for a single text
|
|
1013
|
+
*/
|
|
889
1014
|
async generate(text) {
|
|
890
1015
|
const truncated = truncateText(text);
|
|
891
1016
|
const response = await client.embeddings.create({
|
|
@@ -895,6 +1020,9 @@ function createEmbeddingService(config) {
|
|
|
895
1020
|
});
|
|
896
1021
|
return response.data[0].embedding;
|
|
897
1022
|
},
|
|
1023
|
+
/**
|
|
1024
|
+
* Generate embeddings for multiple texts
|
|
1025
|
+
*/
|
|
898
1026
|
async generateBatch(texts) {
|
|
899
1027
|
const truncated = texts.map((t) => truncateText(t));
|
|
900
1028
|
const response = await client.embeddings.create({
|
|
@@ -904,6 +1032,10 @@ function createEmbeddingService(config) {
|
|
|
904
1032
|
});
|
|
905
1033
|
return response.data.map((d) => d.embedding);
|
|
906
1034
|
},
|
|
1035
|
+
/**
|
|
1036
|
+
* Generate embedding for error context
|
|
1037
|
+
* Combines error message, stack trace, and context
|
|
1038
|
+
*/
|
|
907
1039
|
async generateErrorEmbedding(errorMessage, errorStack, context) {
|
|
908
1040
|
const parts = [];
|
|
909
1041
|
if (context?.language) {
|
|
@@ -917,13 +1049,24 @@ function createEmbeddingService(config) {
|
|
|
917
1049
|
parts.push(`Stack Trace:
|
|
918
1050
|
${errorStack}`);
|
|
919
1051
|
}
|
|
920
|
-
const text = parts.join(
|
|
921
|
-
|
|
922
|
-
|
|
1052
|
+
const text = parts.join("\n");
|
|
1053
|
+
const truncated = truncateText(text);
|
|
1054
|
+
const response = await client.embeddings.create({
|
|
1055
|
+
model,
|
|
1056
|
+
input: truncated,
|
|
1057
|
+
dimensions
|
|
1058
|
+
});
|
|
1059
|
+
return response.data[0].embedding;
|
|
923
1060
|
},
|
|
1061
|
+
/**
|
|
1062
|
+
* Get embedding dimensions
|
|
1063
|
+
*/
|
|
924
1064
|
getDimensions() {
|
|
925
1065
|
return dimensions;
|
|
926
1066
|
},
|
|
1067
|
+
/**
|
|
1068
|
+
* Get model name
|
|
1069
|
+
*/
|
|
927
1070
|
getModel() {
|
|
928
1071
|
return model;
|
|
929
1072
|
}
|
|
@@ -935,19 +1078,23 @@ var EmbeddingService = {
|
|
|
935
1078
|
};
|
|
936
1079
|
|
|
937
1080
|
// src/cloud/client.ts
|
|
1081
|
+
async function getSupabaseCreateClient() {
|
|
1082
|
+
const mod = await import("@supabase/supabase-js");
|
|
1083
|
+
return mod.createClient || mod.default?.createClient;
|
|
1084
|
+
}
|
|
938
1085
|
function mapToKnowledgeEntry(row) {
|
|
939
1086
|
return {
|
|
940
1087
|
id: row.id,
|
|
941
1088
|
errorHash: row.error_hash,
|
|
942
1089
|
errorType: row.error_type,
|
|
943
1090
|
errorMessage: row.error_message,
|
|
944
|
-
errorStack: row.error_stack ||
|
|
1091
|
+
errorStack: row.error_stack || void 0,
|
|
945
1092
|
language: row.language,
|
|
946
|
-
framework: row.framework ||
|
|
947
|
-
dependencies: row.dependencies ||
|
|
1093
|
+
framework: row.framework || void 0,
|
|
1094
|
+
dependencies: row.dependencies || void 0,
|
|
948
1095
|
resolutionDescription: row.resolution_description,
|
|
949
|
-
resolutionCode: row.resolution_code ||
|
|
950
|
-
resolutionSteps: row.resolution_steps ||
|
|
1096
|
+
resolutionCode: row.resolution_code || void 0,
|
|
1097
|
+
resolutionSteps: row.resolution_steps || void 0,
|
|
951
1098
|
contributorId: row.contributor_id,
|
|
952
1099
|
upvotes: row.upvotes || 0,
|
|
953
1100
|
downvotes: row.downvotes || 0,
|
|
@@ -955,15 +1102,16 @@ function mapToKnowledgeEntry(row) {
|
|
|
955
1102
|
createdAt: row.created_at,
|
|
956
1103
|
updatedAt: row.updated_at,
|
|
957
1104
|
isVerified: row.is_verified || false,
|
|
958
|
-
similarity: row.similarity ||
|
|
1105
|
+
similarity: row.similarity || void 0
|
|
959
1106
|
};
|
|
960
1107
|
}
|
|
961
1108
|
async function createCloudClient(config) {
|
|
962
|
-
const
|
|
1109
|
+
const createClient = await getSupabaseCreateClient();
|
|
1110
|
+
const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
963
1111
|
let embedding = null;
|
|
964
1112
|
if (config.openaiApiKey) {
|
|
965
1113
|
try {
|
|
966
|
-
embedding = createEmbeddingService({ apiKey: config.openaiApiKey });
|
|
1114
|
+
embedding = await createEmbeddingService({ apiKey: config.openaiApiKey });
|
|
967
1115
|
} catch (err) {
|
|
968
1116
|
console.warn("[FixHive] Failed to initialize embedding service:", err);
|
|
969
1117
|
}
|
|
@@ -1167,6 +1315,9 @@ var CloudClient = {
|
|
|
1167
1315
|
import { tool } from "@opencode-ai/plugin";
|
|
1168
1316
|
function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
1169
1317
|
return {
|
|
1318
|
+
/**
|
|
1319
|
+
* Search cloud knowledge base for error solutions
|
|
1320
|
+
*/
|
|
1170
1321
|
fixhive_search: tool({
|
|
1171
1322
|
description: "Search FixHive knowledge base for error solutions. Use when encountering errors to find community solutions.",
|
|
1172
1323
|
args: {
|
|
@@ -1195,6 +1346,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1195
1346
|
return formatSearchResults(results.results, false);
|
|
1196
1347
|
}
|
|
1197
1348
|
}),
|
|
1349
|
+
/**
|
|
1350
|
+
* Mark error as resolved and optionally upload solution
|
|
1351
|
+
*/
|
|
1198
1352
|
fixhive_resolve: tool({
|
|
1199
1353
|
description: "Mark an error as resolved and optionally share the solution with the community.",
|
|
1200
1354
|
args: {
|
|
@@ -1230,6 +1384,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1230
1384
|
return "Error marked as resolved locally.";
|
|
1231
1385
|
}
|
|
1232
1386
|
}),
|
|
1387
|
+
/**
|
|
1388
|
+
* List errors in current session
|
|
1389
|
+
*/
|
|
1233
1390
|
fixhive_list: tool({
|
|
1234
1391
|
description: "List errors detected in the current session.",
|
|
1235
1392
|
args: {
|
|
@@ -1248,6 +1405,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1248
1405
|
return formatErrorList(errors);
|
|
1249
1406
|
}
|
|
1250
1407
|
}),
|
|
1408
|
+
/**
|
|
1409
|
+
* Vote on a solution
|
|
1410
|
+
*/
|
|
1251
1411
|
fixhive_vote: tool({
|
|
1252
1412
|
description: "Upvote or downvote a FixHive solution based on whether it helped.",
|
|
1253
1413
|
args: {
|
|
@@ -1265,6 +1425,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1265
1425
|
return args.helpful ? "Thanks for the feedback! Solution upvoted." : "Thanks for the feedback! Solution downvoted.";
|
|
1266
1426
|
}
|
|
1267
1427
|
}),
|
|
1428
|
+
/**
|
|
1429
|
+
* Report inappropriate content
|
|
1430
|
+
*/
|
|
1268
1431
|
fixhive_report: tool({
|
|
1269
1432
|
description: "Report a FixHive solution for inappropriate content, spam, or incorrect information.",
|
|
1270
1433
|
args: {
|
|
@@ -1279,6 +1442,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1279
1442
|
return "Report submitted. Thank you for helping keep FixHive clean!";
|
|
1280
1443
|
}
|
|
1281
1444
|
}),
|
|
1445
|
+
/**
|
|
1446
|
+
* Get usage statistics
|
|
1447
|
+
*/
|
|
1282
1448
|
fixhive_stats: tool({
|
|
1283
1449
|
description: "Get FixHive usage statistics.",
|
|
1284
1450
|
args: {},
|
|
@@ -1300,6 +1466,9 @@ function createTools(localStore, cloudClient, privacyFilter, context) {
|
|
|
1300
1466
|
`;
|
|
1301
1467
|
}
|
|
1302
1468
|
}),
|
|
1469
|
+
/**
|
|
1470
|
+
* Report that a solution was helpful
|
|
1471
|
+
*/
|
|
1303
1472
|
fixhive_helpful: tool({
|
|
1304
1473
|
description: "Report that a FixHive solution was helpful and resolved your issue.",
|
|
1305
1474
|
args: {
|
|
@@ -1334,8 +1503,7 @@ ${r.resolutionCode}
|
|
|
1334
1503
|
if (r.resolutionSteps?.length) {
|
|
1335
1504
|
entry += `
|
|
1336
1505
|
**Steps:**
|
|
1337
|
-
${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join(
|
|
1338
|
-
`)}
|
|
1506
|
+
${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join("\n")}
|
|
1339
1507
|
`;
|
|
1340
1508
|
}
|
|
1341
1509
|
entry += `
|
|
@@ -1343,8 +1511,7 @@ ${r.resolutionSteps.map((s, j) => `${j + 1}. ${s}`).join(`
|
|
|
1343
1511
|
|
|
1344
1512
|
---`;
|
|
1345
1513
|
return entry;
|
|
1346
|
-
}).join(
|
|
1347
|
-
`);
|
|
1514
|
+
}).join("\n");
|
|
1348
1515
|
return `${header}
|
|
1349
1516
|
${entries}
|
|
1350
1517
|
|
|
@@ -1355,8 +1522,9 @@ function formatErrorList(errors) {
|
|
|
1355
1522
|
const table = `
|
|
1356
1523
|
| ID | Type | Status | Message |
|
|
1357
1524
|
|----|------|--------|---------|
|
|
1358
|
-
${errors.map(
|
|
1359
|
-
|
|
1525
|
+
${errors.map(
|
|
1526
|
+
(e) => `| ${e.id.slice(0, 8)} | ${e.errorType} | ${e.status} | ${e.errorMessage.slice(0, 50)}... |`
|
|
1527
|
+
).join("\n")}
|
|
1360
1528
|
`;
|
|
1361
1529
|
return `${header}
|
|
1362
1530
|
${table}
|
|
@@ -1366,7 +1534,8 @@ Use \`fixhive_resolve <id>\` to mark as resolved and share solutions.`;
|
|
|
1366
1534
|
|
|
1367
1535
|
// src/plugin/index.ts
|
|
1368
1536
|
var DEFAULT_CONFIG = {
|
|
1369
|
-
cacheExpirationMs:
|
|
1537
|
+
cacheExpirationMs: 36e5,
|
|
1538
|
+
// 1 hour
|
|
1370
1539
|
embeddingModel: "text-embedding-3-small",
|
|
1371
1540
|
embeddingDimensions: 1536,
|
|
1372
1541
|
similarityThreshold: 0.7,
|
|
@@ -1408,9 +1577,9 @@ var FixHivePlugin = async (ctx) => {
|
|
|
1408
1577
|
console.log("[FixHive] Ready - use fixhive_stats to verify");
|
|
1409
1578
|
const errorProducingTools = ["bash", "edit", "write", "read", "terminal"];
|
|
1410
1579
|
return {
|
|
1580
|
+
// ============ Tool Execution Hook ============
|
|
1411
1581
|
"tool.execute.after": async (input, output) => {
|
|
1412
|
-
if (!errorProducingTools.includes(input.tool))
|
|
1413
|
-
return;
|
|
1582
|
+
if (!errorProducingTools.includes(input.tool)) return;
|
|
1414
1583
|
const detection = errorDetector.detect({
|
|
1415
1584
|
tool: input.tool,
|
|
1416
1585
|
output: output.output,
|
|
@@ -1420,7 +1589,7 @@ var FixHivePlugin = async (ctx) => {
|
|
|
1420
1589
|
});
|
|
1421
1590
|
if (detection.detected && detection.confidence >= 0.5) {
|
|
1422
1591
|
const sanitizedErrorMessage = privacyFilter.sanitize(detection.errorMessage, filterContext).sanitized;
|
|
1423
|
-
const sanitizedErrorStack = detection.errorStack ? privacyFilter.sanitize(detection.errorStack, filterContext).sanitized :
|
|
1592
|
+
const sanitizedErrorStack = detection.errorStack ? privacyFilter.sanitize(detection.errorStack, filterContext).sanitized : void 0;
|
|
1424
1593
|
localStore.createErrorRecord({
|
|
1425
1594
|
errorType: detection.errorType,
|
|
1426
1595
|
errorMessage: sanitizedErrorMessage,
|
|
@@ -1429,6 +1598,7 @@ var FixHivePlugin = async (ctx) => {
|
|
|
1429
1598
|
framework: pluginContext.framework,
|
|
1430
1599
|
toolName: input.tool,
|
|
1431
1600
|
toolInput: {},
|
|
1601
|
+
// Tool input is intentionally omitted to avoid storing sensitive data
|
|
1432
1602
|
sessionId: pluginContext.sessionId || input.sessionID
|
|
1433
1603
|
});
|
|
1434
1604
|
if (cloudClient) {
|
|
@@ -1441,7 +1611,10 @@ var FixHivePlugin = async (ctx) => {
|
|
|
1441
1611
|
limit: 3
|
|
1442
1612
|
});
|
|
1443
1613
|
if (solutions.results.length > 0) {
|
|
1444
|
-
localStore.cacheResults(
|
|
1614
|
+
localStore.cacheResults(
|
|
1615
|
+
generateErrorFingerprint(sanitizedErrorMessage, sanitizedErrorStack),
|
|
1616
|
+
solutions.results
|
|
1617
|
+
);
|
|
1445
1618
|
output.title = `${output.title} [FixHive: ${solutions.results.length} solution(s) found]`;
|
|
1446
1619
|
}
|
|
1447
1620
|
} catch (e) {
|
|
@@ -1451,22 +1624,24 @@ var FixHivePlugin = async (ctx) => {
|
|
|
1451
1624
|
}
|
|
1452
1625
|
}
|
|
1453
1626
|
},
|
|
1627
|
+
// ============ Session Compaction Hook ============
|
|
1454
1628
|
"experimental.session.compacting": async (_input, output) => {
|
|
1455
1629
|
const unresolvedErrors = localStore.getUnresolvedErrors(pluginContext.sessionId);
|
|
1456
1630
|
if (unresolvedErrors.length > 0) {
|
|
1457
1631
|
output.context.push(`
|
|
1458
1632
|
## FixHive: Unresolved Errors in Session
|
|
1459
1633
|
|
|
1460
|
-
${unresolvedErrors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 100)}...`).join(
|
|
1461
|
-
`)}
|
|
1634
|
+
${unresolvedErrors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 100)}...`).join("\n")}
|
|
1462
1635
|
|
|
1463
1636
|
Use \`fixhive_mark_resolved\` when errors are fixed to contribute solutions.
|
|
1464
1637
|
`);
|
|
1465
1638
|
}
|
|
1466
1639
|
},
|
|
1640
|
+
// ============ Chat Message Hook ============
|
|
1467
1641
|
"chat.message": async (input, _output) => {
|
|
1468
1642
|
pluginContext.sessionId = input.sessionID;
|
|
1469
1643
|
},
|
|
1644
|
+
// ============ Custom Tools ============
|
|
1470
1645
|
tool: cloudClient ? createTools(localStore, cloudClient, privacyFilter, pluginContext) : createOfflineTools(localStore, privacyFilter, pluginContext)
|
|
1471
1646
|
};
|
|
1472
1647
|
};
|
|
@@ -1489,8 +1664,7 @@ function createOfflineTools(localStore, _privacyFilter, context) {
|
|
|
1489
1664
|
}
|
|
1490
1665
|
return `## Session Errors (${errors.length})
|
|
1491
1666
|
|
|
1492
|
-
${errors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 80)}...`).join(
|
|
1493
|
-
`)}
|
|
1667
|
+
${errors.map((e) => `- [${e.id.slice(0, 8)}] ${e.errorType}: ${e.errorMessage.slice(0, 80)}...`).join("\n")}
|
|
1494
1668
|
|
|
1495
1669
|
*Cloud features disabled. Set FIXHIVE_SUPABASE_URL and FIXHIVE_SUPABASE_KEY to enable.*`;
|
|
1496
1670
|
}
|
|
@@ -1549,7 +1723,7 @@ function detectLanguage(directory) {
|
|
|
1549
1723
|
return lang;
|
|
1550
1724
|
}
|
|
1551
1725
|
}
|
|
1552
|
-
return;
|
|
1726
|
+
return void 0;
|
|
1553
1727
|
}
|
|
1554
1728
|
function detectFramework(directory) {
|
|
1555
1729
|
const pkgPath = join(directory, "package.json");
|
|
@@ -1557,61 +1731,54 @@ function detectFramework(directory) {
|
|
|
1557
1731
|
try {
|
|
1558
1732
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1559
1733
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1560
|
-
if (deps["next"])
|
|
1561
|
-
|
|
1562
|
-
if (deps["
|
|
1563
|
-
|
|
1564
|
-
if (deps["
|
|
1565
|
-
|
|
1566
|
-
if (deps["
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
return "express";
|
|
1570
|
-
if (deps["fastify"])
|
|
1571
|
-
return "fastify";
|
|
1572
|
-
if (deps["hono"])
|
|
1573
|
-
return "hono";
|
|
1574
|
-
} catch {}
|
|
1734
|
+
if (deps["next"]) return "nextjs";
|
|
1735
|
+
if (deps["react"]) return "react";
|
|
1736
|
+
if (deps["vue"]) return "vue";
|
|
1737
|
+
if (deps["@angular/core"]) return "angular";
|
|
1738
|
+
if (deps["express"]) return "express";
|
|
1739
|
+
if (deps["fastify"]) return "fastify";
|
|
1740
|
+
if (deps["hono"]) return "hono";
|
|
1741
|
+
} catch {
|
|
1742
|
+
}
|
|
1575
1743
|
}
|
|
1576
1744
|
const reqPath = join(directory, "requirements.txt");
|
|
1577
1745
|
if (existsSync2(reqPath)) {
|
|
1578
1746
|
try {
|
|
1579
1747
|
const content = readFileSync(reqPath, "utf-8");
|
|
1580
|
-
if (content.includes("django"))
|
|
1581
|
-
|
|
1582
|
-
if (content.includes("
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
return "fastapi";
|
|
1586
|
-
} catch {}
|
|
1748
|
+
if (content.includes("django")) return "django";
|
|
1749
|
+
if (content.includes("flask")) return "flask";
|
|
1750
|
+
if (content.includes("fastapi")) return "fastapi";
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1587
1753
|
}
|
|
1588
|
-
return;
|
|
1754
|
+
return void 0;
|
|
1589
1755
|
}
|
|
1590
1756
|
var plugin_default = FixHivePlugin;
|
|
1591
1757
|
export {
|
|
1592
|
-
|
|
1593
|
-
sha256,
|
|
1594
|
-
runMigrations,
|
|
1595
|
-
normalizeErrorContent,
|
|
1596
|
-
generateSessionHash,
|
|
1597
|
-
generateErrorFingerprint,
|
|
1598
|
-
generateContributorId,
|
|
1599
|
-
fingerprintsMatch,
|
|
1600
|
-
defaultPrivacyFilter,
|
|
1601
|
-
defaultErrorDetector,
|
|
1602
|
-
plugin_default as default,
|
|
1603
|
-
createPrivacyFilter,
|
|
1604
|
-
createLocalStore,
|
|
1605
|
-
createFilterContext,
|
|
1606
|
-
createErrorDetector,
|
|
1607
|
-
createEmbeddingService,
|
|
1608
|
-
createCloudClient,
|
|
1609
|
-
cosineSimilarity,
|
|
1610
|
-
calculateStringSimilarity,
|
|
1611
|
-
PrivacyFilter,
|
|
1612
|
-
LocalStore,
|
|
1613
|
-
FixHivePlugin,
|
|
1614
|
-
ErrorDetector,
|
|
1758
|
+
CloudClient,
|
|
1615
1759
|
EmbeddingService,
|
|
1616
|
-
|
|
1760
|
+
ErrorDetector,
|
|
1761
|
+
FixHivePlugin,
|
|
1762
|
+
LocalStore,
|
|
1763
|
+
PrivacyFilter,
|
|
1764
|
+
calculateStringSimilarity,
|
|
1765
|
+
cosineSimilarity,
|
|
1766
|
+
createCloudClient,
|
|
1767
|
+
createEmbeddingService,
|
|
1768
|
+
createErrorDetector,
|
|
1769
|
+
createFilterContext,
|
|
1770
|
+
createLocalStore,
|
|
1771
|
+
createPrivacyFilter,
|
|
1772
|
+
plugin_default as default,
|
|
1773
|
+
defaultErrorDetector,
|
|
1774
|
+
defaultPrivacyFilter,
|
|
1775
|
+
fingerprintsMatch,
|
|
1776
|
+
generateContributorId,
|
|
1777
|
+
generateErrorFingerprint,
|
|
1778
|
+
generateSessionHash,
|
|
1779
|
+
normalizeErrorContent,
|
|
1780
|
+
runMigrations,
|
|
1781
|
+
sha256,
|
|
1782
|
+
shortHash
|
|
1617
1783
|
};
|
|
1784
|
+
//# sourceMappingURL=index.js.map
|