@triedotdev/mcp 1.0.61 → 1.0.63
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 +591 -52
- package/dist/agent-smith-W4HUCFGC.js +14 -0
- package/dist/{agent-smith-runner-ZU4R3I2Z.js → agent-smith-runner-QRVOEOBE.js} +13 -7
- package/dist/agent-smith-runner-QRVOEOBE.js.map +1 -0
- package/dist/chunk-4YSLDGBL.js +674 -0
- package/dist/chunk-4YSLDGBL.js.map +1 -0
- package/dist/chunk-7KHT2NKR.js +212 -0
- package/dist/chunk-7KHT2NKR.js.map +1 -0
- package/dist/{chunk-XSPS463E.js → chunk-ALA6733H.js} +492 -14
- package/dist/chunk-ALA6733H.js.map +1 -0
- package/dist/chunk-AQCAMIQQ.js +139 -0
- package/dist/chunk-AQCAMIQQ.js.map +1 -0
- package/dist/chunk-D3DMONAJ.js +904 -0
- package/dist/chunk-D3DMONAJ.js.map +1 -0
- package/dist/{chunk-KB5ZN6K2.js → chunk-GWSNINKX.js} +2 -2
- package/dist/{chunk-32WLOG6E.js → chunk-K6BQBKIR.js} +662 -633
- package/dist/chunk-K6BQBKIR.js.map +1 -0
- package/dist/{chunk-ASGSTVVF.js → chunk-KOFQ47YW.js} +10 -6
- package/dist/chunk-KOFQ47YW.js.map +1 -0
- package/dist/{chunk-XVGHO2Z5.js → chunk-N2AZH3EQ.js} +7683 -4777
- package/dist/chunk-N2AZH3EQ.js.map +1 -0
- package/dist/chunk-PBOVCPKE.js +2566 -0
- package/dist/chunk-PBOVCPKE.js.map +1 -0
- package/dist/{chunk-NUT4G5AY.js → chunk-R7Z7OHTJ.js} +493 -650
- package/dist/chunk-R7Z7OHTJ.js.map +1 -0
- package/dist/chunk-TSHZQKCM.js +933 -0
- package/dist/chunk-TSHZQKCM.js.map +1 -0
- package/dist/{chunk-S4VGGLXF.js → chunk-X2PABPBH.js} +461 -892
- package/dist/chunk-X2PABPBH.js.map +1 -0
- package/dist/cli/create-agent.js +3 -2
- package/dist/cli/create-agent.js.map +1 -1
- package/dist/cli/main.js +1120 -70
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +151 -41
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/goal-manager-KFBOAP4X.js +20 -0
- package/dist/goal-manager-KFBOAP4X.js.map +1 -0
- package/dist/guardian-agent-PULK546O.js +17 -0
- package/dist/guardian-agent-PULK546O.js.map +1 -0
- package/dist/index.js +173 -39
- package/dist/index.js.map +1 -1
- package/dist/issue-store-QRDF3X55.js +22 -0
- package/dist/issue-store-QRDF3X55.js.map +1 -0
- package/dist/workers/agent-worker.js +6 -3
- package/dist/workers/agent-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/agent-smith-57MKX5QC.js +0 -13
- package/dist/agent-smith-runner-ZU4R3I2Z.js.map +0 -1
- package/dist/chunk-32WLOG6E.js.map +0 -1
- package/dist/chunk-ASGSTVVF.js.map +0 -1
- package/dist/chunk-NUT4G5AY.js.map +0 -1
- package/dist/chunk-S4VGGLXF.js.map +0 -1
- package/dist/chunk-XSPS463E.js.map +0 -1
- package/dist/chunk-XVGHO2Z5.js.map +0 -1
- /package/dist/{agent-smith-57MKX5QC.js.map → agent-smith-W4HUCFGC.js.map} +0 -0
- /package/dist/{chunk-KB5ZN6K2.js.map → chunk-GWSNINKX.js.map} +0 -0
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGuardianState
|
|
3
|
+
} from "./chunk-4YSLDGBL.js";
|
|
4
|
+
import {
|
|
5
|
+
BackupManager,
|
|
6
|
+
atomicWriteJSON,
|
|
7
|
+
getMemoryStats,
|
|
8
|
+
safeParseAndValidate,
|
|
9
|
+
searchIssues
|
|
10
|
+
} from "./chunk-TSHZQKCM.js";
|
|
11
|
+
|
|
12
|
+
// src/guardian/insight-store.ts
|
|
13
|
+
import { mkdir, readFile } from "fs/promises";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var InsightDetailsSchema = z.object({
|
|
18
|
+
affectedFiles: z.array(z.string()).optional(),
|
|
19
|
+
issueBreakdown: z.record(z.string(), z.number()).optional(),
|
|
20
|
+
examples: z.array(z.string()).optional(),
|
|
21
|
+
trend: z.enum(["improving", "stable", "worsening"]).optional(),
|
|
22
|
+
comparison: z.string().optional()
|
|
23
|
+
});
|
|
24
|
+
var GuardianInsightSchema = z.object({
|
|
25
|
+
id: z.string(),
|
|
26
|
+
type: z.enum(["observation", "warning", "suggestion", "celebration", "question"]),
|
|
27
|
+
message: z.string(),
|
|
28
|
+
context: z.string().optional(),
|
|
29
|
+
suggestedAction: z.string().optional(),
|
|
30
|
+
actionCommand: z.string().optional(),
|
|
31
|
+
relatedIssues: z.array(z.string()),
|
|
32
|
+
priority: z.number().min(1).max(10),
|
|
33
|
+
timestamp: z.number(),
|
|
34
|
+
dismissed: z.boolean(),
|
|
35
|
+
category: z.enum(["security", "quality", "performance", "pattern", "progress", "general"]),
|
|
36
|
+
details: InsightDetailsSchema.optional()
|
|
37
|
+
});
|
|
38
|
+
var InsightStoreDataSchema = z.object({
|
|
39
|
+
version: z.literal(1),
|
|
40
|
+
insights: z.array(GuardianInsightSchema),
|
|
41
|
+
cooldowns: z.record(z.string(), z.number()),
|
|
42
|
+
// insightKey -> timestamp
|
|
43
|
+
dismissedIds: z.array(z.string()),
|
|
44
|
+
// Track dismissed insight IDs permanently
|
|
45
|
+
lastUpdated: z.string()
|
|
46
|
+
});
|
|
47
|
+
var InsightStore = class _InsightStore {
|
|
48
|
+
projectPath;
|
|
49
|
+
data;
|
|
50
|
+
loaded = false;
|
|
51
|
+
dirty = false;
|
|
52
|
+
// Default cooldown periods (in ms)
|
|
53
|
+
static COOLDOWNS = {
|
|
54
|
+
"pre-push-warning": 6e4,
|
|
55
|
+
// 1 min between pre-push warnings
|
|
56
|
+
"security-warning": 3e4,
|
|
57
|
+
// 30s between security warnings
|
|
58
|
+
"new-issues": 3e4,
|
|
59
|
+
// 30s between new issue observations
|
|
60
|
+
"celebration": 6e4,
|
|
61
|
+
// 1 min between celebrations
|
|
62
|
+
"pattern-suggestion": 12e4,
|
|
63
|
+
// 2 min between pattern suggestions
|
|
64
|
+
"accessibility-visual-qa": 3e5,
|
|
65
|
+
// 5 min between visual QA suggestions
|
|
66
|
+
"goal-suggestion": 3e5,
|
|
67
|
+
// 5 min between goal suggestions
|
|
68
|
+
"risk-prediction": 18e4,
|
|
69
|
+
// 3 min between risk predictions
|
|
70
|
+
"hypothesis-update": 6e5,
|
|
71
|
+
// 10 min between hypothesis updates
|
|
72
|
+
"auto-escalation": 3e5
|
|
73
|
+
// 5 min between auto-escalations
|
|
74
|
+
};
|
|
75
|
+
constructor(projectPath) {
|
|
76
|
+
this.projectPath = projectPath;
|
|
77
|
+
this.data = this.createEmptyData();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the storage file path
|
|
81
|
+
*/
|
|
82
|
+
getStorePath() {
|
|
83
|
+
return join(this.projectPath, ".trie", "memory", "guardian-insights.json");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create empty data structure
|
|
87
|
+
*/
|
|
88
|
+
createEmptyData() {
|
|
89
|
+
return {
|
|
90
|
+
version: 1,
|
|
91
|
+
insights: [],
|
|
92
|
+
cooldowns: {},
|
|
93
|
+
dismissedIds: [],
|
|
94
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Load insights from disk
|
|
99
|
+
*
|
|
100
|
+
* If the file is corrupted, attempts recovery from backup.
|
|
101
|
+
* Returns empty data if no valid file/backup exists.
|
|
102
|
+
*/
|
|
103
|
+
async load() {
|
|
104
|
+
if (this.loaded) {
|
|
105
|
+
return this.data;
|
|
106
|
+
}
|
|
107
|
+
const storePath = this.getStorePath();
|
|
108
|
+
try {
|
|
109
|
+
if (existsSync(storePath)) {
|
|
110
|
+
const content = await readFile(storePath, "utf-8");
|
|
111
|
+
const result = safeParseAndValidate(content, InsightStoreDataSchema);
|
|
112
|
+
if (result.success) {
|
|
113
|
+
this.data = result.data;
|
|
114
|
+
this.loaded = true;
|
|
115
|
+
this.deduplicateInsights();
|
|
116
|
+
return this.data;
|
|
117
|
+
}
|
|
118
|
+
console.error(` \u26A0\uFE0F Insight store corrupted: ${result.error}`);
|
|
119
|
+
const backupManager = new BackupManager(storePath);
|
|
120
|
+
if (await backupManager.recoverFromBackup()) {
|
|
121
|
+
console.error(" \u2705 Recovered from backup");
|
|
122
|
+
const recovered = await readFile(storePath, "utf-8");
|
|
123
|
+
const recoveredResult = safeParseAndValidate(recovered, InsightStoreDataSchema);
|
|
124
|
+
if (recoveredResult.success) {
|
|
125
|
+
this.data = recoveredResult.data;
|
|
126
|
+
this.loaded = true;
|
|
127
|
+
this.deduplicateInsights();
|
|
128
|
+
return this.data;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
console.error(" \u274C No valid backup found, starting fresh");
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(` \u26A0\uFE0F Could not load insight store: ${error}`);
|
|
135
|
+
}
|
|
136
|
+
this.data = this.createEmptyData();
|
|
137
|
+
this.loaded = true;
|
|
138
|
+
return this.data;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Deduplicate existing insights on load
|
|
142
|
+
* Keeps the most recent instance of each unique insight
|
|
143
|
+
*/
|
|
144
|
+
deduplicateInsights() {
|
|
145
|
+
const seen = /* @__PURE__ */ new Map();
|
|
146
|
+
const toRemove = [];
|
|
147
|
+
for (let i = 0; i < this.data.insights.length; i++) {
|
|
148
|
+
const insight = this.data.insights[i];
|
|
149
|
+
if (!insight) continue;
|
|
150
|
+
const contentKey = this.getContentKey(insight);
|
|
151
|
+
if (seen.has(contentKey)) {
|
|
152
|
+
toRemove.push(i);
|
|
153
|
+
} else {
|
|
154
|
+
seen.set(contentKey, i);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (toRemove.length > 0) {
|
|
158
|
+
for (let i = toRemove.length - 1; i >= 0; i--) {
|
|
159
|
+
const idx = toRemove[i];
|
|
160
|
+
if (idx !== void 0) {
|
|
161
|
+
this.data.insights.splice(idx, 1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
this.dirty = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Save insights to disk
|
|
169
|
+
*
|
|
170
|
+
* Creates backup before writing, uses atomic write.
|
|
171
|
+
*/
|
|
172
|
+
async save() {
|
|
173
|
+
if (!this.dirty && this.loaded) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const storePath = this.getStorePath();
|
|
177
|
+
const memoryDir = join(this.projectPath, ".trie", "memory");
|
|
178
|
+
await mkdir(memoryDir, { recursive: true });
|
|
179
|
+
const backupManager = new BackupManager(storePath);
|
|
180
|
+
await backupManager.createBackup();
|
|
181
|
+
this.data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
182
|
+
await atomicWriteJSON(storePath, this.data);
|
|
183
|
+
this.dirty = false;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Generate a content-based key for deduplication
|
|
187
|
+
* Insights with the same content key are considered duplicates
|
|
188
|
+
*/
|
|
189
|
+
getContentKey(insight) {
|
|
190
|
+
const normalizedMessage = insight.message.replace(/\d+/g, "N").toLowerCase().trim();
|
|
191
|
+
return `${insight.type}:${insight.category}:${normalizedMessage}`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Add an insight to the store
|
|
195
|
+
*
|
|
196
|
+
* Checks for duplicates using both insight ID and content similarity.
|
|
197
|
+
* If a similar insight already exists (same type, category, and normalized message),
|
|
198
|
+
* updates its timestamp instead of creating a duplicate.
|
|
199
|
+
* Respects cooldowns to prevent insight spam.
|
|
200
|
+
*/
|
|
201
|
+
async addInsight(insight) {
|
|
202
|
+
await this.load();
|
|
203
|
+
if (this.data.insights.some((i) => i.id === insight.id)) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
if (this.data.dismissedIds.includes(insight.id)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
const contentKey = this.getContentKey(insight);
|
|
210
|
+
const existingIndex = this.data.insights.findIndex(
|
|
211
|
+
(i) => !i.dismissed && this.getContentKey(i) === contentKey
|
|
212
|
+
);
|
|
213
|
+
if (existingIndex >= 0) {
|
|
214
|
+
const existing = this.data.insights[existingIndex];
|
|
215
|
+
if (existing) {
|
|
216
|
+
existing.timestamp = insight.timestamp;
|
|
217
|
+
existing.message = insight.message;
|
|
218
|
+
existing.details = insight.details;
|
|
219
|
+
existing.relatedIssues = insight.relatedIssues;
|
|
220
|
+
existing.suggestedAction = insight.suggestedAction;
|
|
221
|
+
this.data.insights.splice(existingIndex, 1);
|
|
222
|
+
this.data.insights.unshift(existing);
|
|
223
|
+
this.dirty = true;
|
|
224
|
+
await this.save();
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
this.data.insights.unshift(insight);
|
|
229
|
+
if (this.data.insights.length > 100) {
|
|
230
|
+
this.data.insights = this.data.insights.slice(0, 100);
|
|
231
|
+
}
|
|
232
|
+
this.dirty = true;
|
|
233
|
+
await this.save();
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check if a cooldown has expired for an insight type
|
|
238
|
+
*/
|
|
239
|
+
canCreateInsight(insightKey) {
|
|
240
|
+
const lastTime = this.data.cooldowns[insightKey];
|
|
241
|
+
const cooldown = _InsightStore.COOLDOWNS[insightKey] || 3e4;
|
|
242
|
+
if (!lastTime) return true;
|
|
243
|
+
return Date.now() - lastTime > cooldown;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Mark that an insight type was created (set cooldown)
|
|
247
|
+
*/
|
|
248
|
+
async markInsightCreated(insightKey) {
|
|
249
|
+
await this.load();
|
|
250
|
+
this.data.cooldowns[insightKey] = Date.now();
|
|
251
|
+
this.dirty = true;
|
|
252
|
+
await this.save();
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get active (non-dismissed) insights
|
|
256
|
+
*
|
|
257
|
+
* Returns insights sorted by priority (highest first),
|
|
258
|
+
* limited to the specified count.
|
|
259
|
+
*/
|
|
260
|
+
getActiveInsights(limit = 5) {
|
|
261
|
+
return this.data.insights.filter((i) => !i.dismissed).sort((a, b) => b.priority - a.priority).slice(0, limit);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get all insights (including dismissed)
|
|
265
|
+
*/
|
|
266
|
+
getAllInsights() {
|
|
267
|
+
return [...this.data.insights];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Dismiss an insight by ID
|
|
271
|
+
*/
|
|
272
|
+
async dismissInsight(insightId) {
|
|
273
|
+
await this.load();
|
|
274
|
+
const insight = this.data.insights.find((i) => i.id === insightId);
|
|
275
|
+
if (!insight) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
insight.dismissed = true;
|
|
279
|
+
if (!this.data.dismissedIds.includes(insightId)) {
|
|
280
|
+
this.data.dismissedIds.push(insightId);
|
|
281
|
+
if (this.data.dismissedIds.length > 500) {
|
|
282
|
+
this.data.dismissedIds = this.data.dismissedIds.slice(-500);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
this.dirty = true;
|
|
286
|
+
await this.save();
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Remove an insight entirely
|
|
291
|
+
*/
|
|
292
|
+
async removeInsight(insightId) {
|
|
293
|
+
await this.load();
|
|
294
|
+
const index = this.data.insights.findIndex((i) => i.id === insightId);
|
|
295
|
+
if (index === -1) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
this.data.insights.splice(index, 1);
|
|
299
|
+
this.dirty = true;
|
|
300
|
+
await this.save();
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Clear all cooldowns
|
|
305
|
+
*/
|
|
306
|
+
async clearCooldowns() {
|
|
307
|
+
await this.load();
|
|
308
|
+
this.data.cooldowns = {};
|
|
309
|
+
this.dirty = true;
|
|
310
|
+
await this.save();
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get insight by ID
|
|
314
|
+
*/
|
|
315
|
+
getInsight(insightId) {
|
|
316
|
+
return this.data.insights.find((i) => i.id === insightId);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Update an existing insight
|
|
320
|
+
*/
|
|
321
|
+
async updateInsight(insightId, updates) {
|
|
322
|
+
await this.load();
|
|
323
|
+
const insight = this.data.insights.find((i) => i.id === insightId);
|
|
324
|
+
if (!insight) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
Object.assign(insight, updates);
|
|
328
|
+
this.dirty = true;
|
|
329
|
+
await this.save();
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get insights by category
|
|
334
|
+
*/
|
|
335
|
+
getInsightsByCategory(category) {
|
|
336
|
+
return this.data.insights.filter((i) => i.category === category && !i.dismissed);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get insights by type
|
|
340
|
+
*/
|
|
341
|
+
getInsightsByType(type) {
|
|
342
|
+
return this.data.insights.filter((i) => i.type === type && !i.dismissed);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get insights from the last N hours
|
|
346
|
+
*/
|
|
347
|
+
getRecentInsights(hours = 24) {
|
|
348
|
+
const cutoff = Date.now() - hours * 60 * 60 * 1e3;
|
|
349
|
+
return this.data.insights.filter((i) => i.timestamp >= cutoff);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Get statistics about insights
|
|
353
|
+
*/
|
|
354
|
+
getStats() {
|
|
355
|
+
const stats = {
|
|
356
|
+
total: this.data.insights.length,
|
|
357
|
+
active: 0,
|
|
358
|
+
dismissed: 0,
|
|
359
|
+
byCategory: {},
|
|
360
|
+
byType: {}
|
|
361
|
+
};
|
|
362
|
+
for (const insight of this.data.insights) {
|
|
363
|
+
if (insight.dismissed) {
|
|
364
|
+
stats.dismissed++;
|
|
365
|
+
} else {
|
|
366
|
+
stats.active++;
|
|
367
|
+
}
|
|
368
|
+
stats.byCategory[insight.category] = (stats.byCategory[insight.category] || 0) + 1;
|
|
369
|
+
stats.byType[insight.type] = (stats.byType[insight.type] || 0) + 1;
|
|
370
|
+
}
|
|
371
|
+
return stats;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Prune old insights (older than N days)
|
|
375
|
+
*/
|
|
376
|
+
async pruneOldInsights(daysToKeep = 30) {
|
|
377
|
+
await this.load();
|
|
378
|
+
const cutoff = Date.now() - daysToKeep * 24 * 60 * 60 * 1e3;
|
|
379
|
+
const originalCount = this.data.insights.length;
|
|
380
|
+
this.data.insights = this.data.insights.filter((i) => i.timestamp >= cutoff);
|
|
381
|
+
const pruned = originalCount - this.data.insights.length;
|
|
382
|
+
if (pruned > 0) {
|
|
383
|
+
this.dirty = true;
|
|
384
|
+
await this.save();
|
|
385
|
+
}
|
|
386
|
+
return pruned;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Check if the store has been loaded
|
|
390
|
+
*/
|
|
391
|
+
isLoaded() {
|
|
392
|
+
return this.loaded;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Force reload from disk
|
|
396
|
+
*/
|
|
397
|
+
async reload() {
|
|
398
|
+
this.loaded = false;
|
|
399
|
+
this.dirty = false;
|
|
400
|
+
return this.load();
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
var insightStores = /* @__PURE__ */ new Map();
|
|
404
|
+
function getInsightStore(projectPath) {
|
|
405
|
+
let store = insightStores.get(projectPath);
|
|
406
|
+
if (!store) {
|
|
407
|
+
store = new InsightStore(projectPath);
|
|
408
|
+
insightStores.set(projectPath, store);
|
|
409
|
+
}
|
|
410
|
+
return store;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/guardian/goal-manager.ts
|
|
414
|
+
import { basename } from "path";
|
|
415
|
+
var DEFAULT_CONFIG = {
|
|
416
|
+
minConfidence: 0.6,
|
|
417
|
+
maxActiveGoals: 3,
|
|
418
|
+
goalDurationDays: 14,
|
|
419
|
+
reductionTargetPercent: 50
|
|
420
|
+
};
|
|
421
|
+
var GoalManager = class {
|
|
422
|
+
projectPath;
|
|
423
|
+
config;
|
|
424
|
+
guardianState;
|
|
425
|
+
insightStore;
|
|
426
|
+
constructor(projectPath, config = {}) {
|
|
427
|
+
this.projectPath = projectPath;
|
|
428
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
429
|
+
this.guardianState = getGuardianState(projectPath);
|
|
430
|
+
this.insightStore = getInsightStore(projectPath);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Analyze incident patterns from memory
|
|
434
|
+
*/
|
|
435
|
+
async analyzeIncidentPatterns() {
|
|
436
|
+
const patterns = [];
|
|
437
|
+
try {
|
|
438
|
+
const stats = await getMemoryStats(this.projectPath);
|
|
439
|
+
const recentIssues = await searchIssues("", {
|
|
440
|
+
workDir: this.projectPath,
|
|
441
|
+
limit: 500,
|
|
442
|
+
includeResolved: true
|
|
443
|
+
});
|
|
444
|
+
if (recentIssues.length < 5) {
|
|
445
|
+
return patterns;
|
|
446
|
+
}
|
|
447
|
+
const fileIssueCount = /* @__PURE__ */ new Map();
|
|
448
|
+
for (const { issue } of recentIssues) {
|
|
449
|
+
const file = issue.file;
|
|
450
|
+
fileIssueCount.set(file, (fileIssueCount.get(file) || 0) + 1);
|
|
451
|
+
}
|
|
452
|
+
const sortedFiles = [...fileIssueCount.entries()].sort((a, b) => b[1] - a[1]);
|
|
453
|
+
const dirIssueCount = /* @__PURE__ */ new Map();
|
|
454
|
+
for (const [file, count] of sortedFiles) {
|
|
455
|
+
const parts = file.split("/");
|
|
456
|
+
if (parts.length > 1) {
|
|
457
|
+
const dir = parts.slice(0, -1).join("/");
|
|
458
|
+
dirIssueCount.set(dir, (dirIssueCount.get(dir) || 0) + count);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const sortedDirs = [...dirIssueCount.entries()].sort((a, b) => b[1] - a[1]);
|
|
462
|
+
if (sortedDirs.length > 0) {
|
|
463
|
+
const [topDir, topDirCount] = sortedDirs[0];
|
|
464
|
+
const totalIssues = recentIssues.length;
|
|
465
|
+
const percentage = topDirCount / totalIssues * 100;
|
|
466
|
+
if (percentage >= 30) {
|
|
467
|
+
patterns.push({
|
|
468
|
+
type: "file-cluster",
|
|
469
|
+
description: `${basename(topDir)}/ has ${percentage.toFixed(0)}% of issues`,
|
|
470
|
+
metric: `${topDir}_issues`,
|
|
471
|
+
currentValue: topDirCount,
|
|
472
|
+
suggestedTarget: Math.floor(topDirCount * (1 - this.config.reductionTargetPercent / 100)),
|
|
473
|
+
confidence: Math.min(0.9, percentage / 100 + 0.3),
|
|
474
|
+
evidence: [
|
|
475
|
+
`${topDirCount} issues in ${topDir}/`,
|
|
476
|
+
`${percentage.toFixed(0)}% of total issues`,
|
|
477
|
+
`Top files: ${sortedFiles.filter(([f]) => f.startsWith(topDir)).slice(0, 3).map(([f]) => basename(f)).join(", ")}`
|
|
478
|
+
],
|
|
479
|
+
category: "quality"
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const criticalCount = recentIssues.filter((r) => r.issue.severity === "critical").length;
|
|
484
|
+
const seriousCount = recentIssues.filter((r) => r.issue.severity === "serious").length;
|
|
485
|
+
if (criticalCount >= 3) {
|
|
486
|
+
patterns.push({
|
|
487
|
+
type: "severity-trend",
|
|
488
|
+
description: `${criticalCount} critical issues need attention`,
|
|
489
|
+
metric: "critical_issues",
|
|
490
|
+
currentValue: criticalCount,
|
|
491
|
+
suggestedTarget: 0,
|
|
492
|
+
confidence: Math.min(0.95, 0.5 + criticalCount * 0.1),
|
|
493
|
+
evidence: [
|
|
494
|
+
`${criticalCount} critical severity issues`,
|
|
495
|
+
`${seriousCount} serious severity issues`,
|
|
496
|
+
"Critical issues should be zero for production safety"
|
|
497
|
+
],
|
|
498
|
+
category: "security"
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
const agentIssueCount = /* @__PURE__ */ new Map();
|
|
502
|
+
for (const { issue } of recentIssues) {
|
|
503
|
+
agentIssueCount.set(issue.agent, (agentIssueCount.get(issue.agent) || 0) + 1);
|
|
504
|
+
}
|
|
505
|
+
const sortedAgents = [...agentIssueCount.entries()].sort((a, b) => b[1] - a[1]);
|
|
506
|
+
if (sortedAgents.length > 0) {
|
|
507
|
+
const [topAgent, topAgentCount] = sortedAgents[0];
|
|
508
|
+
const percentage = topAgentCount / recentIssues.length * 100;
|
|
509
|
+
if (percentage >= 40 && topAgentCount >= 5) {
|
|
510
|
+
const category = this.agentToCategory(topAgent);
|
|
511
|
+
patterns.push({
|
|
512
|
+
type: "agent-concentration",
|
|
513
|
+
description: `${topAgent} skill finds ${percentage.toFixed(0)}% of issues`,
|
|
514
|
+
metric: `${topAgent}_issues`,
|
|
515
|
+
currentValue: topAgentCount,
|
|
516
|
+
suggestedTarget: Math.floor(topAgentCount * 0.5),
|
|
517
|
+
confidence: Math.min(0.85, percentage / 100 + 0.2),
|
|
518
|
+
evidence: [
|
|
519
|
+
`${topAgentCount} issues from ${topAgent}`,
|
|
520
|
+
`${percentage.toFixed(0)}% of all issues`,
|
|
521
|
+
`Suggests focused improvement in ${category} area`
|
|
522
|
+
],
|
|
523
|
+
category
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (stats.improvementTrend === "improving") {
|
|
528
|
+
patterns.push({
|
|
529
|
+
type: "time-pattern",
|
|
530
|
+
description: "Quality is improving - maintain the streak",
|
|
531
|
+
metric: "clean_scans",
|
|
532
|
+
currentValue: 0,
|
|
533
|
+
suggestedTarget: 5,
|
|
534
|
+
confidence: 0.7,
|
|
535
|
+
evidence: [
|
|
536
|
+
"Historical trend shows improvement",
|
|
537
|
+
"Momentum is positive",
|
|
538
|
+
"Streak goal can maintain motivation"
|
|
539
|
+
],
|
|
540
|
+
category: "quality"
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error("Failed to analyze incident patterns:", error);
|
|
545
|
+
}
|
|
546
|
+
return patterns;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Convert agent name to category
|
|
550
|
+
*/
|
|
551
|
+
agentToCategory(agent) {
|
|
552
|
+
const categoryMap = {
|
|
553
|
+
"security": "security",
|
|
554
|
+
"privacy": "security",
|
|
555
|
+
"soc2": "security",
|
|
556
|
+
"performance": "performance",
|
|
557
|
+
"accessibility": "quality",
|
|
558
|
+
"test": "coverage",
|
|
559
|
+
"typecheck": "quality",
|
|
560
|
+
"bug-finding": "quality"
|
|
561
|
+
};
|
|
562
|
+
return categoryMap[agent] || "general";
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Generate goal opportunities from patterns
|
|
566
|
+
*/
|
|
567
|
+
async generateGoalOpportunities() {
|
|
568
|
+
const patterns = await this.analyzeIncidentPatterns();
|
|
569
|
+
const opportunities = [];
|
|
570
|
+
const validPatterns = patterns.filter((p) => p.confidence >= this.config.minConfidence);
|
|
571
|
+
await this.guardianState.load();
|
|
572
|
+
const activeGoals = this.guardianState.getActiveGoals();
|
|
573
|
+
const slotsAvailable = this.config.maxActiveGoals - activeGoals.length;
|
|
574
|
+
if (slotsAvailable <= 0) {
|
|
575
|
+
return opportunities;
|
|
576
|
+
}
|
|
577
|
+
for (const pattern of validPatterns.slice(0, slotsAvailable)) {
|
|
578
|
+
const goalType = pattern.type === "time-pattern" ? "streak" : "reduction";
|
|
579
|
+
const deadline = /* @__PURE__ */ new Date();
|
|
580
|
+
deadline.setDate(deadline.getDate() + this.config.goalDurationDays);
|
|
581
|
+
opportunities.push({
|
|
582
|
+
pattern,
|
|
583
|
+
goal: {
|
|
584
|
+
description: this.generateGoalDescription(pattern),
|
|
585
|
+
type: goalType,
|
|
586
|
+
metric: pattern.metric,
|
|
587
|
+
target: pattern.suggestedTarget,
|
|
588
|
+
currentValue: pattern.currentValue,
|
|
589
|
+
startValue: pattern.currentValue,
|
|
590
|
+
status: "active",
|
|
591
|
+
autoGenerated: true,
|
|
592
|
+
confidence: pattern.confidence,
|
|
593
|
+
deadline: deadline.toISOString(),
|
|
594
|
+
category: pattern.category,
|
|
595
|
+
evidence: pattern.evidence
|
|
596
|
+
},
|
|
597
|
+
reasoning: this.generateGoalReasoning(pattern)
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return opportunities;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Generate a human-readable goal description
|
|
604
|
+
*/
|
|
605
|
+
generateGoalDescription(pattern) {
|
|
606
|
+
switch (pattern.type) {
|
|
607
|
+
case "file-cluster":
|
|
608
|
+
return `Reduce issues in ${pattern.description.split(" ")[0]} by ${this.config.reductionTargetPercent}%`;
|
|
609
|
+
case "severity-trend":
|
|
610
|
+
return "Eliminate all critical issues";
|
|
611
|
+
case "agent-concentration":
|
|
612
|
+
return `Reduce ${pattern.description.split(" ")[0]} issues by 50%`;
|
|
613
|
+
case "time-pattern":
|
|
614
|
+
return "Achieve 5 consecutive clean scans";
|
|
615
|
+
default:
|
|
616
|
+
return pattern.description;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Generate reasoning for why this goal was suggested
|
|
621
|
+
*/
|
|
622
|
+
generateGoalReasoning(pattern) {
|
|
623
|
+
const evidence = pattern.evidence.join(". ");
|
|
624
|
+
return `Based on analysis: ${evidence}. Confidence: ${(pattern.confidence * 100).toFixed(0)}%`;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Auto-generate goals and create them (with auto-generated status)
|
|
628
|
+
*
|
|
629
|
+
* Returns goals that need user acceptance.
|
|
630
|
+
*/
|
|
631
|
+
async autoGenerateGoals() {
|
|
632
|
+
const opportunities = await this.generateGoalOpportunities();
|
|
633
|
+
const createdGoals = [];
|
|
634
|
+
for (const opportunity of opportunities) {
|
|
635
|
+
const goal = {
|
|
636
|
+
id: `goal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
637
|
+
...opportunity.goal,
|
|
638
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
639
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
640
|
+
};
|
|
641
|
+
await this.guardianState.addGoal(goal);
|
|
642
|
+
createdGoals.push(goal);
|
|
643
|
+
if (this.insightStore.canCreateInsight("goal-suggestion")) {
|
|
644
|
+
const insight = {
|
|
645
|
+
id: `insight-goal-${goal.id}`,
|
|
646
|
+
type: "suggestion",
|
|
647
|
+
message: `\u{1F3AF} New goal suggested: ${goal.description}`,
|
|
648
|
+
context: opportunity.reasoning,
|
|
649
|
+
suggestedAction: "Review and accept/reject this goal",
|
|
650
|
+
relatedIssues: [],
|
|
651
|
+
priority: 6,
|
|
652
|
+
timestamp: Date.now(),
|
|
653
|
+
dismissed: false,
|
|
654
|
+
category: "progress",
|
|
655
|
+
details: {
|
|
656
|
+
examples: opportunity.pattern.evidence
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
await this.insightStore.addInsight(insight);
|
|
660
|
+
await this.insightStore.markInsightCreated("goal-suggestion");
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return createdGoals;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Update goal progress based on current state
|
|
667
|
+
*/
|
|
668
|
+
async updateGoalProgress() {
|
|
669
|
+
await this.guardianState.load();
|
|
670
|
+
const activeGoals = this.guardianState.getActiveGoals();
|
|
671
|
+
for (const goal of activeGoals) {
|
|
672
|
+
const currentValue = await this.measureGoalMetric(goal);
|
|
673
|
+
await this.guardianState.updateGoal(goal.id, { currentValue });
|
|
674
|
+
if (this.isGoalAchieved(goal, currentValue)) {
|
|
675
|
+
await this.guardianState.updateGoal(goal.id, {
|
|
676
|
+
status: "achieved",
|
|
677
|
+
achievedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
678
|
+
currentValue
|
|
679
|
+
});
|
|
680
|
+
await this.celebrateGoalAchievement(goal);
|
|
681
|
+
}
|
|
682
|
+
if (goal.deadline) {
|
|
683
|
+
const deadline = new Date(goal.deadline);
|
|
684
|
+
if (Date.now() > deadline.getTime() && !this.isGoalAchieved(goal, currentValue)) {
|
|
685
|
+
await this.guardianState.updateGoal(goal.id, {
|
|
686
|
+
status: "failed",
|
|
687
|
+
currentValue
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Measure the current value of a goal's metric
|
|
695
|
+
*
|
|
696
|
+
* Supports both structured metrics and semantic/natural language goals
|
|
697
|
+
*/
|
|
698
|
+
async measureGoalMetric(goal) {
|
|
699
|
+
try {
|
|
700
|
+
const issues = await searchIssues("", {
|
|
701
|
+
workDir: this.projectPath,
|
|
702
|
+
limit: 1e3,
|
|
703
|
+
includeResolved: false
|
|
704
|
+
});
|
|
705
|
+
if (goal.metric.endsWith("_issues")) {
|
|
706
|
+
const prefix = goal.metric.replace("_issues", "");
|
|
707
|
+
if (prefix.includes("/")) {
|
|
708
|
+
return issues.filter((r) => r.issue.file.startsWith(prefix)).length;
|
|
709
|
+
}
|
|
710
|
+
return issues.filter((r) => r.issue.agent === prefix).length;
|
|
711
|
+
}
|
|
712
|
+
if (goal.metric === "critical_issues") {
|
|
713
|
+
return issues.filter((r) => r.issue.severity === "critical").length;
|
|
714
|
+
}
|
|
715
|
+
const desc = goal.description.toLowerCase();
|
|
716
|
+
if (desc.includes("dead code") || desc.includes("unused") || desc.includes("remove unused")) {
|
|
717
|
+
const deadCodeIssues = issues.filter((r) => {
|
|
718
|
+
const msg = r.issue.issue.toLowerCase();
|
|
719
|
+
return msg.includes("unused") || msg.includes("dead code") || msg.includes("never used") || msg.includes("unreachable") || msg.includes("no-unused") || msg.includes("defined but never");
|
|
720
|
+
});
|
|
721
|
+
return deadCodeIssues.length;
|
|
722
|
+
}
|
|
723
|
+
if (desc.includes("security") || desc.includes("vulnerab") || desc.includes("fix security")) {
|
|
724
|
+
const securityIssues = issues.filter(
|
|
725
|
+
(r) => r.issue.agent === "security" || r.issue.agent === "privacy" || r.issue.severity === "critical"
|
|
726
|
+
);
|
|
727
|
+
return securityIssues.length;
|
|
728
|
+
}
|
|
729
|
+
if (desc.includes("type") || desc.includes("typescript") || desc.includes("any type")) {
|
|
730
|
+
const typeIssues = issues.filter(
|
|
731
|
+
(r) => r.issue.agent === "typecheck" || r.issue.issue.toLowerCase().includes("type") || r.issue.issue.toLowerCase().includes("any")
|
|
732
|
+
);
|
|
733
|
+
return typeIssues.length;
|
|
734
|
+
}
|
|
735
|
+
if (desc.includes("test") || desc.includes("coverage")) {
|
|
736
|
+
const testIssues = issues.filter(
|
|
737
|
+
(r) => r.issue.agent === "test" || r.issue.issue.toLowerCase().includes("test") || r.issue.issue.toLowerCase().includes("coverage")
|
|
738
|
+
);
|
|
739
|
+
return testIssues.length;
|
|
740
|
+
}
|
|
741
|
+
if (desc.includes("performance") || desc.includes("slow") || desc.includes("optimize")) {
|
|
742
|
+
const perfIssues = issues.filter(
|
|
743
|
+
(r) => r.issue.agent === "performance" || r.issue.issue.toLowerCase().includes("performance") || r.issue.issue.toLowerCase().includes("slow")
|
|
744
|
+
);
|
|
745
|
+
return perfIssues.length;
|
|
746
|
+
}
|
|
747
|
+
if (desc.includes("accessibility") || desc.includes("a11y") || desc.includes("accessible")) {
|
|
748
|
+
const a11yIssues = issues.filter(
|
|
749
|
+
(r) => r.issue.agent === "accessibility" || r.issue.issue.toLowerCase().includes("accessibility")
|
|
750
|
+
);
|
|
751
|
+
return a11yIssues.length;
|
|
752
|
+
}
|
|
753
|
+
if (desc.includes("bug") || desc.includes("fix") || desc.includes("error")) {
|
|
754
|
+
const bugIssues = issues.filter(
|
|
755
|
+
(r) => r.issue.agent === "bug-finding" || r.issue.severity === "critical" || r.issue.severity === "serious"
|
|
756
|
+
);
|
|
757
|
+
return bugIssues.length;
|
|
758
|
+
}
|
|
759
|
+
if (desc.includes("quality") || desc.includes("clean") || desc.includes("improve")) {
|
|
760
|
+
return issues.length;
|
|
761
|
+
}
|
|
762
|
+
return issues.length;
|
|
763
|
+
} catch {
|
|
764
|
+
return goal.currentValue;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Check if a goal has been achieved
|
|
769
|
+
*/
|
|
770
|
+
isGoalAchieved(goal, currentValue) {
|
|
771
|
+
switch (goal.type) {
|
|
772
|
+
case "reduction":
|
|
773
|
+
return currentValue <= goal.target;
|
|
774
|
+
case "score":
|
|
775
|
+
return currentValue >= goal.target;
|
|
776
|
+
case "streak":
|
|
777
|
+
return currentValue >= goal.target;
|
|
778
|
+
default:
|
|
779
|
+
return currentValue <= goal.target;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Create a celebration insight for goal achievement
|
|
784
|
+
*/
|
|
785
|
+
async celebrateGoalAchievement(goal) {
|
|
786
|
+
const insight = {
|
|
787
|
+
id: `insight-achieved-${goal.id}`,
|
|
788
|
+
type: "celebration",
|
|
789
|
+
message: `\u{1F389} Goal achieved: ${goal.description}!`,
|
|
790
|
+
context: `Started at ${goal.startValue}, now at ${goal.currentValue}. Target was ${goal.target}.`,
|
|
791
|
+
relatedIssues: [],
|
|
792
|
+
priority: 8,
|
|
793
|
+
timestamp: Date.now(),
|
|
794
|
+
dismissed: false,
|
|
795
|
+
category: "progress"
|
|
796
|
+
};
|
|
797
|
+
await this.insightStore.addInsight(insight);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Accept an auto-generated goal
|
|
801
|
+
*/
|
|
802
|
+
async acceptGoal(goalId) {
|
|
803
|
+
return this.guardianState.respondToGoal(goalId, true);
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Reject an auto-generated goal
|
|
807
|
+
*/
|
|
808
|
+
async rejectGoal(goalId) {
|
|
809
|
+
return this.guardianState.respondToGoal(goalId, false);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Get pending auto-generated goals (awaiting acceptance)
|
|
813
|
+
*/
|
|
814
|
+
getPendingGoals() {
|
|
815
|
+
return this.guardianState.getAutoGeneratedGoals().filter((g) => g.status === "active");
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Get goal progress summary
|
|
819
|
+
*/
|
|
820
|
+
getGoalProgressSummary() {
|
|
821
|
+
const goals = this.guardianState.getAllGoals();
|
|
822
|
+
return {
|
|
823
|
+
active: goals.filter((g) => g.status === "active").length,
|
|
824
|
+
achieved: goals.filter((g) => g.status === "achieved").length,
|
|
825
|
+
failed: goals.filter((g) => g.status === "failed").length,
|
|
826
|
+
pending: goals.filter((g) => g.autoGenerated && g.status === "active").length
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
async function calculateAdaptiveScanFrequency(projectPath) {
|
|
831
|
+
const guardianState = getGuardianState(projectPath);
|
|
832
|
+
await guardianState.load();
|
|
833
|
+
try {
|
|
834
|
+
const stats = await getMemoryStats(projectPath);
|
|
835
|
+
const issues = await searchIssues("", {
|
|
836
|
+
workDir: projectPath,
|
|
837
|
+
limit: 100,
|
|
838
|
+
includeResolved: false
|
|
839
|
+
});
|
|
840
|
+
const criticalCount = issues.filter((r) => r.issue.severity === "critical").length;
|
|
841
|
+
const seriousCount = issues.filter((r) => r.issue.severity === "serious").length;
|
|
842
|
+
if (criticalCount >= 3) {
|
|
843
|
+
return {
|
|
844
|
+
frequencyMs: 6e4,
|
|
845
|
+
reason: `${criticalCount} critical issues detected - scanning frequently`
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
if (criticalCount > 0 || seriousCount >= 5) {
|
|
849
|
+
return {
|
|
850
|
+
frequencyMs: 12e4,
|
|
851
|
+
reason: `Critical or many serious issues - elevated scan frequency`
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
if (seriousCount > 0) {
|
|
855
|
+
return {
|
|
856
|
+
frequencyMs: 18e4,
|
|
857
|
+
reason: `${seriousCount} serious issues - moderate scan frequency`
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
if (stats.totalIssues > 10) {
|
|
861
|
+
return {
|
|
862
|
+
frequencyMs: 3e5,
|
|
863
|
+
reason: "Some issues present - standard scan frequency"
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
frequencyMs: 6e5,
|
|
868
|
+
reason: "Code looks good - relaxed scan frequency"
|
|
869
|
+
};
|
|
870
|
+
} catch {
|
|
871
|
+
return {
|
|
872
|
+
frequencyMs: 3e5,
|
|
873
|
+
reason: "Default scan frequency (5 min)"
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
async function adaptScanFrequency(projectPath) {
|
|
878
|
+
const guardianState = getGuardianState(projectPath);
|
|
879
|
+
const { frequencyMs } = await calculateAdaptiveScanFrequency(projectPath);
|
|
880
|
+
await guardianState.setScanFrequency(frequencyMs);
|
|
881
|
+
return frequencyMs;
|
|
882
|
+
}
|
|
883
|
+
var goalManagers = /* @__PURE__ */ new Map();
|
|
884
|
+
function getGoalManager(projectPath) {
|
|
885
|
+
let manager = goalManagers.get(projectPath);
|
|
886
|
+
if (!manager) {
|
|
887
|
+
manager = new GoalManager(projectPath);
|
|
888
|
+
goalManagers.set(projectPath, manager);
|
|
889
|
+
}
|
|
890
|
+
return manager;
|
|
891
|
+
}
|
|
892
|
+
function clearGoalManagers() {
|
|
893
|
+
goalManagers.clear();
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
export {
|
|
897
|
+
getInsightStore,
|
|
898
|
+
GoalManager,
|
|
899
|
+
calculateAdaptiveScanFrequency,
|
|
900
|
+
adaptScanFrequency,
|
|
901
|
+
getGoalManager,
|
|
902
|
+
clearGoalManagers
|
|
903
|
+
};
|
|
904
|
+
//# sourceMappingURL=chunk-D3DMONAJ.js.map
|