@triedotdev/mcp 1.0.62 → 1.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +591 -52
  2. package/dist/agent-smith-W4HUCFGC.js +14 -0
  3. package/dist/{agent-smith-runner-ZU4R3I2Z.js → agent-smith-runner-QRVOEOBE.js} +13 -7
  4. package/dist/agent-smith-runner-QRVOEOBE.js.map +1 -0
  5. package/dist/chunk-4YSLDGBL.js +674 -0
  6. package/dist/chunk-4YSLDGBL.js.map +1 -0
  7. package/dist/chunk-7KHT2NKR.js +212 -0
  8. package/dist/chunk-7KHT2NKR.js.map +1 -0
  9. package/dist/{chunk-XSPS463E.js → chunk-ALA6733H.js} +492 -14
  10. package/dist/chunk-ALA6733H.js.map +1 -0
  11. package/dist/chunk-AQCAMIQQ.js +139 -0
  12. package/dist/chunk-AQCAMIQQ.js.map +1 -0
  13. package/dist/chunk-D3DMONAJ.js +904 -0
  14. package/dist/chunk-D3DMONAJ.js.map +1 -0
  15. package/dist/{chunk-KB5ZN6K2.js → chunk-GWSNINKX.js} +2 -2
  16. package/dist/{chunk-32WLOG6E.js → chunk-K6BQBKIR.js} +662 -633
  17. package/dist/chunk-K6BQBKIR.js.map +1 -0
  18. package/dist/{chunk-ASGSTVVF.js → chunk-KOFQ47YW.js} +10 -6
  19. package/dist/chunk-KOFQ47YW.js.map +1 -0
  20. package/dist/{chunk-XXNE6HBE.js → chunk-N2AZH3EQ.js} +7697 -4803
  21. package/dist/chunk-N2AZH3EQ.js.map +1 -0
  22. package/dist/chunk-PBOVCPKE.js +2566 -0
  23. package/dist/chunk-PBOVCPKE.js.map +1 -0
  24. package/dist/{chunk-NUT4G5AY.js → chunk-R7Z7OHTJ.js} +493 -650
  25. package/dist/chunk-R7Z7OHTJ.js.map +1 -0
  26. package/dist/chunk-TSHZQKCM.js +933 -0
  27. package/dist/chunk-TSHZQKCM.js.map +1 -0
  28. package/dist/{chunk-S4VGGLXF.js → chunk-X2PABPBH.js} +461 -892
  29. package/dist/chunk-X2PABPBH.js.map +1 -0
  30. package/dist/cli/create-agent.js +3 -2
  31. package/dist/cli/create-agent.js.map +1 -1
  32. package/dist/cli/main.js +1120 -70
  33. package/dist/cli/main.js.map +1 -1
  34. package/dist/cli/yolo-daemon.js +151 -41
  35. package/dist/cli/yolo-daemon.js.map +1 -1
  36. package/dist/goal-manager-KFBOAP4X.js +20 -0
  37. package/dist/goal-manager-KFBOAP4X.js.map +1 -0
  38. package/dist/guardian-agent-PULK546O.js +17 -0
  39. package/dist/guardian-agent-PULK546O.js.map +1 -0
  40. package/dist/index.js +329 -74
  41. package/dist/index.js.map +1 -1
  42. package/dist/issue-store-QRDF3X55.js +22 -0
  43. package/dist/issue-store-QRDF3X55.js.map +1 -0
  44. package/dist/workers/agent-worker.js +6 -3
  45. package/dist/workers/agent-worker.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/agent-smith-57MKX5QC.js +0 -13
  48. package/dist/agent-smith-runner-ZU4R3I2Z.js.map +0 -1
  49. package/dist/chunk-32WLOG6E.js.map +0 -1
  50. package/dist/chunk-ASGSTVVF.js.map +0 -1
  51. package/dist/chunk-NUT4G5AY.js.map +0 -1
  52. package/dist/chunk-S4VGGLXF.js.map +0 -1
  53. package/dist/chunk-XSPS463E.js.map +0 -1
  54. package/dist/chunk-XXNE6HBE.js.map +0 -1
  55. /package/dist/{agent-smith-57MKX5QC.js.map → agent-smith-W4HUCFGC.js.map} +0 -0
  56. /package/dist/{chunk-KB5ZN6K2.js.map → chunk-GWSNINKX.js.map} +0 -0
@@ -0,0 +1,2566 @@
1
+ import {
2
+ getGoalManager,
3
+ getInsightStore
4
+ } from "./chunk-D3DMONAJ.js";
5
+ import {
6
+ getGuardianState
7
+ } from "./chunk-4YSLDGBL.js";
8
+ import {
9
+ findCrossProjectPatterns,
10
+ recordToGlobalMemory
11
+ } from "./chunk-7KHT2NKR.js";
12
+ import {
13
+ isAIAvailable,
14
+ runAIAnalysis
15
+ } from "./chunk-AQCAMIQQ.js";
16
+ import {
17
+ getHistoricalInsights,
18
+ searchIssues,
19
+ storeIssues
20
+ } from "./chunk-TSHZQKCM.js";
21
+
22
+ // src/guardian/guardian-agent.ts
23
+ import { basename as basename2 } from "path";
24
+
25
+ // src/guardian/risk-predictor.ts
26
+ import { basename, dirname } from "path";
27
+ var DEFAULT_CONFIG = {
28
+ historyDays: 30,
29
+ incidentThreshold: 3,
30
+ riskWeights: {
31
+ incidentCount: 0.35,
32
+ recency: 0.25,
33
+ severity: 0.2,
34
+ complexity: 0.1,
35
+ churn: 0.1
36
+ }
37
+ };
38
+ var RiskPredictor = class {
39
+ projectPath;
40
+ config;
41
+ insightStore;
42
+ constructor(projectPath, config = {}) {
43
+ this.projectPath = projectPath;
44
+ this.config = { ...DEFAULT_CONFIG, ...config };
45
+ this.insightStore = getInsightStore(projectPath);
46
+ }
47
+ /**
48
+ * Calculate risk for a specific file or directory
49
+ */
50
+ async calculateRisk(target) {
51
+ const factors = [];
52
+ let totalRisk = 0;
53
+ try {
54
+ const issues = await searchIssues(target, {
55
+ workDir: this.projectPath,
56
+ limit: 500,
57
+ includeResolved: true
58
+ });
59
+ const targetIssues = issues.filter(
60
+ (r) => r.issue.file === target || r.issue.file.startsWith(target + "/") || dirname(r.issue.file) === target
61
+ );
62
+ const incidentCount = targetIssues.length;
63
+ const incidentWeight = this.config.riskWeights.incidentCount;
64
+ const incidentScore = Math.min(100, incidentCount * 10);
65
+ factors.push({
66
+ name: "Incident Count",
67
+ weight: incidentWeight,
68
+ value: incidentCount,
69
+ description: `${incidentCount} historical incidents`,
70
+ trend: this.calculateTrend(targetIssues.map((i) => i.issue))
71
+ });
72
+ totalRisk += incidentScore * incidentWeight;
73
+ let recencyScore = 0;
74
+ let lastIncidentDaysAgo;
75
+ if (targetIssues.length > 0) {
76
+ const latestIssue = targetIssues.sort((a, b) => new Date(b.issue.timestamp).getTime() - new Date(a.issue.timestamp).getTime())[0];
77
+ if (latestIssue) {
78
+ const daysSince = (Date.now() - new Date(latestIssue.issue.timestamp).getTime()) / (1e3 * 60 * 60 * 24);
79
+ lastIncidentDaysAgo = Math.floor(daysSince);
80
+ if (daysSince < 1) recencyScore = 100;
81
+ else if (daysSince < 7) recencyScore = 80;
82
+ else if (daysSince < 14) recencyScore = 60;
83
+ else if (daysSince < 30) recencyScore = 40;
84
+ else recencyScore = 20;
85
+ }
86
+ }
87
+ factors.push({
88
+ name: "Recency",
89
+ weight: this.config.riskWeights.recency,
90
+ value: lastIncidentDaysAgo ?? 999,
91
+ description: lastIncidentDaysAgo !== void 0 ? `Last incident ${lastIncidentDaysAgo} days ago` : "No recent incidents"
92
+ });
93
+ totalRisk += recencyScore * this.config.riskWeights.recency;
94
+ const criticalCount = targetIssues.filter((r) => r.issue.severity === "critical").length;
95
+ const seriousCount = targetIssues.filter((r) => r.issue.severity === "serious").length;
96
+ const severityScore = Math.min(100, criticalCount * 25 + seriousCount * 10);
97
+ factors.push({
98
+ name: "Severity",
99
+ weight: this.config.riskWeights.severity,
100
+ value: severityScore,
101
+ description: `${criticalCount} critical, ${seriousCount} serious`
102
+ });
103
+ totalRisk += severityScore * this.config.riskWeights.severity;
104
+ const uniqueAgents = new Set(targetIssues.map((r) => r.issue.agent)).size;
105
+ const complexityScore = Math.min(100, uniqueAgents * 20);
106
+ factors.push({
107
+ name: "Complexity",
108
+ weight: this.config.riskWeights.complexity,
109
+ value: uniqueAgents,
110
+ description: `Issues from ${uniqueAgents} different analysis types`
111
+ });
112
+ totalRisk += complexityScore * this.config.riskWeights.complexity;
113
+ const uniqueDates = new Set(
114
+ targetIssues.map((r) => r.issue.timestamp.split("T")[0])
115
+ ).size;
116
+ const churnScore = Math.min(100, uniqueDates * 10);
117
+ factors.push({
118
+ name: "Churn",
119
+ weight: this.config.riskWeights.churn,
120
+ value: uniqueDates,
121
+ description: `Issues found on ${uniqueDates} different days`
122
+ });
123
+ totalRisk += churnScore * this.config.riskWeights.churn;
124
+ const trend = this.calculateTrend(targetIssues.map((i) => i.issue));
125
+ let predictedRisk = totalRisk;
126
+ if (trend === "increasing") {
127
+ predictedRisk = Math.min(100, totalRisk * 1.2);
128
+ } else if (trend === "decreasing") {
129
+ predictedRisk = Math.max(0, totalRisk * 0.8);
130
+ }
131
+ const recommendations = this.generateRecommendations(factors, totalRisk);
132
+ const confidence = Math.min(0.95, 0.3 + incidentCount * 0.05);
133
+ const prediction = {
134
+ target,
135
+ currentRisk: Math.round(totalRisk),
136
+ predictedRisk: Math.round(predictedRisk),
137
+ trend,
138
+ factors,
139
+ confidence,
140
+ recommendations,
141
+ incidentCount
142
+ };
143
+ if (lastIncidentDaysAgo !== void 0) {
144
+ prediction.lastIncidentDaysAgo = lastIncidentDaysAgo;
145
+ }
146
+ return prediction;
147
+ } catch (error) {
148
+ console.error(`Failed to calculate risk for ${target}:`, error);
149
+ return {
150
+ target,
151
+ currentRisk: 50,
152
+ predictedRisk: 50,
153
+ trend: "stable",
154
+ factors: [],
155
+ confidence: 0.1,
156
+ recommendations: ["Unable to analyze - insufficient data"],
157
+ incidentCount: 0
158
+ };
159
+ }
160
+ }
161
+ /**
162
+ * Calculate trend from issues
163
+ */
164
+ calculateTrend(issues) {
165
+ if (issues.length < 3) return "stable";
166
+ const now = Date.now();
167
+ const recentCutoff = now - 7 * 24 * 60 * 60 * 1e3;
168
+ const olderCutoff = now - 30 * 24 * 60 * 60 * 1e3;
169
+ const recentIssues = issues.filter((i) => new Date(i.timestamp).getTime() > recentCutoff).length;
170
+ const olderIssues = issues.filter((i) => {
171
+ const time = new Date(i.timestamp).getTime();
172
+ return time > olderCutoff && time <= recentCutoff;
173
+ }).length;
174
+ const recentRate = recentIssues / 7;
175
+ const olderRate = olderIssues / 23;
176
+ if (recentRate > olderRate * 1.5) return "increasing";
177
+ if (recentRate < olderRate * 0.5) return "decreasing";
178
+ return "stable";
179
+ }
180
+ /**
181
+ * Generate recommendations based on risk factors
182
+ */
183
+ generateRecommendations(factors, totalRisk) {
184
+ const recommendations = [];
185
+ if (totalRisk >= 70) {
186
+ recommendations.push("Consider extra code review before merging changes");
187
+ }
188
+ const incidentFactor = factors.find((f) => f.name === "Incident Count");
189
+ if (incidentFactor && incidentFactor.value >= 5) {
190
+ recommendations.push("This area has recurring issues - consider refactoring");
191
+ }
192
+ const recencyFactor = factors.find((f) => f.name === "Recency");
193
+ if (recencyFactor && recencyFactor.value < 7) {
194
+ recommendations.push("Recent incident activity - monitor closely");
195
+ }
196
+ const severityFactor = factors.find((f) => f.name === "Severity");
197
+ if (severityFactor && severityFactor.value >= 25) {
198
+ recommendations.push("High severity issues present - prioritize fixes");
199
+ }
200
+ const complexityFactor = factors.find((f) => f.name === "Complexity");
201
+ if (complexityFactor && complexityFactor.value >= 4) {
202
+ recommendations.push("Multiple issue types - consider comprehensive review");
203
+ }
204
+ if (recommendations.length === 0) {
205
+ recommendations.push("No specific concerns - maintain normal review process");
206
+ }
207
+ return recommendations;
208
+ }
209
+ /**
210
+ * Predict risk trend for files that have recently changed
211
+ */
212
+ async predictRiskTrend(changedFiles) {
213
+ const predictions = await Promise.all(
214
+ changedFiles.map((f) => this.calculateRisk(f))
215
+ );
216
+ const highRiskFiles = predictions.filter((p) => p.currentRisk >= 50);
217
+ const totalRisk = predictions.reduce((sum, p) => sum + p.currentRisk, 0) / Math.max(1, predictions.length);
218
+ const trends = predictions.map((p) => p.trend);
219
+ const increasingCount = trends.filter((t) => t === "increasing").length;
220
+ const decreasingCount = trends.filter((t) => t === "decreasing").length;
221
+ let overallTrend = "stable";
222
+ if (increasingCount > decreasingCount + 1) {
223
+ overallTrend = "increasing";
224
+ } else if (decreasingCount > increasingCount + 1) {
225
+ overallTrend = "decreasing";
226
+ }
227
+ const factors = highRiskFiles.map((p) => ({
228
+ file: p.target,
229
+ reasons: p.incidentCount > 0 ? ["past incidents", ...p.recommendations.slice(0, 2)] : ["no historical data"]
230
+ }));
231
+ return {
232
+ trend: overallTrend,
233
+ factors,
234
+ overallRisk: Math.round(totalRisk)
235
+ };
236
+ }
237
+ /**
238
+ * Get project-wide risk summary
239
+ */
240
+ async getProjectRiskSummary() {
241
+ try {
242
+ const issues = await searchIssues("", {
243
+ workDir: this.projectPath,
244
+ limit: 1e3,
245
+ includeResolved: true
246
+ });
247
+ const dirIssueCount = /* @__PURE__ */ new Map();
248
+ for (const { issue } of issues) {
249
+ const dir = dirname(issue.file);
250
+ const existing = dirIssueCount.get(dir) || [];
251
+ existing.push(issue);
252
+ dirIssueCount.set(dir, existing);
253
+ }
254
+ const hotspotDirs = [...dirIssueCount.entries()].filter(([_, issues2]) => issues2.length >= this.config.incidentThreshold).sort((a, b) => b[1].length - a[1].length).slice(0, 5);
255
+ const hotspots = await Promise.all(
256
+ hotspotDirs.map(([dir]) => this.calculateRisk(dir))
257
+ );
258
+ const allDirs = [...dirIssueCount.keys()];
259
+ const lowRiskPredictions = await Promise.all(
260
+ allDirs.map((dir) => this.calculateRisk(dir))
261
+ );
262
+ const lowRiskAreas = lowRiskPredictions.filter((p) => p.currentRisk < 20 && p.incidentCount === 0).map((p) => p.target).slice(0, 5);
263
+ const overallRisk = hotspots.length > 0 ? hotspots.reduce((sum, p) => sum + p.currentRisk, 0) / hotspots.length : 0;
264
+ const trends = hotspots.map((p) => p.trend);
265
+ const increasingCount = trends.filter((t) => t === "increasing").length;
266
+ const decreasingCount = trends.filter((t) => t === "decreasing").length;
267
+ let trend = "stable";
268
+ if (increasingCount > decreasingCount) {
269
+ trend = "increasing";
270
+ } else if (decreasingCount > increasingCount) {
271
+ trend = "decreasing";
272
+ }
273
+ const confidence = Math.min(0.9, 0.4 + issues.length * 1e-3);
274
+ return {
275
+ overallRisk: Math.round(overallRisk),
276
+ trend,
277
+ hotspots,
278
+ lowRiskAreas,
279
+ predictions: hotspots,
280
+ confidence
281
+ };
282
+ } catch (error) {
283
+ console.error("Failed to get project risk summary:", error);
284
+ return {
285
+ overallRisk: 0,
286
+ trend: "stable",
287
+ hotspots: [],
288
+ lowRiskAreas: [],
289
+ predictions: [],
290
+ confidence: 0.1
291
+ };
292
+ }
293
+ }
294
+ /**
295
+ * Create risk prediction insight
296
+ */
297
+ async generateRiskInsight() {
298
+ if (!this.insightStore.canCreateInsight("risk-prediction")) {
299
+ return null;
300
+ }
301
+ const summary = await this.getProjectRiskSummary();
302
+ if (summary.hotspots.length === 0 || summary.overallRisk < 30) {
303
+ return null;
304
+ }
305
+ const topHotspot = summary.hotspots[0];
306
+ if (!topHotspot) return null;
307
+ const trendMap = {
308
+ "increasing": "worsening",
309
+ "stable": "stable",
310
+ "decreasing": "improving"
311
+ };
312
+ const insight = {
313
+ id: `insight-risk-${Date.now()}`,
314
+ type: "warning",
315
+ message: `\u26A0\uFE0F ${basename(topHotspot.target)}/ has elevated risk (${topHotspot.currentRisk}/100)`,
316
+ context: `${topHotspot.incidentCount} past incidents. ${topHotspot.recommendations[0]}`,
317
+ suggestedAction: "Request extra review before merging changes to this area",
318
+ relatedIssues: [],
319
+ priority: Math.min(9, 5 + Math.floor(topHotspot.currentRisk / 20)),
320
+ timestamp: Date.now(),
321
+ dismissed: false,
322
+ category: "security",
323
+ details: {
324
+ affectedFiles: summary.hotspots.map((h) => basename(h.target)),
325
+ trend: trendMap[summary.trend],
326
+ examples: topHotspot.recommendations
327
+ }
328
+ };
329
+ await this.insightStore.addInsight(insight);
330
+ await this.insightStore.markInsightCreated("risk-prediction");
331
+ return insight;
332
+ }
333
+ };
334
+ var riskPredictors = /* @__PURE__ */ new Map();
335
+ function getRiskPredictor(projectPath) {
336
+ let predictor = riskPredictors.get(projectPath);
337
+ if (!predictor) {
338
+ predictor = new RiskPredictor(projectPath);
339
+ riskPredictors.set(projectPath, predictor);
340
+ }
341
+ return predictor;
342
+ }
343
+
344
+ // src/guardian/hypothesis.ts
345
+ import { dirname as dirname2 } from "path";
346
+ var HYPOTHESIS_TEMPLATES = [
347
+ {
348
+ category: "timing",
349
+ statement: "Issues spike after weekend deployments",
350
+ testCriteria: "Compare Monday issue counts to other days",
351
+ minConfidence: 0.3,
352
+ dataRequired: ["issue_timestamps", "deployment_data"]
353
+ },
354
+ {
355
+ category: "timing",
356
+ statement: "Code quality declines on Fridays",
357
+ testCriteria: "Compare Friday issue introduction rate to weekly average",
358
+ minConfidence: 0.3,
359
+ dataRequired: ["issue_timestamps"]
360
+ },
361
+ {
362
+ category: "pattern",
363
+ statement: "Auth-related code has the highest incident rate",
364
+ testCriteria: "Compare auth/ issue count to other directories",
365
+ minConfidence: 0.4,
366
+ dataRequired: ["issue_files"]
367
+ },
368
+ {
369
+ category: "pattern",
370
+ statement: "Security issues cluster in specific directories",
371
+ testCriteria: "Check if security issues are concentrated (>50% in <3 dirs)",
372
+ minConfidence: 0.4,
373
+ dataRequired: ["issue_files", "issue_agents"]
374
+ },
375
+ {
376
+ category: "code",
377
+ statement: "Files with multiple authors have more issues",
378
+ testCriteria: "Correlate file author count with issue count",
379
+ minConfidence: 0.4,
380
+ dataRequired: ["issue_files", "git_blame"]
381
+ },
382
+ {
383
+ category: "code",
384
+ statement: "Recently modified files are more likely to have issues",
385
+ testCriteria: "Compare issue rate for recent vs old files",
386
+ minConfidence: 0.3,
387
+ dataRequired: ["issue_timestamps", "file_modification_times"]
388
+ },
389
+ {
390
+ category: "pattern",
391
+ statement: "Critical issues often come in clusters",
392
+ testCriteria: "Check temporal clustering of critical issues",
393
+ minConfidence: 0.3,
394
+ dataRequired: ["issue_timestamps", "issue_severities"]
395
+ }
396
+ ];
397
+ var HypothesisEngine = class {
398
+ projectPath;
399
+ guardianState;
400
+ insightStore;
401
+ constructor(projectPath) {
402
+ this.projectPath = projectPath;
403
+ this.guardianState = getGuardianState(projectPath);
404
+ this.insightStore = getInsightStore(projectPath);
405
+ }
406
+ /**
407
+ * Create a new hypothesis
408
+ */
409
+ async createHypothesis(statement, options) {
410
+ const hypothesis = {
411
+ id: `hyp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
412
+ statement,
413
+ confidence: options?.initialConfidence ?? 0.5,
414
+ status: "proposed",
415
+ evidence: [],
416
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
417
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
418
+ testCriteria: options?.testCriteria,
419
+ category: options?.category ?? "general"
420
+ };
421
+ await this.guardianState.addHypothesis(hypothesis);
422
+ return hypothesis;
423
+ }
424
+ /**
425
+ * Auto-generate hypotheses based on detected patterns
426
+ */
427
+ async autoGenerateHypotheses() {
428
+ const generated = [];
429
+ try {
430
+ await this.guardianState.load();
431
+ const existing = this.guardianState.getAllHypotheses();
432
+ const existingStatements = new Set(existing.map((h) => h.statement.toLowerCase()));
433
+ const issues = await searchIssues("", {
434
+ workDir: this.projectPath,
435
+ limit: 500,
436
+ includeResolved: true
437
+ });
438
+ if (issues.length < 10) {
439
+ return generated;
440
+ }
441
+ for (const template of HYPOTHESIS_TEMPLATES) {
442
+ if (existingStatements.has(template.statement.toLowerCase())) {
443
+ continue;
444
+ }
445
+ const evidence = await this.gatherInitialEvidence(template, issues);
446
+ if (evidence.length > 0 && evidence[0].weight >= template.minConfidence) {
447
+ const hypothesis = await this.createHypothesis(template.statement, {
448
+ category: template.category,
449
+ testCriteria: template.testCriteria,
450
+ initialConfidence: evidence[0].weight
451
+ });
452
+ for (const e of evidence) {
453
+ await this.guardianState.addEvidence(hypothesis.id, e);
454
+ }
455
+ generated.push(hypothesis);
456
+ if (generated.length >= 2) break;
457
+ }
458
+ }
459
+ } catch (error) {
460
+ console.error("Failed to auto-generate hypotheses:", error);
461
+ }
462
+ return generated;
463
+ }
464
+ /**
465
+ * Gather initial evidence for a hypothesis template
466
+ */
467
+ async gatherInitialEvidence(template, issues) {
468
+ const evidence = [];
469
+ switch (template.category) {
470
+ case "timing":
471
+ evidence.push(...this.analyzeTimingPatterns(template, issues));
472
+ break;
473
+ case "pattern":
474
+ evidence.push(...this.analyzeLocationPatterns(template, issues));
475
+ break;
476
+ case "code":
477
+ evidence.push(...this.analyzeCodePatterns(template, issues));
478
+ break;
479
+ }
480
+ return evidence;
481
+ }
482
+ /**
483
+ * Analyze timing patterns in issues
484
+ */
485
+ analyzeTimingPatterns(template, issues) {
486
+ const evidence = [];
487
+ const dayOfWeekCounts = {};
488
+ for (const { issue } of issues) {
489
+ const date = new Date(issue.timestamp);
490
+ const day = date.getDay();
491
+ dayOfWeekCounts[day] = (dayOfWeekCounts[day] || 0) + 1;
492
+ }
493
+ if (template.statement.includes("Friday")) {
494
+ const fridayCount = dayOfWeekCounts[5] || 0;
495
+ const avgOtherDays = (Object.values(dayOfWeekCounts).reduce((a, b) => a + b, 0) - fridayCount) / 4;
496
+ if (fridayCount > avgOtherDays * 1.5) {
497
+ evidence.push({
498
+ type: "supporting",
499
+ description: `Friday has ${((fridayCount / avgOtherDays - 1) * 100).toFixed(0)}% more issues than average`,
500
+ weight: Math.min(0.8, 0.3 + (fridayCount / avgOtherDays - 1) * 0.2),
501
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
502
+ });
503
+ } else {
504
+ evidence.push({
505
+ type: "contradicting",
506
+ description: "Friday issue count is not significantly higher",
507
+ weight: 0.3,
508
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
509
+ });
510
+ }
511
+ }
512
+ if (template.statement.includes("weekend") || template.statement.includes("Monday")) {
513
+ const mondayCount = dayOfWeekCounts[1] || 0;
514
+ const avgOtherDays = (Object.values(dayOfWeekCounts).reduce((a, b) => a + b, 0) - mondayCount) / 4;
515
+ if (mondayCount > avgOtherDays * 1.5) {
516
+ evidence.push({
517
+ type: "supporting",
518
+ description: `Monday has ${((mondayCount / avgOtherDays - 1) * 100).toFixed(0)}% more issues than average`,
519
+ weight: Math.min(0.8, 0.3 + (mondayCount / avgOtherDays - 1) * 0.2),
520
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
521
+ });
522
+ }
523
+ }
524
+ return evidence;
525
+ }
526
+ /**
527
+ * Analyze location patterns in issues
528
+ */
529
+ analyzeLocationPatterns(template, issues) {
530
+ const evidence = [];
531
+ const dirCounts = {};
532
+ for (const { issue } of issues) {
533
+ const dir = dirname2(issue.file);
534
+ dirCounts[dir] = (dirCounts[dir] || 0) + 1;
535
+ }
536
+ const sortedDirs = Object.entries(dirCounts).sort(([, a], [, b]) => b - a);
537
+ if (template.statement.includes("Auth")) {
538
+ const authDirs = sortedDirs.filter(
539
+ ([dir]) => dir.toLowerCase().includes("auth") || dir.toLowerCase().includes("login") || dir.toLowerCase().includes("session")
540
+ );
541
+ const authCount = authDirs.reduce((sum, [, count]) => sum + count, 0);
542
+ const percentage = authCount / issues.length * 100;
543
+ if (percentage >= 20) {
544
+ evidence.push({
545
+ type: "supporting",
546
+ description: `Auth-related code has ${percentage.toFixed(0)}% of issues`,
547
+ weight: Math.min(0.85, 0.4 + percentage / 100),
548
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
549
+ });
550
+ }
551
+ }
552
+ if (template.statement.includes("cluster")) {
553
+ const topThreeDirs = sortedDirs.slice(0, 3);
554
+ const topThreeCount = topThreeDirs.reduce((sum, [, count]) => sum + count, 0);
555
+ const percentage = topThreeCount / issues.length * 100;
556
+ if (percentage >= 50) {
557
+ evidence.push({
558
+ type: "supporting",
559
+ description: `Top 3 directories have ${percentage.toFixed(0)}% of issues`,
560
+ weight: Math.min(0.8, 0.3 + percentage / 100),
561
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
562
+ });
563
+ }
564
+ }
565
+ return evidence;
566
+ }
567
+ /**
568
+ * Analyze code patterns
569
+ */
570
+ analyzeCodePatterns(template, issues) {
571
+ const evidence = [];
572
+ if (template.statement.includes("cluster")) {
573
+ const criticalIssues = issues.filter((r) => r.issue.severity === "critical");
574
+ if (criticalIssues.length >= 3) {
575
+ const timestamps = criticalIssues.map((r) => new Date(r.issue.timestamp).getTime()).sort((a, b) => a - b);
576
+ let clusteredCount = 0;
577
+ for (let i = 1; i < timestamps.length; i++) {
578
+ if (timestamps[i] - timestamps[i - 1] < 24 * 60 * 60 * 1e3) {
579
+ clusteredCount++;
580
+ }
581
+ }
582
+ const clusterRatio = clusteredCount / (timestamps.length - 1);
583
+ if (clusterRatio >= 0.5) {
584
+ evidence.push({
585
+ type: "supporting",
586
+ description: `${(clusterRatio * 100).toFixed(0)}% of critical issues occur within 24h of another`,
587
+ weight: Math.min(0.75, 0.3 + clusterRatio * 0.4),
588
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
589
+ });
590
+ }
591
+ }
592
+ }
593
+ return evidence;
594
+ }
595
+ /**
596
+ * Update confidence scores based on new data
597
+ */
598
+ async updateConfidenceFromOutcomes() {
599
+ const analyses = [];
600
+ try {
601
+ await this.guardianState.load();
602
+ const activeHypotheses = this.guardianState.getActiveHypotheses();
603
+ for (const hypothesis of activeHypotheses) {
604
+ const analysis = await this.analyzeHypothesis(hypothesis);
605
+ analyses.push(analysis);
606
+ if (analysis.statusChange && this.insightStore.canCreateInsight("hypothesis-update")) {
607
+ const insight = this.createHypothesisInsight(analysis);
608
+ await this.insightStore.addInsight(insight);
609
+ await this.insightStore.markInsightCreated("hypothesis-update");
610
+ }
611
+ }
612
+ await this.guardianState.updateHypothesisAccuracy();
613
+ } catch (error) {
614
+ console.error("Failed to update hypothesis confidence:", error);
615
+ }
616
+ return analyses;
617
+ }
618
+ /**
619
+ * Analyze a single hypothesis
620
+ */
621
+ async analyzeHypothesis(hypothesis) {
622
+ const issues = await searchIssues("", {
623
+ workDir: this.projectPath,
624
+ limit: 100,
625
+ includeResolved: false
626
+ });
627
+ const template = HYPOTHESIS_TEMPLATES.find(
628
+ (t) => t.statement.toLowerCase() === hypothesis.statement.toLowerCase()
629
+ );
630
+ const recentEvidence = [];
631
+ if (template) {
632
+ recentEvidence.push(...await this.gatherInitialEvidence(template, issues));
633
+ } else {
634
+ recentEvidence.push(...this.gatherSemanticEvidence(hypothesis, issues));
635
+ }
636
+ const oldConfidence = hypothesis.confidence;
637
+ let newConfidence = oldConfidence;
638
+ for (const evidence of recentEvidence) {
639
+ if (evidence.type === "supporting") {
640
+ newConfidence = Math.min(1, newConfidence + evidence.weight * 0.1);
641
+ } else {
642
+ newConfidence = Math.max(0, newConfidence - evidence.weight * 0.1);
643
+ }
644
+ }
645
+ let statusChange;
646
+ if (newConfidence >= 0.8 && hypothesis.evidence.length >= 3) {
647
+ statusChange = "validated";
648
+ await this.guardianState.updateHypothesis(hypothesis.id, {
649
+ status: "validated",
650
+ confidence: newConfidence,
651
+ validatedAt: (/* @__PURE__ */ new Date()).toISOString()
652
+ });
653
+ } else if (newConfidence <= 0.2 && hypothesis.evidence.length >= 3) {
654
+ statusChange = "invalidated";
655
+ await this.guardianState.updateHypothesis(hypothesis.id, {
656
+ status: "invalidated",
657
+ confidence: newConfidence
658
+ });
659
+ } else {
660
+ await this.guardianState.updateHypothesis(hypothesis.id, {
661
+ confidence: newConfidence
662
+ });
663
+ }
664
+ for (const evidence of recentEvidence) {
665
+ await this.guardianState.addEvidence(hypothesis.id, evidence);
666
+ }
667
+ const analysis = {
668
+ hypothesis: { ...hypothesis, confidence: newConfidence },
669
+ recentEvidence,
670
+ confidenceChange: newConfidence - oldConfidence,
671
+ actionRequired: statusChange !== void 0
672
+ };
673
+ if (statusChange) {
674
+ analysis.statusChange = statusChange;
675
+ }
676
+ return analysis;
677
+ }
678
+ /**
679
+ * Gather evidence for user-created hypotheses using semantic matching
680
+ *
681
+ * This enables agentic tracking for natural language hypotheses like:
682
+ * - "Mondays have more bugs than Fridays"
683
+ * - "Code reviews reduce bug rate"
684
+ * - "Security issues cluster in auth code"
685
+ */
686
+ gatherSemanticEvidence(hypothesis, issues) {
687
+ const evidence = [];
688
+ const stmt = hypothesis.statement.toLowerCase();
689
+ if (stmt.includes("monday") || stmt.includes("friday") || stmt.includes("weekend") || stmt.includes("morning") || stmt.includes("afternoon")) {
690
+ const dayNames = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
691
+ const dayOfWeekCounts = { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0 };
692
+ for (const { issue } of issues) {
693
+ const date = new Date(issue.timestamp);
694
+ const day = date.getDay();
695
+ dayOfWeekCounts[day] = (dayOfWeekCounts[day] || 0) + 1;
696
+ }
697
+ const totalIssues = Object.values(dayOfWeekCounts).reduce((a, b) => a + b, 0);
698
+ const avgPerDay = totalIssues / 7;
699
+ for (let dayIdx = 0; dayIdx < 7; dayIdx++) {
700
+ const dayName = dayNames[dayIdx];
701
+ if (stmt.includes(dayName)) {
702
+ const dayCount = dayOfWeekCounts[dayIdx] || 0;
703
+ if (stmt.includes("more") || stmt.includes("higher") || stmt.includes("spike")) {
704
+ if (dayCount > avgPerDay * 1.3) {
705
+ evidence.push({
706
+ type: "supporting",
707
+ description: `${dayName.charAt(0).toUpperCase() + dayName.slice(1)} has ${dayCount} issues (${((dayCount / avgPerDay - 1) * 100).toFixed(0)}% above average)`,
708
+ weight: Math.min(0.7, 0.3 + (dayCount / avgPerDay - 1) * 0.3),
709
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
710
+ });
711
+ } else {
712
+ evidence.push({
713
+ type: "contradicting",
714
+ description: `${dayName.charAt(0).toUpperCase() + dayName.slice(1)} has average or below average issue count`,
715
+ weight: 0.3,
716
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
717
+ });
718
+ }
719
+ } else if (stmt.includes("fewer") || stmt.includes("less") || stmt.includes("reduce")) {
720
+ if (dayCount < avgPerDay * 0.7) {
721
+ evidence.push({
722
+ type: "supporting",
723
+ description: `${dayName.charAt(0).toUpperCase() + dayName.slice(1)} has ${dayCount} issues (${((1 - dayCount / avgPerDay) * 100).toFixed(0)}% below average)`,
724
+ weight: Math.min(0.7, 0.3 + (1 - dayCount / avgPerDay) * 0.3),
725
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
726
+ });
727
+ }
728
+ }
729
+ }
730
+ }
731
+ }
732
+ if (stmt.includes("review") || stmt.includes("pr") || stmt.includes("pull request")) {
733
+ const recentIssues = issues.slice(0, 30);
734
+ const olderIssues = issues.slice(30);
735
+ if (recentIssues.length >= 10 && olderIssues.length >= 10) {
736
+ const recentRate = recentIssues.length;
737
+ const olderRate = olderIssues.length;
738
+ if (stmt.includes("reduce") || stmt.includes("fewer") || stmt.includes("less")) {
739
+ if (recentRate < olderRate) {
740
+ evidence.push({
741
+ type: "supporting",
742
+ description: `Recent period has ${((1 - recentRate / olderRate) * 100).toFixed(0)}% fewer issues`,
743
+ weight: Math.min(0.6, 0.3 + (1 - recentRate / olderRate) * 0.3),
744
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
745
+ });
746
+ } else {
747
+ evidence.push({
748
+ type: "contradicting",
749
+ description: `Issue rate has not decreased recently`,
750
+ weight: 0.2,
751
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
752
+ });
753
+ }
754
+ }
755
+ }
756
+ }
757
+ if (stmt.includes("cluster") || stmt.includes("concentrate") || stmt.includes("auth") || stmt.includes("specific") || stmt.includes("certain files")) {
758
+ const dirCounts = {};
759
+ for (const { issue } of issues) {
760
+ const dir = dirname2(issue.file);
761
+ dirCounts[dir] = (dirCounts[dir] || 0) + 1;
762
+ }
763
+ const sortedDirs = Object.entries(dirCounts).sort(([, a], [, b]) => b - a);
764
+ const topThreeCount = sortedDirs.slice(0, 3).reduce((sum, [, count]) => sum + count, 0);
765
+ const totalCount = issues.length;
766
+ const concentration = topThreeCount / totalCount;
767
+ if (concentration >= 0.5) {
768
+ evidence.push({
769
+ type: "supporting",
770
+ description: `Top 3 directories have ${(concentration * 100).toFixed(0)}% of issues (concentrated)`,
771
+ weight: Math.min(0.7, 0.3 + concentration * 0.4),
772
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
773
+ });
774
+ } else {
775
+ evidence.push({
776
+ type: "contradicting",
777
+ description: `Issues are distributed across many directories`,
778
+ weight: 0.3,
779
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
780
+ });
781
+ }
782
+ }
783
+ if (stmt.includes("critical") || stmt.includes("security") || stmt.includes("severity")) {
784
+ const criticalCount = issues.filter((r) => r.issue.severity === "critical").length;
785
+ const seriousCount = issues.filter((r) => r.issue.severity === "serious").length;
786
+ const highSeverityRatio = (criticalCount + seriousCount) / issues.length;
787
+ if (stmt.includes("increase") || stmt.includes("more") || stmt.includes("rise")) {
788
+ if (highSeverityRatio > 0.3) {
789
+ evidence.push({
790
+ type: "supporting",
791
+ description: `${(highSeverityRatio * 100).toFixed(0)}% of issues are high severity`,
792
+ weight: Math.min(0.7, 0.3 + highSeverityRatio),
793
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
794
+ });
795
+ }
796
+ } else if (stmt.includes("decrease") || stmt.includes("fewer") || stmt.includes("reduce")) {
797
+ if (highSeverityRatio < 0.2) {
798
+ evidence.push({
799
+ type: "supporting",
800
+ description: `Only ${(highSeverityRatio * 100).toFixed(0)}% of issues are high severity`,
801
+ weight: 0.5,
802
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
803
+ });
804
+ }
805
+ }
806
+ }
807
+ const agents = ["security", "performance", "accessibility", "test", "typecheck", "bug-finding"];
808
+ for (const agent of agents) {
809
+ if (stmt.includes(agent) || stmt.includes(agent.replace("-", " "))) {
810
+ const agentIssues = issues.filter((r) => r.issue.agent === agent);
811
+ const agentRatio = agentIssues.length / issues.length;
812
+ if (stmt.includes("most") || stmt.includes("majority") || stmt.includes("main")) {
813
+ if (agentRatio > 0.4) {
814
+ evidence.push({
815
+ type: "supporting",
816
+ description: `${agent} accounts for ${(agentRatio * 100).toFixed(0)}% of issues`,
817
+ weight: Math.min(0.7, 0.3 + agentRatio),
818
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
819
+ });
820
+ } else {
821
+ evidence.push({
822
+ type: "contradicting",
823
+ description: `${agent} only accounts for ${(agentRatio * 100).toFixed(0)}% of issues`,
824
+ weight: 0.3,
825
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
826
+ });
827
+ }
828
+ }
829
+ }
830
+ }
831
+ return evidence;
832
+ }
833
+ /**
834
+ * Create an insight for hypothesis status change
835
+ */
836
+ createHypothesisInsight(analysis) {
837
+ const isValidated = analysis.statusChange === "validated";
838
+ return {
839
+ id: `insight-hyp-${analysis.hypothesis.id}`,
840
+ type: isValidated ? "celebration" : "observation",
841
+ message: isValidated ? `\u{1F52E} Hypothesis confirmed: "${analysis.hypothesis.statement}"` : `\u274C Hypothesis disproven: "${analysis.hypothesis.statement}"`,
842
+ context: `Confidence: ${(analysis.hypothesis.confidence * 100).toFixed(0)}%. ${analysis.recentEvidence.length} evidence points analyzed.`,
843
+ relatedIssues: [],
844
+ priority: 5,
845
+ timestamp: Date.now(),
846
+ dismissed: false,
847
+ category: "pattern",
848
+ details: {
849
+ examples: analysis.recentEvidence.map((e) => `${e.type}: ${e.description}`)
850
+ }
851
+ };
852
+ }
853
+ /**
854
+ * Get hypothesis by ID
855
+ */
856
+ async getHypothesis(hypothesisId) {
857
+ await this.guardianState.load();
858
+ return this.guardianState.getHypothesis(hypothesisId);
859
+ }
860
+ /**
861
+ * Get all hypotheses
862
+ */
863
+ async getAllHypotheses() {
864
+ await this.guardianState.load();
865
+ return this.guardianState.getAllHypotheses();
866
+ }
867
+ /**
868
+ * Get validated hypotheses
869
+ */
870
+ async getValidatedHypotheses() {
871
+ await this.guardianState.load();
872
+ return this.guardianState.getValidatedHypotheses();
873
+ }
874
+ /**
875
+ * Get hypothesis accuracy stats
876
+ */
877
+ getAccuracy() {
878
+ const metrics = this.guardianState.getMetrics();
879
+ return metrics.hypothesisAccuracy;
880
+ }
881
+ };
882
+ var hypothesisEngines = /* @__PURE__ */ new Map();
883
+ function getHypothesisEngine(projectPath) {
884
+ let engine = hypothesisEngines.get(projectPath);
885
+ if (!engine) {
886
+ engine = new HypothesisEngine(projectPath);
887
+ hypothesisEngines.set(projectPath, engine);
888
+ }
889
+ return engine;
890
+ }
891
+
892
+ // src/integrations/slack.ts
893
+ var SlackIntegration = class {
894
+ constructor(config) {
895
+ this.config = config;
896
+ }
897
+ /**
898
+ * Send scan completion notification
899
+ */
900
+ async sendScanNotification(_issues, priorityReport, repositoryName, branch = "main") {
901
+ const { urgent, high, medium, low } = priorityReport;
902
+ const emoji = urgent.length > 0 ? "\u{1F6A8}" : high.length > 0 ? "\u26A0\uFE0F" : "\u2705";
903
+ const message = {
904
+ blocks: [
905
+ {
906
+ type: "section",
907
+ text: {
908
+ type: "mrkdwn",
909
+ text: `${emoji} *Trie Security Scan Complete*
910
+ *Repository:* ${repositoryName} (${branch})`
911
+ }
912
+ },
913
+ {
914
+ type: "section",
915
+ fields: [
916
+ {
917
+ type: "mrkdwn",
918
+ text: `*\u{1F6A8} Urgent:* ${urgent.length}`
919
+ },
920
+ {
921
+ type: "mrkdwn",
922
+ text: `*\u26A0\uFE0F High:* ${high.length}`
923
+ },
924
+ {
925
+ type: "mrkdwn",
926
+ text: `*\u{1F538} Medium:* ${medium.length}`
927
+ },
928
+ {
929
+ type: "mrkdwn",
930
+ text: `*\u{1F539} Low:* ${low.length}`
931
+ }
932
+ ]
933
+ }
934
+ ],
935
+ attachments: []
936
+ };
937
+ if (urgent.length > 0) {
938
+ const urgentDetails = urgent.slice(0, 5).map(
939
+ (group) => `\u2022 ${group.description} (${group.count} instances)`
940
+ ).join("\n");
941
+ message.attachments.push({
942
+ color: "danger",
943
+ title: "\u{1F6A8} Urgent Issues - Immediate Action Required",
944
+ text: urgentDetails,
945
+ footer: urgent.length > 5 ? `... and ${urgent.length - 5} more urgent issues` : void 0
946
+ });
947
+ }
948
+ if (high.length > 0) {
949
+ const highDetails = high.slice(0, 3).map(
950
+ (group) => `\u2022 ${group.description} (${group.count} instances)`
951
+ ).join("\n");
952
+ message.attachments.push({
953
+ color: "warning",
954
+ title: "\u26A0\uFE0F High Priority Issues",
955
+ text: highDetails,
956
+ footer: high.length > 3 ? `... and ${high.length - 3} more high priority issues` : void 0
957
+ });
958
+ }
959
+ if (priorityReport.recommendations.length > 0) {
960
+ message.attachments.push({
961
+ color: "good",
962
+ title: "\u{1F4A1} Recommendations",
963
+ text: priorityReport.recommendations.slice(0, 3).join("\n")
964
+ });
965
+ }
966
+ await this.sendMessage(message);
967
+ }
968
+ /**
969
+ * Send critical issue alert
970
+ */
971
+ async sendCriticalAlert(issues, repositoryName) {
972
+ const message = {
973
+ text: `\u{1F6A8} CRITICAL SECURITY ALERT: ${repositoryName}`,
974
+ blocks: [
975
+ {
976
+ type: "section",
977
+ text: {
978
+ type: "mrkdwn",
979
+ text: `\u{1F6A8} *CRITICAL SECURITY ALERT*
980
+ *Repository:* ${repositoryName}
981
+ *Critical Issues:* ${issues.length}`
982
+ }
983
+ }
984
+ ],
985
+ attachments: issues.slice(0, 5).map((issue) => ({
986
+ color: "danger",
987
+ title: `${issue.file}:${issue.line || "?"}`,
988
+ text: issue.issue.slice(0, 200),
989
+ fields: [
990
+ {
991
+ title: "Fix",
992
+ value: issue.fix.slice(0, 100),
993
+ short: false
994
+ }
995
+ ],
996
+ footer: `Agent: ${issue.agent}`,
997
+ ts: Math.floor(Date.now() / 1e3)
998
+ }))
999
+ };
1000
+ if (issues.length > 5) {
1001
+ message.attachments.push({
1002
+ color: "danger",
1003
+ text: `... and ${issues.length - 5} more critical issues. View full report for details.`
1004
+ });
1005
+ }
1006
+ await this.sendMessage(message);
1007
+ }
1008
+ /**
1009
+ * Send team notification
1010
+ */
1011
+ async sendTeamNotification(notification) {
1012
+ const emoji = this.getNotificationEmoji(notification.type);
1013
+ const message = {
1014
+ blocks: [
1015
+ {
1016
+ type: "section",
1017
+ text: {
1018
+ type: "mrkdwn",
1019
+ text: `${emoji} *${notification.title}*
1020
+ ${notification.message}`
1021
+ }
1022
+ }
1023
+ ]
1024
+ };
1025
+ if (notification.type === "assignment" && notification.data) {
1026
+ const { assignment, issue } = notification.data;
1027
+ message.attachments = [{
1028
+ color: this.getAssignmentColor(assignment.priority),
1029
+ fields: [
1030
+ {
1031
+ title: "Priority",
1032
+ value: assignment.priority.toUpperCase(),
1033
+ short: true
1034
+ },
1035
+ {
1036
+ title: "Due Date",
1037
+ value: assignment.dueDate ? new Date(assignment.dueDate).toLocaleDateString() : "Not set",
1038
+ short: true
1039
+ },
1040
+ {
1041
+ title: "File",
1042
+ value: issue.file,
1043
+ short: false
1044
+ }
1045
+ ]
1046
+ }];
1047
+ }
1048
+ await this.sendMessage(message);
1049
+ }
1050
+ /**
1051
+ * Send daily/weekly team summary
1052
+ */
1053
+ async sendTeamSummary(period, stats) {
1054
+ const emoji = period === "daily" ? "\u{1F4C5}" : "\u{1F4CA}";
1055
+ const title = `${emoji} ${period.charAt(0).toUpperCase() + period.slice(1)} Security Summary`;
1056
+ const message = {
1057
+ blocks: [
1058
+ {
1059
+ type: "section",
1060
+ text: {
1061
+ type: "mrkdwn",
1062
+ text: `*${title}*`
1063
+ }
1064
+ },
1065
+ {
1066
+ type: "section",
1067
+ fields: [
1068
+ {
1069
+ type: "mrkdwn",
1070
+ text: `*\u{1F50D} New Issues:* ${stats.newIssues}`
1071
+ },
1072
+ {
1073
+ type: "mrkdwn",
1074
+ text: `*\u2705 Resolved:* ${stats.resolvedIssues}`
1075
+ },
1076
+ {
1077
+ type: "mrkdwn",
1078
+ text: `*\u23F0 Overdue:* ${stats.overdueIssues}`
1079
+ },
1080
+ {
1081
+ type: "mrkdwn",
1082
+ text: `*\u{1F4C8} Net Change:* ${stats.resolvedIssues - stats.newIssues > 0 ? "+" : ""}${stats.resolvedIssues - stats.newIssues}`
1083
+ }
1084
+ ]
1085
+ }
1086
+ ],
1087
+ attachments: []
1088
+ };
1089
+ if (stats.topCategories.length > 0) {
1090
+ const categoriesText = stats.topCategories.slice(0, 5).map((cat) => `\u2022 ${cat.category}: ${cat.count}`).join("\n");
1091
+ message.attachments.push({
1092
+ color: "good",
1093
+ title: "\u{1F4CA} Top Issue Categories",
1094
+ text: categoriesText
1095
+ });
1096
+ }
1097
+ if (stats.topContributors.length > 0) {
1098
+ const contributorsText = stats.topContributors.slice(0, 5).map((contrib) => `\u2022 ${contrib.name}: ${contrib.resolved} resolved`).join("\n");
1099
+ message.attachments.push({
1100
+ color: "good",
1101
+ title: "\u{1F3C6} Top Contributors",
1102
+ text: contributorsText
1103
+ });
1104
+ }
1105
+ await this.sendMessage(message);
1106
+ }
1107
+ /**
1108
+ * Send bulk fix notification
1109
+ */
1110
+ async sendBulkFixNotification(fixedGroups, totalFixed, repositoryName) {
1111
+ const message = {
1112
+ blocks: [
1113
+ {
1114
+ type: "section",
1115
+ text: {
1116
+ type: "mrkdwn",
1117
+ text: `\u26A1 *Bulk Fix Applied*
1118
+ *Repository:* ${repositoryName}
1119
+ *Issues Fixed:* ${totalFixed}`
1120
+ }
1121
+ }
1122
+ ],
1123
+ attachments: [{
1124
+ color: "good",
1125
+ title: "\u{1F527} Fixed Issue Groups",
1126
+ text: fixedGroups.map(
1127
+ (group) => `\u2022 ${group.description} (${group.count} instances)`
1128
+ ).join("\n")
1129
+ }]
1130
+ };
1131
+ await this.sendMessage(message);
1132
+ }
1133
+ /**
1134
+ * Send escalation notification
1135
+ */
1136
+ async sendEscalationNotification(overdueAssignments) {
1137
+ const message = {
1138
+ text: "\u{1F6A8} OVERDUE ISSUE ESCALATION",
1139
+ blocks: [
1140
+ {
1141
+ type: "section",
1142
+ text: {
1143
+ type: "mrkdwn",
1144
+ text: `\u{1F6A8} *Overdue Issue Escalation*
1145
+ ${overdueAssignments.length} issues are overdue and require attention.`
1146
+ }
1147
+ }
1148
+ ],
1149
+ attachments: overdueAssignments.map((assignment) => ({
1150
+ color: "warning",
1151
+ title: `Issue ${assignment.issueId}`,
1152
+ fields: [
1153
+ {
1154
+ title: "Assignee",
1155
+ value: assignment.assignee,
1156
+ short: true
1157
+ },
1158
+ {
1159
+ title: "Days Overdue",
1160
+ value: assignment.daysOverdue.toString(),
1161
+ short: true
1162
+ },
1163
+ {
1164
+ title: "Priority",
1165
+ value: assignment.priority.toUpperCase(),
1166
+ short: true
1167
+ }
1168
+ ]
1169
+ }))
1170
+ };
1171
+ await this.sendMessage(message);
1172
+ }
1173
+ /**
1174
+ * Send message to Slack
1175
+ */
1176
+ async sendMessage(message) {
1177
+ const payload = {
1178
+ ...message,
1179
+ channel: this.config.channel || message.channel,
1180
+ username: this.config.username || "Trie Security Bot",
1181
+ icon_emoji: this.config.iconEmoji || ":shield:"
1182
+ };
1183
+ try {
1184
+ const response = await fetch(this.config.webhookUrl, {
1185
+ method: "POST",
1186
+ headers: {
1187
+ "Content-Type": "application/json"
1188
+ },
1189
+ body: JSON.stringify(payload)
1190
+ });
1191
+ if (!response.ok) {
1192
+ throw new Error(`Slack API error: ${response.status} ${response.statusText}`);
1193
+ }
1194
+ } catch (error) {
1195
+ console.error("Failed to send Slack notification:", error);
1196
+ throw error;
1197
+ }
1198
+ }
1199
+ /**
1200
+ * Get emoji for notification type
1201
+ */
1202
+ getNotificationEmoji(type) {
1203
+ const emojis = {
1204
+ assignment: "\u{1F4CB}",
1205
+ escalation: "\u{1F6A8}",
1206
+ completion: "\u2705",
1207
+ reminder: "\u23F0"
1208
+ };
1209
+ return emojis[type] ?? "\u{1F4E2}";
1210
+ }
1211
+ /**
1212
+ * Get color for assignment priority
1213
+ */
1214
+ getAssignmentColor(priority) {
1215
+ const colors = {
1216
+ urgent: "danger",
1217
+ high: "warning",
1218
+ medium: "good",
1219
+ low: "#36a64f"
1220
+ };
1221
+ return colors[priority] ?? "good";
1222
+ }
1223
+ /**
1224
+ * Test Slack connection
1225
+ */
1226
+ async testConnection() {
1227
+ try {
1228
+ await this.sendMessage({
1229
+ text: "\u{1F9EA} Trie Slack Integration Test",
1230
+ blocks: [{
1231
+ type: "section",
1232
+ text: {
1233
+ type: "mrkdwn",
1234
+ text: "*Trie Slack Integration Test*\nIf you see this message, the integration is working correctly! \u{1F389}"
1235
+ }
1236
+ }]
1237
+ });
1238
+ return true;
1239
+ } catch (error) {
1240
+ console.error("Slack connection test failed:", error);
1241
+ return false;
1242
+ }
1243
+ }
1244
+ };
1245
+
1246
+ // src/guardian/escalation.ts
1247
+ var DEFAULT_CONFIG2 = {
1248
+ enabled: true,
1249
+ targets: [],
1250
+ cooldownMinutes: 15,
1251
+ maxEscalationsPerHour: 5,
1252
+ respectQuietHours: true,
1253
+ criticalBypassQuietHours: true,
1254
+ draftFallbackEnabled: true
1255
+ };
1256
+ var EscalationManager = class {
1257
+ projectPath;
1258
+ config;
1259
+ guardianState;
1260
+ insightStore;
1261
+ escalationHistory = [];
1262
+ slackClient;
1263
+ constructor(projectPath, config = {}) {
1264
+ this.projectPath = projectPath;
1265
+ this.config = { ...DEFAULT_CONFIG2, ...config };
1266
+ this.guardianState = getGuardianState(projectPath);
1267
+ this.insightStore = getInsightStore(projectPath);
1268
+ const slackTarget = this.config.targets.find((t) => t.type === "slack" && t.enabled);
1269
+ if (slackTarget?.config.webhookUrl) {
1270
+ this.slackClient = new SlackIntegration({
1271
+ webhookUrl: slackTarget.config.webhookUrl,
1272
+ channel: slackTarget.config.channel,
1273
+ username: slackTarget.config.username
1274
+ });
1275
+ }
1276
+ }
1277
+ /**
1278
+ * Check if an issue should be auto-escalated
1279
+ */
1280
+ shouldAutoEscalate(issue) {
1281
+ if (!this.config.enabled) {
1282
+ return false;
1283
+ }
1284
+ if (issue.severity !== "critical") {
1285
+ return false;
1286
+ }
1287
+ const isSecurityIssue = issue.category === "security" || issue.agent === "security" || issue.issue.toLowerCase().includes("security") || issue.issue.toLowerCase().includes("vulnerability") || issue.issue.toLowerCase().includes("injection") || issue.issue.toLowerCase().includes("xss") || issue.issue.toLowerCase().includes("secret");
1288
+ if (!isSecurityIssue) {
1289
+ return false;
1290
+ }
1291
+ if (this.guardianState.isQuietHours()) {
1292
+ if (!this.config.criticalBypassQuietHours) {
1293
+ return false;
1294
+ }
1295
+ }
1296
+ if (!this.insightStore.canCreateInsight("auto-escalation")) {
1297
+ return false;
1298
+ }
1299
+ const recentEscalations = this.escalationHistory.filter(
1300
+ (e) => e.timestamp > Date.now() - 60 * 60 * 1e3
1301
+ );
1302
+ if (recentEscalations.length >= this.config.maxEscalationsPerHour) {
1303
+ return false;
1304
+ }
1305
+ const recentlyEscalated = this.escalationHistory.some(
1306
+ (e) => e.issues.some((i) => i.file === issue.file) && e.timestamp > Date.now() - this.config.cooldownMinutes * 60 * 1e3
1307
+ );
1308
+ if (recentlyEscalated) {
1309
+ return false;
1310
+ }
1311
+ return this.hasValidEscalationTarget("critical", "security");
1312
+ }
1313
+ /**
1314
+ * Check if we have a valid escalation target
1315
+ */
1316
+ hasValidEscalationTarget(severity, category) {
1317
+ return this.config.targets.some(
1318
+ (t) => t.enabled && t.forSeverities.includes(severity) && (t.forCategories.includes(category) || t.forCategories.includes("all"))
1319
+ );
1320
+ }
1321
+ /**
1322
+ * Auto-escalate if issue is critical
1323
+ */
1324
+ async autoEscalateIfCritical(issue) {
1325
+ if (!this.shouldAutoEscalate(issue)) {
1326
+ if (issue.severity === "critical" && this.config.draftFallbackEnabled) {
1327
+ if (this.guardianState.isQuietHours()) {
1328
+ return this.createDraftEscalation([issue], "Blocked by quiet hours");
1329
+ }
1330
+ }
1331
+ return {
1332
+ action: "no_action",
1333
+ reason: "Issue does not meet escalation criteria"
1334
+ };
1335
+ }
1336
+ const message = this.createEscalationMessage([issue]);
1337
+ try {
1338
+ await this.sendEscalation(message);
1339
+ this.escalationHistory.push(message);
1340
+ await this.insightStore.markInsightCreated("auto-escalation");
1341
+ await this.createEscalationInsight(message);
1342
+ await this.guardianState.useRiskBudget(1);
1343
+ return {
1344
+ action: "auto_escalated",
1345
+ reason: "Critical security issue auto-escalated",
1346
+ message
1347
+ };
1348
+ } catch (error) {
1349
+ console.error("Failed to send escalation:", error);
1350
+ if (this.config.draftFallbackEnabled) {
1351
+ return this.createDraftEscalation([issue], String(error));
1352
+ }
1353
+ return {
1354
+ action: "failed",
1355
+ reason: `Send failed: ${error}`,
1356
+ message
1357
+ };
1358
+ }
1359
+ }
1360
+ /**
1361
+ * Escalate multiple issues at once
1362
+ */
1363
+ async escalateIssues(issues) {
1364
+ const criticalSecurityIssues = issues.filter(
1365
+ (i) => i.severity === "critical" && (i.category === "security" || i.agent === "security")
1366
+ );
1367
+ if (criticalSecurityIssues.length === 0) {
1368
+ return {
1369
+ action: "no_action",
1370
+ reason: "No critical security issues to escalate"
1371
+ };
1372
+ }
1373
+ if (this.guardianState.isQuietHours() && !this.config.criticalBypassQuietHours) {
1374
+ return this.createDraftEscalation(criticalSecurityIssues, "Blocked by quiet hours");
1375
+ }
1376
+ const message = this.createEscalationMessage(criticalSecurityIssues);
1377
+ try {
1378
+ await this.sendEscalation(message);
1379
+ this.escalationHistory.push(message);
1380
+ await this.insightStore.markInsightCreated("auto-escalation");
1381
+ await this.createEscalationInsight(message);
1382
+ return {
1383
+ action: "auto_escalated",
1384
+ reason: `Escalated ${criticalSecurityIssues.length} critical security issues`,
1385
+ message
1386
+ };
1387
+ } catch (error) {
1388
+ if (this.config.draftFallbackEnabled) {
1389
+ return this.createDraftEscalation(criticalSecurityIssues, String(error));
1390
+ }
1391
+ return {
1392
+ action: "failed",
1393
+ reason: `Send failed: ${error}`,
1394
+ message
1395
+ };
1396
+ }
1397
+ }
1398
+ /**
1399
+ * Create an escalation message
1400
+ */
1401
+ createEscalationMessage(issues) {
1402
+ const severity = issues.some((i) => i.severity === "critical") ? "critical" : "serious";
1403
+ return {
1404
+ id: `esc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1405
+ severity,
1406
+ title: `\u{1F6A8} ${severity.toUpperCase()} Security Alert: ${issues.length} issue${issues.length > 1 ? "s" : ""} detected`,
1407
+ body: this.formatEscalationBody(issues),
1408
+ issues,
1409
+ timestamp: Date.now(),
1410
+ sent: false
1411
+ };
1412
+ }
1413
+ /**
1414
+ * Format escalation body text
1415
+ */
1416
+ formatEscalationBody(issues) {
1417
+ const lines = [
1418
+ `**Critical Security Issues Detected**`,
1419
+ ``,
1420
+ `**Project:** ${this.projectPath.split("/").pop()}`,
1421
+ `**Issues:** ${issues.length}`,
1422
+ `**Detected:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
1423
+ ``,
1424
+ `**Summary:**`
1425
+ ];
1426
+ for (const issue of issues.slice(0, 5)) {
1427
+ lines.push(`- \`${issue.file}:${issue.line || "?"}\`: ${issue.issue.slice(0, 100)}`);
1428
+ }
1429
+ if (issues.length > 5) {
1430
+ lines.push(`- ... and ${issues.length - 5} more`);
1431
+ }
1432
+ lines.push("");
1433
+ lines.push("**Action Required:** Review and address these issues immediately.");
1434
+ return lines.join("\n");
1435
+ }
1436
+ /**
1437
+ * Send escalation through configured channels
1438
+ */
1439
+ async sendEscalation(message) {
1440
+ const enabledTargets = this.config.targets.filter((t) => t.enabled);
1441
+ if (enabledTargets.length === 0) {
1442
+ throw new Error("No enabled escalation targets configured");
1443
+ }
1444
+ const errors = [];
1445
+ for (const target of enabledTargets) {
1446
+ try {
1447
+ switch (target.type) {
1448
+ case "slack":
1449
+ await this.sendToSlack(message, target);
1450
+ break;
1451
+ case "email":
1452
+ await this.sendToEmail(message, target);
1453
+ break;
1454
+ case "webhook":
1455
+ await this.sendToWebhook(message, target);
1456
+ break;
1457
+ }
1458
+ message.sent = true;
1459
+ message.sentAt = Date.now();
1460
+ message.channel = target.type;
1461
+ } catch (error) {
1462
+ errors.push(`${target.type}: ${error}`);
1463
+ }
1464
+ }
1465
+ if (errors.length === enabledTargets.length) {
1466
+ throw new Error(`All escalation channels failed: ${errors.join("; ")}`);
1467
+ }
1468
+ }
1469
+ /**
1470
+ * Send to Slack
1471
+ */
1472
+ async sendToSlack(message, target) {
1473
+ if (!target.config.webhookUrl) {
1474
+ throw new Error("Slack webhook URL not configured");
1475
+ }
1476
+ if (this.slackClient) {
1477
+ await this.slackClient.sendCriticalAlert(message.issues, this.projectPath.split("/").pop() || "Unknown");
1478
+ } else {
1479
+ const slack = new SlackIntegration({
1480
+ webhookUrl: target.config.webhookUrl,
1481
+ channel: target.config.channel,
1482
+ username: target.config.username || "Trie Guardian"
1483
+ });
1484
+ await slack.sendCriticalAlert(message.issues, this.projectPath.split("/").pop() || "Unknown");
1485
+ }
1486
+ }
1487
+ /**
1488
+ * Send to email (placeholder - would need email provider integration)
1489
+ */
1490
+ async sendToEmail(message, target) {
1491
+ if (!target.config.email) {
1492
+ throw new Error("Email address not configured");
1493
+ }
1494
+ console.log(`[EMAIL ESCALATION] To: ${target.config.email}`);
1495
+ console.log(`Subject: ${message.title}`);
1496
+ console.log(`Body: ${message.body}`);
1497
+ }
1498
+ /**
1499
+ * Send to webhook
1500
+ */
1501
+ async sendToWebhook(message, target) {
1502
+ if (!target.config.webhookUrl) {
1503
+ throw new Error("Webhook URL not configured");
1504
+ }
1505
+ const response = await fetch(target.config.webhookUrl, {
1506
+ method: "POST",
1507
+ headers: {
1508
+ "Content-Type": "application/json"
1509
+ },
1510
+ body: JSON.stringify({
1511
+ id: message.id,
1512
+ severity: message.severity,
1513
+ title: message.title,
1514
+ body: message.body,
1515
+ issues: message.issues.map((i) => ({
1516
+ file: i.file,
1517
+ line: i.line,
1518
+ severity: i.severity,
1519
+ issue: i.issue,
1520
+ agent: i.agent
1521
+ })),
1522
+ timestamp: message.timestamp,
1523
+ project: this.projectPath
1524
+ })
1525
+ });
1526
+ if (!response.ok) {
1527
+ throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
1528
+ }
1529
+ }
1530
+ /**
1531
+ * Create a draft escalation (when sending is blocked)
1532
+ */
1533
+ createDraftEscalation(issues, reason) {
1534
+ const message = this.createEscalationMessage(issues);
1535
+ message.error = reason;
1536
+ this.escalationHistory.push(message);
1537
+ return {
1538
+ action: "draft_only",
1539
+ reason,
1540
+ message,
1541
+ draftLocation: `.trie/drafts/escalation-${message.id}.json`
1542
+ };
1543
+ }
1544
+ /**
1545
+ * Create insight about escalation
1546
+ */
1547
+ async createEscalationInsight(message) {
1548
+ const insight = {
1549
+ id: `insight-esc-${message.id}`,
1550
+ type: "warning",
1551
+ message: `\u{1F6A8} Auto-escalated: ${message.issues.length} critical security issue${message.issues.length > 1 ? "s" : ""}`,
1552
+ context: `Sent to ${message.channel}. Issues in: ${[...new Set(message.issues.map((i) => i.file))].slice(0, 3).join(", ")}`,
1553
+ relatedIssues: message.issues.map((i) => i.id),
1554
+ priority: 10,
1555
+ timestamp: Date.now(),
1556
+ dismissed: false,
1557
+ category: "security",
1558
+ details: {
1559
+ affectedFiles: [...new Set(message.issues.map((i) => i.file))]
1560
+ }
1561
+ };
1562
+ await this.insightStore.addInsight(insight);
1563
+ }
1564
+ /**
1565
+ * Get escalation history
1566
+ */
1567
+ getEscalationHistory() {
1568
+ return [...this.escalationHistory];
1569
+ }
1570
+ /**
1571
+ * Get pending draft escalations
1572
+ */
1573
+ getPendingDrafts() {
1574
+ return this.escalationHistory.filter((e) => !e.sent);
1575
+ }
1576
+ /**
1577
+ * Retry a failed/draft escalation
1578
+ */
1579
+ async retryEscalation(messageId) {
1580
+ const message = this.escalationHistory.find((e) => e.id === messageId);
1581
+ if (!message) {
1582
+ return {
1583
+ action: "failed",
1584
+ reason: "Escalation message not found"
1585
+ };
1586
+ }
1587
+ try {
1588
+ await this.sendEscalation(message);
1589
+ return {
1590
+ action: "auto_escalated",
1591
+ reason: "Retry successful",
1592
+ message
1593
+ };
1594
+ } catch (error) {
1595
+ return {
1596
+ action: "failed",
1597
+ reason: `Retry failed: ${error}`,
1598
+ message
1599
+ };
1600
+ }
1601
+ }
1602
+ /**
1603
+ * Configure escalation targets
1604
+ */
1605
+ addEscalationTarget(target) {
1606
+ this.config.targets.push(target);
1607
+ if (target.type === "slack" && target.enabled && target.config.webhookUrl) {
1608
+ this.slackClient = new SlackIntegration({
1609
+ webhookUrl: target.config.webhookUrl,
1610
+ channel: target.config.channel,
1611
+ username: target.config.username
1612
+ });
1613
+ }
1614
+ }
1615
+ /**
1616
+ * Get current configuration
1617
+ */
1618
+ getConfig() {
1619
+ return { ...this.config };
1620
+ }
1621
+ /**
1622
+ * Update configuration
1623
+ */
1624
+ setConfig(config) {
1625
+ this.config = { ...this.config, ...config };
1626
+ }
1627
+ };
1628
+ var escalationManagers = /* @__PURE__ */ new Map();
1629
+ function getEscalationManager(projectPath) {
1630
+ let manager = escalationManagers.get(projectPath);
1631
+ if (!manager) {
1632
+ manager = new EscalationManager(projectPath);
1633
+ escalationManagers.set(projectPath, manager);
1634
+ }
1635
+ return manager;
1636
+ }
1637
+
1638
+ // src/guardian/meta-learning.ts
1639
+ var MetaLearner = class {
1640
+ guardianState;
1641
+ insightStore;
1642
+ feedbackHistory = [];
1643
+ weights;
1644
+ constructor(projectPath) {
1645
+ this.guardianState = getGuardianState(projectPath);
1646
+ this.insightStore = getInsightStore(projectPath);
1647
+ this.weights = {
1648
+ security: 1,
1649
+ quality: 1,
1650
+ performance: 1,
1651
+ pattern: 1,
1652
+ progress: 1,
1653
+ general: 1
1654
+ };
1655
+ }
1656
+ /**
1657
+ * Record user feedback on an insight
1658
+ */
1659
+ async recordFeedback(feedback, context) {
1660
+ await this.insightStore.load();
1661
+ const insight = this.insightStore.getInsight(context.insightId);
1662
+ if (!insight) {
1663
+ console.error(`Insight not found: ${context.insightId}`);
1664
+ return;
1665
+ }
1666
+ const record = {
1667
+ insightId: context.insightId,
1668
+ insightType: insight.type,
1669
+ insightCategory: insight.category,
1670
+ feedback,
1671
+ timestamp: Date.now()
1672
+ };
1673
+ if (context.latencyMs !== void 0) {
1674
+ record.latencyMs = context.latencyMs;
1675
+ }
1676
+ this.feedbackHistory.push(record);
1677
+ await this.guardianState.recordInsightFeedback(
1678
+ feedback === "dismissed" ? "dismissed" : feedback === "acted" ? "acted" : "helpful"
1679
+ );
1680
+ await this.adjustWeights(record);
1681
+ }
1682
+ /**
1683
+ * Adjust insight weights based on feedback
1684
+ */
1685
+ async adjustWeights(feedback) {
1686
+ const category = feedback.insightCategory;
1687
+ const currentWeight = this.weights[category];
1688
+ if (currentWeight === void 0) return;
1689
+ let adjustment = 0;
1690
+ switch (feedback.feedback) {
1691
+ case "acted":
1692
+ adjustment = 0.05;
1693
+ break;
1694
+ case "helpful":
1695
+ adjustment = 0.02;
1696
+ break;
1697
+ case "dismissed":
1698
+ adjustment = -0.03;
1699
+ break;
1700
+ case "ignored":
1701
+ adjustment = -0.01;
1702
+ break;
1703
+ }
1704
+ this.weights[category] = Math.max(0.3, Math.min(1.5, currentWeight + adjustment));
1705
+ }
1706
+ /**
1707
+ * Get current insight weights
1708
+ */
1709
+ getWeights() {
1710
+ return { ...this.weights };
1711
+ }
1712
+ /**
1713
+ * Get weight for a category (used to adjust insight priority)
1714
+ */
1715
+ getCategoryWeight(category) {
1716
+ return this.weights[category] ?? 1;
1717
+ }
1718
+ /**
1719
+ * Calculate category effectiveness
1720
+ */
1721
+ calculateCategoryEffectiveness() {
1722
+ const categories = ["security", "quality", "performance", "pattern", "progress", "general"];
1723
+ const effectiveness = [];
1724
+ for (const category of categories) {
1725
+ const categoryFeedback = this.feedbackHistory.filter((f) => f.insightCategory === category);
1726
+ if (categoryFeedback.length === 0) {
1727
+ effectiveness.push({
1728
+ category,
1729
+ totalInsights: 0,
1730
+ helpfulCount: 0,
1731
+ dismissedCount: 0,
1732
+ actedCount: 0,
1733
+ effectivenessScore: 0.5
1734
+ // Neutral when no data
1735
+ });
1736
+ continue;
1737
+ }
1738
+ const helpfulCount = categoryFeedback.filter((f) => f.feedback === "helpful" || f.feedback === "acted").length;
1739
+ const dismissedCount = categoryFeedback.filter((f) => f.feedback === "dismissed").length;
1740
+ const actedCount = categoryFeedback.filter((f) => f.feedback === "acted").length;
1741
+ const score = (helpfulCount + actedCount) / Math.max(1, categoryFeedback.length);
1742
+ effectiveness.push({
1743
+ category,
1744
+ totalInsights: categoryFeedback.length,
1745
+ helpfulCount,
1746
+ dismissedCount,
1747
+ actedCount,
1748
+ effectivenessScore: score
1749
+ });
1750
+ }
1751
+ return effectiveness;
1752
+ }
1753
+ /**
1754
+ * Get agent effectiveness metrics
1755
+ */
1756
+ getEffectiveness() {
1757
+ return this.guardianState.getMetrics();
1758
+ }
1759
+ /**
1760
+ * Generate a comprehensive effectiveness report
1761
+ */
1762
+ async generateEffectivenessReport() {
1763
+ await this.guardianState.load();
1764
+ const metrics = this.guardianState.getMetrics();
1765
+ const categoryBreakdown = this.calculateCategoryEffectiveness();
1766
+ const overallScore = metrics.userSatisfaction * 0.4 + metrics.predictiveAccuracy * 0.3 + (1 - metrics.falsePositiveRate) * 0.3;
1767
+ const recommendations = this.generateRecommendations(metrics, categoryBreakdown);
1768
+ const trend = this.calculateTrend();
1769
+ return {
1770
+ overallScore,
1771
+ predictiveAccuracy: metrics.predictiveAccuracy,
1772
+ userSatisfaction: metrics.userSatisfaction,
1773
+ falsePositiveRate: metrics.falsePositiveRate,
1774
+ categoryBreakdown,
1775
+ recommendations,
1776
+ trends: trend
1777
+ };
1778
+ }
1779
+ /**
1780
+ * Generate improvement recommendations
1781
+ */
1782
+ generateRecommendations(metrics, categoryBreakdown) {
1783
+ const recommendations = [];
1784
+ if (metrics.falsePositiveRate > 0.3) {
1785
+ recommendations.push("High dismissal rate detected. Consider raising insight thresholds.");
1786
+ }
1787
+ if (metrics.userSatisfaction < 0.5) {
1788
+ recommendations.push("User satisfaction is low. Review insight quality and relevance.");
1789
+ }
1790
+ const weakCategories = categoryBreakdown.filter((c) => c.totalInsights >= 5 && c.effectivenessScore < 0.4).map((c) => c.category);
1791
+ if (weakCategories.length > 0) {
1792
+ recommendations.push(
1793
+ `Low effectiveness in: ${weakCategories.join(", ")}. Consider adjusting insight generation for these categories.`
1794
+ );
1795
+ }
1796
+ const strongCategories = categoryBreakdown.filter((c) => c.totalInsights >= 5 && c.effectivenessScore > 0.7).map((c) => c.category);
1797
+ if (strongCategories.length > 0) {
1798
+ recommendations.push(
1799
+ `Strong performance in: ${strongCategories.join(", ")}. Consider emphasizing these insights.`
1800
+ );
1801
+ }
1802
+ if (metrics.predictiveAccuracy < 0.6 && metrics.totalPredictions >= 10) {
1803
+ recommendations.push("Prediction accuracy below target. Review risk scoring factors.");
1804
+ }
1805
+ if (metrics.userSatisfaction >= 0.7 && metrics.falsePositiveRate <= 0.2) {
1806
+ recommendations.push("Agent performance is excellent. Keep up the good work!");
1807
+ }
1808
+ return recommendations;
1809
+ }
1810
+ /**
1811
+ * Calculate trend direction
1812
+ */
1813
+ calculateTrend() {
1814
+ const now = Date.now();
1815
+ const recentCutoff = now - 7 * 24 * 60 * 60 * 1e3;
1816
+ const olderCutoff = now - 30 * 24 * 60 * 60 * 1e3;
1817
+ const recentFeedback = this.feedbackHistory.filter((f) => f.timestamp > recentCutoff);
1818
+ const olderFeedback = this.feedbackHistory.filter(
1819
+ (f) => f.timestamp > olderCutoff && f.timestamp <= recentCutoff
1820
+ );
1821
+ if (recentFeedback.length < 5 || olderFeedback.length < 5) {
1822
+ return { period: "last 30 days", direction: "stable" };
1823
+ }
1824
+ const recentPositive = recentFeedback.filter(
1825
+ (f) => f.feedback === "helpful" || f.feedback === "acted"
1826
+ ).length / recentFeedback.length;
1827
+ const olderPositive = olderFeedback.filter(
1828
+ (f) => f.feedback === "helpful" || f.feedback === "acted"
1829
+ ).length / olderFeedback.length;
1830
+ const diff = recentPositive - olderPositive;
1831
+ if (diff > 0.1) {
1832
+ return { period: "last 30 days", direction: "improving" };
1833
+ } else if (diff < -0.1) {
1834
+ return { period: "last 30 days", direction: "declining" };
1835
+ }
1836
+ return { period: "last 30 days", direction: "stable" };
1837
+ }
1838
+ /**
1839
+ * Should an insight be shown based on learning?
1840
+ *
1841
+ * Uses weights and user patterns to filter low-value insights.
1842
+ */
1843
+ shouldShowInsight(insight) {
1844
+ const weight = this.getCategoryWeight(insight.category);
1845
+ if (weight < 0.5 && insight.priority < 5) {
1846
+ return false;
1847
+ }
1848
+ const adjustedPriority = insight.priority * weight;
1849
+ return adjustedPriority >= 3;
1850
+ }
1851
+ /**
1852
+ * Adjust insight priority based on learning
1853
+ */
1854
+ adjustInsightPriority(insight) {
1855
+ const weight = this.getCategoryWeight(insight.category);
1856
+ return Math.round(insight.priority * weight);
1857
+ }
1858
+ /**
1859
+ * Get feedback history stats
1860
+ */
1861
+ getFeedbackStats() {
1862
+ const stats = {
1863
+ total: this.feedbackHistory.length,
1864
+ helpful: 0,
1865
+ dismissed: 0,
1866
+ acted: 0,
1867
+ ignored: 0,
1868
+ averageLatencyMs: null
1869
+ };
1870
+ let totalLatency = 0;
1871
+ let latencyCount = 0;
1872
+ for (const feedback of this.feedbackHistory) {
1873
+ switch (feedback.feedback) {
1874
+ case "helpful":
1875
+ stats.helpful++;
1876
+ break;
1877
+ case "dismissed":
1878
+ stats.dismissed++;
1879
+ break;
1880
+ case "acted":
1881
+ stats.acted++;
1882
+ break;
1883
+ case "ignored":
1884
+ stats.ignored++;
1885
+ break;
1886
+ }
1887
+ if (feedback.latencyMs !== void 0) {
1888
+ totalLatency += feedback.latencyMs;
1889
+ latencyCount++;
1890
+ }
1891
+ }
1892
+ if (latencyCount > 0) {
1893
+ stats.averageLatencyMs = Math.round(totalLatency / latencyCount);
1894
+ }
1895
+ return stats;
1896
+ }
1897
+ /**
1898
+ * Reset learning (for testing)
1899
+ */
1900
+ reset() {
1901
+ this.feedbackHistory = [];
1902
+ this.weights = {
1903
+ security: 1,
1904
+ quality: 1,
1905
+ performance: 1,
1906
+ pattern: 1,
1907
+ progress: 1,
1908
+ general: 1
1909
+ };
1910
+ }
1911
+ };
1912
+ var metaLearners = /* @__PURE__ */ new Map();
1913
+ function getMetaLearner(projectPath) {
1914
+ let learner = metaLearners.get(projectPath);
1915
+ if (!learner) {
1916
+ learner = new MetaLearner(projectPath);
1917
+ metaLearners.set(projectPath, learner);
1918
+ }
1919
+ return learner;
1920
+ }
1921
+
1922
+ // src/guardian/guardian-agent.ts
1923
+ var PERSONALITY = {
1924
+ greetings: [
1925
+ "Hey, quick heads up...",
1926
+ "Noticed something while scanning...",
1927
+ "Worth mentioning...",
1928
+ "Just so you know...",
1929
+ "Spotted this..."
1930
+ ],
1931
+ warnings: [
1932
+ "This needs your attention:",
1933
+ "I'd pause on this:",
1934
+ "Before you push...",
1935
+ "Hold up \u2014",
1936
+ "Important:"
1937
+ ],
1938
+ celebrations: [
1939
+ "Nice work!",
1940
+ "Good catch!",
1941
+ "That's better.",
1942
+ "Clean.",
1943
+ "Progress."
1944
+ ],
1945
+ suggestions: [
1946
+ "You might want to",
1947
+ "Consider",
1948
+ "Suggestion:",
1949
+ "Quick win:",
1950
+ "Idea:"
1951
+ ],
1952
+ questions: [
1953
+ "Did you mean to",
1954
+ "Should this",
1955
+ "Is it intentional that",
1956
+ "Quick question:"
1957
+ ]
1958
+ };
1959
+ var GuardianAgent = class {
1960
+ projectPath;
1961
+ projectName;
1962
+ lastIssueHashes = /* @__PURE__ */ new Set();
1963
+ initialized = false;
1964
+ // Persistent stores (Phase 1 Guardian Agency)
1965
+ insightStore;
1966
+ guardianState;
1967
+ // Agency modules (Phases 2-4)
1968
+ goalManager;
1969
+ riskPredictor;
1970
+ hypothesisEngine;
1971
+ escalationManager;
1972
+ metaLearner;
1973
+ constructor(projectPath) {
1974
+ this.projectPath = projectPath;
1975
+ this.projectName = basename2(projectPath);
1976
+ this.insightStore = getInsightStore(projectPath);
1977
+ this.guardianState = getGuardianState(projectPath);
1978
+ this.goalManager = getGoalManager(projectPath);
1979
+ this.riskPredictor = getRiskPredictor(projectPath);
1980
+ this.hypothesisEngine = getHypothesisEngine(projectPath);
1981
+ this.escalationManager = getEscalationManager(projectPath);
1982
+ this.metaLearner = getMetaLearner(projectPath);
1983
+ }
1984
+ /**
1985
+ * Initialize the Guardian - loads persistent state and historical data
1986
+ */
1987
+ async initialize() {
1988
+ if (this.initialized) return;
1989
+ try {
1990
+ await this.insightStore.load();
1991
+ await this.guardianState.load();
1992
+ await this.guardianState.touchActive();
1993
+ const historical = await getHistoricalInsights(this.projectPath);
1994
+ if (historical && historical.totalHistoricalIssues > 0) {
1995
+ const trend = historical.improvementTrend;
1996
+ if (trend === "improving" && this.canCreateInsight("progress-trend")) {
1997
+ await this.addInsight(this.createInsight({
1998
+ type: "celebration",
1999
+ message: `${this.pick(PERSONALITY.celebrations)} Your code quality has been improving over time.`,
2000
+ priority: 2,
2001
+ category: "progress"
2002
+ }));
2003
+ await this.markInsightCreated("progress-trend");
2004
+ } else if (trend === "declining" && this.canCreateInsight("progress-trend")) {
2005
+ await this.addInsight(this.createInsight({
2006
+ type: "observation",
2007
+ message: `${this.pick(PERSONALITY.greetings)} Issue count has been trending up. Might be worth a focused cleanup session.`,
2008
+ suggestedAction: "Run `trie scan` and address top issues",
2009
+ priority: 5,
2010
+ category: "quality"
2011
+ }));
2012
+ await this.markInsightCreated("progress-trend");
2013
+ }
2014
+ }
2015
+ this.initialized = true;
2016
+ } catch {
2017
+ this.initialized = true;
2018
+ }
2019
+ }
2020
+ /**
2021
+ * Add an insight to persistent storage
2022
+ */
2023
+ async addInsight(insight) {
2024
+ return this.insightStore.addInsight(insight);
2025
+ }
2026
+ /**
2027
+ * Restart the Guardian (reload state from disk)
2028
+ */
2029
+ async restart() {
2030
+ this.initialized = false;
2031
+ await this.insightStore.reload();
2032
+ await this.guardianState.reload();
2033
+ await this.initialize();
2034
+ }
2035
+ /**
2036
+ * Load state from disk (for testing)
2037
+ */
2038
+ async loadState() {
2039
+ await this.guardianState.load();
2040
+ return {
2041
+ goals: this.guardianState.getAllGoals(),
2042
+ riskBudget: this.guardianState.getRiskBudget()
2043
+ };
2044
+ }
2045
+ /**
2046
+ * Process new issues from a scan and generate insights
2047
+ * Stores issues in memory and optionally uses LLM for deeper analysis
2048
+ */
2049
+ async processIssues(issues, context) {
2050
+ const newInsights = [];
2051
+ try {
2052
+ await storeIssues(issues, this.projectName, this.projectPath);
2053
+ await recordToGlobalMemory(issues, this.projectName, this.projectPath);
2054
+ } catch {
2055
+ }
2056
+ const currentHashes = new Set(issues.map((i) => `${i.file}:${i.line}:${i.issue.slice(0, 50)}`));
2057
+ const newIssues = issues.filter((i) => !this.lastIssueHashes.has(`${i.file}:${i.line}:${i.issue.slice(0, 50)}`));
2058
+ const fixedCount = [...this.lastIssueHashes].filter((h) => !currentHashes.has(h)).length;
2059
+ const accessibilityIssues = issues.filter(
2060
+ (i) => i.agent === "accessibility" && (i.severity === "critical" || i.severity === "serious")
2061
+ );
2062
+ if (accessibilityIssues.length >= 2 && this.canCreateInsight("accessibility-visual-qa")) {
2063
+ const critical = accessibilityIssues.filter((i) => i.severity === "critical");
2064
+ const serious = accessibilityIssues.filter((i) => i.severity === "serious");
2065
+ const breakdown = {};
2066
+ if (critical.length > 0) breakdown["critical"] = critical.length;
2067
+ if (serious.length > 0) breakdown["serious"] = serious.length;
2068
+ const affectedFiles = [...new Set(accessibilityIssues.map((i) => i.file))];
2069
+ const examples = [...critical, ...serious].slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 60)}`);
2070
+ const insight = this.createInsight({
2071
+ type: "suggestion",
2072
+ message: `Found ${accessibilityIssues.length} accessibility issue${accessibilityIssues.length > 1 ? "s" : ""} that could block users. Screenshots would help validate real impact.`,
2073
+ suggestedAction: "Capture screenshots for visual analysis",
2074
+ actionCommand: 'trie_visual_qa_browser url:"http://localhost:3000"',
2075
+ priority: 7,
2076
+ category: "quality",
2077
+ relatedIssues: accessibilityIssues.map((i) => i.id),
2078
+ details: {
2079
+ affectedFiles: affectedFiles.map((f) => basename2(f)),
2080
+ issueBreakdown: breakdown,
2081
+ examples
2082
+ }
2083
+ });
2084
+ newInsights.push(insight);
2085
+ await this.addInsight(insight);
2086
+ await this.markInsightCreated("accessibility-visual-qa");
2087
+ }
2088
+ const securityIssues = issues.filter(
2089
+ (i) => i.category === "security" || i.issue.toLowerCase().includes("secret") || i.issue.toLowerCase().includes("vulnerability") || i.issue.toLowerCase().includes("injection") || i.issue.toLowerCase().includes("xss")
2090
+ );
2091
+ if (securityIssues.length > 0 && this.canCreateInsight("security-warning")) {
2092
+ const critical = securityIssues.filter((i) => i.severity === "critical");
2093
+ const serious = securityIssues.filter((i) => i.severity === "serious");
2094
+ if (critical.length > 0 || serious.length >= 2) {
2095
+ const breakdown = {};
2096
+ if (critical.length > 0) breakdown["critical"] = critical.length;
2097
+ if (serious.length > 0) breakdown["serious"] = serious.length;
2098
+ const affectedFiles = [...new Set(securityIssues.map((i) => i.file))];
2099
+ const examples = [...critical, ...serious].slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 60)}`);
2100
+ const insight = this.createInsight({
2101
+ type: "warning",
2102
+ message: `Found ${securityIssues.length} security issue${securityIssues.length > 1 ? "s" : ""} that could expose your app.`,
2103
+ suggestedAction: `Review ${affectedFiles.length} file${affectedFiles.length > 1 ? "s" : ""} immediately`,
2104
+ actionCommand: "trie scan --skill security",
2105
+ priority: 10,
2106
+ category: "security",
2107
+ relatedIssues: securityIssues.map((i) => i.id),
2108
+ details: {
2109
+ affectedFiles: affectedFiles.map((f) => basename2(f)),
2110
+ issueBreakdown: breakdown,
2111
+ examples
2112
+ }
2113
+ });
2114
+ newInsights.push(insight);
2115
+ await this.addInsight(insight);
2116
+ await this.markInsightCreated("security-warning");
2117
+ }
2118
+ }
2119
+ if (newIssues.length >= 3 && context.filesChanged && context.filesChanged.length > 0 && this.canCreateInsight("new-issues")) {
2120
+ const affectedFiles = [...new Set(newIssues.map((i) => basename2(i.file)))];
2121
+ const filesChanged = context.filesChanged.map((f) => basename2(f));
2122
+ const breakdown = {};
2123
+ const critical = newIssues.filter((i) => i.severity === "critical");
2124
+ const serious = newIssues.filter((i) => i.severity === "serious");
2125
+ const moderate = newIssues.filter((i) => i.severity === "moderate");
2126
+ if (critical.length > 0) breakdown["critical"] = critical.length;
2127
+ if (serious.length > 0) breakdown["serious"] = serious.length;
2128
+ if (moderate.length > 0) breakdown["moderate"] = moderate.length;
2129
+ const examples = newIssues.sort((a, b) => {
2130
+ const sev = { critical: 3, serious: 2, moderate: 1, minor: 0, low: 0 };
2131
+ return (sev[b.severity] || 0) - (sev[a.severity] || 0);
2132
+ }).slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 50)}`);
2133
+ const insight = this.createInsight({
2134
+ type: "observation",
2135
+ message: `Recent changes introduced ${newIssues.length} new issues in ${affectedFiles.slice(0, 3).join(", ")}.`,
2136
+ suggestedAction: `Review ${affectedFiles.length} affected file${affectedFiles.length > 1 ? "s" : ""}`,
2137
+ priority: 6,
2138
+ category: "quality",
2139
+ details: {
2140
+ affectedFiles: affectedFiles.slice(0, 8),
2141
+ issueBreakdown: breakdown,
2142
+ examples,
2143
+ comparison: `Changed: ${filesChanged.slice(0, 3).join(", ")}${filesChanged.length > 3 ? ` +${filesChanged.length - 3} more` : ""}`
2144
+ }
2145
+ });
2146
+ newInsights.push(insight);
2147
+ await this.addInsight(insight);
2148
+ await this.markInsightCreated("new-issues");
2149
+ }
2150
+ if (fixedCount > 0 && newIssues.length === 0 && this.canCreateInsight("celebration")) {
2151
+ const insight = this.createInsight({
2152
+ type: "celebration",
2153
+ message: `${this.pick(PERSONALITY.celebrations)} ${fixedCount} issue${fixedCount > 1 ? "s" : ""} resolved.`,
2154
+ priority: 3,
2155
+ category: "progress"
2156
+ });
2157
+ newInsights.push(insight);
2158
+ await this.addInsight(insight);
2159
+ await this.markInsightCreated("celebration");
2160
+ }
2161
+ if (this.canCreateInsight("pattern-suggestion")) {
2162
+ try {
2163
+ const globalPatterns = await findCrossProjectPatterns();
2164
+ const crossProjectPattern = globalPatterns.find((p) => p.projects.length > 1 && p.occurrences > 3);
2165
+ if (crossProjectPattern) {
2166
+ const insight = this.createInsight({
2167
+ type: "suggestion",
2168
+ message: `"${crossProjectPattern.description.slice(0, 40)}..." appears in ${crossProjectPattern.projects.length} projects. Consider a shared lint rule.`,
2169
+ priority: 4,
2170
+ category: "pattern"
2171
+ });
2172
+ newInsights.push(insight);
2173
+ await this.addInsight(insight);
2174
+ await this.markInsightCreated("pattern-suggestion");
2175
+ }
2176
+ } catch {
2177
+ }
2178
+ }
2179
+ if (context.isWatchMode && this.shouldWarnBeforePush(issues) && this.canCreateInsight("pre-push-warning")) {
2180
+ const criticalIssues = issues.filter((i) => i.severity === "critical");
2181
+ const seriousIssues = issues.filter((i) => i.severity === "serious");
2182
+ const moderateIssues = issues.filter((i) => i.severity === "moderate");
2183
+ const breakdown = {};
2184
+ if (criticalIssues.length > 0) breakdown["critical"] = criticalIssues.length;
2185
+ if (seriousIssues.length > 0) breakdown["serious"] = seriousIssues.length;
2186
+ if (moderateIssues.length > 0) breakdown["moderate"] = moderateIssues.length;
2187
+ const criticalFiles = [...new Set(criticalIssues.map((i) => basename2(i.file)))];
2188
+ const seriousFiles = [...new Set(seriousIssues.map((i) => basename2(i.file)))];
2189
+ const affectedFiles = [.../* @__PURE__ */ new Set([...criticalFiles, ...seriousFiles])].slice(0, 5);
2190
+ const examples = [...criticalIssues, ...seriousIssues].slice(0, 4).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 50)}`);
2191
+ const totalBlocking = criticalIssues.length + seriousIssues.length;
2192
+ const trend = this.lastIssueHashes.size > 0 ? totalBlocking > this.lastIssueHashes.size ? "worsening" : "improving" : void 0;
2193
+ const insightDetails = {
2194
+ affectedFiles,
2195
+ issueBreakdown: breakdown,
2196
+ examples
2197
+ };
2198
+ if (trend) insightDetails.trend = trend;
2199
+ if (moderateIssues.length > 0) insightDetails.comparison = `+${moderateIssues.length} moderate (non-blocking)`;
2200
+ const insight = this.createInsight({
2201
+ type: "warning",
2202
+ message: `${totalBlocking} issue${totalBlocking > 1 ? "s" : ""} need attention before pushing.`,
2203
+ suggestedAction: "Run pre-push check",
2204
+ actionCommand: "trie check",
2205
+ priority: 8,
2206
+ category: "quality",
2207
+ details: insightDetails
2208
+ });
2209
+ newInsights.push(insight);
2210
+ await this.addInsight(insight);
2211
+ await this.markInsightCreated("pre-push-warning");
2212
+ }
2213
+ if (isAIAvailable() && issues.length > 0 && newIssues.length > 0) {
2214
+ const aiInsights = await this.generateAIInsights(issues, newIssues);
2215
+ newInsights.push(...aiInsights);
2216
+ }
2217
+ if (this.canCreateInsight("goal-progress")) {
2218
+ try {
2219
+ const patterns = await this.goalManager.analyzeIncidentPatterns();
2220
+ const significantPatterns = patterns.filter((p) => p.confidence >= 0.6 && p.currentValue >= 3);
2221
+ if (significantPatterns.length > 0) {
2222
+ await this.goalManager.autoGenerateGoals();
2223
+ }
2224
+ await this.goalManager.updateGoalProgress();
2225
+ const goals = this.guardianState.getAllGoals();
2226
+ for (const goal of goals.filter((g) => g.status === "active")) {
2227
+ const startVal = goal.startValue ?? goal.currentValue;
2228
+ if (startVal > 0 && goal.currentValue <= startVal) {
2229
+ const percentComplete = Math.round((1 - goal.currentValue / startVal) * 100);
2230
+ if (percentComplete >= 50 && this.canCreateInsight(`goal-${goal.id}`)) {
2231
+ const insight = this.createInsight({
2232
+ type: "celebration",
2233
+ message: `\u{1F3AF} Goal "${goal.description}" is ${percentComplete}% complete! ${goal.currentValue} issues remaining (started at ${startVal}).`,
2234
+ priority: 4,
2235
+ category: "progress",
2236
+ details: {
2237
+ affectedFiles: goal.category ? [goal.category] : [],
2238
+ comparison: `${startVal} \u2192 ${goal.currentValue}`
2239
+ }
2240
+ });
2241
+ newInsights.push(insight);
2242
+ await this.addInsight(insight);
2243
+ await this.markInsightCreated(`goal-${goal.id}`);
2244
+ }
2245
+ }
2246
+ }
2247
+ await this.markInsightCreated("goal-progress");
2248
+ } catch {
2249
+ }
2250
+ }
2251
+ if (context.filesChanged && context.filesChanged.length > 0 && this.canCreateInsight("risk-prediction")) {
2252
+ try {
2253
+ const riskSummary = await this.riskPredictor.predictRiskTrend(context.filesChanged);
2254
+ if (riskSummary.overallRisk >= 50) {
2255
+ const highRiskFactors = riskSummary.factors.filter((f) => f.reasons.length > 0);
2256
+ if (highRiskFactors.length > 0) {
2257
+ const topFile = highRiskFactors[0];
2258
+ const insight = this.createInsight({
2259
+ type: "warning",
2260
+ message: `\u{1F52E} Risk score ${Math.round(riskSummary.overallRisk)}/100 for changed files (${riskSummary.trend}). ${topFile ? basename2(topFile.file) + " is high-risk." : ""}`,
2261
+ suggestedAction: "Consider extra review before pushing",
2262
+ priority: 7,
2263
+ category: "quality",
2264
+ details: {
2265
+ affectedFiles: highRiskFactors.slice(0, 5).map((f) => basename2(f.file)),
2266
+ trend: riskSummary.trend === "increasing" ? "worsening" : riskSummary.trend === "decreasing" ? "improving" : "stable"
2267
+ }
2268
+ });
2269
+ newInsights.push(insight);
2270
+ await this.addInsight(insight);
2271
+ await this.markInsightCreated("risk-prediction");
2272
+ }
2273
+ }
2274
+ } catch {
2275
+ }
2276
+ }
2277
+ if (this.canCreateInsight("hypothesis-update")) {
2278
+ try {
2279
+ const hypotheses = this.guardianState.getAllHypotheses();
2280
+ const testingHypotheses = hypotheses.filter((h) => h.status === "testing");
2281
+ if (testingHypotheses.length < 3) {
2282
+ await this.hypothesisEngine.autoGenerateHypotheses();
2283
+ }
2284
+ const validatedHypotheses = hypotheses.filter(
2285
+ (h) => h.status === "validated" && h.validatedAt && Date.now() - new Date(h.validatedAt).getTime() < 24 * 60 * 60 * 1e3
2286
+ // Within last 24h
2287
+ );
2288
+ for (const hypothesis of validatedHypotheses.slice(0, 2)) {
2289
+ if (this.canCreateInsight(`hypothesis-${hypothesis.id}`)) {
2290
+ const insight = this.createInsight({
2291
+ type: "celebration",
2292
+ message: `\u2705 Hypothesis validated: "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`,
2293
+ priority: 5,
2294
+ category: "pattern"
2295
+ });
2296
+ newInsights.push(insight);
2297
+ await this.addInsight(insight);
2298
+ await this.markInsightCreated(`hypothesis-${hypothesis.id}`);
2299
+ }
2300
+ }
2301
+ await this.markInsightCreated("hypothesis-update");
2302
+ } catch {
2303
+ }
2304
+ }
2305
+ const criticalSecurityIssues = issues.filter(
2306
+ (i) => i.severity === "critical" && (i.category === "security" || i.issue.toLowerCase().includes("secret"))
2307
+ );
2308
+ if (criticalSecurityIssues.length > 0) {
2309
+ try {
2310
+ const result = await this.escalationManager.escalateIssues(criticalSecurityIssues);
2311
+ if (result.action === "auto_escalated" && result.message) {
2312
+ const insight = this.createInsight({
2313
+ type: "observation",
2314
+ message: `\u{1F6A8} Auto-escalated ${criticalSecurityIssues.length} critical security issue(s). Check your notifications.`,
2315
+ priority: 9,
2316
+ category: "security",
2317
+ details: {
2318
+ affectedFiles: criticalSecurityIssues.slice(0, 5).map((i) => basename2(i.file)),
2319
+ examples: criticalSecurityIssues.slice(0, 3).map((i) => i.issue.slice(0, 60))
2320
+ }
2321
+ });
2322
+ newInsights.push(insight);
2323
+ await this.addInsight(insight);
2324
+ }
2325
+ } catch {
2326
+ }
2327
+ }
2328
+ this.lastIssueHashes = currentHashes;
2329
+ await this.guardianState.recordScan();
2330
+ try {
2331
+ const riskLevel = issues.filter((i) => i.severity === "critical").length > 0 ? "critical" : issues.filter((i) => i.severity === "serious").length >= 3 ? "high" : issues.length > 10 ? "medium" : "low";
2332
+ const { calculateAdaptiveScanFrequency } = await import("./goal-manager-KFBOAP4X.js");
2333
+ const result = await calculateAdaptiveScanFrequency(riskLevel);
2334
+ await this.guardianState.setScanFrequency(result.frequencyMs);
2335
+ } catch {
2336
+ }
2337
+ return newInsights;
2338
+ }
2339
+ /**
2340
+ * Use LLM to generate deeper insights (optional, requires ANTHROPIC_API_KEY)
2341
+ */
2342
+ async generateAIInsights(_allIssues, newIssues) {
2343
+ const insights = [];
2344
+ try {
2345
+ if (newIssues.length < 2) return insights;
2346
+ const issuesSummary = newIssues.slice(0, 10).map((i) => ({
2347
+ severity: i.severity,
2348
+ issue: i.issue.slice(0, 100),
2349
+ file: basename2(i.file),
2350
+ agent: i.agent
2351
+ }));
2352
+ const result = await runAIAnalysis({
2353
+ systemPrompt: `You are a helpful code guardian. Analyze these issues and provide ONE brief, conversational insight.
2354
+ Be specific and actionable. Speak like a helpful colleague, not a system.
2355
+ Keep your response under 100 words. Focus on the most important pattern or concern.`,
2356
+ userPrompt: `New issues found:
2357
+ ${JSON.stringify(issuesSummary, null, 2)}
2358
+
2359
+ What's the most important thing the developer should know? Provide a brief, conversational insight.`,
2360
+ maxTokens: 200,
2361
+ temperature: 0.7
2362
+ });
2363
+ if (result.success && result.content) {
2364
+ const insight = this.createInsight({
2365
+ type: "observation",
2366
+ message: result.content.trim(),
2367
+ priority: 5,
2368
+ category: "general"
2369
+ });
2370
+ insights.push(insight);
2371
+ await this.addInsight(insight);
2372
+ }
2373
+ } catch {
2374
+ }
2375
+ return insights;
2376
+ }
2377
+ /**
2378
+ * Get active insights (not dismissed, still relevant)
2379
+ * Now uses persistent storage (Phase 1 Guardian Agency)
2380
+ */
2381
+ getActiveInsights() {
2382
+ return this.insightStore.getActiveInsights();
2383
+ }
2384
+ /**
2385
+ * Dismiss an insight
2386
+ * Now persists to disk (Phase 1 Guardian Agency)
2387
+ */
2388
+ async dismissInsight(insightId) {
2389
+ const result = await this.insightStore.dismissInsight(insightId);
2390
+ if (result) {
2391
+ await this.guardianState.recordInsightFeedback("dismissed");
2392
+ }
2393
+ return result;
2394
+ }
2395
+ /**
2396
+ * Record that a user found an insight helpful
2397
+ */
2398
+ async markInsightHelpful(_insightId) {
2399
+ await this.guardianState.recordInsightFeedback("helpful");
2400
+ }
2401
+ /**
2402
+ * Record that a user acted on an insight
2403
+ */
2404
+ async markInsightActedOn(_insightId) {
2405
+ await this.guardianState.recordInsightFeedback("acted");
2406
+ }
2407
+ /**
2408
+ * Get insight statistics
2409
+ */
2410
+ getInsightStats() {
2411
+ return this.insightStore.getStats();
2412
+ }
2413
+ /**
2414
+ * Get agent metrics (for dashboard display)
2415
+ */
2416
+ getAgentMetrics() {
2417
+ return this.guardianState.getMetrics();
2418
+ }
2419
+ /**
2420
+ * Check if in quiet hours
2421
+ */
2422
+ isQuietHours() {
2423
+ return this.guardianState.isQuietHours();
2424
+ }
2425
+ /**
2426
+ * Check if in crunch mode
2427
+ */
2428
+ isInCrunchMode() {
2429
+ return this.guardianState.isInCrunchMode();
2430
+ }
2431
+ /**
2432
+ * Get the insight store (for advanced operations)
2433
+ */
2434
+ getInsightStore() {
2435
+ return this.insightStore;
2436
+ }
2437
+ /**
2438
+ * Get the guardian state (for advanced operations)
2439
+ */
2440
+ getGuardianState() {
2441
+ return this.guardianState;
2442
+ }
2443
+ /**
2444
+ * Get agency modules (for watch mode integration)
2445
+ */
2446
+ getGoalManager() {
2447
+ return this.goalManager;
2448
+ }
2449
+ getRiskPredictor() {
2450
+ return this.riskPredictor;
2451
+ }
2452
+ getHypothesisEngine() {
2453
+ return this.hypothesisEngine;
2454
+ }
2455
+ getEscalationManager() {
2456
+ return this.escalationManager;
2457
+ }
2458
+ getMetaLearner() {
2459
+ return this.metaLearner;
2460
+ }
2461
+ /**
2462
+ * Get a rich agency status for display in watch mode
2463
+ */
2464
+ async getAgencyStatus() {
2465
+ const goals = this.guardianState.getAllGoals();
2466
+ const activeGoals = goals.filter((g) => g.status === "active");
2467
+ const completedGoals = goals.filter((g) => g.status === "achieved");
2468
+ const hypotheses = this.guardianState.getAllHypotheses();
2469
+ const testingHypotheses = hypotheses.filter((h) => h.status === "testing");
2470
+ const validatedHypotheses = hypotheses.filter((h) => h.status === "validated");
2471
+ const metrics = this.guardianState.getMetrics();
2472
+ const budget = this.guardianState.getRiskBudget();
2473
+ const dailyActionsRemaining = budget.daily - budget.usedToday;
2474
+ let riskLevel = "low";
2475
+ if (dailyActionsRemaining <= 1) riskLevel = "critical";
2476
+ else if (dailyActionsRemaining <= 3) riskLevel = "high";
2477
+ else if (dailyActionsRemaining <= 5) riskLevel = "medium";
2478
+ const totalFeedback = metrics.helpfulInsights + metrics.dismissedInsights + metrics.actedOnInsights;
2479
+ const effectiveness = totalFeedback > 0 ? Math.round((metrics.helpfulInsights + metrics.actedOnInsights) / totalFeedback * 100) : 100;
2480
+ const topGoal = activeGoals[0]?.description;
2481
+ const topHypothesis = testingHypotheses[0]?.statement?.slice(0, 50);
2482
+ const result = {
2483
+ goals: {
2484
+ active: activeGoals.length,
2485
+ completed: completedGoals.length
2486
+ },
2487
+ hypotheses: {
2488
+ testing: testingHypotheses.length,
2489
+ validated: validatedHypotheses.length
2490
+ },
2491
+ riskLevel,
2492
+ scanFrequency: this.guardianState.getScanFrequencyMs(),
2493
+ effectiveness,
2494
+ isQuietHours: this.guardianState.isQuietHours()
2495
+ };
2496
+ if (topGoal) result.goals.topGoal = topGoal;
2497
+ if (topHypothesis) result.hypotheses.topHypothesis = topHypothesis;
2498
+ return result;
2499
+ }
2500
+ /**
2501
+ * Get a summary for the current state
2502
+ */
2503
+ getSummary(issues) {
2504
+ const critical = issues.filter((i) => i.severity === "critical").length;
2505
+ const serious = issues.filter((i) => i.severity === "serious").length;
2506
+ const total = issues.length;
2507
+ if (total === 0) {
2508
+ return "All clear. Your code looks good.";
2509
+ }
2510
+ if (critical > 0) {
2511
+ return `${critical} critical issue${critical > 1 ? "s" : ""} need${critical === 1 ? "s" : ""} immediate attention.`;
2512
+ }
2513
+ if (serious > 0) {
2514
+ return `${serious} serious issue${serious > 1 ? "s" : ""} worth reviewing before shipping.`;
2515
+ }
2516
+ return `${total} minor thing${total > 1 ? "s" : ""} to polish when you have time.`;
2517
+ }
2518
+ // === Private helpers ===
2519
+ shouldWarnBeforePush(issues) {
2520
+ const critical = issues.filter((i) => i.severity === "critical").length;
2521
+ const serious = issues.filter((i) => i.severity === "serious").length;
2522
+ return critical > 0 || serious >= 3;
2523
+ }
2524
+ /**
2525
+ * Check if we should create an insight of this type (cooldown not expired)
2526
+ * Now uses persistent storage (Phase 1 Guardian Agency)
2527
+ */
2528
+ canCreateInsight(insightKey) {
2529
+ return this.insightStore.canCreateInsight(insightKey);
2530
+ }
2531
+ /**
2532
+ * Mark that we created an insight of this type
2533
+ * Now persists to disk (Phase 1 Guardian Agency)
2534
+ */
2535
+ async markInsightCreated(insightKey) {
2536
+ await this.insightStore.markInsightCreated(insightKey);
2537
+ }
2538
+ createInsight(params) {
2539
+ return {
2540
+ id: `insight-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2541
+ timestamp: Date.now(),
2542
+ dismissed: false,
2543
+ relatedIssues: params.relatedIssues || [],
2544
+ ...params
2545
+ };
2546
+ }
2547
+ pick(arr) {
2548
+ return arr[Math.floor(Math.random() * arr.length)];
2549
+ }
2550
+ };
2551
+ var guardianInstances = /* @__PURE__ */ new Map();
2552
+ function getGuardian(projectPath) {
2553
+ let guardian = guardianInstances.get(projectPath);
2554
+ if (!guardian) {
2555
+ guardian = new GuardianAgent(projectPath);
2556
+ guardianInstances.set(projectPath, guardian);
2557
+ }
2558
+ return guardian;
2559
+ }
2560
+
2561
+ export {
2562
+ SlackIntegration,
2563
+ GuardianAgent,
2564
+ getGuardian
2565
+ };
2566
+ //# sourceMappingURL=chunk-PBOVCPKE.js.map