@triedotdev/mcp 1.0.169 → 1.0.171
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -540
- package/dist/chunk-2YXOBNKW.js +619 -0
- package/dist/chunk-2YXOBNKW.js.map +1 -0
- package/dist/chunk-QR64Y5TI.js +363 -0
- package/dist/chunk-QR64Y5TI.js.map +1 -0
- package/dist/cli/main.d.ts +0 -15
- package/dist/cli/main.js +356 -3098
- package/dist/cli/main.js.map +1 -1
- package/dist/index.js +2 -34
- package/dist/index.js.map +1 -1
- package/dist/server/mcp-server.js +2 -34
- package/package.json +8 -31
- package/dist/autonomy-config-FSERX3O3.js +0 -30
- package/dist/autonomy-config-FSERX3O3.js.map +0 -1
- package/dist/chat-store-JNGNTDSN.js +0 -15
- package/dist/chat-store-JNGNTDSN.js.map +0 -1
- package/dist/chunk-2HF65EHQ.js +0 -311
- package/dist/chunk-2HF65EHQ.js.map +0 -1
- package/dist/chunk-3XR6WVAW.js +0 -4011
- package/dist/chunk-3XR6WVAW.js.map +0 -1
- package/dist/chunk-43X6JBEM.js +0 -36
- package/dist/chunk-43X6JBEM.js.map +0 -1
- package/dist/chunk-6NLHFIYA.js +0 -344
- package/dist/chunk-6NLHFIYA.js.map +0 -1
- package/dist/chunk-7IO4YUI3.js +0 -1827
- package/dist/chunk-7IO4YUI3.js.map +0 -1
- package/dist/chunk-AHD2CBQ7.js +0 -846
- package/dist/chunk-AHD2CBQ7.js.map +0 -1
- package/dist/chunk-BUTOP5EB.js +0 -931
- package/dist/chunk-BUTOP5EB.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-EFWVF6TI.js +0 -267
- package/dist/chunk-EFWVF6TI.js.map +0 -1
- package/dist/chunk-F6WFNUAY.js +0 -216
- package/dist/chunk-F6WFNUAY.js.map +0 -1
- package/dist/chunk-FBNURWRY.js +0 -662
- package/dist/chunk-FBNURWRY.js.map +0 -1
- package/dist/chunk-FQ45QP5A.js +0 -361
- package/dist/chunk-FQ45QP5A.js.map +0 -1
- package/dist/chunk-FVRO5RN3.js +0 -1306
- package/dist/chunk-FVRO5RN3.js.map +0 -1
- package/dist/chunk-G2TGF6TR.js +0 -573
- package/dist/chunk-G2TGF6TR.js.map +0 -1
- package/dist/chunk-G3I7SZLW.js +0 -354
- package/dist/chunk-G3I7SZLW.js.map +0 -1
- package/dist/chunk-GTKYBOXL.js +0 -700
- package/dist/chunk-GTKYBOXL.js.map +0 -1
- package/dist/chunk-HVCDY3AK.js +0 -850
- package/dist/chunk-HVCDY3AK.js.map +0 -1
- package/dist/chunk-I2O5OYQT.js +0 -727
- package/dist/chunk-I2O5OYQT.js.map +0 -1
- package/dist/chunk-JVMBCWKS.js +0 -348
- package/dist/chunk-JVMBCWKS.js.map +0 -1
- package/dist/chunk-KCUOWRPX.js +0 -816
- package/dist/chunk-KCUOWRPX.js.map +0 -1
- package/dist/chunk-KDHN2ZQE.js +0 -313
- package/dist/chunk-KDHN2ZQE.js.map +0 -1
- package/dist/chunk-ME2OERF5.js +0 -345
- package/dist/chunk-ME2OERF5.js.map +0 -1
- package/dist/chunk-OBQ74FOU.js +0 -27
- package/dist/chunk-OBQ74FOU.js.map +0 -1
- package/dist/chunk-Q5EKA5YA.js +0 -254
- package/dist/chunk-Q5EKA5YA.js.map +0 -1
- package/dist/chunk-Q63FFI6D.js +0 -132
- package/dist/chunk-Q63FFI6D.js.map +0 -1
- package/dist/chunk-SASNMSB5.js +0 -12597
- package/dist/chunk-SASNMSB5.js.map +0 -1
- package/dist/chunk-T63OHG4Q.js +0 -440
- package/dist/chunk-T63OHG4Q.js.map +0 -1
- package/dist/chunk-TN5WEKWI.js +0 -173
- package/dist/chunk-TN5WEKWI.js.map +0 -1
- package/dist/chunk-VUL52BQL.js +0 -402
- package/dist/chunk-VUL52BQL.js.map +0 -1
- package/dist/chunk-VVITXIHN.js +0 -189
- package/dist/chunk-VVITXIHN.js.map +0 -1
- package/dist/chunk-WCN7S3EI.js +0 -14
- package/dist/chunk-WCN7S3EI.js.map +0 -1
- package/dist/chunk-XPZZFPBZ.js +0 -491
- package/dist/chunk-XPZZFPBZ.js.map +0 -1
- package/dist/chunk-ZJF5FTBX.js +0 -1396
- package/dist/chunk-ZJF5FTBX.js.map +0 -1
- package/dist/chunk-ZV2K6M7T.js +0 -74
- package/dist/chunk-ZV2K6M7T.js.map +0 -1
- package/dist/cli/create-agent.d.ts +0 -1
- package/dist/cli/create-agent.js +0 -1050
- package/dist/cli/create-agent.js.map +0 -1
- package/dist/cli/yolo-daemon.d.ts +0 -1
- package/dist/cli/yolo-daemon.js +0 -421
- package/dist/cli/yolo-daemon.js.map +0 -1
- package/dist/client-NJPZE5JT.js +0 -28
- package/dist/client-NJPZE5JT.js.map +0 -1
- package/dist/codebase-index-VAPF32XX.js +0 -12
- package/dist/codebase-index-VAPF32XX.js.map +0 -1
- package/dist/fast-analyzer-3GCCZMLK.js +0 -216
- package/dist/fast-analyzer-3GCCZMLK.js.map +0 -1
- package/dist/git-EO5SRFMN.js +0 -28
- package/dist/git-EO5SRFMN.js.map +0 -1
- package/dist/github-ingester-ZOKK6GRS.js +0 -11
- package/dist/github-ingester-ZOKK6GRS.js.map +0 -1
- package/dist/goal-manager-QUKX2W6C.js +0 -25
- package/dist/goal-manager-QUKX2W6C.js.map +0 -1
- package/dist/goal-validator-2SFSKKVU.js +0 -24
- package/dist/goal-validator-2SFSKKVU.js.map +0 -1
- package/dist/graph-B3NA4S7I.js +0 -10
- package/dist/graph-B3NA4S7I.js.map +0 -1
- package/dist/hypothesis-KCPBR652.js +0 -23
- package/dist/hypothesis-KCPBR652.js.map +0 -1
- package/dist/incident-index-EFNUSGWL.js +0 -11
- package/dist/incident-index-EFNUSGWL.js.map +0 -1
- package/dist/insight-store-EC4PLSAW.js +0 -22
- package/dist/insight-store-EC4PLSAW.js.map +0 -1
- package/dist/issue-store-YAXTNRRY.js +0 -36
- package/dist/issue-store-YAXTNRRY.js.map +0 -1
- package/dist/ledger-TWZTGDFA.js +0 -58
- package/dist/ledger-TWZTGDFA.js.map +0 -1
- package/dist/linear-ingester-XXPAZZRW.js +0 -11
- package/dist/linear-ingester-XXPAZZRW.js.map +0 -1
- package/dist/output-manager-RVJ37XKA.js +0 -13
- package/dist/output-manager-RVJ37XKA.js.map +0 -1
- package/dist/parse-goal-violation-SACGFG3C.js +0 -8
- package/dist/parse-goal-violation-SACGFG3C.js.map +0 -1
- package/dist/pattern-discovery-F7LU5K6E.js +0 -8
- package/dist/pattern-discovery-F7LU5K6E.js.map +0 -1
- package/dist/progress-SRQ2V3BP.js +0 -18
- package/dist/progress-SRQ2V3BP.js.map +0 -1
- package/dist/project-state-AHPA77SM.js +0 -28
- package/dist/project-state-AHPA77SM.js.map +0 -1
- package/dist/sync-M2FSWPBC.js +0 -12
- package/dist/sync-M2FSWPBC.js.map +0 -1
- package/dist/terminal-spawn-5YXDMUCF.js +0 -157
- package/dist/terminal-spawn-5YXDMUCF.js.map +0 -1
- package/dist/tiered-storage-DYNC5CQ6.js +0 -13
- package/dist/tiered-storage-DYNC5CQ6.js.map +0 -1
- package/dist/trie-agent-I3HAHY2G.js +0 -26
- package/dist/trie-agent-I3HAHY2G.js.map +0 -1
- package/dist/ui/chat.html +0 -1014
- package/dist/ui/goals.html +0 -967
- package/dist/ui/hypotheses.html +0 -1011
- package/dist/ui/ledger.html +0 -954
- package/dist/ui/nudges.html +0 -995
- package/dist/vibe-code-signatures-5ZULYP3D.js +0 -987
- package/dist/vibe-code-signatures-5ZULYP3D.js.map +0 -1
- package/dist/vulnerability-signatures-2URZSXAQ.js +0 -983
- package/dist/vulnerability-signatures-2URZSXAQ.js.map +0 -1
package/dist/chunk-7IO4YUI3.js
DELETED
|
@@ -1,1827 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getHypothesisEngine
|
|
3
|
-
} from "./chunk-I2O5OYQT.js";
|
|
4
|
-
import {
|
|
5
|
-
getGoalManager
|
|
6
|
-
} from "./chunk-FBNURWRY.js";
|
|
7
|
-
import {
|
|
8
|
-
getInsightStore
|
|
9
|
-
} from "./chunk-T63OHG4Q.js";
|
|
10
|
-
import {
|
|
11
|
-
GotchaPredictor,
|
|
12
|
-
SlackIntegration,
|
|
13
|
-
findCrossProjectPatterns,
|
|
14
|
-
recordToGlobalMemory
|
|
15
|
-
} from "./chunk-AHD2CBQ7.js";
|
|
16
|
-
import {
|
|
17
|
-
ContextGraph
|
|
18
|
-
} from "./chunk-VUL52BQL.js";
|
|
19
|
-
import {
|
|
20
|
-
getStorage
|
|
21
|
-
} from "./chunk-BUTOP5EB.js";
|
|
22
|
-
import {
|
|
23
|
-
isAIAvailable,
|
|
24
|
-
runAIAnalysis
|
|
25
|
-
} from "./chunk-FQ45QP5A.js";
|
|
26
|
-
import {
|
|
27
|
-
autoResolveIssues,
|
|
28
|
-
getHistoricalInsights,
|
|
29
|
-
getIssueHash,
|
|
30
|
-
searchIssues,
|
|
31
|
-
storeIssues
|
|
32
|
-
} from "./chunk-KCUOWRPX.js";
|
|
33
|
-
import {
|
|
34
|
-
getProjectState
|
|
35
|
-
} from "./chunk-GTKYBOXL.js";
|
|
36
|
-
|
|
37
|
-
// src/agent/trie-agent.ts
|
|
38
|
-
import { basename as basename2 } from "path";
|
|
39
|
-
|
|
40
|
-
// src/agent/risk-predictor.ts
|
|
41
|
-
import { basename, dirname } from "path";
|
|
42
|
-
var DEFAULT_CONFIG = {
|
|
43
|
-
historyDays: 30,
|
|
44
|
-
incidentThreshold: 3,
|
|
45
|
-
riskWeights: {
|
|
46
|
-
incidentCount: 0.35,
|
|
47
|
-
recency: 0.25,
|
|
48
|
-
severity: 0.2,
|
|
49
|
-
complexity: 0.1,
|
|
50
|
-
churn: 0.1
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
var RiskPredictor = class {
|
|
54
|
-
projectPath;
|
|
55
|
-
config;
|
|
56
|
-
insightStore;
|
|
57
|
-
constructor(projectPath, config = {}) {
|
|
58
|
-
this.projectPath = projectPath;
|
|
59
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
60
|
-
this.insightStore = getInsightStore(projectPath);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Calculate risk for a specific file or directory
|
|
64
|
-
*/
|
|
65
|
-
async calculateRisk(target) {
|
|
66
|
-
const factors = [];
|
|
67
|
-
let totalRisk = 0;
|
|
68
|
-
try {
|
|
69
|
-
const issues = await searchIssues(target, {
|
|
70
|
-
workDir: this.projectPath,
|
|
71
|
-
limit: 500,
|
|
72
|
-
includeResolved: true
|
|
73
|
-
});
|
|
74
|
-
const targetIssues = issues.filter(
|
|
75
|
-
(r) => r.issue.file === target || r.issue.file.startsWith(target + "/") || dirname(r.issue.file) === target
|
|
76
|
-
);
|
|
77
|
-
const incidentCount = targetIssues.length;
|
|
78
|
-
const incidentWeight = this.config.riskWeights.incidentCount;
|
|
79
|
-
const incidentScore = Math.min(100, incidentCount * 10);
|
|
80
|
-
factors.push({
|
|
81
|
-
name: "Incident Count",
|
|
82
|
-
weight: incidentWeight,
|
|
83
|
-
value: incidentCount,
|
|
84
|
-
description: `${incidentCount} historical incidents`,
|
|
85
|
-
trend: this.calculateTrend(targetIssues.map((i) => i.issue))
|
|
86
|
-
});
|
|
87
|
-
totalRisk += incidentScore * incidentWeight;
|
|
88
|
-
let recencyScore = 0;
|
|
89
|
-
let lastIncidentDaysAgo;
|
|
90
|
-
if (targetIssues.length > 0) {
|
|
91
|
-
const latestIssue = targetIssues.sort((a, b) => new Date(b.issue.timestamp).getTime() - new Date(a.issue.timestamp).getTime())[0];
|
|
92
|
-
if (latestIssue) {
|
|
93
|
-
const daysSince = (Date.now() - new Date(latestIssue.issue.timestamp).getTime()) / (1e3 * 60 * 60 * 24);
|
|
94
|
-
lastIncidentDaysAgo = Math.floor(daysSince);
|
|
95
|
-
if (daysSince < 1) recencyScore = 100;
|
|
96
|
-
else if (daysSince < 7) recencyScore = 80;
|
|
97
|
-
else if (daysSince < 14) recencyScore = 60;
|
|
98
|
-
else if (daysSince < 30) recencyScore = 40;
|
|
99
|
-
else recencyScore = 20;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
factors.push({
|
|
103
|
-
name: "Recency",
|
|
104
|
-
weight: this.config.riskWeights.recency,
|
|
105
|
-
value: lastIncidentDaysAgo ?? 999,
|
|
106
|
-
description: lastIncidentDaysAgo !== void 0 ? `Last incident ${lastIncidentDaysAgo} days ago` : "No recent incidents"
|
|
107
|
-
});
|
|
108
|
-
totalRisk += recencyScore * this.config.riskWeights.recency;
|
|
109
|
-
const criticalCount = targetIssues.filter((r) => r.issue.severity === "critical").length;
|
|
110
|
-
const seriousCount = targetIssues.filter((r) => r.issue.severity === "serious").length;
|
|
111
|
-
const severityScore = Math.min(100, criticalCount * 25 + seriousCount * 10);
|
|
112
|
-
factors.push({
|
|
113
|
-
name: "Severity",
|
|
114
|
-
weight: this.config.riskWeights.severity,
|
|
115
|
-
value: severityScore,
|
|
116
|
-
description: `${criticalCount} critical, ${seriousCount} serious`
|
|
117
|
-
});
|
|
118
|
-
totalRisk += severityScore * this.config.riskWeights.severity;
|
|
119
|
-
const uniqueAgents = new Set(targetIssues.map((r) => r.issue.agent)).size;
|
|
120
|
-
const complexityScore = Math.min(100, uniqueAgents * 20);
|
|
121
|
-
factors.push({
|
|
122
|
-
name: "Complexity",
|
|
123
|
-
weight: this.config.riskWeights.complexity,
|
|
124
|
-
value: uniqueAgents,
|
|
125
|
-
description: `Issues from ${uniqueAgents} different analysis types`
|
|
126
|
-
});
|
|
127
|
-
totalRisk += complexityScore * this.config.riskWeights.complexity;
|
|
128
|
-
const uniqueDates = new Set(
|
|
129
|
-
targetIssues.map((r) => r.issue.timestamp.split("T")[0])
|
|
130
|
-
).size;
|
|
131
|
-
const churnScore = Math.min(100, uniqueDates * 10);
|
|
132
|
-
factors.push({
|
|
133
|
-
name: "Churn",
|
|
134
|
-
weight: this.config.riskWeights.churn,
|
|
135
|
-
value: uniqueDates,
|
|
136
|
-
description: `Issues found on ${uniqueDates} different days`
|
|
137
|
-
});
|
|
138
|
-
totalRisk += churnScore * this.config.riskWeights.churn;
|
|
139
|
-
const trend = this.calculateTrend(targetIssues.map((i) => i.issue));
|
|
140
|
-
let predictedRisk = totalRisk;
|
|
141
|
-
if (trend === "increasing") {
|
|
142
|
-
predictedRisk = Math.min(100, totalRisk * 1.2);
|
|
143
|
-
} else if (trend === "decreasing") {
|
|
144
|
-
predictedRisk = Math.max(0, totalRisk * 0.8);
|
|
145
|
-
}
|
|
146
|
-
const recommendations = this.generateRecommendations(factors, totalRisk);
|
|
147
|
-
const confidence = Math.min(0.95, 0.3 + incidentCount * 0.05);
|
|
148
|
-
const prediction = {
|
|
149
|
-
target,
|
|
150
|
-
currentRisk: Math.round(totalRisk),
|
|
151
|
-
predictedRisk: Math.round(predictedRisk),
|
|
152
|
-
trend,
|
|
153
|
-
factors,
|
|
154
|
-
confidence,
|
|
155
|
-
recommendations,
|
|
156
|
-
incidentCount
|
|
157
|
-
};
|
|
158
|
-
if (lastIncidentDaysAgo !== void 0) {
|
|
159
|
-
prediction.lastIncidentDaysAgo = lastIncidentDaysAgo;
|
|
160
|
-
}
|
|
161
|
-
return prediction;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
console.error(`Failed to calculate risk for ${target}:`, error);
|
|
164
|
-
return {
|
|
165
|
-
target,
|
|
166
|
-
currentRisk: 50,
|
|
167
|
-
predictedRisk: 50,
|
|
168
|
-
trend: "stable",
|
|
169
|
-
factors: [],
|
|
170
|
-
confidence: 0.1,
|
|
171
|
-
recommendations: ["Unable to analyze - insufficient data"],
|
|
172
|
-
incidentCount: 0
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Calculate trend from issues
|
|
178
|
-
*/
|
|
179
|
-
calculateTrend(issues) {
|
|
180
|
-
if (issues.length < 3) return "stable";
|
|
181
|
-
const now = Date.now();
|
|
182
|
-
const recentCutoff = now - 7 * 24 * 60 * 60 * 1e3;
|
|
183
|
-
const olderCutoff = now - 30 * 24 * 60 * 60 * 1e3;
|
|
184
|
-
const recentIssues = issues.filter((i) => new Date(i.timestamp).getTime() > recentCutoff).length;
|
|
185
|
-
const olderIssues = issues.filter((i) => {
|
|
186
|
-
const time = new Date(i.timestamp).getTime();
|
|
187
|
-
return time > olderCutoff && time <= recentCutoff;
|
|
188
|
-
}).length;
|
|
189
|
-
const recentRate = recentIssues / 7;
|
|
190
|
-
const olderRate = olderIssues / 23;
|
|
191
|
-
if (recentRate > olderRate * 1.5) return "increasing";
|
|
192
|
-
if (recentRate < olderRate * 0.5) return "decreasing";
|
|
193
|
-
return "stable";
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Generate recommendations based on risk factors
|
|
197
|
-
*/
|
|
198
|
-
generateRecommendations(factors, totalRisk) {
|
|
199
|
-
const recommendations = [];
|
|
200
|
-
if (totalRisk >= 70) {
|
|
201
|
-
recommendations.push("Consider extra code review before merging changes");
|
|
202
|
-
}
|
|
203
|
-
const incidentFactor = factors.find((f) => f.name === "Incident Count");
|
|
204
|
-
if (incidentFactor && incidentFactor.value >= 5) {
|
|
205
|
-
recommendations.push("This area has recurring issues - consider refactoring");
|
|
206
|
-
}
|
|
207
|
-
const recencyFactor = factors.find((f) => f.name === "Recency");
|
|
208
|
-
if (recencyFactor && recencyFactor.value < 7) {
|
|
209
|
-
recommendations.push("Recent incident activity - monitor closely");
|
|
210
|
-
}
|
|
211
|
-
const severityFactor = factors.find((f) => f.name === "Severity");
|
|
212
|
-
if (severityFactor && severityFactor.value >= 25) {
|
|
213
|
-
recommendations.push("High severity issues present - prioritize fixes");
|
|
214
|
-
}
|
|
215
|
-
const complexityFactor = factors.find((f) => f.name === "Complexity");
|
|
216
|
-
if (complexityFactor && complexityFactor.value >= 4) {
|
|
217
|
-
recommendations.push("Multiple issue types - consider comprehensive review");
|
|
218
|
-
}
|
|
219
|
-
if (recommendations.length === 0) {
|
|
220
|
-
recommendations.push("No specific concerns - maintain normal review process");
|
|
221
|
-
}
|
|
222
|
-
return recommendations;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Predict risk trend for files that have recently changed
|
|
226
|
-
*/
|
|
227
|
-
async predictRiskTrend(changedFiles) {
|
|
228
|
-
const predictions = await Promise.all(
|
|
229
|
-
changedFiles.map((f) => this.calculateRisk(f))
|
|
230
|
-
);
|
|
231
|
-
const highRiskFiles = predictions.filter((p) => p.currentRisk >= 50);
|
|
232
|
-
const totalRisk = predictions.reduce((sum, p) => sum + p.currentRisk, 0) / Math.max(1, predictions.length);
|
|
233
|
-
const trends = predictions.map((p) => p.trend);
|
|
234
|
-
const increasingCount = trends.filter((t) => t === "increasing").length;
|
|
235
|
-
const decreasingCount = trends.filter((t) => t === "decreasing").length;
|
|
236
|
-
let overallTrend = "stable";
|
|
237
|
-
if (increasingCount > decreasingCount + 1) {
|
|
238
|
-
overallTrend = "increasing";
|
|
239
|
-
} else if (decreasingCount > increasingCount + 1) {
|
|
240
|
-
overallTrend = "decreasing";
|
|
241
|
-
}
|
|
242
|
-
const factors = highRiskFiles.map((p) => ({
|
|
243
|
-
file: p.target,
|
|
244
|
-
reasons: p.incidentCount > 0 ? ["past incidents", ...p.recommendations.slice(0, 2)] : ["no historical data"]
|
|
245
|
-
}));
|
|
246
|
-
return {
|
|
247
|
-
trend: overallTrend,
|
|
248
|
-
factors,
|
|
249
|
-
overallRisk: Math.round(totalRisk)
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Get project-wide risk summary
|
|
254
|
-
*/
|
|
255
|
-
async getProjectRiskSummary() {
|
|
256
|
-
try {
|
|
257
|
-
const issues = await searchIssues("", {
|
|
258
|
-
workDir: this.projectPath,
|
|
259
|
-
limit: 1e3,
|
|
260
|
-
includeResolved: true
|
|
261
|
-
});
|
|
262
|
-
const dirIssueCount = /* @__PURE__ */ new Map();
|
|
263
|
-
for (const { issue } of issues) {
|
|
264
|
-
const dir = dirname(issue.file);
|
|
265
|
-
const existing = dirIssueCount.get(dir) || [];
|
|
266
|
-
existing.push(issue);
|
|
267
|
-
dirIssueCount.set(dir, existing);
|
|
268
|
-
}
|
|
269
|
-
const hotspotDirs = [...dirIssueCount.entries()].filter(([_, issues2]) => issues2.length >= this.config.incidentThreshold).sort((a, b) => b[1].length - a[1].length).slice(0, 5);
|
|
270
|
-
const hotspots = await Promise.all(
|
|
271
|
-
hotspotDirs.map(([dir]) => this.calculateRisk(dir))
|
|
272
|
-
);
|
|
273
|
-
const allDirs = [...dirIssueCount.keys()];
|
|
274
|
-
const lowRiskPredictions = await Promise.all(
|
|
275
|
-
allDirs.map((dir) => this.calculateRisk(dir))
|
|
276
|
-
);
|
|
277
|
-
const lowRiskAreas = lowRiskPredictions.filter((p) => p.currentRisk < 20 && p.incidentCount === 0).map((p) => p.target).slice(0, 5);
|
|
278
|
-
const overallRisk = hotspots.length > 0 ? hotspots.reduce((sum, p) => sum + p.currentRisk, 0) / hotspots.length : 0;
|
|
279
|
-
const trends = hotspots.map((p) => p.trend);
|
|
280
|
-
const increasingCount = trends.filter((t) => t === "increasing").length;
|
|
281
|
-
const decreasingCount = trends.filter((t) => t === "decreasing").length;
|
|
282
|
-
let trend = "stable";
|
|
283
|
-
if (increasingCount > decreasingCount) {
|
|
284
|
-
trend = "increasing";
|
|
285
|
-
} else if (decreasingCount > increasingCount) {
|
|
286
|
-
trend = "decreasing";
|
|
287
|
-
}
|
|
288
|
-
const confidence = Math.min(0.9, 0.4 + issues.length * 1e-3);
|
|
289
|
-
return {
|
|
290
|
-
overallRisk: Math.round(overallRisk),
|
|
291
|
-
trend,
|
|
292
|
-
hotspots,
|
|
293
|
-
lowRiskAreas,
|
|
294
|
-
predictions: hotspots,
|
|
295
|
-
confidence
|
|
296
|
-
};
|
|
297
|
-
} catch (error) {
|
|
298
|
-
console.error("Failed to get project risk summary:", error);
|
|
299
|
-
return {
|
|
300
|
-
overallRisk: 0,
|
|
301
|
-
trend: "stable",
|
|
302
|
-
hotspots: [],
|
|
303
|
-
lowRiskAreas: [],
|
|
304
|
-
predictions: [],
|
|
305
|
-
confidence: 0.1
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Create risk prediction insight
|
|
311
|
-
*/
|
|
312
|
-
async generateRiskInsight() {
|
|
313
|
-
if (!this.insightStore.canCreateInsight("risk-prediction")) {
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
|
-
const summary = await this.getProjectRiskSummary();
|
|
317
|
-
if (summary.hotspots.length === 0 || summary.overallRisk < 30) {
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
const topHotspot = summary.hotspots[0];
|
|
321
|
-
if (!topHotspot) return null;
|
|
322
|
-
const trendMap = {
|
|
323
|
-
"increasing": "worsening",
|
|
324
|
-
"stable": "stable",
|
|
325
|
-
"decreasing": "improving"
|
|
326
|
-
};
|
|
327
|
-
const insight = {
|
|
328
|
-
id: `insight-risk-${Date.now()}`,
|
|
329
|
-
type: "warning",
|
|
330
|
-
message: `[!] ${basename(topHotspot.target)}/ has elevated risk (${topHotspot.currentRisk}/100)`,
|
|
331
|
-
context: `${topHotspot.incidentCount} past incidents. ${topHotspot.recommendations[0]}`,
|
|
332
|
-
suggestedAction: "Request extra review before merging changes to this area",
|
|
333
|
-
relatedIssues: [],
|
|
334
|
-
priority: Math.min(9, 5 + Math.floor(topHotspot.currentRisk / 20)),
|
|
335
|
-
timestamp: Date.now(),
|
|
336
|
-
dismissed: false,
|
|
337
|
-
category: "security",
|
|
338
|
-
details: {
|
|
339
|
-
affectedFiles: summary.hotspots.map((h) => basename(h.target)),
|
|
340
|
-
trend: trendMap[summary.trend],
|
|
341
|
-
examples: topHotspot.recommendations
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
await this.insightStore.addInsight(insight);
|
|
345
|
-
await this.insightStore.markInsightCreated("risk-prediction");
|
|
346
|
-
return insight;
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
var riskPredictors = /* @__PURE__ */ new Map();
|
|
350
|
-
function getRiskPredictor(projectPath) {
|
|
351
|
-
let predictor = riskPredictors.get(projectPath);
|
|
352
|
-
if (!predictor) {
|
|
353
|
-
predictor = new RiskPredictor(projectPath);
|
|
354
|
-
riskPredictors.set(projectPath, predictor);
|
|
355
|
-
}
|
|
356
|
-
return predictor;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/agent/escalation.ts
|
|
360
|
-
var DEFAULT_CONFIG2 = {
|
|
361
|
-
enabled: true,
|
|
362
|
-
targets: [],
|
|
363
|
-
cooldownMinutes: 15,
|
|
364
|
-
maxEscalationsPerHour: 5,
|
|
365
|
-
respectQuietHours: true,
|
|
366
|
-
criticalBypassQuietHours: true,
|
|
367
|
-
draftFallbackEnabled: true
|
|
368
|
-
};
|
|
369
|
-
var EscalationManager = class {
|
|
370
|
-
projectPath;
|
|
371
|
-
config;
|
|
372
|
-
projectState;
|
|
373
|
-
insightStore;
|
|
374
|
-
escalationHistory = [];
|
|
375
|
-
slackClient;
|
|
376
|
-
constructor(projectPath, config = {}) {
|
|
377
|
-
this.projectPath = projectPath;
|
|
378
|
-
this.config = { ...DEFAULT_CONFIG2, ...config };
|
|
379
|
-
this.projectState = getProjectState(projectPath);
|
|
380
|
-
this.insightStore = getInsightStore(projectPath);
|
|
381
|
-
const slackTarget = this.config.targets.find((t) => t.type === "slack" && t.enabled);
|
|
382
|
-
if (slackTarget?.config.webhookUrl) {
|
|
383
|
-
this.slackClient = new SlackIntegration({
|
|
384
|
-
webhookUrl: slackTarget.config.webhookUrl,
|
|
385
|
-
channel: slackTarget.config.channel,
|
|
386
|
-
username: slackTarget.config.username
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Check if an issue should be auto-escalated
|
|
392
|
-
*/
|
|
393
|
-
shouldAutoEscalate(issue) {
|
|
394
|
-
if (!this.config.enabled) {
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
if (issue.severity !== "critical") {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
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");
|
|
401
|
-
if (!isSecurityIssue) {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
404
|
-
if (this.projectState.isQuietHours()) {
|
|
405
|
-
if (!this.config.criticalBypassQuietHours) {
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
if (!this.insightStore.canCreateInsight("auto-escalation")) {
|
|
410
|
-
return false;
|
|
411
|
-
}
|
|
412
|
-
const recentEscalations = this.escalationHistory.filter(
|
|
413
|
-
(e) => e.timestamp > Date.now() - 60 * 60 * 1e3
|
|
414
|
-
);
|
|
415
|
-
if (recentEscalations.length >= this.config.maxEscalationsPerHour) {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
const recentlyEscalated = this.escalationHistory.some(
|
|
419
|
-
(e) => e.issues.some((i) => i.file === issue.file) && e.timestamp > Date.now() - this.config.cooldownMinutes * 60 * 1e3
|
|
420
|
-
);
|
|
421
|
-
if (recentlyEscalated) {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
return this.hasValidEscalationTarget("critical", "security");
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Check if we have a valid escalation target
|
|
428
|
-
*/
|
|
429
|
-
hasValidEscalationTarget(severity, category) {
|
|
430
|
-
return this.config.targets.some(
|
|
431
|
-
(t) => t.enabled && t.forSeverities.includes(severity) && (t.forCategories.includes(category) || t.forCategories.includes("all"))
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Auto-escalate if issue is critical
|
|
436
|
-
*/
|
|
437
|
-
async autoEscalateIfCritical(issue) {
|
|
438
|
-
if (!this.shouldAutoEscalate(issue)) {
|
|
439
|
-
if (issue.severity === "critical" && this.config.draftFallbackEnabled) {
|
|
440
|
-
if (this.projectState.isQuietHours()) {
|
|
441
|
-
return this.createDraftEscalation([issue], "Blocked by quiet hours");
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
return {
|
|
445
|
-
action: "no_action",
|
|
446
|
-
reason: "Issue does not meet escalation criteria"
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
const message = this.createEscalationMessage([issue]);
|
|
450
|
-
try {
|
|
451
|
-
await this.sendEscalation(message);
|
|
452
|
-
this.escalationHistory.push(message);
|
|
453
|
-
await this.insightStore.markInsightCreated("auto-escalation");
|
|
454
|
-
await this.createEscalationInsight(message);
|
|
455
|
-
await this.projectState.useRiskBudget(1);
|
|
456
|
-
return {
|
|
457
|
-
action: "auto_escalated",
|
|
458
|
-
reason: "Critical security issue auto-escalated",
|
|
459
|
-
message
|
|
460
|
-
};
|
|
461
|
-
} catch (error) {
|
|
462
|
-
console.error("Failed to send escalation:", error);
|
|
463
|
-
if (this.config.draftFallbackEnabled) {
|
|
464
|
-
return this.createDraftEscalation([issue], String(error));
|
|
465
|
-
}
|
|
466
|
-
return {
|
|
467
|
-
action: "failed",
|
|
468
|
-
reason: `Send failed: ${error}`,
|
|
469
|
-
message
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Escalate multiple issues at once
|
|
475
|
-
*/
|
|
476
|
-
async escalateIssues(issues) {
|
|
477
|
-
const criticalSecurityIssues = issues.filter(
|
|
478
|
-
(i) => i.severity === "critical" && (i.category === "security" || i.agent === "security")
|
|
479
|
-
);
|
|
480
|
-
if (criticalSecurityIssues.length === 0) {
|
|
481
|
-
return {
|
|
482
|
-
action: "no_action",
|
|
483
|
-
reason: "No critical security issues to escalate"
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
if (this.projectState.isQuietHours() && !this.config.criticalBypassQuietHours) {
|
|
487
|
-
return this.createDraftEscalation(criticalSecurityIssues, "Blocked by quiet hours");
|
|
488
|
-
}
|
|
489
|
-
const message = this.createEscalationMessage(criticalSecurityIssues);
|
|
490
|
-
try {
|
|
491
|
-
await this.sendEscalation(message);
|
|
492
|
-
this.escalationHistory.push(message);
|
|
493
|
-
await this.insightStore.markInsightCreated("auto-escalation");
|
|
494
|
-
await this.createEscalationInsight(message);
|
|
495
|
-
return {
|
|
496
|
-
action: "auto_escalated",
|
|
497
|
-
reason: `Escalated ${criticalSecurityIssues.length} critical security issues`,
|
|
498
|
-
message
|
|
499
|
-
};
|
|
500
|
-
} catch (error) {
|
|
501
|
-
if (this.config.draftFallbackEnabled) {
|
|
502
|
-
return this.createDraftEscalation(criticalSecurityIssues, String(error));
|
|
503
|
-
}
|
|
504
|
-
return {
|
|
505
|
-
action: "failed",
|
|
506
|
-
reason: `Send failed: ${error}`,
|
|
507
|
-
message
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* Create an escalation message
|
|
513
|
-
*/
|
|
514
|
-
createEscalationMessage(issues) {
|
|
515
|
-
const severity = issues.some((i) => i.severity === "critical") ? "critical" : "serious";
|
|
516
|
-
return {
|
|
517
|
-
id: `esc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
518
|
-
severity,
|
|
519
|
-
title: `${severity.toUpperCase()} Security Alert: ${issues.length} issue${issues.length > 1 ? "s" : ""} detected`,
|
|
520
|
-
body: this.formatEscalationBody(issues),
|
|
521
|
-
issues,
|
|
522
|
-
timestamp: Date.now(),
|
|
523
|
-
sent: false
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* Format escalation body text
|
|
528
|
-
*/
|
|
529
|
-
formatEscalationBody(issues) {
|
|
530
|
-
const lines = [
|
|
531
|
-
`**Critical Security Issues Detected**`,
|
|
532
|
-
``,
|
|
533
|
-
`**Project:** ${this.projectPath.split("/").pop()}`,
|
|
534
|
-
`**Issues:** ${issues.length}`,
|
|
535
|
-
`**Detected:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
536
|
-
``,
|
|
537
|
-
`**Summary:**`
|
|
538
|
-
];
|
|
539
|
-
for (const issue of issues.slice(0, 5)) {
|
|
540
|
-
lines.push(`- \`${issue.file}:${issue.line || "?"}\`: ${issue.issue.slice(0, 100)}`);
|
|
541
|
-
}
|
|
542
|
-
if (issues.length > 5) {
|
|
543
|
-
lines.push(`- ... and ${issues.length - 5} more`);
|
|
544
|
-
}
|
|
545
|
-
lines.push("");
|
|
546
|
-
lines.push("**Action Required:** Review and address these issues immediately.");
|
|
547
|
-
return lines.join("\n");
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Send escalation through configured channels
|
|
551
|
-
*/
|
|
552
|
-
async sendEscalation(message) {
|
|
553
|
-
const enabledTargets = this.config.targets.filter((t) => t.enabled);
|
|
554
|
-
if (enabledTargets.length === 0) {
|
|
555
|
-
throw new Error("No enabled escalation targets configured");
|
|
556
|
-
}
|
|
557
|
-
const errors = [];
|
|
558
|
-
for (const target of enabledTargets) {
|
|
559
|
-
try {
|
|
560
|
-
switch (target.type) {
|
|
561
|
-
case "slack":
|
|
562
|
-
await this.sendToSlack(message, target);
|
|
563
|
-
break;
|
|
564
|
-
case "email":
|
|
565
|
-
await this.sendToEmail(message, target);
|
|
566
|
-
break;
|
|
567
|
-
case "webhook":
|
|
568
|
-
await this.sendToWebhook(message, target);
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
message.sent = true;
|
|
572
|
-
message.sentAt = Date.now();
|
|
573
|
-
message.channel = target.type;
|
|
574
|
-
} catch (error) {
|
|
575
|
-
errors.push(`${target.type}: ${error}`);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
if (errors.length === enabledTargets.length) {
|
|
579
|
-
throw new Error(`All escalation channels failed: ${errors.join("; ")}`);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Send to Slack
|
|
584
|
-
*/
|
|
585
|
-
async sendToSlack(message, target) {
|
|
586
|
-
if (!target.config.webhookUrl) {
|
|
587
|
-
throw new Error("Slack webhook URL not configured");
|
|
588
|
-
}
|
|
589
|
-
if (this.slackClient) {
|
|
590
|
-
await this.slackClient.sendCriticalAlert(message.issues, this.projectPath.split("/").pop() || "Unknown");
|
|
591
|
-
} else {
|
|
592
|
-
const slack = new SlackIntegration({
|
|
593
|
-
webhookUrl: target.config.webhookUrl,
|
|
594
|
-
channel: target.config.channel,
|
|
595
|
-
username: target.config.username || "Trie Agent"
|
|
596
|
-
});
|
|
597
|
-
await slack.sendCriticalAlert(message.issues, this.projectPath.split("/").pop() || "Unknown");
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* Send to email (placeholder - would need email provider integration)
|
|
602
|
-
*/
|
|
603
|
-
async sendToEmail(message, target) {
|
|
604
|
-
if (!target.config.email) {
|
|
605
|
-
throw new Error("Email address not configured");
|
|
606
|
-
}
|
|
607
|
-
console.log(`[EMAIL ESCALATION] To: ${target.config.email}`);
|
|
608
|
-
console.log(`Subject: ${message.title}`);
|
|
609
|
-
console.log(`Body: ${message.body}`);
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* Send to webhook
|
|
613
|
-
*/
|
|
614
|
-
async sendToWebhook(message, target) {
|
|
615
|
-
if (!target.config.webhookUrl) {
|
|
616
|
-
throw new Error("Webhook URL not configured");
|
|
617
|
-
}
|
|
618
|
-
const response = await fetch(target.config.webhookUrl, {
|
|
619
|
-
method: "POST",
|
|
620
|
-
headers: {
|
|
621
|
-
"Content-Type": "application/json"
|
|
622
|
-
},
|
|
623
|
-
body: JSON.stringify({
|
|
624
|
-
id: message.id,
|
|
625
|
-
severity: message.severity,
|
|
626
|
-
title: message.title,
|
|
627
|
-
body: message.body,
|
|
628
|
-
issues: message.issues.map((i) => ({
|
|
629
|
-
file: i.file,
|
|
630
|
-
line: i.line,
|
|
631
|
-
severity: i.severity,
|
|
632
|
-
issue: i.issue,
|
|
633
|
-
agent: i.agent
|
|
634
|
-
})),
|
|
635
|
-
timestamp: message.timestamp,
|
|
636
|
-
project: this.projectPath
|
|
637
|
-
})
|
|
638
|
-
});
|
|
639
|
-
if (!response.ok) {
|
|
640
|
-
throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Create a draft escalation (when sending is blocked)
|
|
645
|
-
*/
|
|
646
|
-
createDraftEscalation(issues, reason) {
|
|
647
|
-
const message = this.createEscalationMessage(issues);
|
|
648
|
-
message.error = reason;
|
|
649
|
-
this.escalationHistory.push(message);
|
|
650
|
-
return {
|
|
651
|
-
action: "draft_only",
|
|
652
|
-
reason,
|
|
653
|
-
message,
|
|
654
|
-
draftLocation: `.trie/drafts/escalation-${message.id}.json`
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Create insight about escalation
|
|
659
|
-
*/
|
|
660
|
-
async createEscalationInsight(message) {
|
|
661
|
-
const insight = {
|
|
662
|
-
id: `insight-esc-${message.id}`,
|
|
663
|
-
type: "warning",
|
|
664
|
-
message: `Auto-escalated: ${message.issues.length} critical security issue${message.issues.length > 1 ? "s" : ""}`,
|
|
665
|
-
context: `Sent to ${message.channel}. Issues in: ${[...new Set(message.issues.map((i) => i.file))].slice(0, 3).join(", ")}`,
|
|
666
|
-
relatedIssues: message.issues.map((i) => i.id),
|
|
667
|
-
priority: 10,
|
|
668
|
-
timestamp: Date.now(),
|
|
669
|
-
dismissed: false,
|
|
670
|
-
category: "security",
|
|
671
|
-
details: {
|
|
672
|
-
affectedFiles: [...new Set(message.issues.map((i) => i.file))]
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
await this.insightStore.addInsight(insight);
|
|
676
|
-
}
|
|
677
|
-
/**
|
|
678
|
-
* Get escalation history
|
|
679
|
-
*/
|
|
680
|
-
getEscalationHistory() {
|
|
681
|
-
return [...this.escalationHistory];
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Get pending draft escalations
|
|
685
|
-
*/
|
|
686
|
-
getPendingDrafts() {
|
|
687
|
-
return this.escalationHistory.filter((e) => !e.sent);
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Retry a failed/draft escalation
|
|
691
|
-
*/
|
|
692
|
-
async retryEscalation(messageId) {
|
|
693
|
-
const message = this.escalationHistory.find((e) => e.id === messageId);
|
|
694
|
-
if (!message) {
|
|
695
|
-
return {
|
|
696
|
-
action: "failed",
|
|
697
|
-
reason: "Escalation message not found"
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
try {
|
|
701
|
-
await this.sendEscalation(message);
|
|
702
|
-
return {
|
|
703
|
-
action: "auto_escalated",
|
|
704
|
-
reason: "Retry successful",
|
|
705
|
-
message
|
|
706
|
-
};
|
|
707
|
-
} catch (error) {
|
|
708
|
-
return {
|
|
709
|
-
action: "failed",
|
|
710
|
-
reason: `Retry failed: ${error}`,
|
|
711
|
-
message
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Configure escalation targets
|
|
717
|
-
*/
|
|
718
|
-
addEscalationTarget(target) {
|
|
719
|
-
this.config.targets.push(target);
|
|
720
|
-
if (target.type === "slack" && target.enabled && target.config.webhookUrl) {
|
|
721
|
-
this.slackClient = new SlackIntegration({
|
|
722
|
-
webhookUrl: target.config.webhookUrl,
|
|
723
|
-
channel: target.config.channel,
|
|
724
|
-
username: target.config.username
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Get current configuration
|
|
730
|
-
*/
|
|
731
|
-
getConfig() {
|
|
732
|
-
return { ...this.config };
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Update configuration
|
|
736
|
-
*/
|
|
737
|
-
setConfig(config) {
|
|
738
|
-
this.config = { ...this.config, ...config };
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
var escalationManagers = /* @__PURE__ */ new Map();
|
|
742
|
-
function getEscalationManager(projectPath) {
|
|
743
|
-
let manager = escalationManagers.get(projectPath);
|
|
744
|
-
if (!manager) {
|
|
745
|
-
manager = new EscalationManager(projectPath);
|
|
746
|
-
escalationManagers.set(projectPath, manager);
|
|
747
|
-
}
|
|
748
|
-
return manager;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// src/agent/meta-learning.ts
|
|
752
|
-
var MetaLearner = class {
|
|
753
|
-
projectState;
|
|
754
|
-
insightStore;
|
|
755
|
-
feedbackHistory = [];
|
|
756
|
-
weights;
|
|
757
|
-
constructor(projectPath) {
|
|
758
|
-
this.projectState = getProjectState(projectPath);
|
|
759
|
-
this.insightStore = getInsightStore(projectPath);
|
|
760
|
-
this.weights = {
|
|
761
|
-
security: 1,
|
|
762
|
-
quality: 1,
|
|
763
|
-
performance: 1,
|
|
764
|
-
pattern: 1,
|
|
765
|
-
progress: 1,
|
|
766
|
-
general: 1
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Record user feedback on an insight
|
|
771
|
-
*/
|
|
772
|
-
async recordFeedback(feedback, context) {
|
|
773
|
-
await this.insightStore.load();
|
|
774
|
-
const insight = this.insightStore.getInsight(context.insightId);
|
|
775
|
-
if (!insight) {
|
|
776
|
-
console.error(`Insight not found: ${context.insightId}`);
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
const record = {
|
|
780
|
-
insightId: context.insightId,
|
|
781
|
-
insightType: insight.type,
|
|
782
|
-
insightCategory: insight.category,
|
|
783
|
-
feedback,
|
|
784
|
-
timestamp: Date.now()
|
|
785
|
-
};
|
|
786
|
-
if (context.latencyMs !== void 0) {
|
|
787
|
-
record.latencyMs = context.latencyMs;
|
|
788
|
-
}
|
|
789
|
-
this.feedbackHistory.push(record);
|
|
790
|
-
await this.projectState.recordInsightFeedback(
|
|
791
|
-
feedback === "dismissed" ? "dismissed" : feedback === "acted" ? "acted" : "helpful"
|
|
792
|
-
);
|
|
793
|
-
await this.adjustWeights(record);
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Adjust insight weights based on feedback
|
|
797
|
-
*/
|
|
798
|
-
async adjustWeights(feedback) {
|
|
799
|
-
const category = feedback.insightCategory;
|
|
800
|
-
const currentWeight = this.weights[category];
|
|
801
|
-
if (currentWeight === void 0) return;
|
|
802
|
-
let adjustment = 0;
|
|
803
|
-
switch (feedback.feedback) {
|
|
804
|
-
case "acted":
|
|
805
|
-
adjustment = 0.05;
|
|
806
|
-
break;
|
|
807
|
-
case "helpful":
|
|
808
|
-
adjustment = 0.02;
|
|
809
|
-
break;
|
|
810
|
-
case "dismissed":
|
|
811
|
-
adjustment = -0.03;
|
|
812
|
-
break;
|
|
813
|
-
case "ignored":
|
|
814
|
-
adjustment = -0.01;
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
this.weights[category] = Math.max(0.3, Math.min(1.5, currentWeight + adjustment));
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Get current insight weights
|
|
821
|
-
*/
|
|
822
|
-
getWeights() {
|
|
823
|
-
return { ...this.weights };
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Get weight for a category (used to adjust insight priority)
|
|
827
|
-
*/
|
|
828
|
-
getCategoryWeight(category) {
|
|
829
|
-
return this.weights[category] ?? 1;
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Calculate category effectiveness
|
|
833
|
-
*/
|
|
834
|
-
calculateCategoryEffectiveness() {
|
|
835
|
-
const categories = ["security", "quality", "performance", "pattern", "progress", "general"];
|
|
836
|
-
const effectiveness = [];
|
|
837
|
-
for (const category of categories) {
|
|
838
|
-
const categoryFeedback = this.feedbackHistory.filter((f) => f.insightCategory === category);
|
|
839
|
-
if (categoryFeedback.length === 0) {
|
|
840
|
-
effectiveness.push({
|
|
841
|
-
category,
|
|
842
|
-
totalInsights: 0,
|
|
843
|
-
helpfulCount: 0,
|
|
844
|
-
dismissedCount: 0,
|
|
845
|
-
actedCount: 0,
|
|
846
|
-
effectivenessScore: 0.5
|
|
847
|
-
// Neutral when no data
|
|
848
|
-
});
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
|
-
const helpfulCount = categoryFeedback.filter((f) => f.feedback === "helpful" || f.feedback === "acted").length;
|
|
852
|
-
const dismissedCount = categoryFeedback.filter((f) => f.feedback === "dismissed").length;
|
|
853
|
-
const actedCount = categoryFeedback.filter((f) => f.feedback === "acted").length;
|
|
854
|
-
const score = (helpfulCount + actedCount) / Math.max(1, categoryFeedback.length);
|
|
855
|
-
effectiveness.push({
|
|
856
|
-
category,
|
|
857
|
-
totalInsights: categoryFeedback.length,
|
|
858
|
-
helpfulCount,
|
|
859
|
-
dismissedCount,
|
|
860
|
-
actedCount,
|
|
861
|
-
effectivenessScore: score
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
return effectiveness;
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Get agent effectiveness metrics
|
|
868
|
-
*/
|
|
869
|
-
getEffectiveness() {
|
|
870
|
-
return this.projectState.getMetrics();
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Generate a comprehensive effectiveness report
|
|
874
|
-
*/
|
|
875
|
-
async generateEffectivenessReport() {
|
|
876
|
-
await this.projectState.load();
|
|
877
|
-
const metrics = this.projectState.getMetrics();
|
|
878
|
-
const categoryBreakdown = this.calculateCategoryEffectiveness();
|
|
879
|
-
const overallScore = metrics.userSatisfaction * 0.4 + metrics.predictiveAccuracy * 0.3 + (1 - metrics.falsePositiveRate) * 0.3;
|
|
880
|
-
const recommendations = this.generateRecommendations(metrics, categoryBreakdown);
|
|
881
|
-
const trend = this.calculateTrend();
|
|
882
|
-
return {
|
|
883
|
-
overallScore,
|
|
884
|
-
predictiveAccuracy: metrics.predictiveAccuracy,
|
|
885
|
-
userSatisfaction: metrics.userSatisfaction,
|
|
886
|
-
falsePositiveRate: metrics.falsePositiveRate,
|
|
887
|
-
categoryBreakdown,
|
|
888
|
-
recommendations,
|
|
889
|
-
trends: trend
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Generate improvement recommendations
|
|
894
|
-
*/
|
|
895
|
-
generateRecommendations(metrics, categoryBreakdown) {
|
|
896
|
-
const recommendations = [];
|
|
897
|
-
if (metrics.falsePositiveRate > 0.3) {
|
|
898
|
-
recommendations.push("High dismissal rate detected. Consider raising insight thresholds.");
|
|
899
|
-
}
|
|
900
|
-
if (metrics.userSatisfaction < 0.5) {
|
|
901
|
-
recommendations.push("User satisfaction is low. Review insight quality and relevance.");
|
|
902
|
-
}
|
|
903
|
-
const weakCategories = categoryBreakdown.filter((c) => c.totalInsights >= 5 && c.effectivenessScore < 0.4).map((c) => c.category);
|
|
904
|
-
if (weakCategories.length > 0) {
|
|
905
|
-
recommendations.push(
|
|
906
|
-
`Low effectiveness in: ${weakCategories.join(", ")}. Consider adjusting insight generation for these categories.`
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
const strongCategories = categoryBreakdown.filter((c) => c.totalInsights >= 5 && c.effectivenessScore > 0.7).map((c) => c.category);
|
|
910
|
-
if (strongCategories.length > 0) {
|
|
911
|
-
recommendations.push(
|
|
912
|
-
`Strong performance in: ${strongCategories.join(", ")}. Consider emphasizing these insights.`
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
if (metrics.predictiveAccuracy < 0.6 && metrics.totalPredictions >= 10) {
|
|
916
|
-
recommendations.push("Prediction accuracy below target. Review risk scoring factors.");
|
|
917
|
-
}
|
|
918
|
-
if (metrics.userSatisfaction >= 0.7 && metrics.falsePositiveRate <= 0.2) {
|
|
919
|
-
recommendations.push("Agent performance is excellent. Keep up the good work!");
|
|
920
|
-
}
|
|
921
|
-
return recommendations;
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* Calculate trend direction
|
|
925
|
-
*/
|
|
926
|
-
calculateTrend() {
|
|
927
|
-
const now = Date.now();
|
|
928
|
-
const recentCutoff = now - 7 * 24 * 60 * 60 * 1e3;
|
|
929
|
-
const olderCutoff = now - 30 * 24 * 60 * 60 * 1e3;
|
|
930
|
-
const recentFeedback = this.feedbackHistory.filter((f) => f.timestamp > recentCutoff);
|
|
931
|
-
const olderFeedback = this.feedbackHistory.filter(
|
|
932
|
-
(f) => f.timestamp > olderCutoff && f.timestamp <= recentCutoff
|
|
933
|
-
);
|
|
934
|
-
if (recentFeedback.length < 5 || olderFeedback.length < 5) {
|
|
935
|
-
return { period: "last 30 days", direction: "stable" };
|
|
936
|
-
}
|
|
937
|
-
const recentPositive = recentFeedback.filter(
|
|
938
|
-
(f) => f.feedback === "helpful" || f.feedback === "acted"
|
|
939
|
-
).length / recentFeedback.length;
|
|
940
|
-
const olderPositive = olderFeedback.filter(
|
|
941
|
-
(f) => f.feedback === "helpful" || f.feedback === "acted"
|
|
942
|
-
).length / olderFeedback.length;
|
|
943
|
-
const diff = recentPositive - olderPositive;
|
|
944
|
-
if (diff > 0.1) {
|
|
945
|
-
return { period: "last 30 days", direction: "improving" };
|
|
946
|
-
} else if (diff < -0.1) {
|
|
947
|
-
return { period: "last 30 days", direction: "declining" };
|
|
948
|
-
}
|
|
949
|
-
return { period: "last 30 days", direction: "stable" };
|
|
950
|
-
}
|
|
951
|
-
/**
|
|
952
|
-
* Should an insight be shown based on learning?
|
|
953
|
-
*
|
|
954
|
-
* Uses weights and user patterns to filter low-value insights.
|
|
955
|
-
*/
|
|
956
|
-
shouldShowInsight(insight) {
|
|
957
|
-
const weight = this.getCategoryWeight(insight.category);
|
|
958
|
-
if (weight < 0.5 && insight.priority < 5) {
|
|
959
|
-
return false;
|
|
960
|
-
}
|
|
961
|
-
const adjustedPriority = insight.priority * weight;
|
|
962
|
-
return adjustedPriority >= 3;
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Adjust insight priority based on learning
|
|
966
|
-
*/
|
|
967
|
-
adjustInsightPriority(insight) {
|
|
968
|
-
const weight = this.getCategoryWeight(insight.category);
|
|
969
|
-
return Math.round(insight.priority * weight);
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* Get feedback history stats
|
|
973
|
-
*/
|
|
974
|
-
getFeedbackStats() {
|
|
975
|
-
const stats = {
|
|
976
|
-
total: this.feedbackHistory.length,
|
|
977
|
-
helpful: 0,
|
|
978
|
-
dismissed: 0,
|
|
979
|
-
acted: 0,
|
|
980
|
-
ignored: 0,
|
|
981
|
-
averageLatencyMs: null
|
|
982
|
-
};
|
|
983
|
-
let totalLatency = 0;
|
|
984
|
-
let latencyCount = 0;
|
|
985
|
-
for (const feedback of this.feedbackHistory) {
|
|
986
|
-
switch (feedback.feedback) {
|
|
987
|
-
case "helpful":
|
|
988
|
-
stats.helpful++;
|
|
989
|
-
break;
|
|
990
|
-
case "dismissed":
|
|
991
|
-
stats.dismissed++;
|
|
992
|
-
break;
|
|
993
|
-
case "acted":
|
|
994
|
-
stats.acted++;
|
|
995
|
-
break;
|
|
996
|
-
case "ignored":
|
|
997
|
-
stats.ignored++;
|
|
998
|
-
break;
|
|
999
|
-
}
|
|
1000
|
-
if (feedback.latencyMs !== void 0) {
|
|
1001
|
-
totalLatency += feedback.latencyMs;
|
|
1002
|
-
latencyCount++;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
if (latencyCount > 0) {
|
|
1006
|
-
stats.averageLatencyMs = Math.round(totalLatency / latencyCount);
|
|
1007
|
-
}
|
|
1008
|
-
return stats;
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Reset learning (for testing)
|
|
1012
|
-
*/
|
|
1013
|
-
reset() {
|
|
1014
|
-
this.feedbackHistory = [];
|
|
1015
|
-
this.weights = {
|
|
1016
|
-
security: 1,
|
|
1017
|
-
quality: 1,
|
|
1018
|
-
performance: 1,
|
|
1019
|
-
pattern: 1,
|
|
1020
|
-
progress: 1,
|
|
1021
|
-
general: 1
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
var metaLearners = /* @__PURE__ */ new Map();
|
|
1026
|
-
function getMetaLearner(projectPath) {
|
|
1027
|
-
let learner = metaLearners.get(projectPath);
|
|
1028
|
-
if (!learner) {
|
|
1029
|
-
learner = new MetaLearner(projectPath);
|
|
1030
|
-
metaLearners.set(projectPath, learner);
|
|
1031
|
-
}
|
|
1032
|
-
return learner;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// src/agent/trie-agent.ts
|
|
1036
|
-
var PERSONALITY = {
|
|
1037
|
-
greetings: [
|
|
1038
|
-
"Hey, quick heads up...",
|
|
1039
|
-
"Noticed something while scanning...",
|
|
1040
|
-
"Worth mentioning...",
|
|
1041
|
-
"Just so you know...",
|
|
1042
|
-
"Spotted this..."
|
|
1043
|
-
],
|
|
1044
|
-
warnings: [
|
|
1045
|
-
"This needs your attention:",
|
|
1046
|
-
"I'd pause on this:",
|
|
1047
|
-
"Before you push...",
|
|
1048
|
-
"Hold up \u2014",
|
|
1049
|
-
"Important:"
|
|
1050
|
-
],
|
|
1051
|
-
celebrations: [
|
|
1052
|
-
"Nice work!",
|
|
1053
|
-
"Good catch!",
|
|
1054
|
-
"That's better.",
|
|
1055
|
-
"Clean.",
|
|
1056
|
-
"Progress."
|
|
1057
|
-
],
|
|
1058
|
-
suggestions: [
|
|
1059
|
-
"You might want to",
|
|
1060
|
-
"Consider",
|
|
1061
|
-
"Suggestion:",
|
|
1062
|
-
"Quick win:",
|
|
1063
|
-
"Idea:"
|
|
1064
|
-
],
|
|
1065
|
-
questions: [
|
|
1066
|
-
"Did you mean to",
|
|
1067
|
-
"Should this",
|
|
1068
|
-
"Is it intentional that",
|
|
1069
|
-
"Quick question:"
|
|
1070
|
-
]
|
|
1071
|
-
};
|
|
1072
|
-
var TrieAgent = class {
|
|
1073
|
-
projectPath;
|
|
1074
|
-
projectName;
|
|
1075
|
-
lastIssueHashes = /* @__PURE__ */ new Set();
|
|
1076
|
-
initialized = false;
|
|
1077
|
-
// Persistent stores
|
|
1078
|
-
insightStore;
|
|
1079
|
-
projectState;
|
|
1080
|
-
// Agency modules
|
|
1081
|
-
goalManager;
|
|
1082
|
-
riskPredictor;
|
|
1083
|
-
hypothesisEngine;
|
|
1084
|
-
escalationManager;
|
|
1085
|
-
metaLearner;
|
|
1086
|
-
gotchaPredictor;
|
|
1087
|
-
constructor(projectPath) {
|
|
1088
|
-
this.projectPath = projectPath;
|
|
1089
|
-
this.projectName = basename2(projectPath);
|
|
1090
|
-
this.insightStore = getInsightStore(projectPath);
|
|
1091
|
-
this.projectState = getProjectState(projectPath);
|
|
1092
|
-
this.goalManager = getGoalManager(projectPath);
|
|
1093
|
-
this.riskPredictor = getRiskPredictor(projectPath);
|
|
1094
|
-
this.hypothesisEngine = getHypothesisEngine(projectPath);
|
|
1095
|
-
this.escalationManager = getEscalationManager(projectPath);
|
|
1096
|
-
this.metaLearner = getMetaLearner(projectPath);
|
|
1097
|
-
this.gotchaPredictor = new GotchaPredictor(projectPath, new ContextGraph(projectPath));
|
|
1098
|
-
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Initialize the agent - loads persistent state and historical data
|
|
1101
|
-
*/
|
|
1102
|
-
async initialize() {
|
|
1103
|
-
if (this.initialized) return;
|
|
1104
|
-
try {
|
|
1105
|
-
await this.insightStore.load();
|
|
1106
|
-
await this.projectState.load();
|
|
1107
|
-
await this.projectState.touchActive();
|
|
1108
|
-
const historical = await getHistoricalInsights(this.projectPath);
|
|
1109
|
-
if (historical && historical.totalHistoricalIssues > 0) {
|
|
1110
|
-
const trend = historical.improvementTrend;
|
|
1111
|
-
if (trend === "improving" && this.canCreateInsight("progress-trend")) {
|
|
1112
|
-
await this.addInsight(this.createInsight({
|
|
1113
|
-
type: "celebration",
|
|
1114
|
-
message: `${this.pick(PERSONALITY.celebrations)} Your code quality has been improving over time.`,
|
|
1115
|
-
priority: 2,
|
|
1116
|
-
category: "progress"
|
|
1117
|
-
}));
|
|
1118
|
-
await this.markInsightCreated("progress-trend");
|
|
1119
|
-
} else if (trend === "declining" && this.canCreateInsight("progress-trend")) {
|
|
1120
|
-
await this.addInsight(this.createInsight({
|
|
1121
|
-
type: "observation",
|
|
1122
|
-
message: `${this.pick(PERSONALITY.greetings)} Issue count has been trending up. Might be worth a focused cleanup session.`,
|
|
1123
|
-
suggestedAction: "Run `trie watch` to monitor and address issues",
|
|
1124
|
-
priority: 5,
|
|
1125
|
-
category: "quality"
|
|
1126
|
-
}));
|
|
1127
|
-
await this.markInsightCreated("progress-trend");
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
this.initialized = true;
|
|
1131
|
-
} catch {
|
|
1132
|
-
this.initialized = true;
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Add an insight to persistent storage
|
|
1137
|
-
*/
|
|
1138
|
-
async addInsight(insight) {
|
|
1139
|
-
return this.insightStore.addInsight(insight);
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Restart the agent (reload state from disk)
|
|
1143
|
-
*/
|
|
1144
|
-
async restart() {
|
|
1145
|
-
this.initialized = false;
|
|
1146
|
-
await this.insightStore.reload();
|
|
1147
|
-
await this.projectState.reload();
|
|
1148
|
-
await this.initialize();
|
|
1149
|
-
}
|
|
1150
|
-
/**
|
|
1151
|
-
* Load state from disk (for testing)
|
|
1152
|
-
*/
|
|
1153
|
-
async loadState() {
|
|
1154
|
-
await this.projectState.load();
|
|
1155
|
-
return {
|
|
1156
|
-
goals: this.projectState.getAllGoals(),
|
|
1157
|
-
riskBudget: this.projectState.getRiskBudget()
|
|
1158
|
-
};
|
|
1159
|
-
}
|
|
1160
|
-
/**
|
|
1161
|
-
* Process new issues from a scan and generate insights
|
|
1162
|
-
* Stores issues in memory and optionally uses LLM for deeper analysis
|
|
1163
|
-
*/
|
|
1164
|
-
async processIssues(issues, context) {
|
|
1165
|
-
const newInsights = [];
|
|
1166
|
-
try {
|
|
1167
|
-
await storeIssues(issues, this.projectName, this.projectPath);
|
|
1168
|
-
await recordToGlobalMemory(issues, this.projectName, this.projectPath);
|
|
1169
|
-
if (context.filesChanged && context.filesChanged.length > 0) {
|
|
1170
|
-
const currentHashes2 = new Set(issues.map((i) => getIssueHash(i)));
|
|
1171
|
-
await autoResolveIssues(currentHashes2, context.filesChanged, this.projectPath);
|
|
1172
|
-
}
|
|
1173
|
-
} catch {
|
|
1174
|
-
}
|
|
1175
|
-
const currentHashes = new Set(issues.map((i) => `${i.file}:${i.line}:${i.issue.slice(0, 50)}`));
|
|
1176
|
-
const newIssues = issues.filter((i) => !this.lastIssueHashes.has(`${i.file}:${i.line}:${i.issue.slice(0, 50)}`));
|
|
1177
|
-
const fixedCount = [...this.lastIssueHashes].filter((h) => !currentHashes.has(h)).length;
|
|
1178
|
-
const accessibilityIssues = issues.filter(
|
|
1179
|
-
(i) => i.agent === "accessibility" && (i.severity === "critical" || i.severity === "serious")
|
|
1180
|
-
);
|
|
1181
|
-
if (accessibilityIssues.length >= 2 && this.canCreateInsight("accessibility-visual-qa")) {
|
|
1182
|
-
const critical = accessibilityIssues.filter((i) => i.severity === "critical");
|
|
1183
|
-
const serious = accessibilityIssues.filter((i) => i.severity === "serious");
|
|
1184
|
-
const breakdown = {};
|
|
1185
|
-
if (critical.length > 0) breakdown["critical"] = critical.length;
|
|
1186
|
-
if (serious.length > 0) breakdown["serious"] = serious.length;
|
|
1187
|
-
const affectedFiles = [...new Set(accessibilityIssues.map((i) => i.file))];
|
|
1188
|
-
const examples = [...critical, ...serious].slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 60)}`);
|
|
1189
|
-
const insight = this.createInsight({
|
|
1190
|
-
type: "suggestion",
|
|
1191
|
-
message: `Found ${accessibilityIssues.length} accessibility issue${accessibilityIssues.length > 1 ? "s" : ""} that could block users. Screenshots would help validate real impact.`,
|
|
1192
|
-
suggestedAction: "Capture screenshots for visual analysis",
|
|
1193
|
-
actionCommand: 'trie_visual_qa_browser url:"http://localhost:3000"',
|
|
1194
|
-
priority: 7,
|
|
1195
|
-
category: "quality",
|
|
1196
|
-
relatedIssues: accessibilityIssues.map((i) => i.id),
|
|
1197
|
-
details: {
|
|
1198
|
-
affectedFiles: affectedFiles.map((f) => basename2(f)),
|
|
1199
|
-
issueBreakdown: breakdown,
|
|
1200
|
-
examples
|
|
1201
|
-
}
|
|
1202
|
-
});
|
|
1203
|
-
newInsights.push(insight);
|
|
1204
|
-
await this.addInsight(insight);
|
|
1205
|
-
await this.markInsightCreated("accessibility-visual-qa");
|
|
1206
|
-
}
|
|
1207
|
-
const securityIssues = issues.filter(
|
|
1208
|
-
(i) => i.category === "security" || i.issue.toLowerCase().includes("secret") || i.issue.toLowerCase().includes("vulnerability") || i.issue.toLowerCase().includes("injection") || i.issue.toLowerCase().includes("xss")
|
|
1209
|
-
);
|
|
1210
|
-
if (securityIssues.length > 0 && this.canCreateInsight("security-warning")) {
|
|
1211
|
-
const critical = securityIssues.filter((i) => i.severity === "critical");
|
|
1212
|
-
const serious = securityIssues.filter((i) => i.severity === "serious");
|
|
1213
|
-
if (critical.length > 0 || serious.length >= 2) {
|
|
1214
|
-
const breakdown = {};
|
|
1215
|
-
if (critical.length > 0) breakdown["critical"] = critical.length;
|
|
1216
|
-
if (serious.length > 0) breakdown["serious"] = serious.length;
|
|
1217
|
-
const affectedFiles = [...new Set(securityIssues.map((i) => i.file))];
|
|
1218
|
-
const examples = [...critical, ...serious].slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 60)}`);
|
|
1219
|
-
const insight = this.createInsight({
|
|
1220
|
-
type: "warning",
|
|
1221
|
-
message: `Found ${securityIssues.length} security issue${securityIssues.length > 1 ? "s" : ""} that could expose your app.`,
|
|
1222
|
-
suggestedAction: `Review ${affectedFiles.length} file${affectedFiles.length > 1 ? "s" : ""} immediately`,
|
|
1223
|
-
actionCommand: "trie check",
|
|
1224
|
-
priority: 10,
|
|
1225
|
-
category: "security",
|
|
1226
|
-
relatedIssues: securityIssues.map((i) => i.id),
|
|
1227
|
-
details: {
|
|
1228
|
-
affectedFiles: affectedFiles.map((f) => basename2(f)),
|
|
1229
|
-
issueBreakdown: breakdown,
|
|
1230
|
-
examples
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
newInsights.push(insight);
|
|
1234
|
-
await this.addInsight(insight);
|
|
1235
|
-
await this.markInsightCreated("security-warning");
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
if (newIssues.length >= 3 && context.filesChanged && context.filesChanged.length > 0 && this.canCreateInsight("new-issues")) {
|
|
1239
|
-
const affectedFiles = [...new Set(newIssues.map((i) => basename2(i.file)))];
|
|
1240
|
-
const filesChanged = context.filesChanged.map((f) => basename2(f));
|
|
1241
|
-
const breakdown = {};
|
|
1242
|
-
const critical = newIssues.filter((i) => i.severity === "critical");
|
|
1243
|
-
const serious = newIssues.filter((i) => i.severity === "serious");
|
|
1244
|
-
const moderate = newIssues.filter((i) => i.severity === "moderate");
|
|
1245
|
-
if (critical.length > 0) breakdown["critical"] = critical.length;
|
|
1246
|
-
if (serious.length > 0) breakdown["serious"] = serious.length;
|
|
1247
|
-
if (moderate.length > 0) breakdown["moderate"] = moderate.length;
|
|
1248
|
-
const examples = newIssues.sort((a, b) => {
|
|
1249
|
-
const sev = { critical: 3, serious: 2, moderate: 1, minor: 0, low: 0 };
|
|
1250
|
-
return (sev[b.severity] || 0) - (sev[a.severity] || 0);
|
|
1251
|
-
}).slice(0, 3).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 50)}`);
|
|
1252
|
-
const insight = this.createInsight({
|
|
1253
|
-
type: "observation",
|
|
1254
|
-
message: `Recent changes introduced ${newIssues.length} new issues in ${affectedFiles.slice(0, 3).join(", ")}.`,
|
|
1255
|
-
suggestedAction: `Review ${affectedFiles.length} affected file${affectedFiles.length > 1 ? "s" : ""}`,
|
|
1256
|
-
priority: 6,
|
|
1257
|
-
category: "quality",
|
|
1258
|
-
details: {
|
|
1259
|
-
affectedFiles: affectedFiles.slice(0, 8),
|
|
1260
|
-
issueBreakdown: breakdown,
|
|
1261
|
-
examples,
|
|
1262
|
-
comparison: `Changed: ${filesChanged.slice(0, 3).join(", ")}${filesChanged.length > 3 ? ` +${filesChanged.length - 3} more` : ""}`
|
|
1263
|
-
}
|
|
1264
|
-
});
|
|
1265
|
-
newInsights.push(insight);
|
|
1266
|
-
await this.addInsight(insight);
|
|
1267
|
-
await this.markInsightCreated("new-issues");
|
|
1268
|
-
}
|
|
1269
|
-
if (fixedCount > 0 && newIssues.length === 0 && this.canCreateInsight("celebration")) {
|
|
1270
|
-
const insight = this.createInsight({
|
|
1271
|
-
type: "celebration",
|
|
1272
|
-
message: `${this.pick(PERSONALITY.celebrations)} ${fixedCount} issue${fixedCount > 1 ? "s" : ""} resolved.`,
|
|
1273
|
-
priority: 3,
|
|
1274
|
-
category: "progress"
|
|
1275
|
-
});
|
|
1276
|
-
newInsights.push(insight);
|
|
1277
|
-
await this.addInsight(insight);
|
|
1278
|
-
await this.markInsightCreated("celebration");
|
|
1279
|
-
}
|
|
1280
|
-
if (issues.length >= 10 && this.canCreateInsight("hypothesis-generation")) {
|
|
1281
|
-
try {
|
|
1282
|
-
const patterns = [];
|
|
1283
|
-
const observations = [];
|
|
1284
|
-
if (newIssues.length > issues.length * 0.3) {
|
|
1285
|
-
observations.push(`High new issue rate: ${newIssues.length} new out of ${issues.length} total`);
|
|
1286
|
-
}
|
|
1287
|
-
const fileFreq = {};
|
|
1288
|
-
for (const issue of issues) {
|
|
1289
|
-
fileFreq[issue.file] = (fileFreq[issue.file] || 0) + 1;
|
|
1290
|
-
}
|
|
1291
|
-
const hotFiles = Object.entries(fileFreq).filter(([, count]) => count >= 3);
|
|
1292
|
-
if (hotFiles.length > 0) {
|
|
1293
|
-
patterns.push(`Hotspot files: ${hotFiles.map(([f]) => basename2(f)).join(", ")}`);
|
|
1294
|
-
}
|
|
1295
|
-
const generated = await this.hypothesisEngine.generateHypothesesWithAI({
|
|
1296
|
-
recentIssues: issues.map((i) => ({ issue: i, score: 1 })),
|
|
1297
|
-
patterns,
|
|
1298
|
-
observations
|
|
1299
|
-
});
|
|
1300
|
-
for (const hypothesis of generated) {
|
|
1301
|
-
const insight = this.createInsight({
|
|
1302
|
-
type: "observation",
|
|
1303
|
-
message: `${this.pick(PERSONALITY.questions)} New hypothesis to test: "${hypothesis.statement}"`,
|
|
1304
|
-
context: `Confidence: ${Math.round(hypothesis.confidence * 100)}%`,
|
|
1305
|
-
suggestedAction: `Monitor this pattern over time`,
|
|
1306
|
-
priority: 4,
|
|
1307
|
-
category: "pattern",
|
|
1308
|
-
details: {
|
|
1309
|
-
hypothesis: hypothesis.statement,
|
|
1310
|
-
testCriteria: hypothesis.testCriteria || "Collecting evidence...",
|
|
1311
|
-
confidence: Math.round(hypothesis.confidence * 100)
|
|
1312
|
-
}
|
|
1313
|
-
});
|
|
1314
|
-
newInsights.push(insight);
|
|
1315
|
-
await this.addInsight(insight);
|
|
1316
|
-
}
|
|
1317
|
-
if (generated.length > 0) {
|
|
1318
|
-
await this.markInsightCreated("hypothesis-generation");
|
|
1319
|
-
}
|
|
1320
|
-
} catch (error) {
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
if (issues.length >= 5 && this.canCreateInsight("pattern-discovery")) {
|
|
1324
|
-
try {
|
|
1325
|
-
const { IncidentIndex } = await import("./incident-index-EFNUSGWL.js");
|
|
1326
|
-
const { TriePatternDiscovery } = await import("./pattern-discovery-F7LU5K6E.js");
|
|
1327
|
-
const { ContextGraph: ContextGraph2 } = await import("./graph-B3NA4S7I.js");
|
|
1328
|
-
const graph = new ContextGraph2(this.projectPath);
|
|
1329
|
-
const incidentIndex = await IncidentIndex.build(graph, this.projectPath);
|
|
1330
|
-
const discovery = new TriePatternDiscovery(graph, incidentIndex);
|
|
1331
|
-
const hotPatterns = discovery.discoverHotPatterns(2);
|
|
1332
|
-
let patternsAdded = 0;
|
|
1333
|
-
for (const hot of hotPatterns.slice(0, 5)) {
|
|
1334
|
-
const existingPatterns = await graph.listNodes();
|
|
1335
|
-
const alreadyExists = existingPatterns.some(
|
|
1336
|
-
(n) => n.type === "pattern" && n.data.description?.includes(hot.path)
|
|
1337
|
-
);
|
|
1338
|
-
if (!alreadyExists) {
|
|
1339
|
-
await graph.addNode("pattern", {
|
|
1340
|
-
description: `${hot.type === "directory" ? "Directory" : "File"} hot zone: ${hot.path}`,
|
|
1341
|
-
appliesTo: [hot.path],
|
|
1342
|
-
confidence: Math.min(0.95, hot.confidence),
|
|
1343
|
-
occurrences: hot.incidentCount,
|
|
1344
|
-
firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1345
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1346
|
-
isAntiPattern: hot.incidentCount >= 3,
|
|
1347
|
-
source: "local"
|
|
1348
|
-
});
|
|
1349
|
-
patternsAdded++;
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
if (patternsAdded > 0) {
|
|
1353
|
-
const insight = this.createInsight({
|
|
1354
|
-
type: "observation",
|
|
1355
|
-
message: `${this.pick(PERSONALITY.greetings)} Discovered ${patternsAdded} pattern${patternsAdded > 1 ? "s" : ""} in your codebase.`,
|
|
1356
|
-
suggestedAction: `Check Memory \u2192 Learned Patterns to see`,
|
|
1357
|
-
priority: 5,
|
|
1358
|
-
category: "pattern",
|
|
1359
|
-
details: {
|
|
1360
|
-
patternsCount: patternsAdded,
|
|
1361
|
-
topPattern: hotPatterns[0]?.path
|
|
1362
|
-
}
|
|
1363
|
-
});
|
|
1364
|
-
newInsights.push(insight);
|
|
1365
|
-
await this.addInsight(insight);
|
|
1366
|
-
await this.markInsightCreated("pattern-discovery");
|
|
1367
|
-
}
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
if (this.canCreateInsight("pattern-suggestion")) {
|
|
1372
|
-
try {
|
|
1373
|
-
const globalPatterns = await findCrossProjectPatterns();
|
|
1374
|
-
const crossProjectPattern = globalPatterns.find((p) => p.projects.length > 1 && p.occurrences > 3);
|
|
1375
|
-
if (crossProjectPattern) {
|
|
1376
|
-
const insight = this.createInsight({
|
|
1377
|
-
type: "suggestion",
|
|
1378
|
-
message: `"${crossProjectPattern.description}" appears in ${crossProjectPattern.projects.length} projects. Consider a shared lint rule.`,
|
|
1379
|
-
priority: 4,
|
|
1380
|
-
category: "pattern"
|
|
1381
|
-
});
|
|
1382
|
-
newInsights.push(insight);
|
|
1383
|
-
await this.addInsight(insight);
|
|
1384
|
-
await this.markInsightCreated("pattern-suggestion");
|
|
1385
|
-
}
|
|
1386
|
-
} catch {
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
if (context.isWatchMode && this.shouldWarnBeforePush(issues) && this.canCreateInsight("pre-push-warning")) {
|
|
1390
|
-
const criticalIssues = issues.filter((i) => i.severity === "critical");
|
|
1391
|
-
const seriousIssues = issues.filter((i) => i.severity === "serious");
|
|
1392
|
-
const moderateIssues = issues.filter((i) => i.severity === "moderate");
|
|
1393
|
-
const breakdown = {};
|
|
1394
|
-
if (criticalIssues.length > 0) breakdown["critical"] = criticalIssues.length;
|
|
1395
|
-
if (seriousIssues.length > 0) breakdown["serious"] = seriousIssues.length;
|
|
1396
|
-
if (moderateIssues.length > 0) breakdown["moderate"] = moderateIssues.length;
|
|
1397
|
-
const criticalFiles = [...new Set(criticalIssues.map((i) => basename2(i.file)))];
|
|
1398
|
-
const seriousFiles = [...new Set(seriousIssues.map((i) => basename2(i.file)))];
|
|
1399
|
-
const affectedFiles = [.../* @__PURE__ */ new Set([...criticalFiles, ...seriousFiles])].slice(0, 5);
|
|
1400
|
-
const examples = [...criticalIssues, ...seriousIssues].slice(0, 4).map((i) => `${basename2(i.file)}:${i.line} - ${i.issue.slice(0, 50)}`);
|
|
1401
|
-
const totalBlocking = criticalIssues.length + seriousIssues.length;
|
|
1402
|
-
const trend = this.lastIssueHashes.size > 0 ? totalBlocking > this.lastIssueHashes.size ? "worsening" : "improving" : void 0;
|
|
1403
|
-
const insightDetails = {
|
|
1404
|
-
affectedFiles,
|
|
1405
|
-
issueBreakdown: breakdown,
|
|
1406
|
-
examples
|
|
1407
|
-
};
|
|
1408
|
-
if (trend) insightDetails.trend = trend;
|
|
1409
|
-
if (moderateIssues.length > 0) insightDetails.comparison = `+${moderateIssues.length} moderate (non-blocking)`;
|
|
1410
|
-
const insight = this.createInsight({
|
|
1411
|
-
type: "warning",
|
|
1412
|
-
message: `${totalBlocking} issue${totalBlocking > 1 ? "s" : ""} need attention before pushing.`,
|
|
1413
|
-
suggestedAction: "Run pre-push check",
|
|
1414
|
-
actionCommand: "trie check",
|
|
1415
|
-
priority: 8,
|
|
1416
|
-
category: "quality",
|
|
1417
|
-
details: insightDetails
|
|
1418
|
-
});
|
|
1419
|
-
newInsights.push(insight);
|
|
1420
|
-
await this.addInsight(insight);
|
|
1421
|
-
await this.markInsightCreated("pre-push-warning");
|
|
1422
|
-
}
|
|
1423
|
-
if (isAIAvailable() && issues.length > 0 && newIssues.length > 0) {
|
|
1424
|
-
const aiInsights = await this.generateAIInsights(issues, newIssues);
|
|
1425
|
-
newInsights.push(...aiInsights);
|
|
1426
|
-
}
|
|
1427
|
-
if (this.canCreateInsight("goal-progress")) {
|
|
1428
|
-
try {
|
|
1429
|
-
const patterns = await this.goalManager.analyzeIncidentPatterns();
|
|
1430
|
-
const significantPatterns = patterns.filter((p) => p.confidence >= 0.6 && p.currentValue >= 3);
|
|
1431
|
-
if (significantPatterns.length > 0) {
|
|
1432
|
-
await this.goalManager.autoGenerateGoals();
|
|
1433
|
-
}
|
|
1434
|
-
await this.goalManager.updateGoalProgress();
|
|
1435
|
-
const goals = this.projectState.getAllGoals();
|
|
1436
|
-
for (const goal of goals.filter((g) => g.status === "active")) {
|
|
1437
|
-
const startVal = goal.startValue ?? goal.currentValue;
|
|
1438
|
-
if (startVal > 0 && goal.currentValue <= startVal) {
|
|
1439
|
-
const percentComplete = Math.round((1 - goal.currentValue / startVal) * 100);
|
|
1440
|
-
if (percentComplete >= 50 && this.canCreateInsight(`goal-${goal.id}`)) {
|
|
1441
|
-
const insight = this.createInsight({
|
|
1442
|
-
type: "celebration",
|
|
1443
|
-
message: `Goal "${goal.description}" is ${percentComplete}% complete! ${goal.currentValue} issues remaining (started at ${startVal}).`,
|
|
1444
|
-
priority: 4,
|
|
1445
|
-
category: "progress",
|
|
1446
|
-
details: {
|
|
1447
|
-
affectedFiles: goal.category ? [goal.category] : [],
|
|
1448
|
-
comparison: `${startVal} \u2192 ${goal.currentValue}`
|
|
1449
|
-
}
|
|
1450
|
-
});
|
|
1451
|
-
newInsights.push(insight);
|
|
1452
|
-
await this.addInsight(insight);
|
|
1453
|
-
await this.markInsightCreated(`goal-${goal.id}`);
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
await this.markInsightCreated("goal-progress");
|
|
1458
|
-
} catch {
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
if (context.filesChanged && context.filesChanged.length > 0 && this.canCreateInsight("risk-prediction")) {
|
|
1462
|
-
try {
|
|
1463
|
-
const riskSummary = await this.riskPredictor.predictRiskTrend(context.filesChanged);
|
|
1464
|
-
if (riskSummary.overallRisk >= 50) {
|
|
1465
|
-
const highRiskFactors = riskSummary.factors.filter((f) => f.reasons.length > 0);
|
|
1466
|
-
if (highRiskFactors.length > 0) {
|
|
1467
|
-
const topFile = highRiskFactors[0];
|
|
1468
|
-
const insight = this.createInsight({
|
|
1469
|
-
type: "warning",
|
|
1470
|
-
message: `Risk score ${Math.round(riskSummary.overallRisk)}/100 for changed files (${riskSummary.trend}). ${topFile ? basename2(topFile.file) + " is high-risk." : ""}`,
|
|
1471
|
-
suggestedAction: "Consider extra review before pushing",
|
|
1472
|
-
priority: 7,
|
|
1473
|
-
category: "quality",
|
|
1474
|
-
details: {
|
|
1475
|
-
affectedFiles: highRiskFactors.slice(0, 5).map((f) => basename2(f.file)),
|
|
1476
|
-
trend: riskSummary.trend === "increasing" ? "worsening" : riskSummary.trend === "decreasing" ? "improving" : "stable"
|
|
1477
|
-
}
|
|
1478
|
-
});
|
|
1479
|
-
newInsights.push(insight);
|
|
1480
|
-
await this.addInsight(insight);
|
|
1481
|
-
await this.markInsightCreated("risk-prediction");
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
} catch {
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
if (context.filesChanged && context.filesChanged.length > 0) {
|
|
1488
|
-
try {
|
|
1489
|
-
const gotchas = await this.gotchaPredictor.predictGotchas(context.filesChanged);
|
|
1490
|
-
const storage = getStorage(this.projectPath);
|
|
1491
|
-
for (const gotcha of gotchas) {
|
|
1492
|
-
if (this.canCreateInsight(`gotcha-${gotcha.id}`)) {
|
|
1493
|
-
const explanation = await this.gotchaPredictor.synthesizeGotchaExplanation(gotcha);
|
|
1494
|
-
const insight = this.createInsight({
|
|
1495
|
-
type: "warning",
|
|
1496
|
-
message: `[Gotcha] ${gotcha.message}`,
|
|
1497
|
-
context: explanation,
|
|
1498
|
-
suggestedAction: gotcha.recommendation,
|
|
1499
|
-
priority: gotcha.riskLevel === "critical" ? 9 : 7,
|
|
1500
|
-
category: "quality",
|
|
1501
|
-
details: {
|
|
1502
|
-
affectedFiles: context.filesChanged.map((f) => basename2(f)),
|
|
1503
|
-
examples: [gotcha.message, explanation]
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
newInsights.push(insight);
|
|
1507
|
-
await this.addInsight(insight);
|
|
1508
|
-
await this.markInsightCreated(`gotcha-${gotcha.id}`);
|
|
1509
|
-
}
|
|
1510
|
-
const primaryFile = context.filesChanged?.[0];
|
|
1511
|
-
const ledgerGotcha = {
|
|
1512
|
-
id: gotcha.id,
|
|
1513
|
-
message: gotcha.message,
|
|
1514
|
-
confidence: gotcha.confidence,
|
|
1515
|
-
riskLevel: gotcha.riskLevel,
|
|
1516
|
-
recommendation: gotcha.recommendation,
|
|
1517
|
-
...primaryFile && { file: primaryFile },
|
|
1518
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1519
|
-
...gotcha.precedentId && { precedentId: gotcha.precedentId },
|
|
1520
|
-
tags: gotcha.evidence.matchingPatterns,
|
|
1521
|
-
evidence: gotcha.evidence,
|
|
1522
|
-
resolved: false
|
|
1523
|
-
};
|
|
1524
|
-
await storage.storeGotcha(ledgerGotcha);
|
|
1525
|
-
}
|
|
1526
|
-
} catch {
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
if (this.canCreateInsight("hypothesis-update")) {
|
|
1530
|
-
try {
|
|
1531
|
-
const hypotheses = this.projectState.getAllHypotheses();
|
|
1532
|
-
const testingHypotheses = hypotheses.filter((h) => h.status === "testing");
|
|
1533
|
-
if (testingHypotheses.length < 3) {
|
|
1534
|
-
await this.hypothesisEngine.autoGenerateHypotheses();
|
|
1535
|
-
}
|
|
1536
|
-
const validatedHypotheses = hypotheses.filter(
|
|
1537
|
-
(h) => h.status === "validated" && h.validatedAt && Date.now() - new Date(h.validatedAt).getTime() < 24 * 60 * 60 * 1e3
|
|
1538
|
-
// Within last 24h
|
|
1539
|
-
);
|
|
1540
|
-
for (const hypothesis of validatedHypotheses.slice(0, 2)) {
|
|
1541
|
-
if (this.canCreateInsight(`hypothesis-${hypothesis.id}`)) {
|
|
1542
|
-
const insight = this.createInsight({
|
|
1543
|
-
type: "celebration",
|
|
1544
|
-
message: `Hypothesis validated: "${hypothesis.statement}" (${Math.round(hypothesis.confidence * 100)}% confidence)`,
|
|
1545
|
-
priority: 5,
|
|
1546
|
-
category: "pattern"
|
|
1547
|
-
});
|
|
1548
|
-
newInsights.push(insight);
|
|
1549
|
-
await this.addInsight(insight);
|
|
1550
|
-
await this.markInsightCreated(`hypothesis-${hypothesis.id}`);
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
await this.markInsightCreated("hypothesis-update");
|
|
1554
|
-
} catch {
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
const criticalSecurityIssues = issues.filter(
|
|
1558
|
-
(i) => i.severity === "critical" && (i.category === "security" || i.issue.toLowerCase().includes("secret"))
|
|
1559
|
-
);
|
|
1560
|
-
if (criticalSecurityIssues.length > 0) {
|
|
1561
|
-
try {
|
|
1562
|
-
const result = await this.escalationManager.escalateIssues(criticalSecurityIssues);
|
|
1563
|
-
if (result.action === "auto_escalated" && result.message) {
|
|
1564
|
-
const insight = this.createInsight({
|
|
1565
|
-
type: "observation",
|
|
1566
|
-
message: `Auto-escalated ${criticalSecurityIssues.length} critical security issue(s). Check your notifications.`,
|
|
1567
|
-
priority: 9,
|
|
1568
|
-
category: "security",
|
|
1569
|
-
details: {
|
|
1570
|
-
affectedFiles: criticalSecurityIssues.slice(0, 5).map((i) => basename2(i.file)),
|
|
1571
|
-
examples: criticalSecurityIssues.slice(0, 3).map((i) => i.issue.slice(0, 60))
|
|
1572
|
-
}
|
|
1573
|
-
});
|
|
1574
|
-
newInsights.push(insight);
|
|
1575
|
-
await this.addInsight(insight);
|
|
1576
|
-
}
|
|
1577
|
-
} catch {
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
this.lastIssueHashes = currentHashes;
|
|
1581
|
-
await this.projectState.recordScan();
|
|
1582
|
-
try {
|
|
1583
|
-
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";
|
|
1584
|
-
const { calculateAdaptiveScanFrequency } = await import("./goal-manager-QUKX2W6C.js");
|
|
1585
|
-
const result = await calculateAdaptiveScanFrequency(riskLevel);
|
|
1586
|
-
await this.projectState.setScanFrequency(result.frequencyMs);
|
|
1587
|
-
} catch {
|
|
1588
|
-
}
|
|
1589
|
-
try {
|
|
1590
|
-
const { getStorage: getStorage2 } = await import("./tiered-storage-DYNC5CQ6.js");
|
|
1591
|
-
const storage = getStorage2(this.projectPath);
|
|
1592
|
-
const resolvedIssueIds = [...this.lastIssueHashes].filter((h) => !currentHashes.has(h)).map((h) => {
|
|
1593
|
-
return h.split(":").join("-");
|
|
1594
|
-
});
|
|
1595
|
-
if (resolvedIssueIds.length > 0) {
|
|
1596
|
-
const resolvedCount = await storage.autoResolveFixedNudges(resolvedIssueIds);
|
|
1597
|
-
if (resolvedCount > 0) {
|
|
1598
|
-
console.debug(`[Agent] Auto-resolved ${resolvedCount} nudge${resolvedCount !== 1 ? "s" : ""} for fixed issues`);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
} catch {
|
|
1602
|
-
}
|
|
1603
|
-
return newInsights;
|
|
1604
|
-
}
|
|
1605
|
-
/**
|
|
1606
|
-
* Use LLM to generate deeper insights (optional, requires ANTHROPIC_API_KEY)
|
|
1607
|
-
*/
|
|
1608
|
-
async generateAIInsights(_allIssues, newIssues) {
|
|
1609
|
-
const insights = [];
|
|
1610
|
-
try {
|
|
1611
|
-
if (newIssues.length < 2) return insights;
|
|
1612
|
-
const issuesSummary = newIssues.slice(0, 10).map((i) => ({
|
|
1613
|
-
severity: i.severity,
|
|
1614
|
-
issue: i.issue.slice(0, 100),
|
|
1615
|
-
file: basename2(i.file),
|
|
1616
|
-
agent: i.agent
|
|
1617
|
-
}));
|
|
1618
|
-
const result = await runAIAnalysis({
|
|
1619
|
-
systemPrompt: `You are a helpful code reviewer. Analyze these issues and provide ONE brief, conversational insight.
|
|
1620
|
-
Be specific and actionable. Speak like a helpful colleague, not a system.
|
|
1621
|
-
Keep your response under 100 words. Focus on the most important pattern or concern.`,
|
|
1622
|
-
userPrompt: `New issues found:
|
|
1623
|
-
${JSON.stringify(issuesSummary, null, 2)}
|
|
1624
|
-
|
|
1625
|
-
What's the most important thing the developer should know? Provide a brief, conversational insight.`,
|
|
1626
|
-
maxTokens: 200,
|
|
1627
|
-
temperature: 0.7
|
|
1628
|
-
});
|
|
1629
|
-
if (result.success && result.content) {
|
|
1630
|
-
const insight = this.createInsight({
|
|
1631
|
-
type: "observation",
|
|
1632
|
-
message: result.content.trim(),
|
|
1633
|
-
priority: 5,
|
|
1634
|
-
category: "general"
|
|
1635
|
-
});
|
|
1636
|
-
insights.push(insight);
|
|
1637
|
-
await this.addInsight(insight);
|
|
1638
|
-
}
|
|
1639
|
-
} catch {
|
|
1640
|
-
}
|
|
1641
|
-
return insights;
|
|
1642
|
-
}
|
|
1643
|
-
/**
|
|
1644
|
-
* Get active insights (not dismissed, still relevant)
|
|
1645
|
-
*/
|
|
1646
|
-
getActiveInsights() {
|
|
1647
|
-
return this.insightStore.getActiveInsights();
|
|
1648
|
-
}
|
|
1649
|
-
/**
|
|
1650
|
-
* Dismiss an insight
|
|
1651
|
-
*/
|
|
1652
|
-
async dismissInsight(insightId) {
|
|
1653
|
-
const result = await this.insightStore.dismissInsight(insightId);
|
|
1654
|
-
if (result) {
|
|
1655
|
-
await this.projectState.recordInsightFeedback("dismissed");
|
|
1656
|
-
}
|
|
1657
|
-
return result;
|
|
1658
|
-
}
|
|
1659
|
-
/**
|
|
1660
|
-
* Record that a user found an insight helpful
|
|
1661
|
-
*/
|
|
1662
|
-
async markInsightHelpful(_insightId) {
|
|
1663
|
-
await this.projectState.recordInsightFeedback("helpful");
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* Record that a user acted on an insight
|
|
1667
|
-
*/
|
|
1668
|
-
async markInsightActedOn(_insightId) {
|
|
1669
|
-
await this.projectState.recordInsightFeedback("acted");
|
|
1670
|
-
}
|
|
1671
|
-
/**
|
|
1672
|
-
* Get insight statistics
|
|
1673
|
-
*/
|
|
1674
|
-
getInsightStats() {
|
|
1675
|
-
return this.insightStore.getStats();
|
|
1676
|
-
}
|
|
1677
|
-
/**
|
|
1678
|
-
* Get agent metrics (for dashboard display)
|
|
1679
|
-
*/
|
|
1680
|
-
getAgentMetrics() {
|
|
1681
|
-
return this.projectState.getMetrics();
|
|
1682
|
-
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Check if in quiet hours
|
|
1685
|
-
*/
|
|
1686
|
-
isQuietHours() {
|
|
1687
|
-
return this.projectState.isQuietHours();
|
|
1688
|
-
}
|
|
1689
|
-
/**
|
|
1690
|
-
* Check if in crunch mode
|
|
1691
|
-
*/
|
|
1692
|
-
isInCrunchMode() {
|
|
1693
|
-
return this.projectState.isInCrunchMode();
|
|
1694
|
-
}
|
|
1695
|
-
/**
|
|
1696
|
-
* Get the insight store (for advanced operations)
|
|
1697
|
-
*/
|
|
1698
|
-
getInsightStore() {
|
|
1699
|
-
return this.insightStore;
|
|
1700
|
-
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Get the project state (for advanced operations)
|
|
1703
|
-
*/
|
|
1704
|
-
getProjectState() {
|
|
1705
|
-
return this.projectState;
|
|
1706
|
-
}
|
|
1707
|
-
/**
|
|
1708
|
-
* Get agency modules (for watch mode integration)
|
|
1709
|
-
*/
|
|
1710
|
-
getGoalManager() {
|
|
1711
|
-
return this.goalManager;
|
|
1712
|
-
}
|
|
1713
|
-
getRiskPredictor() {
|
|
1714
|
-
return this.riskPredictor;
|
|
1715
|
-
}
|
|
1716
|
-
getHypothesisEngine() {
|
|
1717
|
-
return this.hypothesisEngine;
|
|
1718
|
-
}
|
|
1719
|
-
getEscalationManager() {
|
|
1720
|
-
return this.escalationManager;
|
|
1721
|
-
}
|
|
1722
|
-
getMetaLearner() {
|
|
1723
|
-
return this.metaLearner;
|
|
1724
|
-
}
|
|
1725
|
-
/**
|
|
1726
|
-
* Get a rich agency status for display in watch mode
|
|
1727
|
-
*/
|
|
1728
|
-
async getAgencyStatus() {
|
|
1729
|
-
const goals = this.projectState.getAllGoals();
|
|
1730
|
-
const activeGoals = goals.filter((g) => g.status === "active");
|
|
1731
|
-
const completedGoals = goals.filter((g) => g.status === "achieved");
|
|
1732
|
-
const hypotheses = this.projectState.getAllHypotheses();
|
|
1733
|
-
const testingHypotheses = hypotheses.filter((h) => h.status === "testing");
|
|
1734
|
-
const validatedHypotheses = hypotheses.filter((h) => h.status === "validated");
|
|
1735
|
-
const metrics = this.projectState.getMetrics();
|
|
1736
|
-
const budget = this.projectState.getRiskBudget();
|
|
1737
|
-
const dailyActionsRemaining = budget.daily - budget.usedToday;
|
|
1738
|
-
let riskLevel = "low";
|
|
1739
|
-
if (dailyActionsRemaining <= 1) riskLevel = "critical";
|
|
1740
|
-
else if (dailyActionsRemaining <= 3) riskLevel = "high";
|
|
1741
|
-
else if (dailyActionsRemaining <= 5) riskLevel = "medium";
|
|
1742
|
-
const totalFeedback = metrics.helpfulInsights + metrics.dismissedInsights + metrics.actedOnInsights;
|
|
1743
|
-
const effectiveness = totalFeedback > 0 ? Math.round((metrics.helpfulInsights + metrics.actedOnInsights) / totalFeedback * 100) : 100;
|
|
1744
|
-
const topGoal = activeGoals[0]?.description;
|
|
1745
|
-
const topHypothesis = testingHypotheses[0]?.statement?.slice(0, 50);
|
|
1746
|
-
const result = {
|
|
1747
|
-
goals: {
|
|
1748
|
-
active: activeGoals.length,
|
|
1749
|
-
completed: completedGoals.length
|
|
1750
|
-
},
|
|
1751
|
-
hypotheses: {
|
|
1752
|
-
testing: testingHypotheses.length,
|
|
1753
|
-
validated: validatedHypotheses.length
|
|
1754
|
-
},
|
|
1755
|
-
riskLevel,
|
|
1756
|
-
scanFrequency: this.projectState.getScanFrequencyMs(),
|
|
1757
|
-
effectiveness,
|
|
1758
|
-
isQuietHours: this.projectState.isQuietHours()
|
|
1759
|
-
};
|
|
1760
|
-
if (topGoal) result.goals.topGoal = topGoal;
|
|
1761
|
-
if (topHypothesis) result.hypotheses.topHypothesis = topHypothesis;
|
|
1762
|
-
return result;
|
|
1763
|
-
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Get a summary for the current state
|
|
1766
|
-
*/
|
|
1767
|
-
getSummary(issues) {
|
|
1768
|
-
const critical = issues.filter((i) => i.severity === "critical").length;
|
|
1769
|
-
const serious = issues.filter((i) => i.severity === "serious").length;
|
|
1770
|
-
const total = issues.length;
|
|
1771
|
-
if (total === 0) {
|
|
1772
|
-
return "All clear. Your code looks good.";
|
|
1773
|
-
}
|
|
1774
|
-
if (critical > 0) {
|
|
1775
|
-
return `${critical} critical issue${critical > 1 ? "s" : ""} need${critical === 1 ? "s" : ""} immediate attention.`;
|
|
1776
|
-
}
|
|
1777
|
-
if (serious > 0) {
|
|
1778
|
-
return `${serious} serious issue${serious > 1 ? "s" : ""} worth reviewing before shipping.`;
|
|
1779
|
-
}
|
|
1780
|
-
return `${total} minor thing${total > 1 ? "s" : ""} to polish when you have time.`;
|
|
1781
|
-
}
|
|
1782
|
-
// === Private helpers ===
|
|
1783
|
-
shouldWarnBeforePush(issues) {
|
|
1784
|
-
const critical = issues.filter((i) => i.severity === "critical").length;
|
|
1785
|
-
const serious = issues.filter((i) => i.severity === "serious").length;
|
|
1786
|
-
return critical > 0 || serious >= 3;
|
|
1787
|
-
}
|
|
1788
|
-
/**
|
|
1789
|
-
* Check if we should create an insight of this type (cooldown not expired)
|
|
1790
|
-
*/
|
|
1791
|
-
canCreateInsight(insightKey) {
|
|
1792
|
-
return this.insightStore.canCreateInsight(insightKey);
|
|
1793
|
-
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Mark that we created an insight of this type
|
|
1796
|
-
*/
|
|
1797
|
-
async markInsightCreated(insightKey) {
|
|
1798
|
-
await this.insightStore.markInsightCreated(insightKey);
|
|
1799
|
-
}
|
|
1800
|
-
createInsight(params) {
|
|
1801
|
-
return {
|
|
1802
|
-
id: `insight-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1803
|
-
timestamp: Date.now(),
|
|
1804
|
-
dismissed: false,
|
|
1805
|
-
relatedIssues: params.relatedIssues || [],
|
|
1806
|
-
...params
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
pick(arr) {
|
|
1810
|
-
return arr[Math.floor(Math.random() * arr.length)];
|
|
1811
|
-
}
|
|
1812
|
-
};
|
|
1813
|
-
var agentInstances = /* @__PURE__ */ new Map();
|
|
1814
|
-
function getTrieAgent(projectPath) {
|
|
1815
|
-
let agent = agentInstances.get(projectPath);
|
|
1816
|
-
if (!agent) {
|
|
1817
|
-
agent = new TrieAgent(projectPath);
|
|
1818
|
-
agentInstances.set(projectPath, agent);
|
|
1819
|
-
}
|
|
1820
|
-
return agent;
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
export {
|
|
1824
|
-
TrieAgent,
|
|
1825
|
-
getTrieAgent
|
|
1826
|
-
};
|
|
1827
|
-
//# sourceMappingURL=chunk-7IO4YUI3.js.map
|