@triedotdev/mcp 1.0.113 → 1.0.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-fix-apply-PCAHWLXF.js +10 -0
- package/dist/autonomy-config-JXB7WCZ2.js +30 -0
- package/dist/chunk-2GIAROBF.js +173 -0
- package/dist/chunk-2GIAROBF.js.map +1 -0
- package/dist/{chunk-33WL3D7A.js → chunk-2SIFK7OW.js} +7 -419
- package/dist/chunk-2SIFK7OW.js.map +1 -0
- package/dist/chunk-43X6JBEM.js +36 -0
- package/dist/chunk-43X6JBEM.js.map +1 -0
- package/dist/chunk-55DOQNHJ.js +772 -0
- package/dist/chunk-55DOQNHJ.js.map +1 -0
- package/dist/chunk-6LXSA2OZ.js +425 -0
- package/dist/chunk-6LXSA2OZ.js.map +1 -0
- package/dist/{chunk-SDS3UVFY.js → chunk-AOFYU6T3.js} +113 -559
- package/dist/chunk-AOFYU6T3.js.map +1 -0
- package/dist/{chunk-6QR6QZIX.js → chunk-D3EXBJE2.js} +25 -658
- package/dist/chunk-D3EXBJE2.js.map +1 -0
- package/dist/chunk-DJ2YAGHK.js +50 -0
- package/dist/chunk-DJ2YAGHK.js.map +1 -0
- package/dist/chunk-DZREHOGW.js +706 -0
- package/dist/chunk-DZREHOGW.js.map +1 -0
- package/dist/chunk-I2GFI3AM.js +340 -0
- package/dist/chunk-I2GFI3AM.js.map +1 -0
- package/dist/chunk-KRH642MT.js +947 -0
- package/dist/chunk-KRH642MT.js.map +1 -0
- package/dist/{chunk-QYOACM2C.js → chunk-MVNJPJBK.js} +22 -252
- package/dist/chunk-MVNJPJBK.js.map +1 -0
- package/dist/chunk-NS2MSZMB.js +394 -0
- package/dist/chunk-NS2MSZMB.js.map +1 -0
- package/dist/chunk-SWSK7ANT.js +340 -0
- package/dist/chunk-SWSK7ANT.js.map +1 -0
- package/dist/chunk-VRLMTOB6.js +566 -0
- package/dist/chunk-VRLMTOB6.js.map +1 -0
- package/dist/chunk-YR4BMGYO.js +130 -0
- package/dist/chunk-YR4BMGYO.js.map +1 -0
- package/dist/chunk-ZV2K6M7T.js +74 -0
- package/dist/chunk-ZV2K6M7T.js.map +1 -0
- package/dist/{chunk-2764KZZQ.js → chunk-ZYKEILVK.js} +451 -1069
- package/dist/chunk-ZYKEILVK.js.map +1 -0
- package/dist/cli/main.js +107 -375
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +18 -8
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/client-7XZHCMD3.js +28 -0
- package/dist/client-7XZHCMD3.js.map +1 -0
- package/dist/{goal-manager-AP4LTE6U.js → goal-manager-LMS6ZJB7.js} +7 -3
- package/dist/goal-manager-LMS6ZJB7.js.map +1 -0
- package/dist/goal-validator-T5HEYBC5.js +186 -0
- package/dist/goal-validator-T5HEYBC5.js.map +1 -0
- package/dist/graph-U5JWSAB5.js +10 -0
- package/dist/graph-U5JWSAB5.js.map +1 -0
- package/dist/guardian-agent-EXP7APLC.js +25 -0
- package/dist/guardian-agent-EXP7APLC.js.map +1 -0
- package/dist/hypothesis-KGC3P54C.js +19 -0
- package/dist/hypothesis-KGC3P54C.js.map +1 -0
- package/dist/incident-index-PNIVT47T.js +11 -0
- package/dist/incident-index-PNIVT47T.js.map +1 -0
- package/dist/index.js +369 -43
- package/dist/index.js.map +1 -1
- package/dist/ledger-SR6OEBLO.js +15 -0
- package/dist/ledger-SR6OEBLO.js.map +1 -0
- package/dist/output-manager-BOTMXSND.js +13 -0
- package/dist/output-manager-BOTMXSND.js.map +1 -0
- package/dist/pattern-discovery-F7LU5K6E.js +8 -0
- package/dist/pattern-discovery-F7LU5K6E.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-2764KZZQ.js.map +0 -1
- package/dist/chunk-33WL3D7A.js.map +0 -1
- package/dist/chunk-6JPPYG7F.js +0 -1813
- package/dist/chunk-6JPPYG7F.js.map +0 -1
- package/dist/chunk-6QR6QZIX.js.map +0 -1
- package/dist/chunk-QYOACM2C.js.map +0 -1
- package/dist/chunk-SDS3UVFY.js.map +0 -1
- package/dist/guardian-agent-XEYNG7RH.js +0 -18
- /package/dist/{goal-manager-AP4LTE6U.js.map → auto-fix-apply-PCAHWLXF.js.map} +0 -0
- /package/dist/{guardian-agent-XEYNG7RH.js.map → autonomy-config-JXB7WCZ2.js.map} +0 -0
package/dist/chunk-6JPPYG7F.js
DELETED
|
@@ -1,1813 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getTrieDirectory,
|
|
3
|
-
getWorkingDirectory
|
|
4
|
-
} from "./chunk-R4AAPFXC.js";
|
|
5
|
-
|
|
6
|
-
// src/memory/compactor.ts
|
|
7
|
-
import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
|
|
8
|
-
import { existsSync as existsSync2 } from "fs";
|
|
9
|
-
import { join as join2 } from "path";
|
|
10
|
-
|
|
11
|
-
// src/utils/atomic-write.ts
|
|
12
|
-
import { writeFile, rename, unlink, mkdir } from "fs/promises";
|
|
13
|
-
import { randomBytes } from "crypto";
|
|
14
|
-
import { dirname } from "path";
|
|
15
|
-
async function atomicWriteFile(filePath, data, options = {}) {
|
|
16
|
-
const { createDir = true, encoding = "utf-8" } = options;
|
|
17
|
-
const tempSuffix = randomBytes(6).toString("hex");
|
|
18
|
-
const tempPath = `${filePath}.${tempSuffix}.tmp`;
|
|
19
|
-
try {
|
|
20
|
-
if (createDir) {
|
|
21
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
if (typeof data === "string") {
|
|
24
|
-
await writeFile(tempPath, data, { encoding });
|
|
25
|
-
} else {
|
|
26
|
-
await writeFile(tempPath, data);
|
|
27
|
-
}
|
|
28
|
-
await rename(tempPath, filePath);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
try {
|
|
31
|
-
await unlink(tempPath);
|
|
32
|
-
} catch {
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function atomicWriteJSON(filePath, data, options = {}) {
|
|
38
|
-
const { spaces = 2, ...writeOptions } = options;
|
|
39
|
-
const json = JSON.stringify(data, null, spaces);
|
|
40
|
-
await atomicWriteFile(filePath, json, writeOptions);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// src/utils/backup-manager.ts
|
|
44
|
-
import { copyFile, readdir, unlink as unlink2, readFile, stat } from "fs/promises";
|
|
45
|
-
import { existsSync } from "fs";
|
|
46
|
-
import { dirname as dirname2, basename, join } from "path";
|
|
47
|
-
var BackupManager = class {
|
|
48
|
-
filePath;
|
|
49
|
-
maxBackups;
|
|
50
|
-
validator;
|
|
51
|
-
backupDir;
|
|
52
|
-
baseFileName;
|
|
53
|
-
constructor(filePath, options = {}) {
|
|
54
|
-
this.filePath = filePath;
|
|
55
|
-
this.maxBackups = options.maxBackups ?? 5;
|
|
56
|
-
this.validator = options.validator;
|
|
57
|
-
this.backupDir = dirname2(filePath);
|
|
58
|
-
this.baseFileName = basename(filePath);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Create a backup of the current file
|
|
62
|
-
*
|
|
63
|
-
* @returns The backup file path, or null if source doesn't exist
|
|
64
|
-
*/
|
|
65
|
-
async createBackup() {
|
|
66
|
-
if (!existsSync(this.filePath)) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
const timestamp = Date.now();
|
|
70
|
-
const backupPath = this.getBackupPath(timestamp);
|
|
71
|
-
await copyFile(this.filePath, backupPath);
|
|
72
|
-
await this.pruneOldBackups();
|
|
73
|
-
return backupPath;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* List all backups sorted by timestamp (newest first)
|
|
77
|
-
*/
|
|
78
|
-
async listBackups() {
|
|
79
|
-
if (!existsSync(this.backupDir)) {
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
const files = await readdir(this.backupDir);
|
|
83
|
-
const backupPattern = new RegExp(
|
|
84
|
-
`^${this.escapeRegex(this.baseFileName)}\\.backup\\.(\\d+)$`
|
|
85
|
-
);
|
|
86
|
-
const backups = [];
|
|
87
|
-
for (const file of files) {
|
|
88
|
-
const match = file.match(backupPattern);
|
|
89
|
-
if (match) {
|
|
90
|
-
const timestamp = parseInt(match[1], 10);
|
|
91
|
-
const backupPath = join(this.backupDir, file);
|
|
92
|
-
try {
|
|
93
|
-
const stats = await stat(backupPath);
|
|
94
|
-
backups.push({
|
|
95
|
-
path: backupPath,
|
|
96
|
-
timestamp,
|
|
97
|
-
size: stats.size
|
|
98
|
-
});
|
|
99
|
-
} catch {
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return backups.sort((a, b) => b.timestamp - a.timestamp);
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Find the first valid backup
|
|
107
|
-
*
|
|
108
|
-
* Iterates through backups from newest to oldest,
|
|
109
|
-
* returning the first one that passes validation.
|
|
110
|
-
*
|
|
111
|
-
* @returns Path to valid backup, or null if none found
|
|
112
|
-
*/
|
|
113
|
-
async findValidBackup() {
|
|
114
|
-
const backups = await this.listBackups();
|
|
115
|
-
for (const backup of backups) {
|
|
116
|
-
if (await this.validateBackup(backup.path)) {
|
|
117
|
-
return backup.path;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Validate a backup file
|
|
124
|
-
*
|
|
125
|
-
* If a custom validator was provided, uses that.
|
|
126
|
-
* Otherwise, attempts JSON parse for .json files.
|
|
127
|
-
*
|
|
128
|
-
* @returns true if backup is valid
|
|
129
|
-
*/
|
|
130
|
-
async validateBackup(backupPath) {
|
|
131
|
-
try {
|
|
132
|
-
const content = await readFile(backupPath, "utf-8");
|
|
133
|
-
if (this.validator) {
|
|
134
|
-
return this.validator(content);
|
|
135
|
-
}
|
|
136
|
-
if (this.filePath.endsWith(".json")) {
|
|
137
|
-
JSON.parse(content);
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
return content.length > 0;
|
|
141
|
-
} catch {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Recover from the most recent valid backup
|
|
147
|
-
*
|
|
148
|
-
* @returns true if recovery was successful
|
|
149
|
-
*/
|
|
150
|
-
async recoverFromBackup() {
|
|
151
|
-
const validBackup = await this.findValidBackup();
|
|
152
|
-
if (!validBackup) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
await copyFile(validBackup, this.filePath);
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Remove old backups beyond the max limit
|
|
160
|
-
*/
|
|
161
|
-
async pruneOldBackups() {
|
|
162
|
-
const backups = await this.listBackups();
|
|
163
|
-
const toRemove = backups.slice(this.maxBackups);
|
|
164
|
-
for (const backup of toRemove) {
|
|
165
|
-
try {
|
|
166
|
-
await unlink2(backup.path);
|
|
167
|
-
} catch {
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Get the backup path for a given timestamp
|
|
173
|
-
*/
|
|
174
|
-
getBackupPath(timestamp) {
|
|
175
|
-
return join(this.backupDir, `${this.baseFileName}.backup.${timestamp}`);
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Escape special regex characters in a string
|
|
179
|
-
*/
|
|
180
|
-
escapeRegex(str) {
|
|
181
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Get the number of existing backups
|
|
185
|
-
*/
|
|
186
|
-
async getBackupCount() {
|
|
187
|
-
const backups = await this.listBackups();
|
|
188
|
-
return backups.length;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Get the most recent backup timestamp
|
|
192
|
-
*/
|
|
193
|
-
async getLatestBackupTime() {
|
|
194
|
-
const backups = await this.listBackups();
|
|
195
|
-
return backups[0]?.timestamp ?? null;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Delete all backups
|
|
199
|
-
*/
|
|
200
|
-
async clearBackups() {
|
|
201
|
-
const backups = await this.listBackups();
|
|
202
|
-
let deleted = 0;
|
|
203
|
-
for (const backup of backups) {
|
|
204
|
-
try {
|
|
205
|
-
await unlink2(backup.path);
|
|
206
|
-
deleted++;
|
|
207
|
-
} catch {
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return deleted;
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// src/memory/validation.ts
|
|
215
|
-
import { z } from "zod";
|
|
216
|
-
var StoredIssueSchema = z.object({
|
|
217
|
-
id: z.string(),
|
|
218
|
-
hash: z.string(),
|
|
219
|
-
severity: z.string(),
|
|
220
|
-
issue: z.string(),
|
|
221
|
-
fix: z.string(),
|
|
222
|
-
file: z.string(),
|
|
223
|
-
line: z.number().optional(),
|
|
224
|
-
agent: z.string(),
|
|
225
|
-
category: z.string().optional(),
|
|
226
|
-
timestamp: z.string(),
|
|
227
|
-
project: z.string(),
|
|
228
|
-
resolved: z.boolean().optional(),
|
|
229
|
-
resolvedAt: z.string().optional()
|
|
230
|
-
});
|
|
231
|
-
var IssueIndexSchema = z.array(StoredIssueSchema);
|
|
232
|
-
var FixAppliedSchema = z.object({
|
|
233
|
-
project: z.string(),
|
|
234
|
-
timestamp: z.string(),
|
|
235
|
-
fix: z.string()
|
|
236
|
-
});
|
|
237
|
-
var GlobalPatternSchema = z.object({
|
|
238
|
-
id: z.string(),
|
|
239
|
-
pattern: z.string(),
|
|
240
|
-
description: z.string(),
|
|
241
|
-
severity: z.string(),
|
|
242
|
-
agent: z.string(),
|
|
243
|
-
occurrences: z.number(),
|
|
244
|
-
projects: z.array(z.string()),
|
|
245
|
-
firstSeen: z.string(),
|
|
246
|
-
lastSeen: z.string(),
|
|
247
|
-
fixApplied: FixAppliedSchema.optional()
|
|
248
|
-
});
|
|
249
|
-
var GlobalPatternsIndexSchema = z.array(GlobalPatternSchema);
|
|
250
|
-
var ProjectSummarySchema = z.object({
|
|
251
|
-
name: z.string(),
|
|
252
|
-
path: z.string(),
|
|
253
|
-
lastScan: z.string(),
|
|
254
|
-
healthScore: z.number(),
|
|
255
|
-
totalIssues: z.number(),
|
|
256
|
-
patterns: z.array(z.string())
|
|
257
|
-
});
|
|
258
|
-
var PatternSummarySchema = z.object({
|
|
259
|
-
pattern: z.string(),
|
|
260
|
-
count: z.number(),
|
|
261
|
-
severity: z.string(),
|
|
262
|
-
agent: z.string(),
|
|
263
|
-
exampleFix: z.string()
|
|
264
|
-
});
|
|
265
|
-
var HotFileSchema = z.object({
|
|
266
|
-
file: z.string(),
|
|
267
|
-
count: z.number()
|
|
268
|
-
});
|
|
269
|
-
var CompactedSummarySchema = z.object({
|
|
270
|
-
period: z.string(),
|
|
271
|
-
startDate: z.string(),
|
|
272
|
-
endDate: z.string(),
|
|
273
|
-
totalIssues: z.number(),
|
|
274
|
-
resolvedCount: z.number(),
|
|
275
|
-
bySeverity: z.record(z.string(), z.number()),
|
|
276
|
-
byAgent: z.record(z.string(), z.number()),
|
|
277
|
-
topPatterns: z.array(PatternSummarySchema),
|
|
278
|
-
hotFiles: z.array(HotFileSchema),
|
|
279
|
-
compactedAt: z.string()
|
|
280
|
-
});
|
|
281
|
-
var CompactedSummariesIndexSchema = z.array(CompactedSummarySchema);
|
|
282
|
-
function safeParseAndValidate(content, schema) {
|
|
283
|
-
try {
|
|
284
|
-
const parsed = JSON.parse(content);
|
|
285
|
-
const result = schema.safeParse(parsed);
|
|
286
|
-
if (result.success) {
|
|
287
|
-
return { success: true, data: result.data };
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
success: false,
|
|
291
|
-
error: `Validation failed: ${result.error.issues[0]?.message || "Unknown error"}`
|
|
292
|
-
};
|
|
293
|
-
} catch (error) {
|
|
294
|
-
return {
|
|
295
|
-
success: false,
|
|
296
|
-
error: `JSON parse failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// src/memory/compactor.ts
|
|
302
|
-
async function compactOldIssues(issues, options = {}) {
|
|
303
|
-
const keepDays = options.keepDays ?? 30;
|
|
304
|
-
const minIssues = options.minIssuesToCompact ?? 100;
|
|
305
|
-
const cutoffDate = /* @__PURE__ */ new Date();
|
|
306
|
-
cutoffDate.setDate(cutoffDate.getDate() - keepDays);
|
|
307
|
-
const oldIssues = issues.filter((i) => new Date(i.timestamp) < cutoffDate);
|
|
308
|
-
const recentIssues = issues.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
309
|
-
if (oldIssues.length < minIssues) {
|
|
310
|
-
return { summary: null, remaining: issues };
|
|
311
|
-
}
|
|
312
|
-
const summary = buildSummary(oldIssues);
|
|
313
|
-
return { summary, remaining: recentIssues };
|
|
314
|
-
}
|
|
315
|
-
function buildSummary(issues) {
|
|
316
|
-
const sorted = issues.sort(
|
|
317
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
318
|
-
);
|
|
319
|
-
const bySeverity = {};
|
|
320
|
-
const byAgent = {};
|
|
321
|
-
const patternMap = /* @__PURE__ */ new Map();
|
|
322
|
-
const fileCount = /* @__PURE__ */ new Map();
|
|
323
|
-
for (const issue of issues) {
|
|
324
|
-
bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;
|
|
325
|
-
byAgent[issue.agent] = (byAgent[issue.agent] || 0) + 1;
|
|
326
|
-
const patternKey = normalizePattern(issue.issue);
|
|
327
|
-
const existing = patternMap.get(patternKey);
|
|
328
|
-
if (existing) {
|
|
329
|
-
existing.count++;
|
|
330
|
-
} else {
|
|
331
|
-
patternMap.set(patternKey, { count: 1, issue });
|
|
332
|
-
}
|
|
333
|
-
const fileName = issue.file.split("/").pop() || issue.file;
|
|
334
|
-
fileCount.set(fileName, (fileCount.get(fileName) || 0) + 1);
|
|
335
|
-
}
|
|
336
|
-
const topPatterns = Array.from(patternMap.entries()).sort((a, b) => b[1].count - a[1].count).slice(0, 10).map(([pattern, data]) => ({
|
|
337
|
-
pattern: pattern.slice(0, 100),
|
|
338
|
-
count: data.count,
|
|
339
|
-
severity: data.issue.severity,
|
|
340
|
-
agent: data.issue.agent,
|
|
341
|
-
exampleFix: data.issue.fix.slice(0, 200)
|
|
342
|
-
}));
|
|
343
|
-
const hotFiles = Array.from(fileCount.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([file, count]) => ({ file, count }));
|
|
344
|
-
return {
|
|
345
|
-
period: `${sorted[0]?.timestamp.split("T")[0]} to ${sorted[sorted.length - 1]?.timestamp.split("T")[0]}`,
|
|
346
|
-
startDate: sorted[0]?.timestamp || "",
|
|
347
|
-
endDate: sorted[sorted.length - 1]?.timestamp || "",
|
|
348
|
-
totalIssues: issues.length,
|
|
349
|
-
resolvedCount: issues.filter((i) => i.resolved).length,
|
|
350
|
-
bySeverity,
|
|
351
|
-
byAgent,
|
|
352
|
-
topPatterns,
|
|
353
|
-
hotFiles,
|
|
354
|
-
compactedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
function normalizePattern(text) {
|
|
358
|
-
return text.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/["']/g, "").replace(/\s+/g, " ").trim().slice(0, 150);
|
|
359
|
-
}
|
|
360
|
-
async function saveCompactedSummary(summary, projectDir) {
|
|
361
|
-
const memoryDir = join2(getTrieDirectory(projectDir), "memory");
|
|
362
|
-
await mkdir2(memoryDir, { recursive: true });
|
|
363
|
-
const summaryPath = join2(memoryDir, "compacted-summaries.json");
|
|
364
|
-
let summaries = [];
|
|
365
|
-
try {
|
|
366
|
-
if (existsSync2(summaryPath)) {
|
|
367
|
-
const content = await readFile2(summaryPath, "utf-8");
|
|
368
|
-
const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);
|
|
369
|
-
if (result.success) {
|
|
370
|
-
summaries = result.data;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
} catch {
|
|
374
|
-
summaries = [];
|
|
375
|
-
}
|
|
376
|
-
summaries.push(summary);
|
|
377
|
-
if (summaries.length > 12) {
|
|
378
|
-
summaries = summaries.slice(-12);
|
|
379
|
-
}
|
|
380
|
-
const backupManager = new BackupManager(summaryPath);
|
|
381
|
-
await backupManager.createBackup();
|
|
382
|
-
await atomicWriteJSON(summaryPath, summaries);
|
|
383
|
-
}
|
|
384
|
-
async function loadCompactedSummaries(projectDir) {
|
|
385
|
-
const summaryPath = join2(getTrieDirectory(projectDir), "memory", "compacted-summaries.json");
|
|
386
|
-
try {
|
|
387
|
-
if (existsSync2(summaryPath)) {
|
|
388
|
-
const content = await readFile2(summaryPath, "utf-8");
|
|
389
|
-
const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);
|
|
390
|
-
if (result.success) {
|
|
391
|
-
return result.data;
|
|
392
|
-
}
|
|
393
|
-
const backupManager = new BackupManager(summaryPath);
|
|
394
|
-
if (await backupManager.recoverFromBackup()) {
|
|
395
|
-
const recovered = await readFile2(summaryPath, "utf-8");
|
|
396
|
-
const recoveredResult = safeParseAndValidate(recovered, CompactedSummariesIndexSchema);
|
|
397
|
-
if (recoveredResult.success) {
|
|
398
|
-
return recoveredResult.data;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
} catch {
|
|
403
|
-
}
|
|
404
|
-
return [];
|
|
405
|
-
}
|
|
406
|
-
async function getHistoricalInsights(projectDir) {
|
|
407
|
-
const summaries = await loadCompactedSummaries(projectDir);
|
|
408
|
-
if (summaries.length === 0) {
|
|
409
|
-
return {
|
|
410
|
-
totalHistoricalIssues: 0,
|
|
411
|
-
recurringPatterns: [],
|
|
412
|
-
improvementTrend: "unknown"
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
const totalHistoricalIssues = summaries.reduce((sum, s) => sum + s.totalIssues, 0);
|
|
416
|
-
const patternCounts = /* @__PURE__ */ new Map();
|
|
417
|
-
for (const summary of summaries) {
|
|
418
|
-
for (const pattern of summary.topPatterns) {
|
|
419
|
-
const key = pattern.pattern;
|
|
420
|
-
const existing = patternCounts.get(key);
|
|
421
|
-
if (existing) {
|
|
422
|
-
existing.count += pattern.count;
|
|
423
|
-
existing.appearances++;
|
|
424
|
-
} else {
|
|
425
|
-
patternCounts.set(key, { ...pattern, appearances: 1 });
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
const recurringPatterns = Array.from(patternCounts.values()).filter((p) => p.appearances >= 2).sort((a, b) => b.count - a.count).slice(0, 5);
|
|
430
|
-
let improvementTrend = "unknown";
|
|
431
|
-
if (summaries.length >= 2) {
|
|
432
|
-
const recent = summaries.slice(-2);
|
|
433
|
-
const olderCount = recent[0]?.totalIssues || 0;
|
|
434
|
-
const newerCount = recent[1]?.totalIssues || 0;
|
|
435
|
-
if (newerCount < olderCount * 0.8) {
|
|
436
|
-
improvementTrend = "improving";
|
|
437
|
-
} else if (newerCount > olderCount * 1.2) {
|
|
438
|
-
improvementTrend = "declining";
|
|
439
|
-
} else {
|
|
440
|
-
improvementTrend = "stable";
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
return {
|
|
444
|
-
totalHistoricalIssues,
|
|
445
|
-
recurringPatterns,
|
|
446
|
-
improvementTrend
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// src/memory/issue-store.ts
|
|
451
|
-
import { mkdir as mkdir4, writeFile as writeFile2, readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
452
|
-
import { createHash as createHash2 } from "crypto";
|
|
453
|
-
import { existsSync as existsSync4 } from "fs";
|
|
454
|
-
import { join as join4 } from "path";
|
|
455
|
-
|
|
456
|
-
// src/memory/bm25.ts
|
|
457
|
-
var BM25Index = class _BM25Index {
|
|
458
|
-
documents = /* @__PURE__ */ new Map();
|
|
459
|
-
termFrequencies = /* @__PURE__ */ new Map();
|
|
460
|
-
documentFrequencies = /* @__PURE__ */ new Map();
|
|
461
|
-
documentLengths = /* @__PURE__ */ new Map();
|
|
462
|
-
avgDocLength = 0;
|
|
463
|
-
k1 = 1.5;
|
|
464
|
-
b = 0.75;
|
|
465
|
-
/**
|
|
466
|
-
* Add a document to the index
|
|
467
|
-
*/
|
|
468
|
-
addDocument(doc) {
|
|
469
|
-
const tokens = this.tokenize(doc.text);
|
|
470
|
-
this.documents.set(doc.id, doc);
|
|
471
|
-
this.documentLengths.set(doc.id, tokens.length);
|
|
472
|
-
const termFreq = /* @__PURE__ */ new Map();
|
|
473
|
-
const seenTerms = /* @__PURE__ */ new Set();
|
|
474
|
-
for (const token of tokens) {
|
|
475
|
-
termFreq.set(token, (termFreq.get(token) || 0) + 1);
|
|
476
|
-
if (!seenTerms.has(token)) {
|
|
477
|
-
seenTerms.add(token);
|
|
478
|
-
this.documentFrequencies.set(token, (this.documentFrequencies.get(token) || 0) + 1);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
this.termFrequencies.set(doc.id, termFreq);
|
|
482
|
-
this.updateAvgDocLength();
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Add multiple documents
|
|
486
|
-
*/
|
|
487
|
-
addDocuments(docs) {
|
|
488
|
-
for (const doc of docs) {
|
|
489
|
-
this.addDocument(doc);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Search the index
|
|
494
|
-
*/
|
|
495
|
-
search(query, limit = 10) {
|
|
496
|
-
const queryTokens = this.tokenize(query);
|
|
497
|
-
const scores = /* @__PURE__ */ new Map();
|
|
498
|
-
const N = this.documents.size;
|
|
499
|
-
for (const [docId] of this.documents) {
|
|
500
|
-
let score = 0;
|
|
501
|
-
const docLength = this.documentLengths.get(docId) || 0;
|
|
502
|
-
const termFreqs = this.termFrequencies.get(docId);
|
|
503
|
-
if (!termFreqs) continue;
|
|
504
|
-
for (const term of queryTokens) {
|
|
505
|
-
const tf = termFreqs.get(term) || 0;
|
|
506
|
-
if (tf === 0) continue;
|
|
507
|
-
const df = this.documentFrequencies.get(term) || 0;
|
|
508
|
-
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
509
|
-
const numerator = tf * (this.k1 + 1);
|
|
510
|
-
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.avgDocLength));
|
|
511
|
-
score += idf * (numerator / denominator);
|
|
512
|
-
}
|
|
513
|
-
if (score > 0) {
|
|
514
|
-
scores.set(docId, score);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return Array.from(scores.entries()).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, score]) => {
|
|
518
|
-
const metadata = this.documents.get(id)?.metadata;
|
|
519
|
-
const result = { id, score };
|
|
520
|
-
if (metadata !== void 0) {
|
|
521
|
-
result.metadata = metadata;
|
|
522
|
-
}
|
|
523
|
-
return result;
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* Get document count
|
|
528
|
-
*/
|
|
529
|
-
get size() {
|
|
530
|
-
return this.documents.size;
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Clear the index
|
|
534
|
-
*/
|
|
535
|
-
clear() {
|
|
536
|
-
this.documents.clear();
|
|
537
|
-
this.termFrequencies.clear();
|
|
538
|
-
this.documentFrequencies.clear();
|
|
539
|
-
this.documentLengths.clear();
|
|
540
|
-
this.avgDocLength = 0;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Serialize the index to JSON
|
|
544
|
-
*/
|
|
545
|
-
serialize() {
|
|
546
|
-
return JSON.stringify({
|
|
547
|
-
documents: Array.from(this.documents.entries()),
|
|
548
|
-
termFrequencies: Array.from(this.termFrequencies.entries()).map(([k, v]) => [k, Array.from(v.entries())]),
|
|
549
|
-
documentFrequencies: Array.from(this.documentFrequencies.entries()),
|
|
550
|
-
documentLengths: Array.from(this.documentLengths.entries()),
|
|
551
|
-
avgDocLength: this.avgDocLength
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Load from serialized JSON
|
|
556
|
-
*/
|
|
557
|
-
static deserialize(json) {
|
|
558
|
-
const data = JSON.parse(json);
|
|
559
|
-
const index = new _BM25Index();
|
|
560
|
-
index.documents = new Map(data.documents);
|
|
561
|
-
index.termFrequencies = new Map(data.termFrequencies.map(([k, v]) => [k, new Map(v)]));
|
|
562
|
-
index.documentFrequencies = new Map(data.documentFrequencies);
|
|
563
|
-
index.documentLengths = new Map(data.documentLengths);
|
|
564
|
-
index.avgDocLength = data.avgDocLength;
|
|
565
|
-
return index;
|
|
566
|
-
}
|
|
567
|
-
tokenize(text) {
|
|
568
|
-
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((token) => token.length > 2 && !this.isStopWord(token));
|
|
569
|
-
}
|
|
570
|
-
isStopWord(word) {
|
|
571
|
-
const stopWords = /* @__PURE__ */ new Set([
|
|
572
|
-
"the",
|
|
573
|
-
"be",
|
|
574
|
-
"to",
|
|
575
|
-
"of",
|
|
576
|
-
"and",
|
|
577
|
-
"a",
|
|
578
|
-
"in",
|
|
579
|
-
"that",
|
|
580
|
-
"have",
|
|
581
|
-
"i",
|
|
582
|
-
"it",
|
|
583
|
-
"for",
|
|
584
|
-
"not",
|
|
585
|
-
"on",
|
|
586
|
-
"with",
|
|
587
|
-
"he",
|
|
588
|
-
"as",
|
|
589
|
-
"you",
|
|
590
|
-
"do",
|
|
591
|
-
"at",
|
|
592
|
-
"this",
|
|
593
|
-
"but",
|
|
594
|
-
"his",
|
|
595
|
-
"by",
|
|
596
|
-
"from",
|
|
597
|
-
"they",
|
|
598
|
-
"we",
|
|
599
|
-
"say",
|
|
600
|
-
"her",
|
|
601
|
-
"she",
|
|
602
|
-
"or",
|
|
603
|
-
"an",
|
|
604
|
-
"will",
|
|
605
|
-
"my",
|
|
606
|
-
"one",
|
|
607
|
-
"all",
|
|
608
|
-
"would",
|
|
609
|
-
"there",
|
|
610
|
-
"their",
|
|
611
|
-
"what",
|
|
612
|
-
"so",
|
|
613
|
-
"up",
|
|
614
|
-
"out",
|
|
615
|
-
"if",
|
|
616
|
-
"about",
|
|
617
|
-
"who",
|
|
618
|
-
"get",
|
|
619
|
-
"which",
|
|
620
|
-
"go",
|
|
621
|
-
"me",
|
|
622
|
-
"when",
|
|
623
|
-
"make",
|
|
624
|
-
"can",
|
|
625
|
-
"like",
|
|
626
|
-
"time",
|
|
627
|
-
"no",
|
|
628
|
-
"just",
|
|
629
|
-
"him",
|
|
630
|
-
"know",
|
|
631
|
-
"take",
|
|
632
|
-
"into",
|
|
633
|
-
"year",
|
|
634
|
-
"your",
|
|
635
|
-
"some",
|
|
636
|
-
"could",
|
|
637
|
-
"them",
|
|
638
|
-
"see",
|
|
639
|
-
"other",
|
|
640
|
-
"than",
|
|
641
|
-
"then",
|
|
642
|
-
"now",
|
|
643
|
-
"look",
|
|
644
|
-
"only",
|
|
645
|
-
"come",
|
|
646
|
-
"its",
|
|
647
|
-
"over",
|
|
648
|
-
"also",
|
|
649
|
-
"back",
|
|
650
|
-
"after",
|
|
651
|
-
"use",
|
|
652
|
-
"two",
|
|
653
|
-
"how",
|
|
654
|
-
"our",
|
|
655
|
-
"first",
|
|
656
|
-
"way",
|
|
657
|
-
"even",
|
|
658
|
-
"new",
|
|
659
|
-
"want",
|
|
660
|
-
"because",
|
|
661
|
-
"any",
|
|
662
|
-
"these",
|
|
663
|
-
"give",
|
|
664
|
-
"day",
|
|
665
|
-
"most",
|
|
666
|
-
"us",
|
|
667
|
-
"should",
|
|
668
|
-
"been",
|
|
669
|
-
"has",
|
|
670
|
-
"was",
|
|
671
|
-
"are"
|
|
672
|
-
]);
|
|
673
|
-
return stopWords.has(word);
|
|
674
|
-
}
|
|
675
|
-
updateAvgDocLength() {
|
|
676
|
-
if (this.documentLengths.size === 0) {
|
|
677
|
-
this.avgDocLength = 0;
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
const total = Array.from(this.documentLengths.values()).reduce((a, b) => a + b, 0);
|
|
681
|
-
this.avgDocLength = total / this.documentLengths.size;
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
// src/memory/ledger.ts
|
|
686
|
-
import { createHash } from "crypto";
|
|
687
|
-
import { mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
|
|
688
|
-
import { existsSync as existsSync3 } from "fs";
|
|
689
|
-
import { join as join3 } from "path";
|
|
690
|
-
var LEDGER_FILENAME = "ledger.json";
|
|
691
|
-
var GENESIS_HASH = "0".repeat(64);
|
|
692
|
-
var LEDGER_VERSION = 1;
|
|
693
|
-
async function appendIssuesToLedger(issues, workDir) {
|
|
694
|
-
if (issues.length === 0) return null;
|
|
695
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
696
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
697
|
-
await mkdir3(memoryDir, { recursive: true });
|
|
698
|
-
const blocks = await loadLedger(projectDir);
|
|
699
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
700
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
701
|
-
const entries = issues.map((issue) => ({
|
|
702
|
-
id: issue.id,
|
|
703
|
-
hash: issue.hash,
|
|
704
|
-
severity: issue.severity,
|
|
705
|
-
file: issue.file,
|
|
706
|
-
agent: issue.agent,
|
|
707
|
-
timestamp: issue.timestamp
|
|
708
|
-
}));
|
|
709
|
-
const previousBlock = blocks[blocks.length - 1];
|
|
710
|
-
const block = previousBlock && previousBlock.date === today ? previousBlock : createBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH);
|
|
711
|
-
if (block !== previousBlock) {
|
|
712
|
-
blocks.push(block);
|
|
713
|
-
}
|
|
714
|
-
block.entries = [...block.entries, ...entries];
|
|
715
|
-
block.merkleRoot = computeMerkleRoot(block.entries.map((entry) => entry.hash));
|
|
716
|
-
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
717
|
-
block.updatedAt = now;
|
|
718
|
-
await saveLedger(blocks, projectDir);
|
|
719
|
-
return block;
|
|
720
|
-
}
|
|
721
|
-
function computeMerkleRoot(hashes) {
|
|
722
|
-
if (hashes.length === 0) {
|
|
723
|
-
return sha256("");
|
|
724
|
-
}
|
|
725
|
-
let level = hashes.slice();
|
|
726
|
-
while (level.length > 1) {
|
|
727
|
-
const nextLevel = [];
|
|
728
|
-
for (let i = 0; i < level.length; i += 2) {
|
|
729
|
-
const left = level[i];
|
|
730
|
-
const right = level[i + 1] ?? left;
|
|
731
|
-
nextLevel.push(sha256(`${left}:${right}`));
|
|
732
|
-
}
|
|
733
|
-
level = nextLevel;
|
|
734
|
-
}
|
|
735
|
-
return level[0];
|
|
736
|
-
}
|
|
737
|
-
function computeBlockHash(previousHash, merkleRoot, date, version) {
|
|
738
|
-
return sha256(`${version}:${date}:${previousHash}:${merkleRoot}`);
|
|
739
|
-
}
|
|
740
|
-
function createBlock(date, now, previousHash) {
|
|
741
|
-
return {
|
|
742
|
-
version: LEDGER_VERSION,
|
|
743
|
-
date,
|
|
744
|
-
entries: [],
|
|
745
|
-
previousHash,
|
|
746
|
-
merkleRoot: "",
|
|
747
|
-
blockHash: "",
|
|
748
|
-
createdAt: now,
|
|
749
|
-
updatedAt: now
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
async function loadLedger(projectDir) {
|
|
753
|
-
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
754
|
-
try {
|
|
755
|
-
if (!existsSync3(ledgerPath)) return [];
|
|
756
|
-
const content = await readFile3(ledgerPath, "utf-8");
|
|
757
|
-
const parsed = JSON.parse(content);
|
|
758
|
-
if (!Array.isArray(parsed)) return [];
|
|
759
|
-
return parsed;
|
|
760
|
-
} catch {
|
|
761
|
-
return [];
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
async function saveLedger(blocks, projectDir) {
|
|
765
|
-
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
766
|
-
await atomicWriteJSON(ledgerPath, blocks);
|
|
767
|
-
}
|
|
768
|
-
function sha256(input) {
|
|
769
|
-
return createHash("sha256").update(input).digest("hex");
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// src/memory/issue-store.ts
|
|
773
|
-
async function storeIssues(issues, project, workDir) {
|
|
774
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
775
|
-
const memoryDir = join4(getTrieDirectory(projectDir), "memory");
|
|
776
|
-
await mkdir4(memoryDir, { recursive: true });
|
|
777
|
-
const stored = [];
|
|
778
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
779
|
-
const seenHashes = /* @__PURE__ */ new Set();
|
|
780
|
-
let duplicates = 0;
|
|
781
|
-
for (const issue of issues) {
|
|
782
|
-
const hash = hashIssue(issue);
|
|
783
|
-
if (seenHashes.has(hash)) {
|
|
784
|
-
duplicates++;
|
|
785
|
-
continue;
|
|
786
|
-
}
|
|
787
|
-
seenHashes.add(hash);
|
|
788
|
-
const storedIssue = {
|
|
789
|
-
id: issue.id,
|
|
790
|
-
hash,
|
|
791
|
-
severity: issue.severity,
|
|
792
|
-
issue: issue.issue,
|
|
793
|
-
fix: issue.fix,
|
|
794
|
-
file: issue.file,
|
|
795
|
-
line: issue.line,
|
|
796
|
-
agent: issue.agent,
|
|
797
|
-
category: issue.category,
|
|
798
|
-
timestamp: now,
|
|
799
|
-
project,
|
|
800
|
-
resolved: false,
|
|
801
|
-
resolvedAt: void 0
|
|
802
|
-
};
|
|
803
|
-
stored.push(storedIssue);
|
|
804
|
-
}
|
|
805
|
-
await appendToDailyLog(stored, projectDir);
|
|
806
|
-
await appendIssuesToLedger(stored, projectDir);
|
|
807
|
-
const dedupedCount = await updateIssueIndex(stored, projectDir);
|
|
808
|
-
return { stored: dedupedCount, duplicates: duplicates + (stored.length - dedupedCount) };
|
|
809
|
-
}
|
|
810
|
-
async function searchIssues(query, options = {}) {
|
|
811
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
812
|
-
const limit = options.limit || 10;
|
|
813
|
-
const allIssues = await loadIssueIndex(projectDir);
|
|
814
|
-
if (allIssues.length === 0) {
|
|
815
|
-
return [];
|
|
816
|
-
}
|
|
817
|
-
const filteredIssues = allIssues.filter((issue) => {
|
|
818
|
-
if (options.project && issue.project !== options.project) return false;
|
|
819
|
-
if (options.severity && !options.severity.includes(issue.severity)) return false;
|
|
820
|
-
if (options.agent && issue.agent !== options.agent) return false;
|
|
821
|
-
if (!options.includeResolved && issue.resolved) return false;
|
|
822
|
-
return true;
|
|
823
|
-
});
|
|
824
|
-
if (filteredIssues.length === 0) {
|
|
825
|
-
return [];
|
|
826
|
-
}
|
|
827
|
-
const bm25 = new BM25Index();
|
|
828
|
-
const issueMap = /* @__PURE__ */ new Map();
|
|
829
|
-
for (const issue of filteredIssues) {
|
|
830
|
-
const searchText = `${issue.issue} ${issue.fix} ${issue.file} ${issue.agent} ${issue.category || ""} ${issue.severity}`;
|
|
831
|
-
bm25.addDocument({
|
|
832
|
-
id: issue.id,
|
|
833
|
-
text: searchText
|
|
834
|
-
});
|
|
835
|
-
issueMap.set(issue.id, issue);
|
|
836
|
-
}
|
|
837
|
-
const bm25Results = bm25.search(query, limit);
|
|
838
|
-
return bm25Results.map((result) => ({
|
|
839
|
-
issue: issueMap.get(result.id),
|
|
840
|
-
score: result.score,
|
|
841
|
-
matchType: "bm25"
|
|
842
|
-
}));
|
|
843
|
-
}
|
|
844
|
-
async function findSimilarIssues(issue, options = {}) {
|
|
845
|
-
const query = `${issue.issue} ${issue.fix} ${issue.agent}`;
|
|
846
|
-
const searchOptions = {
|
|
847
|
-
limit: (options.limit || 5) + 5,
|
|
848
|
-
// Get extra to account for filtering
|
|
849
|
-
includeResolved: true
|
|
850
|
-
};
|
|
851
|
-
if (options.workDir !== void 0) {
|
|
852
|
-
searchOptions.workDir = options.workDir;
|
|
853
|
-
}
|
|
854
|
-
const results = await searchIssues(query, searchOptions);
|
|
855
|
-
let filtered = results.filter((r) => r.issue.id !== issue.id);
|
|
856
|
-
if (options.excludeSameFile) {
|
|
857
|
-
filtered = filtered.filter((r) => r.issue.file !== issue.file);
|
|
858
|
-
}
|
|
859
|
-
return filtered.slice(0, options.limit || 5);
|
|
860
|
-
}
|
|
861
|
-
async function markIssueResolved(issueId, workDir) {
|
|
862
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
863
|
-
const index = await loadIssueIndex(projectDir);
|
|
864
|
-
const issue = index.find((i) => i.id === issueId);
|
|
865
|
-
if (!issue) return false;
|
|
866
|
-
issue.resolved = true;
|
|
867
|
-
issue.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
868
|
-
await saveIssueIndex(index, projectDir);
|
|
869
|
-
return true;
|
|
870
|
-
}
|
|
871
|
-
async function autoResolveIssues(newIssueHashes, scannedFiles, workDir) {
|
|
872
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
873
|
-
const index = await loadIssueIndex(projectDir);
|
|
874
|
-
const scannedFileSet = new Set(scannedFiles.map((f) => f.replace(/\\/g, "/")));
|
|
875
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
876
|
-
let resolvedCount = 0;
|
|
877
|
-
let stillActiveCount = 0;
|
|
878
|
-
for (const issue of index) {
|
|
879
|
-
if (issue.resolved) continue;
|
|
880
|
-
const normalizedFile = issue.file.replace(/\\/g, "/");
|
|
881
|
-
if (!scannedFileSet.has(normalizedFile)) {
|
|
882
|
-
stillActiveCount++;
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
885
|
-
if (!newIssueHashes.has(issue.hash)) {
|
|
886
|
-
issue.resolved = true;
|
|
887
|
-
issue.resolvedAt = now;
|
|
888
|
-
resolvedCount++;
|
|
889
|
-
} else {
|
|
890
|
-
stillActiveCount++;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (resolvedCount > 0) {
|
|
894
|
-
await saveIssueIndex(index, projectDir);
|
|
895
|
-
}
|
|
896
|
-
return { resolved: resolvedCount, stillActive: stillActiveCount };
|
|
897
|
-
}
|
|
898
|
-
function getIssueHash(issue) {
|
|
899
|
-
return hashIssue(issue);
|
|
900
|
-
}
|
|
901
|
-
async function getMemoryStats(workDir) {
|
|
902
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
903
|
-
const index = await loadIssueIndex(projectDir);
|
|
904
|
-
const historical = await getHistoricalInsights(projectDir);
|
|
905
|
-
const MAX_ISSUES = 1e4;
|
|
906
|
-
const uniqueHashes = new Set(index.map((i) => i.hash));
|
|
907
|
-
const stats = {
|
|
908
|
-
totalIssues: index.length,
|
|
909
|
-
activeIssues: 0,
|
|
910
|
-
issuesByAgent: {},
|
|
911
|
-
issuesBySeverity: {},
|
|
912
|
-
activeIssuesBySeverity: {},
|
|
913
|
-
oldestIssue: void 0,
|
|
914
|
-
newestIssue: void 0,
|
|
915
|
-
resolvedCount: 0,
|
|
916
|
-
historicalIssues: historical.totalHistoricalIssues,
|
|
917
|
-
improvementTrend: historical.improvementTrend,
|
|
918
|
-
capacityInfo: {
|
|
919
|
-
current: index.length,
|
|
920
|
-
max: MAX_ISSUES,
|
|
921
|
-
percentFull: Math.round(index.length / MAX_ISSUES * 100),
|
|
922
|
-
isAtCap: index.length >= MAX_ISSUES
|
|
923
|
-
},
|
|
924
|
-
deduplicationStats: {
|
|
925
|
-
duplicatesAvoided: index.length - uniqueHashes.size,
|
|
926
|
-
uniquePatterns: uniqueHashes.size
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
for (const issue of index) {
|
|
930
|
-
stats.issuesByAgent[issue.agent] = (stats.issuesByAgent[issue.agent] || 0) + 1;
|
|
931
|
-
stats.issuesBySeverity[issue.severity] = (stats.issuesBySeverity[issue.severity] || 0) + 1;
|
|
932
|
-
if (issue.resolved) {
|
|
933
|
-
stats.resolvedCount++;
|
|
934
|
-
} else {
|
|
935
|
-
stats.activeIssues++;
|
|
936
|
-
stats.activeIssuesBySeverity[issue.severity] = (stats.activeIssuesBySeverity[issue.severity] || 0) + 1;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (index.length > 0) {
|
|
940
|
-
const sorted = [...index].sort(
|
|
941
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
942
|
-
);
|
|
943
|
-
const oldest = sorted[0]?.timestamp;
|
|
944
|
-
const newest = sorted[sorted.length - 1]?.timestamp;
|
|
945
|
-
if (oldest !== void 0) {
|
|
946
|
-
stats.oldestIssue = oldest;
|
|
947
|
-
}
|
|
948
|
-
if (newest !== void 0) {
|
|
949
|
-
stats.newestIssue = newest;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
return stats;
|
|
953
|
-
}
|
|
954
|
-
async function getRecentIssues(options = {}) {
|
|
955
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
956
|
-
const index = await loadIssueIndex(projectDir);
|
|
957
|
-
const limit = options.limit || 20;
|
|
958
|
-
const daysBack = options.daysBack || 7;
|
|
959
|
-
const includeResolved = options.includeResolved ?? false;
|
|
960
|
-
const cutoff = /* @__PURE__ */ new Date();
|
|
961
|
-
cutoff.setDate(cutoff.getDate() - daysBack);
|
|
962
|
-
return index.filter((i) => {
|
|
963
|
-
if (new Date(i.timestamp) < cutoff) return false;
|
|
964
|
-
if (!includeResolved && i.resolved) return false;
|
|
965
|
-
return true;
|
|
966
|
-
}).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
|
|
967
|
-
}
|
|
968
|
-
async function purgeIssues(strategy, options = {}) {
|
|
969
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
970
|
-
const index = await loadIssueIndex(projectDir);
|
|
971
|
-
const originalCount = index.length;
|
|
972
|
-
let remaining = [];
|
|
973
|
-
switch (strategy) {
|
|
974
|
-
case "smart":
|
|
975
|
-
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
976
|
-
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
977
|
-
remaining = index.filter((i) => {
|
|
978
|
-
const isRecent = new Date(i.timestamp) >= thirtyDaysAgo;
|
|
979
|
-
const isImportant = ["critical", "high"].includes(i.severity);
|
|
980
|
-
const isUnresolved = !i.resolved;
|
|
981
|
-
return isRecent || isImportant || isUnresolved;
|
|
982
|
-
});
|
|
983
|
-
break;
|
|
984
|
-
case "resolved":
|
|
985
|
-
remaining = index.filter((i) => !i.resolved);
|
|
986
|
-
break;
|
|
987
|
-
case "old":
|
|
988
|
-
const daysOld = options.daysOld || 90;
|
|
989
|
-
const cutoffDate = /* @__PURE__ */ new Date();
|
|
990
|
-
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
991
|
-
remaining = index.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
992
|
-
break;
|
|
993
|
-
case "all":
|
|
994
|
-
remaining = [];
|
|
995
|
-
break;
|
|
996
|
-
}
|
|
997
|
-
await saveIssueIndex(remaining, projectDir);
|
|
998
|
-
return {
|
|
999
|
-
removed: originalCount - remaining.length,
|
|
1000
|
-
remaining: remaining.length,
|
|
1001
|
-
strategy
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
async function getDailyLogs(workDir) {
|
|
1005
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1006
|
-
const memoryDir = join4(getTrieDirectory(projectDir), "memory");
|
|
1007
|
-
try {
|
|
1008
|
-
if (!existsSync4(memoryDir)) return [];
|
|
1009
|
-
const files = await readdir2(memoryDir);
|
|
1010
|
-
return files.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort().reverse();
|
|
1011
|
-
} catch {
|
|
1012
|
-
return [];
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
async function appendToDailyLog(issues, projectDir) {
|
|
1016
|
-
const memoryDir = join4(getTrieDirectory(projectDir), "memory");
|
|
1017
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1018
|
-
const logPath = join4(memoryDir, `${today}.md`);
|
|
1019
|
-
let content = "";
|
|
1020
|
-
try {
|
|
1021
|
-
if (existsSync4(logPath)) {
|
|
1022
|
-
content = await readFile4(logPath, "utf-8");
|
|
1023
|
-
} else {
|
|
1024
|
-
content = `# Issue Log: ${today}
|
|
1025
|
-
|
|
1026
|
-
`;
|
|
1027
|
-
}
|
|
1028
|
-
} catch {
|
|
1029
|
-
content = `# Issue Log: ${today}
|
|
1030
|
-
|
|
1031
|
-
`;
|
|
1032
|
-
}
|
|
1033
|
-
const time = (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0];
|
|
1034
|
-
const newEntries = issues.map(
|
|
1035
|
-
(i) => `## [${time}] ${i.severity.toUpperCase()}: ${i.issue.slice(0, 80)}${i.issue.length > 80 ? "..." : ""}
|
|
1036
|
-
- **File:** \`${i.file}\`${i.line ? `:${i.line}` : ""}
|
|
1037
|
-
- **Agent:** ${i.agent}
|
|
1038
|
-
- **Fix:** ${i.fix.slice(0, 200)}${i.fix.length > 200 ? "..." : ""}
|
|
1039
|
-
`
|
|
1040
|
-
).join("\n");
|
|
1041
|
-
content += newEntries + "\n";
|
|
1042
|
-
await writeFile2(logPath, content);
|
|
1043
|
-
}
|
|
1044
|
-
async function loadIssueIndex(projectDir) {
|
|
1045
|
-
const indexPath = join4(getTrieDirectory(projectDir), "memory", "issues.json");
|
|
1046
|
-
try {
|
|
1047
|
-
if (existsSync4(indexPath)) {
|
|
1048
|
-
const content = await readFile4(indexPath, "utf-8");
|
|
1049
|
-
const result = safeParseAndValidate(content, IssueIndexSchema);
|
|
1050
|
-
if (result.success) {
|
|
1051
|
-
return result.data;
|
|
1052
|
-
}
|
|
1053
|
-
console.error(` Issue index corrupted: ${result.error}`);
|
|
1054
|
-
const backupManager = new BackupManager(indexPath);
|
|
1055
|
-
if (await backupManager.recoverFromBackup()) {
|
|
1056
|
-
console.error(" \u2705 Recovered from backup");
|
|
1057
|
-
const recovered = await readFile4(indexPath, "utf-8");
|
|
1058
|
-
const recoveredResult = safeParseAndValidate(recovered, IssueIndexSchema);
|
|
1059
|
-
if (recoveredResult.success) {
|
|
1060
|
-
return recoveredResult.data;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
console.error(" No valid backup found, starting fresh");
|
|
1064
|
-
}
|
|
1065
|
-
} catch {
|
|
1066
|
-
}
|
|
1067
|
-
return [];
|
|
1068
|
-
}
|
|
1069
|
-
async function updateIssueIndex(newIssues, projectDir) {
|
|
1070
|
-
const memoryDir = join4(getTrieDirectory(projectDir), "memory");
|
|
1071
|
-
await mkdir4(memoryDir, { recursive: true });
|
|
1072
|
-
let existing = await loadIssueIndex(projectDir);
|
|
1073
|
-
const hashSet = new Set(existing.map((i) => i.hash));
|
|
1074
|
-
const toAdd = newIssues.filter((i) => !hashSet.has(i.hash));
|
|
1075
|
-
const dedupedCount = toAdd.length;
|
|
1076
|
-
existing = [...existing, ...toAdd];
|
|
1077
|
-
if (existing.length > 500) {
|
|
1078
|
-
const { summary, remaining } = await compactOldIssues(existing, {
|
|
1079
|
-
keepDays: 30,
|
|
1080
|
-
minIssuesToCompact: 100
|
|
1081
|
-
});
|
|
1082
|
-
if (summary) {
|
|
1083
|
-
await saveCompactedSummary(summary, projectDir);
|
|
1084
|
-
existing = remaining;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
if (existing.length > 1e4) {
|
|
1088
|
-
existing = intelligentPrune(existing, 1e4);
|
|
1089
|
-
}
|
|
1090
|
-
await saveIssueIndex(existing, projectDir);
|
|
1091
|
-
return dedupedCount;
|
|
1092
|
-
}
|
|
1093
|
-
function intelligentPrune(issues, targetCount) {
|
|
1094
|
-
const severityWeight = {
|
|
1095
|
-
critical: 100,
|
|
1096
|
-
high: 50,
|
|
1097
|
-
moderate: 20,
|
|
1098
|
-
low: 10,
|
|
1099
|
-
info: 5
|
|
1100
|
-
};
|
|
1101
|
-
const scored = issues.map((issue) => {
|
|
1102
|
-
const ageInDays = (Date.now() - new Date(issue.timestamp).getTime()) / (1e3 * 60 * 60 * 24);
|
|
1103
|
-
const recencyScore = Math.max(0, 100 - ageInDays * 2);
|
|
1104
|
-
const severityScore = severityWeight[issue.severity] || 10;
|
|
1105
|
-
const resolvedPenalty = issue.resolved ? -50 : 0;
|
|
1106
|
-
return {
|
|
1107
|
-
issue,
|
|
1108
|
-
score: recencyScore + severityScore + resolvedPenalty
|
|
1109
|
-
};
|
|
1110
|
-
});
|
|
1111
|
-
return scored.sort((a, b) => b.score - a.score).slice(0, targetCount).map((s) => s.issue);
|
|
1112
|
-
}
|
|
1113
|
-
async function saveIssueIndex(issues, projectDir) {
|
|
1114
|
-
const memoryDir = join4(getTrieDirectory(projectDir), "memory");
|
|
1115
|
-
await mkdir4(memoryDir, { recursive: true });
|
|
1116
|
-
const indexPath = join4(memoryDir, "issues.json");
|
|
1117
|
-
const backupManager = new BackupManager(indexPath);
|
|
1118
|
-
await backupManager.createBackup();
|
|
1119
|
-
await atomicWriteJSON(indexPath, issues);
|
|
1120
|
-
}
|
|
1121
|
-
function hashIssue(issue) {
|
|
1122
|
-
const content = `${issue.issue}|${issue.file}|${issue.severity}|${issue.agent}`;
|
|
1123
|
-
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
// src/guardian/guardian-state.ts
|
|
1127
|
-
import { mkdir as mkdir5, readFile as readFile5 } from "fs/promises";
|
|
1128
|
-
import { existsSync as existsSync5 } from "fs";
|
|
1129
|
-
import { join as join5 } from "path";
|
|
1130
|
-
import { z as z2 } from "zod";
|
|
1131
|
-
var GoalSchema = z2.object({
|
|
1132
|
-
id: z2.string(),
|
|
1133
|
-
description: z2.string(),
|
|
1134
|
-
type: z2.enum(["streak", "reduction", "score", "custom"]),
|
|
1135
|
-
metric: z2.string(),
|
|
1136
|
-
target: z2.number(),
|
|
1137
|
-
currentValue: z2.number(),
|
|
1138
|
-
startValue: z2.number().optional(),
|
|
1139
|
-
status: z2.enum(["active", "achieved", "failed", "paused", "rejected"]),
|
|
1140
|
-
autoGenerated: z2.boolean(),
|
|
1141
|
-
confidence: z2.number().min(0).max(1).optional(),
|
|
1142
|
-
// For auto-generated goals
|
|
1143
|
-
createdAt: z2.string(),
|
|
1144
|
-
updatedAt: z2.string(),
|
|
1145
|
-
achievedAt: z2.string().optional(),
|
|
1146
|
-
deadline: z2.string().optional(),
|
|
1147
|
-
category: z2.enum(["security", "quality", "performance", "coverage", "general"]).optional(),
|
|
1148
|
-
evidence: z2.array(z2.string()).optional()
|
|
1149
|
-
// Why this goal was generated
|
|
1150
|
-
});
|
|
1151
|
-
var HypothesisSchema = z2.object({
|
|
1152
|
-
id: z2.string(),
|
|
1153
|
-
statement: z2.string(),
|
|
1154
|
-
confidence: z2.number().min(0).max(1),
|
|
1155
|
-
// 0-1, adjusts based on validation
|
|
1156
|
-
status: z2.enum(["proposed", "testing", "validated", "invalidated", "retired"]),
|
|
1157
|
-
evidence: z2.array(z2.object({
|
|
1158
|
-
type: z2.enum(["supporting", "contradicting"]),
|
|
1159
|
-
description: z2.string(),
|
|
1160
|
-
timestamp: z2.string(),
|
|
1161
|
-
weight: z2.number().min(0).max(1).optional()
|
|
1162
|
-
})),
|
|
1163
|
-
createdAt: z2.string(),
|
|
1164
|
-
updatedAt: z2.string(),
|
|
1165
|
-
validatedAt: z2.string().optional(),
|
|
1166
|
-
testCriteria: z2.string().optional(),
|
|
1167
|
-
category: z2.enum(["timing", "pattern", "team", "code", "general"]).optional()
|
|
1168
|
-
});
|
|
1169
|
-
var RiskBudgetSchema = z2.object({
|
|
1170
|
-
daily: z2.number(),
|
|
1171
|
-
weekly: z2.number(),
|
|
1172
|
-
usedToday: z2.number(),
|
|
1173
|
-
usedThisWeek: z2.number(),
|
|
1174
|
-
lastResetDay: z2.string(),
|
|
1175
|
-
// ISO date (YYYY-MM-DD)
|
|
1176
|
-
lastResetWeek: z2.string()
|
|
1177
|
-
// ISO week (YYYY-WNN)
|
|
1178
|
-
});
|
|
1179
|
-
var AgentMetricsSchema = z2.object({
|
|
1180
|
-
predictiveAccuracy: z2.number().min(0).max(1),
|
|
1181
|
-
falsePositiveRate: z2.number().min(0).max(1),
|
|
1182
|
-
userSatisfaction: z2.number().min(0).max(1),
|
|
1183
|
-
hypothesisAccuracy: z2.number().min(0).max(1),
|
|
1184
|
-
totalPredictions: z2.number(),
|
|
1185
|
-
correctPredictions: z2.number(),
|
|
1186
|
-
totalInsights: z2.number(),
|
|
1187
|
-
helpfulInsights: z2.number(),
|
|
1188
|
-
dismissedInsights: z2.number(),
|
|
1189
|
-
actedOnInsights: z2.number()
|
|
1190
|
-
});
|
|
1191
|
-
var TimingContextSchema = z2.object({
|
|
1192
|
-
quietHoursStart: z2.number().min(0).max(23),
|
|
1193
|
-
// Hour of day (0-23)
|
|
1194
|
-
quietHoursEnd: z2.number().min(0).max(23),
|
|
1195
|
-
quietHoursEnabled: z2.boolean(),
|
|
1196
|
-
workDays: z2.array(z2.number().min(0).max(6)),
|
|
1197
|
-
// 0 = Sunday
|
|
1198
|
-
lastActiveTimestamp: z2.number(),
|
|
1199
|
-
timezone: z2.string().optional(),
|
|
1200
|
-
crunchMode: z2.boolean(),
|
|
1201
|
-
// During crunch, defer low-priority items
|
|
1202
|
-
crunchModeUntil: z2.string().optional()
|
|
1203
|
-
});
|
|
1204
|
-
var GuardianStateDataSchema = z2.object({
|
|
1205
|
-
version: z2.literal(1),
|
|
1206
|
-
goals: z2.array(GoalSchema),
|
|
1207
|
-
hypotheses: z2.array(HypothesisSchema),
|
|
1208
|
-
riskBudget: RiskBudgetSchema,
|
|
1209
|
-
metrics: AgentMetricsSchema,
|
|
1210
|
-
timing: TimingContextSchema,
|
|
1211
|
-
scanFrequencyMs: z2.number(),
|
|
1212
|
-
// Current scan frequency
|
|
1213
|
-
lastScanTimestamp: z2.number().optional(),
|
|
1214
|
-
lastUpdated: z2.string()
|
|
1215
|
-
});
|
|
1216
|
-
var GuardianState = class {
|
|
1217
|
-
projectPath;
|
|
1218
|
-
data;
|
|
1219
|
-
loaded = false;
|
|
1220
|
-
dirty = false;
|
|
1221
|
-
constructor(projectPath) {
|
|
1222
|
-
this.projectPath = projectPath;
|
|
1223
|
-
this.data = this.createDefaultState();
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Get the storage file path
|
|
1227
|
-
*/
|
|
1228
|
-
getStorePath() {
|
|
1229
|
-
return join5(getTrieDirectory(this.projectPath), "memory", "guardian-state.json");
|
|
1230
|
-
}
|
|
1231
|
-
/**
|
|
1232
|
-
* Create default state
|
|
1233
|
-
*/
|
|
1234
|
-
createDefaultState() {
|
|
1235
|
-
const now = /* @__PURE__ */ new Date();
|
|
1236
|
-
const today = now.toISOString().split("T")[0];
|
|
1237
|
-
const week = this.getISOWeek(now);
|
|
1238
|
-
return {
|
|
1239
|
-
version: 1,
|
|
1240
|
-
goals: [],
|
|
1241
|
-
hypotheses: [],
|
|
1242
|
-
riskBudget: {
|
|
1243
|
-
daily: 10,
|
|
1244
|
-
weekly: 50,
|
|
1245
|
-
usedToday: 0,
|
|
1246
|
-
usedThisWeek: 0,
|
|
1247
|
-
lastResetDay: today,
|
|
1248
|
-
lastResetWeek: week
|
|
1249
|
-
},
|
|
1250
|
-
metrics: {
|
|
1251
|
-
predictiveAccuracy: 0.5,
|
|
1252
|
-
// Start neutral
|
|
1253
|
-
falsePositiveRate: 0.5,
|
|
1254
|
-
userSatisfaction: 0.5,
|
|
1255
|
-
hypothesisAccuracy: 0.5,
|
|
1256
|
-
totalPredictions: 0,
|
|
1257
|
-
correctPredictions: 0,
|
|
1258
|
-
totalInsights: 0,
|
|
1259
|
-
helpfulInsights: 0,
|
|
1260
|
-
dismissedInsights: 0,
|
|
1261
|
-
actedOnInsights: 0
|
|
1262
|
-
},
|
|
1263
|
-
timing: {
|
|
1264
|
-
quietHoursStart: 21,
|
|
1265
|
-
// 9 PM
|
|
1266
|
-
quietHoursEnd: 8,
|
|
1267
|
-
// 8 AM
|
|
1268
|
-
quietHoursEnabled: true,
|
|
1269
|
-
workDays: [1, 2, 3, 4, 5],
|
|
1270
|
-
// Mon-Fri
|
|
1271
|
-
lastActiveTimestamp: Date.now(),
|
|
1272
|
-
crunchMode: false
|
|
1273
|
-
},
|
|
1274
|
-
scanFrequencyMs: 3e5,
|
|
1275
|
-
// 5 minutes default
|
|
1276
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1277
|
-
};
|
|
1278
|
-
}
|
|
1279
|
-
/**
|
|
1280
|
-
* Get ISO week string (YYYY-WNN)
|
|
1281
|
-
*/
|
|
1282
|
-
getISOWeek(date) {
|
|
1283
|
-
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
1284
|
-
const dayNum = d.getUTCDay() || 7;
|
|
1285
|
-
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
1286
|
-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
1287
|
-
const weekNo = Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
1288
|
-
return `${d.getUTCFullYear()}-W${weekNo.toString().padStart(2, "0")}`;
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Load state from disk
|
|
1292
|
-
*/
|
|
1293
|
-
async load() {
|
|
1294
|
-
if (this.loaded) {
|
|
1295
|
-
return this.data;
|
|
1296
|
-
}
|
|
1297
|
-
const storePath = this.getStorePath();
|
|
1298
|
-
try {
|
|
1299
|
-
if (existsSync5(storePath)) {
|
|
1300
|
-
const content = await readFile5(storePath, "utf-8");
|
|
1301
|
-
const result = safeParseAndValidate(content, GuardianStateDataSchema);
|
|
1302
|
-
if (result.success) {
|
|
1303
|
-
this.data = result.data;
|
|
1304
|
-
this.loaded = true;
|
|
1305
|
-
await this.checkAndResetBudgets();
|
|
1306
|
-
return this.data;
|
|
1307
|
-
}
|
|
1308
|
-
console.error(` Guardian state corrupted: ${result.error}`);
|
|
1309
|
-
const backupManager = new BackupManager(storePath);
|
|
1310
|
-
if (await backupManager.recoverFromBackup()) {
|
|
1311
|
-
console.error(" \u2705 Recovered from backup");
|
|
1312
|
-
const recovered = await readFile5(storePath, "utf-8");
|
|
1313
|
-
const recoveredResult = safeParseAndValidate(recovered, GuardianStateDataSchema);
|
|
1314
|
-
if (recoveredResult.success) {
|
|
1315
|
-
this.data = recoveredResult.data;
|
|
1316
|
-
this.loaded = true;
|
|
1317
|
-
return this.data;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
console.error(" No valid backup found, starting fresh");
|
|
1321
|
-
}
|
|
1322
|
-
} catch (error) {
|
|
1323
|
-
console.error(` Could not load guardian state: ${error}`);
|
|
1324
|
-
}
|
|
1325
|
-
this.data = this.createDefaultState();
|
|
1326
|
-
this.loaded = true;
|
|
1327
|
-
return this.data;
|
|
1328
|
-
}
|
|
1329
|
-
/**
|
|
1330
|
-
* Save state to disk
|
|
1331
|
-
*/
|
|
1332
|
-
async save() {
|
|
1333
|
-
if (!this.dirty && this.loaded) {
|
|
1334
|
-
return;
|
|
1335
|
-
}
|
|
1336
|
-
const storePath = this.getStorePath();
|
|
1337
|
-
const memoryDir = join5(getTrieDirectory(this.projectPath), "memory");
|
|
1338
|
-
await mkdir5(memoryDir, { recursive: true });
|
|
1339
|
-
const backupManager = new BackupManager(storePath);
|
|
1340
|
-
await backupManager.createBackup();
|
|
1341
|
-
this.data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
1342
|
-
await atomicWriteJSON(storePath, this.data);
|
|
1343
|
-
this.dirty = false;
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Check and reset daily/weekly budgets if needed
|
|
1347
|
-
*/
|
|
1348
|
-
async checkAndResetBudgets() {
|
|
1349
|
-
const now = /* @__PURE__ */ new Date();
|
|
1350
|
-
const today = now.toISOString().split("T")[0];
|
|
1351
|
-
const week = this.getISOWeek(now);
|
|
1352
|
-
let needsSave = false;
|
|
1353
|
-
if (this.data.riskBudget.lastResetDay !== today) {
|
|
1354
|
-
this.data.riskBudget.usedToday = 0;
|
|
1355
|
-
this.data.riskBudget.lastResetDay = today;
|
|
1356
|
-
needsSave = true;
|
|
1357
|
-
}
|
|
1358
|
-
if (this.data.riskBudget.lastResetWeek !== week) {
|
|
1359
|
-
this.data.riskBudget.usedThisWeek = 0;
|
|
1360
|
-
this.data.riskBudget.lastResetWeek = week;
|
|
1361
|
-
needsSave = true;
|
|
1362
|
-
}
|
|
1363
|
-
if (needsSave) {
|
|
1364
|
-
this.dirty = true;
|
|
1365
|
-
await this.save();
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
// ========================================================================
|
|
1369
|
-
// Goals
|
|
1370
|
-
// ========================================================================
|
|
1371
|
-
/**
|
|
1372
|
-
* Add a goal
|
|
1373
|
-
*/
|
|
1374
|
-
async addGoal(goal) {
|
|
1375
|
-
await this.load();
|
|
1376
|
-
if (this.data.goals.some((g) => g.id === goal.id)) {
|
|
1377
|
-
return false;
|
|
1378
|
-
}
|
|
1379
|
-
this.data.goals.push(goal);
|
|
1380
|
-
this.dirty = true;
|
|
1381
|
-
await this.save();
|
|
1382
|
-
return true;
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* Update a goal
|
|
1386
|
-
*/
|
|
1387
|
-
async updateGoal(goalId, updates) {
|
|
1388
|
-
await this.load();
|
|
1389
|
-
const goal = this.data.goals.find((g) => g.id === goalId);
|
|
1390
|
-
if (!goal) {
|
|
1391
|
-
return false;
|
|
1392
|
-
}
|
|
1393
|
-
Object.assign(goal, updates, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1394
|
-
this.dirty = true;
|
|
1395
|
-
await this.save();
|
|
1396
|
-
return true;
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Get active goals
|
|
1400
|
-
*/
|
|
1401
|
-
getActiveGoals() {
|
|
1402
|
-
return this.data.goals.filter((g) => g.status === "active");
|
|
1403
|
-
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Get all goals
|
|
1406
|
-
*/
|
|
1407
|
-
getAllGoals() {
|
|
1408
|
-
return [...this.data.goals];
|
|
1409
|
-
}
|
|
1410
|
-
/**
|
|
1411
|
-
* Get goal by ID
|
|
1412
|
-
*/
|
|
1413
|
-
getGoal(goalId) {
|
|
1414
|
-
return this.data.goals.find((g) => g.id === goalId);
|
|
1415
|
-
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Remove a goal
|
|
1418
|
-
*/
|
|
1419
|
-
async removeGoal(goalId) {
|
|
1420
|
-
await this.load();
|
|
1421
|
-
const index = this.data.goals.findIndex((g) => g.id === goalId);
|
|
1422
|
-
if (index === -1) {
|
|
1423
|
-
return false;
|
|
1424
|
-
}
|
|
1425
|
-
this.data.goals.splice(index, 1);
|
|
1426
|
-
this.dirty = true;
|
|
1427
|
-
await this.save();
|
|
1428
|
-
return true;
|
|
1429
|
-
}
|
|
1430
|
-
/**
|
|
1431
|
-
* Get auto-generated goals
|
|
1432
|
-
*/
|
|
1433
|
-
getAutoGeneratedGoals() {
|
|
1434
|
-
return this.data.goals.filter((g) => g.autoGenerated);
|
|
1435
|
-
}
|
|
1436
|
-
/**
|
|
1437
|
-
* Accept or reject an auto-generated goal
|
|
1438
|
-
*/
|
|
1439
|
-
async respondToGoal(goalId, accept) {
|
|
1440
|
-
return this.updateGoal(goalId, {
|
|
1441
|
-
status: accept ? "active" : "rejected"
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
// ========================================================================
|
|
1445
|
-
// Hypotheses
|
|
1446
|
-
// ========================================================================
|
|
1447
|
-
/**
|
|
1448
|
-
* Add a hypothesis
|
|
1449
|
-
*/
|
|
1450
|
-
async addHypothesis(hypothesis) {
|
|
1451
|
-
await this.load();
|
|
1452
|
-
if (this.data.hypotheses.some((h) => h.id === hypothesis.id)) {
|
|
1453
|
-
return false;
|
|
1454
|
-
}
|
|
1455
|
-
this.data.hypotheses.push(hypothesis);
|
|
1456
|
-
this.dirty = true;
|
|
1457
|
-
await this.save();
|
|
1458
|
-
return true;
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* Update a hypothesis
|
|
1462
|
-
*/
|
|
1463
|
-
async updateHypothesis(hypothesisId, updates) {
|
|
1464
|
-
await this.load();
|
|
1465
|
-
const hypothesis = this.data.hypotheses.find((h) => h.id === hypothesisId);
|
|
1466
|
-
if (!hypothesis) {
|
|
1467
|
-
return false;
|
|
1468
|
-
}
|
|
1469
|
-
Object.assign(hypothesis, updates, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1470
|
-
this.dirty = true;
|
|
1471
|
-
await this.save();
|
|
1472
|
-
return true;
|
|
1473
|
-
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Add evidence to a hypothesis
|
|
1476
|
-
*/
|
|
1477
|
-
async addEvidence(hypothesisId, evidence) {
|
|
1478
|
-
await this.load();
|
|
1479
|
-
const hypothesis = this.data.hypotheses.find((h) => h.id === hypothesisId);
|
|
1480
|
-
if (!hypothesis) {
|
|
1481
|
-
return false;
|
|
1482
|
-
}
|
|
1483
|
-
hypothesis.evidence.push({
|
|
1484
|
-
...evidence,
|
|
1485
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1486
|
-
});
|
|
1487
|
-
if (hypothesis.status === "proposed" && hypothesis.evidence.length === 1) {
|
|
1488
|
-
hypothesis.status = "testing";
|
|
1489
|
-
}
|
|
1490
|
-
const supportingCount = hypothesis.evidence.filter((e) => e.type === "supporting").length;
|
|
1491
|
-
const contradictingCount = hypothesis.evidence.filter((e) => e.type === "contradicting").length;
|
|
1492
|
-
const total = supportingCount + contradictingCount;
|
|
1493
|
-
if (total > 0) {
|
|
1494
|
-
hypothesis.confidence = supportingCount / total;
|
|
1495
|
-
if (hypothesis.status === "testing") {
|
|
1496
|
-
if (hypothesis.confidence > 0.8 && supportingCount >= 3) {
|
|
1497
|
-
hypothesis.status = "validated";
|
|
1498
|
-
hypothesis.validatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1499
|
-
} else if (hypothesis.confidence < 0.2 && contradictingCount >= 3) {
|
|
1500
|
-
hypothesis.status = "invalidated";
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
hypothesis.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1505
|
-
this.dirty = true;
|
|
1506
|
-
await this.save();
|
|
1507
|
-
return true;
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* Get active hypotheses
|
|
1511
|
-
*/
|
|
1512
|
-
getActiveHypotheses() {
|
|
1513
|
-
return this.data.hypotheses.filter(
|
|
1514
|
-
(h) => h.status === "proposed" || h.status === "testing"
|
|
1515
|
-
);
|
|
1516
|
-
}
|
|
1517
|
-
/**
|
|
1518
|
-
* Get validated hypotheses
|
|
1519
|
-
*/
|
|
1520
|
-
getValidatedHypotheses() {
|
|
1521
|
-
return this.data.hypotheses.filter((h) => h.status === "validated");
|
|
1522
|
-
}
|
|
1523
|
-
/**
|
|
1524
|
-
* Get all hypotheses
|
|
1525
|
-
*/
|
|
1526
|
-
getAllHypotheses() {
|
|
1527
|
-
return [...this.data.hypotheses];
|
|
1528
|
-
}
|
|
1529
|
-
/**
|
|
1530
|
-
* Get hypothesis by ID
|
|
1531
|
-
*/
|
|
1532
|
-
getHypothesis(hypothesisId) {
|
|
1533
|
-
return this.data.hypotheses.find((h) => h.id === hypothesisId);
|
|
1534
|
-
}
|
|
1535
|
-
// ========================================================================
|
|
1536
|
-
// Risk Budget
|
|
1537
|
-
// ========================================================================
|
|
1538
|
-
/**
|
|
1539
|
-
* Get current risk budget
|
|
1540
|
-
*/
|
|
1541
|
-
getRiskBudget() {
|
|
1542
|
-
return { ...this.data.riskBudget };
|
|
1543
|
-
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Use risk budget
|
|
1546
|
-
*/
|
|
1547
|
-
async useRiskBudget(amount) {
|
|
1548
|
-
await this.load();
|
|
1549
|
-
await this.checkAndResetBudgets();
|
|
1550
|
-
if (this.data.riskBudget.usedToday + amount > this.data.riskBudget.daily) {
|
|
1551
|
-
return false;
|
|
1552
|
-
}
|
|
1553
|
-
if (this.data.riskBudget.usedThisWeek + amount > this.data.riskBudget.weekly) {
|
|
1554
|
-
return false;
|
|
1555
|
-
}
|
|
1556
|
-
this.data.riskBudget.usedToday += amount;
|
|
1557
|
-
this.data.riskBudget.usedThisWeek += amount;
|
|
1558
|
-
this.dirty = true;
|
|
1559
|
-
await this.save();
|
|
1560
|
-
return true;
|
|
1561
|
-
}
|
|
1562
|
-
/**
|
|
1563
|
-
* Check if risk budget is available
|
|
1564
|
-
*/
|
|
1565
|
-
hasRiskBudget(amount = 1) {
|
|
1566
|
-
return this.data.riskBudget.usedToday + amount <= this.data.riskBudget.daily && this.data.riskBudget.usedThisWeek + amount <= this.data.riskBudget.weekly;
|
|
1567
|
-
}
|
|
1568
|
-
/**
|
|
1569
|
-
* Update risk budget limits
|
|
1570
|
-
*/
|
|
1571
|
-
async setRiskBudget(daily, weekly) {
|
|
1572
|
-
await this.load();
|
|
1573
|
-
this.data.riskBudget.daily = daily;
|
|
1574
|
-
this.data.riskBudget.weekly = weekly;
|
|
1575
|
-
this.dirty = true;
|
|
1576
|
-
await this.save();
|
|
1577
|
-
}
|
|
1578
|
-
// ========================================================================
|
|
1579
|
-
// Metrics
|
|
1580
|
-
// ========================================================================
|
|
1581
|
-
/**
|
|
1582
|
-
* Get agent metrics
|
|
1583
|
-
*/
|
|
1584
|
-
getMetrics() {
|
|
1585
|
-
return { ...this.data.metrics };
|
|
1586
|
-
}
|
|
1587
|
-
/**
|
|
1588
|
-
* Record a prediction outcome
|
|
1589
|
-
*/
|
|
1590
|
-
async recordPrediction(correct) {
|
|
1591
|
-
await this.load();
|
|
1592
|
-
this.data.metrics.totalPredictions++;
|
|
1593
|
-
if (correct) {
|
|
1594
|
-
this.data.metrics.correctPredictions++;
|
|
1595
|
-
}
|
|
1596
|
-
this.data.metrics.predictiveAccuracy = this.data.metrics.correctPredictions / this.data.metrics.totalPredictions;
|
|
1597
|
-
this.dirty = true;
|
|
1598
|
-
await this.save();
|
|
1599
|
-
}
|
|
1600
|
-
/**
|
|
1601
|
-
* Record user feedback on an insight
|
|
1602
|
-
*/
|
|
1603
|
-
async recordInsightFeedback(feedback) {
|
|
1604
|
-
await this.load();
|
|
1605
|
-
this.data.metrics.totalInsights++;
|
|
1606
|
-
switch (feedback) {
|
|
1607
|
-
case "helpful":
|
|
1608
|
-
this.data.metrics.helpfulInsights++;
|
|
1609
|
-
break;
|
|
1610
|
-
case "dismissed":
|
|
1611
|
-
this.data.metrics.dismissedInsights++;
|
|
1612
|
-
break;
|
|
1613
|
-
case "acted":
|
|
1614
|
-
this.data.metrics.actedOnInsights++;
|
|
1615
|
-
this.data.metrics.helpfulInsights++;
|
|
1616
|
-
break;
|
|
1617
|
-
}
|
|
1618
|
-
if (this.data.metrics.totalInsights > 0) {
|
|
1619
|
-
this.data.metrics.userSatisfaction = this.data.metrics.helpfulInsights / this.data.metrics.totalInsights;
|
|
1620
|
-
this.data.metrics.falsePositiveRate = this.data.metrics.dismissedInsights / this.data.metrics.totalInsights;
|
|
1621
|
-
}
|
|
1622
|
-
this.dirty = true;
|
|
1623
|
-
await this.save();
|
|
1624
|
-
}
|
|
1625
|
-
/**
|
|
1626
|
-
* Update hypothesis accuracy
|
|
1627
|
-
*/
|
|
1628
|
-
async updateHypothesisAccuracy() {
|
|
1629
|
-
await this.load();
|
|
1630
|
-
const validated = this.data.hypotheses.filter((h) => h.status === "validated").length;
|
|
1631
|
-
const invalidated = this.data.hypotheses.filter((h) => h.status === "invalidated").length;
|
|
1632
|
-
const total = validated + invalidated;
|
|
1633
|
-
if (total > 0) {
|
|
1634
|
-
this.data.metrics.hypothesisAccuracy = validated / total;
|
|
1635
|
-
this.dirty = true;
|
|
1636
|
-
await this.save();
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
// ========================================================================
|
|
1640
|
-
// Timing Context
|
|
1641
|
-
// ========================================================================
|
|
1642
|
-
/**
|
|
1643
|
-
* Get timing context
|
|
1644
|
-
*/
|
|
1645
|
-
getTimingContext() {
|
|
1646
|
-
return { ...this.data.timing };
|
|
1647
|
-
}
|
|
1648
|
-
/**
|
|
1649
|
-
* Check if currently in quiet hours
|
|
1650
|
-
*/
|
|
1651
|
-
isQuietHours() {
|
|
1652
|
-
if (!this.data.timing.quietHoursEnabled) {
|
|
1653
|
-
return false;
|
|
1654
|
-
}
|
|
1655
|
-
const now = /* @__PURE__ */ new Date();
|
|
1656
|
-
const hour = now.getHours();
|
|
1657
|
-
const start = this.data.timing.quietHoursStart;
|
|
1658
|
-
const end = this.data.timing.quietHoursEnd;
|
|
1659
|
-
if (start > end) {
|
|
1660
|
-
return hour >= start || hour < end;
|
|
1661
|
-
}
|
|
1662
|
-
return hour >= start && hour < end;
|
|
1663
|
-
}
|
|
1664
|
-
/**
|
|
1665
|
-
* Check if today is a work day
|
|
1666
|
-
*/
|
|
1667
|
-
isWorkDay() {
|
|
1668
|
-
const dayOfWeek = (/* @__PURE__ */ new Date()).getDay();
|
|
1669
|
-
return this.data.timing.workDays.includes(dayOfWeek);
|
|
1670
|
-
}
|
|
1671
|
-
/**
|
|
1672
|
-
* Check if in crunch mode
|
|
1673
|
-
*/
|
|
1674
|
-
isInCrunchMode() {
|
|
1675
|
-
if (!this.data.timing.crunchMode) {
|
|
1676
|
-
return false;
|
|
1677
|
-
}
|
|
1678
|
-
if (this.data.timing.crunchModeUntil) {
|
|
1679
|
-
const until = new Date(this.data.timing.crunchModeUntil);
|
|
1680
|
-
if (Date.now() > until.getTime()) {
|
|
1681
|
-
this.data.timing.crunchMode = false;
|
|
1682
|
-
this.data.timing.crunchModeUntil = void 0;
|
|
1683
|
-
this.dirty = true;
|
|
1684
|
-
return false;
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
return true;
|
|
1688
|
-
}
|
|
1689
|
-
/**
|
|
1690
|
-
* Enable crunch mode
|
|
1691
|
-
*/
|
|
1692
|
-
async setCrunchMode(enabled, until) {
|
|
1693
|
-
await this.load();
|
|
1694
|
-
this.data.timing.crunchMode = enabled;
|
|
1695
|
-
this.data.timing.crunchModeUntil = until?.toISOString();
|
|
1696
|
-
this.dirty = true;
|
|
1697
|
-
await this.save();
|
|
1698
|
-
}
|
|
1699
|
-
/**
|
|
1700
|
-
* Update quiet hours settings
|
|
1701
|
-
*/
|
|
1702
|
-
async setQuietHours(start, end, enabled = true) {
|
|
1703
|
-
await this.load();
|
|
1704
|
-
this.data.timing.quietHoursStart = start;
|
|
1705
|
-
this.data.timing.quietHoursEnd = end;
|
|
1706
|
-
this.data.timing.quietHoursEnabled = enabled;
|
|
1707
|
-
this.dirty = true;
|
|
1708
|
-
await this.save();
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Update work days
|
|
1712
|
-
*/
|
|
1713
|
-
async setWorkDays(days) {
|
|
1714
|
-
await this.load();
|
|
1715
|
-
this.data.timing.workDays = days;
|
|
1716
|
-
this.dirty = true;
|
|
1717
|
-
await this.save();
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Update last active timestamp
|
|
1721
|
-
*/
|
|
1722
|
-
async touchActive() {
|
|
1723
|
-
await this.load();
|
|
1724
|
-
this.data.timing.lastActiveTimestamp = Date.now();
|
|
1725
|
-
this.dirty = true;
|
|
1726
|
-
await this.save();
|
|
1727
|
-
}
|
|
1728
|
-
// ========================================================================
|
|
1729
|
-
// Scan Frequency
|
|
1730
|
-
// ========================================================================
|
|
1731
|
-
/**
|
|
1732
|
-
* Get current scan frequency in milliseconds
|
|
1733
|
-
*/
|
|
1734
|
-
getScanFrequencyMs() {
|
|
1735
|
-
return this.data.scanFrequencyMs;
|
|
1736
|
-
}
|
|
1737
|
-
/**
|
|
1738
|
-
* Set scan frequency
|
|
1739
|
-
*/
|
|
1740
|
-
async setScanFrequency(ms) {
|
|
1741
|
-
await this.load();
|
|
1742
|
-
this.data.scanFrequencyMs = Math.max(1e4, ms);
|
|
1743
|
-
this.dirty = true;
|
|
1744
|
-
await this.save();
|
|
1745
|
-
}
|
|
1746
|
-
/**
|
|
1747
|
-
* Record a scan timestamp
|
|
1748
|
-
*/
|
|
1749
|
-
async recordScan() {
|
|
1750
|
-
await this.load();
|
|
1751
|
-
this.data.lastScanTimestamp = Date.now();
|
|
1752
|
-
this.dirty = true;
|
|
1753
|
-
await this.save();
|
|
1754
|
-
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Get last scan timestamp
|
|
1757
|
-
*/
|
|
1758
|
-
getLastScanTimestamp() {
|
|
1759
|
-
return this.data.lastScanTimestamp;
|
|
1760
|
-
}
|
|
1761
|
-
// ========================================================================
|
|
1762
|
-
// Utility
|
|
1763
|
-
// ========================================================================
|
|
1764
|
-
/**
|
|
1765
|
-
* Get full state data
|
|
1766
|
-
*/
|
|
1767
|
-
getData() {
|
|
1768
|
-
return { ...this.data };
|
|
1769
|
-
}
|
|
1770
|
-
/**
|
|
1771
|
-
* Force reload from disk
|
|
1772
|
-
*/
|
|
1773
|
-
async reload() {
|
|
1774
|
-
this.loaded = false;
|
|
1775
|
-
this.dirty = false;
|
|
1776
|
-
return this.load();
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Check if loaded
|
|
1780
|
-
*/
|
|
1781
|
-
isLoaded() {
|
|
1782
|
-
return this.loaded;
|
|
1783
|
-
}
|
|
1784
|
-
};
|
|
1785
|
-
var guardianStates = /* @__PURE__ */ new Map();
|
|
1786
|
-
function getGuardianState(projectPath) {
|
|
1787
|
-
let state = guardianStates.get(projectPath);
|
|
1788
|
-
if (!state) {
|
|
1789
|
-
state = new GuardianState(projectPath);
|
|
1790
|
-
guardianStates.set(projectPath, state);
|
|
1791
|
-
}
|
|
1792
|
-
return state;
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
export {
|
|
1796
|
-
atomicWriteJSON,
|
|
1797
|
-
BackupManager,
|
|
1798
|
-
GlobalPatternsIndexSchema,
|
|
1799
|
-
safeParseAndValidate,
|
|
1800
|
-
getHistoricalInsights,
|
|
1801
|
-
storeIssues,
|
|
1802
|
-
searchIssues,
|
|
1803
|
-
findSimilarIssues,
|
|
1804
|
-
markIssueResolved,
|
|
1805
|
-
autoResolveIssues,
|
|
1806
|
-
getIssueHash,
|
|
1807
|
-
getMemoryStats,
|
|
1808
|
-
getRecentIssues,
|
|
1809
|
-
purgeIssues,
|
|
1810
|
-
getDailyLogs,
|
|
1811
|
-
getGuardianState
|
|
1812
|
-
};
|
|
1813
|
-
//# sourceMappingURL=chunk-6JPPYG7F.js.map
|