@triedotdev/mcp 1.0.148 → 1.0.149
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 +134 -73
- package/dist/{chunk-4PAAGLKO.js → chunk-53KUI7RQ.js} +6 -4
- package/dist/{chunk-4PAAGLKO.js.map → chunk-53KUI7RQ.js.map} +1 -1
- package/dist/{chunk-XTTZAQWJ.js → chunk-72KSLD7A.js} +4 -4
- package/dist/{chunk-WMDFK7LI.js → chunk-B2AHQ2IR.js} +12 -12
- package/dist/{chunk-LT6VUZG2.js → chunk-CU5VDH6F.js} +2 -2
- package/dist/{chunk-4MJ52WBH.js → chunk-EFWVF6TI.js} +4 -2
- package/dist/chunk-EFWVF6TI.js.map +1 -0
- package/dist/{chunk-N2EDZTKG.js → chunk-GAL7OIYU.js} +11 -11
- package/dist/{chunk-J7CEBSEB.js → chunk-HYNDXZAU.js} +23 -112
- package/dist/chunk-HYNDXZAU.js.map +1 -0
- package/dist/{chunk-YEIJW6X6.js → chunk-ILGMFND2.js} +4 -4
- package/dist/{chunk-3MUCUZ46.js → chunk-OTQEFXHU.js} +2 -2
- package/dist/chunk-QH77RQB3.js +783 -0
- package/dist/chunk-QH77RQB3.js.map +1 -0
- package/dist/{chunk-T6PS3MXJ.js → chunk-ZDDE442Q.js} +5 -5
- package/dist/{chunk-62POBLFC.js → chunk-ZUEAHFSY.js} +180 -965
- package/dist/chunk-ZUEAHFSY.js.map +1 -0
- package/dist/cli/main.js +85 -90
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +24 -148
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{fast-analyzer-MWKCDRGD.js → fast-analyzer-CTT3MCPE.js} +3 -3
- package/dist/{goal-manager-ZBWKWEML.js → goal-manager-IGUMDGCA.js} +7 -6
- package/dist/{goal-validator-DA3JQ6JN.js → goal-validator-DV6DRSGF.js} +6 -5
- package/dist/{hypothesis-JCUMZKTG.js → hypothesis-O72ZLVOW.js} +7 -6
- package/dist/index.js +30 -116
- package/dist/index.js.map +1 -1
- package/dist/{insight-store-A5XXMFD6.js → insight-store-Q62UGMTF.js} +3 -3
- package/dist/{issue-store-LZWZIGM7.js → issue-store-4FPABLC6.js} +6 -3
- package/dist/ledger-43SIVE7X.js +43 -0
- package/dist/{trie-agent-6A7YBNTQ.js → trie-agent-ET3DAP5Y.js} +11 -10
- package/dist/trie-agent-ET3DAP5Y.js.map +1 -0
- package/package.json +5 -1
- package/dist/chunk-4MJ52WBH.js.map +0 -1
- package/dist/chunk-62POBLFC.js.map +0 -1
- package/dist/chunk-J7CEBSEB.js.map +0 -1
- /package/dist/{chunk-XTTZAQWJ.js.map → chunk-72KSLD7A.js.map} +0 -0
- /package/dist/{chunk-WMDFK7LI.js.map → chunk-B2AHQ2IR.js.map} +0 -0
- /package/dist/{chunk-LT6VUZG2.js.map → chunk-CU5VDH6F.js.map} +0 -0
- /package/dist/{chunk-N2EDZTKG.js.map → chunk-GAL7OIYU.js.map} +0 -0
- /package/dist/{chunk-YEIJW6X6.js.map → chunk-ILGMFND2.js.map} +0 -0
- /package/dist/{chunk-3MUCUZ46.js.map → chunk-OTQEFXHU.js.map} +0 -0
- /package/dist/{chunk-T6PS3MXJ.js.map → chunk-ZDDE442Q.js.map} +0 -0
- /package/dist/{fast-analyzer-MWKCDRGD.js.map → fast-analyzer-CTT3MCPE.js.map} +0 -0
- /package/dist/{goal-manager-ZBWKWEML.js.map → goal-manager-IGUMDGCA.js.map} +0 -0
- /package/dist/{goal-validator-DA3JQ6JN.js.map → goal-validator-DV6DRSGF.js.map} +0 -0
- /package/dist/{hypothesis-JCUMZKTG.js.map → hypothesis-O72ZLVOW.js.map} +0 -0
- /package/dist/{insight-store-A5XXMFD6.js.map → insight-store-Q62UGMTF.js.map} +0 -0
- /package/dist/{issue-store-LZWZIGM7.js.map → issue-store-4FPABLC6.js.map} +0 -0
- /package/dist/{trie-agent-6A7YBNTQ.js.map → ledger-43SIVE7X.js.map} +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BackupManager,
|
|
3
|
-
CompactedSummariesIndexSchema,
|
|
4
|
-
IssueIndexSchema,
|
|
5
|
-
safeParseAndValidate
|
|
6
|
-
} from "./chunk-4MJ52WBH.js";
|
|
7
1
|
import {
|
|
8
2
|
atomicWriteJSON
|
|
9
3
|
} from "./chunk-43X6JBEM.js";
|
|
@@ -12,404 +6,17 @@ import {
|
|
|
12
6
|
getWorkingDirectory
|
|
13
7
|
} from "./chunk-SH7H3WRU.js";
|
|
14
8
|
|
|
15
|
-
// src/memory/issue-store.ts
|
|
16
|
-
import { mkdir as mkdir3, writeFile as writeFile2, readFile as readFile3, readdir as readdir2 } from "fs/promises";
|
|
17
|
-
import { createHash as createHash2 } from "crypto";
|
|
18
|
-
import { existsSync as existsSync4 } from "fs";
|
|
19
|
-
import { join as join3 } from "path";
|
|
20
|
-
|
|
21
|
-
// src/memory/bm25.ts
|
|
22
|
-
var BM25Index = class _BM25Index {
|
|
23
|
-
documents = /* @__PURE__ */ new Map();
|
|
24
|
-
termFrequencies = /* @__PURE__ */ new Map();
|
|
25
|
-
documentFrequencies = /* @__PURE__ */ new Map();
|
|
26
|
-
documentLengths = /* @__PURE__ */ new Map();
|
|
27
|
-
avgDocLength = 0;
|
|
28
|
-
k1 = 1.5;
|
|
29
|
-
b = 0.75;
|
|
30
|
-
/**
|
|
31
|
-
* Add a document to the index
|
|
32
|
-
*/
|
|
33
|
-
addDocument(doc) {
|
|
34
|
-
const tokens = this.tokenize(doc.text);
|
|
35
|
-
this.documents.set(doc.id, doc);
|
|
36
|
-
this.documentLengths.set(doc.id, tokens.length);
|
|
37
|
-
const termFreq = /* @__PURE__ */ new Map();
|
|
38
|
-
const seenTerms = /* @__PURE__ */ new Set();
|
|
39
|
-
for (const token of tokens) {
|
|
40
|
-
termFreq.set(token, (termFreq.get(token) || 0) + 1);
|
|
41
|
-
if (!seenTerms.has(token)) {
|
|
42
|
-
seenTerms.add(token);
|
|
43
|
-
this.documentFrequencies.set(token, (this.documentFrequencies.get(token) || 0) + 1);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
this.termFrequencies.set(doc.id, termFreq);
|
|
47
|
-
this.updateAvgDocLength();
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Add multiple documents
|
|
51
|
-
*/
|
|
52
|
-
addDocuments(docs) {
|
|
53
|
-
for (const doc of docs) {
|
|
54
|
-
this.addDocument(doc);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Search the index
|
|
59
|
-
*/
|
|
60
|
-
search(query, limit = 10) {
|
|
61
|
-
const queryTokens = this.tokenize(query);
|
|
62
|
-
const scores = /* @__PURE__ */ new Map();
|
|
63
|
-
const N = this.documents.size;
|
|
64
|
-
for (const [docId] of this.documents) {
|
|
65
|
-
let score = 0;
|
|
66
|
-
const docLength = this.documentLengths.get(docId) || 0;
|
|
67
|
-
const termFreqs = this.termFrequencies.get(docId);
|
|
68
|
-
if (!termFreqs) continue;
|
|
69
|
-
for (const term of queryTokens) {
|
|
70
|
-
const tf = termFreqs.get(term) || 0;
|
|
71
|
-
if (tf === 0) continue;
|
|
72
|
-
const df = this.documentFrequencies.get(term) || 0;
|
|
73
|
-
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
74
|
-
const numerator = tf * (this.k1 + 1);
|
|
75
|
-
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.avgDocLength));
|
|
76
|
-
score += idf * (numerator / denominator);
|
|
77
|
-
}
|
|
78
|
-
if (score > 0) {
|
|
79
|
-
scores.set(docId, score);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return Array.from(scores.entries()).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, score]) => {
|
|
83
|
-
const metadata = this.documents.get(id)?.metadata;
|
|
84
|
-
const result = { id, score };
|
|
85
|
-
if (metadata !== void 0) {
|
|
86
|
-
result.metadata = metadata;
|
|
87
|
-
}
|
|
88
|
-
return result;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get document count
|
|
93
|
-
*/
|
|
94
|
-
get size() {
|
|
95
|
-
return this.documents.size;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Clear the index
|
|
99
|
-
*/
|
|
100
|
-
clear() {
|
|
101
|
-
this.documents.clear();
|
|
102
|
-
this.termFrequencies.clear();
|
|
103
|
-
this.documentFrequencies.clear();
|
|
104
|
-
this.documentLengths.clear();
|
|
105
|
-
this.avgDocLength = 0;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Serialize the index to JSON
|
|
109
|
-
*/
|
|
110
|
-
serialize() {
|
|
111
|
-
return JSON.stringify({
|
|
112
|
-
documents: Array.from(this.documents.entries()),
|
|
113
|
-
termFrequencies: Array.from(this.termFrequencies.entries()).map(([k, v]) => [k, Array.from(v.entries())]),
|
|
114
|
-
documentFrequencies: Array.from(this.documentFrequencies.entries()),
|
|
115
|
-
documentLengths: Array.from(this.documentLengths.entries()),
|
|
116
|
-
avgDocLength: this.avgDocLength
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Load from serialized JSON
|
|
121
|
-
*/
|
|
122
|
-
static deserialize(json) {
|
|
123
|
-
const data = JSON.parse(json);
|
|
124
|
-
const index = new _BM25Index();
|
|
125
|
-
index.documents = new Map(data.documents);
|
|
126
|
-
index.termFrequencies = new Map(data.termFrequencies.map(([k, v]) => [k, new Map(v)]));
|
|
127
|
-
index.documentFrequencies = new Map(data.documentFrequencies);
|
|
128
|
-
index.documentLengths = new Map(data.documentLengths);
|
|
129
|
-
index.avgDocLength = data.avgDocLength;
|
|
130
|
-
return index;
|
|
131
|
-
}
|
|
132
|
-
tokenize(text) {
|
|
133
|
-
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((token) => token.length > 2 && !this.isStopWord(token));
|
|
134
|
-
}
|
|
135
|
-
isStopWord(word) {
|
|
136
|
-
const stopWords = /* @__PURE__ */ new Set([
|
|
137
|
-
"the",
|
|
138
|
-
"be",
|
|
139
|
-
"to",
|
|
140
|
-
"of",
|
|
141
|
-
"and",
|
|
142
|
-
"a",
|
|
143
|
-
"in",
|
|
144
|
-
"that",
|
|
145
|
-
"have",
|
|
146
|
-
"i",
|
|
147
|
-
"it",
|
|
148
|
-
"for",
|
|
149
|
-
"not",
|
|
150
|
-
"on",
|
|
151
|
-
"with",
|
|
152
|
-
"he",
|
|
153
|
-
"as",
|
|
154
|
-
"you",
|
|
155
|
-
"do",
|
|
156
|
-
"at",
|
|
157
|
-
"this",
|
|
158
|
-
"but",
|
|
159
|
-
"his",
|
|
160
|
-
"by",
|
|
161
|
-
"from",
|
|
162
|
-
"they",
|
|
163
|
-
"we",
|
|
164
|
-
"say",
|
|
165
|
-
"her",
|
|
166
|
-
"she",
|
|
167
|
-
"or",
|
|
168
|
-
"an",
|
|
169
|
-
"will",
|
|
170
|
-
"my",
|
|
171
|
-
"one",
|
|
172
|
-
"all",
|
|
173
|
-
"would",
|
|
174
|
-
"there",
|
|
175
|
-
"their",
|
|
176
|
-
"what",
|
|
177
|
-
"so",
|
|
178
|
-
"up",
|
|
179
|
-
"out",
|
|
180
|
-
"if",
|
|
181
|
-
"about",
|
|
182
|
-
"who",
|
|
183
|
-
"get",
|
|
184
|
-
"which",
|
|
185
|
-
"go",
|
|
186
|
-
"me",
|
|
187
|
-
"when",
|
|
188
|
-
"make",
|
|
189
|
-
"can",
|
|
190
|
-
"like",
|
|
191
|
-
"time",
|
|
192
|
-
"no",
|
|
193
|
-
"just",
|
|
194
|
-
"him",
|
|
195
|
-
"know",
|
|
196
|
-
"take",
|
|
197
|
-
"into",
|
|
198
|
-
"year",
|
|
199
|
-
"your",
|
|
200
|
-
"some",
|
|
201
|
-
"could",
|
|
202
|
-
"them",
|
|
203
|
-
"see",
|
|
204
|
-
"other",
|
|
205
|
-
"than",
|
|
206
|
-
"then",
|
|
207
|
-
"now",
|
|
208
|
-
"look",
|
|
209
|
-
"only",
|
|
210
|
-
"come",
|
|
211
|
-
"its",
|
|
212
|
-
"over",
|
|
213
|
-
"also",
|
|
214
|
-
"back",
|
|
215
|
-
"after",
|
|
216
|
-
"use",
|
|
217
|
-
"two",
|
|
218
|
-
"how",
|
|
219
|
-
"our",
|
|
220
|
-
"first",
|
|
221
|
-
"way",
|
|
222
|
-
"even",
|
|
223
|
-
"new",
|
|
224
|
-
"want",
|
|
225
|
-
"because",
|
|
226
|
-
"any",
|
|
227
|
-
"these",
|
|
228
|
-
"give",
|
|
229
|
-
"day",
|
|
230
|
-
"most",
|
|
231
|
-
"us",
|
|
232
|
-
"should",
|
|
233
|
-
"been",
|
|
234
|
-
"has",
|
|
235
|
-
"was",
|
|
236
|
-
"are"
|
|
237
|
-
]);
|
|
238
|
-
return stopWords.has(word);
|
|
239
|
-
}
|
|
240
|
-
updateAvgDocLength() {
|
|
241
|
-
if (this.documentLengths.size === 0) {
|
|
242
|
-
this.avgDocLength = 0;
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const total = Array.from(this.documentLengths.values()).reduce((a, b) => a + b, 0);
|
|
246
|
-
this.avgDocLength = total / this.documentLengths.size;
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
// src/memory/compactor.ts
|
|
251
|
-
import { mkdir, readFile } from "fs/promises";
|
|
252
|
-
import { existsSync } from "fs";
|
|
253
|
-
import { join } from "path";
|
|
254
|
-
async function compactOldIssues(issues, options = {}) {
|
|
255
|
-
const keepDays = options.keepDays ?? 30;
|
|
256
|
-
const minIssues = options.minIssuesToCompact ?? 100;
|
|
257
|
-
const cutoffDate = /* @__PURE__ */ new Date();
|
|
258
|
-
cutoffDate.setDate(cutoffDate.getDate() - keepDays);
|
|
259
|
-
const oldIssues = issues.filter((i) => new Date(i.timestamp) < cutoffDate);
|
|
260
|
-
const recentIssues = issues.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
261
|
-
if (oldIssues.length < minIssues) {
|
|
262
|
-
return { summary: null, remaining: issues };
|
|
263
|
-
}
|
|
264
|
-
const summary = buildSummary(oldIssues);
|
|
265
|
-
return { summary, remaining: recentIssues };
|
|
266
|
-
}
|
|
267
|
-
function buildSummary(issues) {
|
|
268
|
-
const sorted = issues.sort(
|
|
269
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
270
|
-
);
|
|
271
|
-
const bySeverity = {};
|
|
272
|
-
const byAgent = {};
|
|
273
|
-
const patternMap = /* @__PURE__ */ new Map();
|
|
274
|
-
const fileCount = /* @__PURE__ */ new Map();
|
|
275
|
-
for (const issue of issues) {
|
|
276
|
-
bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;
|
|
277
|
-
byAgent[issue.agent] = (byAgent[issue.agent] || 0) + 1;
|
|
278
|
-
const patternKey = normalizePattern(issue.issue);
|
|
279
|
-
const existing = patternMap.get(patternKey);
|
|
280
|
-
if (existing) {
|
|
281
|
-
existing.count++;
|
|
282
|
-
} else {
|
|
283
|
-
patternMap.set(patternKey, { count: 1, issue });
|
|
284
|
-
}
|
|
285
|
-
const fileName = issue.file.split("/").pop() || issue.file;
|
|
286
|
-
fileCount.set(fileName, (fileCount.get(fileName) || 0) + 1);
|
|
287
|
-
}
|
|
288
|
-
const topPatterns = Array.from(patternMap.entries()).sort((a, b) => b[1].count - a[1].count).slice(0, 10).map(([pattern, data]) => ({
|
|
289
|
-
pattern: pattern.slice(0, 100),
|
|
290
|
-
count: data.count,
|
|
291
|
-
severity: data.issue.severity,
|
|
292
|
-
agent: data.issue.agent,
|
|
293
|
-
exampleFix: data.issue.fix.slice(0, 200)
|
|
294
|
-
}));
|
|
295
|
-
const hotFiles = Array.from(fileCount.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([file, count]) => ({ file, count }));
|
|
296
|
-
return {
|
|
297
|
-
period: `${sorted[0]?.timestamp.split("T")[0]} to ${sorted[sorted.length - 1]?.timestamp.split("T")[0]}`,
|
|
298
|
-
startDate: sorted[0]?.timestamp || "",
|
|
299
|
-
endDate: sorted[sorted.length - 1]?.timestamp || "",
|
|
300
|
-
totalIssues: issues.length,
|
|
301
|
-
resolvedCount: issues.filter((i) => i.resolved).length,
|
|
302
|
-
bySeverity,
|
|
303
|
-
byAgent,
|
|
304
|
-
topPatterns,
|
|
305
|
-
hotFiles,
|
|
306
|
-
compactedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
function normalizePattern(text) {
|
|
310
|
-
return text.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/["']/g, "").replace(/\s+/g, " ").trim().slice(0, 150);
|
|
311
|
-
}
|
|
312
|
-
async function saveCompactedSummary(summary, projectDir) {
|
|
313
|
-
const memoryDir = join(getTrieDirectory(projectDir), "memory");
|
|
314
|
-
await mkdir(memoryDir, { recursive: true });
|
|
315
|
-
const summaryPath = join(memoryDir, "compacted-summaries.json");
|
|
316
|
-
let summaries = [];
|
|
317
|
-
try {
|
|
318
|
-
if (existsSync(summaryPath)) {
|
|
319
|
-
const content = await readFile(summaryPath, "utf-8");
|
|
320
|
-
const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);
|
|
321
|
-
if (result.success) {
|
|
322
|
-
summaries = result.data;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
} catch {
|
|
326
|
-
summaries = [];
|
|
327
|
-
}
|
|
328
|
-
summaries.push(summary);
|
|
329
|
-
if (summaries.length > 12) {
|
|
330
|
-
summaries = summaries.slice(-12);
|
|
331
|
-
}
|
|
332
|
-
const backupManager = new BackupManager(summaryPath);
|
|
333
|
-
await backupManager.createBackup();
|
|
334
|
-
await atomicWriteJSON(summaryPath, summaries);
|
|
335
|
-
}
|
|
336
|
-
async function loadCompactedSummaries(projectDir) {
|
|
337
|
-
const summaryPath = join(getTrieDirectory(projectDir), "memory", "compacted-summaries.json");
|
|
338
|
-
try {
|
|
339
|
-
if (existsSync(summaryPath)) {
|
|
340
|
-
const content = await readFile(summaryPath, "utf-8");
|
|
341
|
-
const result = safeParseAndValidate(content, CompactedSummariesIndexSchema);
|
|
342
|
-
if (result.success) {
|
|
343
|
-
return result.data;
|
|
344
|
-
}
|
|
345
|
-
const backupManager = new BackupManager(summaryPath);
|
|
346
|
-
if (await backupManager.recoverFromBackup()) {
|
|
347
|
-
const recovered = await readFile(summaryPath, "utf-8");
|
|
348
|
-
const recoveredResult = safeParseAndValidate(recovered, CompactedSummariesIndexSchema);
|
|
349
|
-
if (recoveredResult.success) {
|
|
350
|
-
return recoveredResult.data;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
return [];
|
|
357
|
-
}
|
|
358
|
-
async function getHistoricalInsights(projectDir) {
|
|
359
|
-
const summaries = await loadCompactedSummaries(projectDir);
|
|
360
|
-
if (summaries.length === 0) {
|
|
361
|
-
return {
|
|
362
|
-
totalHistoricalIssues: 0,
|
|
363
|
-
recurringPatterns: [],
|
|
364
|
-
improvementTrend: "unknown"
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
const totalHistoricalIssues = summaries.reduce((sum, s) => sum + s.totalIssues, 0);
|
|
368
|
-
const patternCounts = /* @__PURE__ */ new Map();
|
|
369
|
-
for (const summary of summaries) {
|
|
370
|
-
for (const pattern of summary.topPatterns) {
|
|
371
|
-
const key = pattern.pattern;
|
|
372
|
-
const existing = patternCounts.get(key);
|
|
373
|
-
if (existing) {
|
|
374
|
-
existing.count += pattern.count;
|
|
375
|
-
existing.appearances++;
|
|
376
|
-
} else {
|
|
377
|
-
patternCounts.set(key, { ...pattern, appearances: 1 });
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
const recurringPatterns = Array.from(patternCounts.values()).filter((p) => p.appearances >= 2).sort((a, b) => b.count - a.count).slice(0, 5);
|
|
382
|
-
let improvementTrend = "unknown";
|
|
383
|
-
if (summaries.length >= 2) {
|
|
384
|
-
const recent = summaries.slice(-2);
|
|
385
|
-
const olderCount = recent[0]?.totalIssues || 0;
|
|
386
|
-
const newerCount = recent[1]?.totalIssues || 0;
|
|
387
|
-
if (newerCount < olderCount * 0.8) {
|
|
388
|
-
improvementTrend = "improving";
|
|
389
|
-
} else if (newerCount > olderCount * 1.2) {
|
|
390
|
-
improvementTrend = "declining";
|
|
391
|
-
} else {
|
|
392
|
-
improvementTrend = "stable";
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return {
|
|
396
|
-
totalHistoricalIssues,
|
|
397
|
-
recurringPatterns,
|
|
398
|
-
improvementTrend
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
9
|
// src/memory/ledger.ts
|
|
403
10
|
import { createHash } from "crypto";
|
|
404
|
-
import { mkdir
|
|
405
|
-
import { existsSync as
|
|
11
|
+
import { mkdir, readFile, writeFile, stat, unlink } from "fs/promises";
|
|
12
|
+
import { existsSync as existsSync2 } from "fs";
|
|
406
13
|
import { createGzip, createGunzip } from "zlib";
|
|
407
14
|
import { pipeline } from "stream/promises";
|
|
408
15
|
import { createReadStream, createWriteStream } from "fs";
|
|
409
|
-
import { join
|
|
16
|
+
import { join } from "path";
|
|
410
17
|
|
|
411
18
|
// src/agent/git.ts
|
|
412
|
-
import { existsSync
|
|
19
|
+
import { existsSync } from "fs";
|
|
413
20
|
import path from "path";
|
|
414
21
|
|
|
415
22
|
// src/utils/command-runner.ts
|
|
@@ -722,8 +329,8 @@ var LEDGER_VERSION = 2;
|
|
|
722
329
|
async function appendIssuesToLedger(issues, workDir, author) {
|
|
723
330
|
if (issues.length === 0) return null;
|
|
724
331
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
725
|
-
const memoryDir =
|
|
726
|
-
await
|
|
332
|
+
const memoryDir = join(getTrieDirectory(projectDir), "memory");
|
|
333
|
+
await mkdir(memoryDir, { recursive: true });
|
|
727
334
|
const isRepo = await isGitRepo(projectDir);
|
|
728
335
|
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
729
336
|
const blockAuthor = author || lastCommit?.author || "unknown";
|
|
@@ -736,7 +343,8 @@ async function appendIssuesToLedger(issues, workDir, author) {
|
|
|
736
343
|
severity: issue.severity,
|
|
737
344
|
file: issue.file,
|
|
738
345
|
agent: issue.agent,
|
|
739
|
-
timestamp: issue.timestamp
|
|
346
|
+
timestamp: issue.timestamp,
|
|
347
|
+
status: "active"
|
|
740
348
|
}));
|
|
741
349
|
const previousBlock = blocks[blocks.length - 1];
|
|
742
350
|
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, blockAuthor, lastCommit?.hash, blocks.length);
|
|
@@ -814,10 +422,10 @@ function createSyncableBlock(date, now, previousHash, author, gitCommit, chainHe
|
|
|
814
422
|
};
|
|
815
423
|
}
|
|
816
424
|
async function loadLedger(projectDir) {
|
|
817
|
-
const ledgerPath =
|
|
425
|
+
const ledgerPath = join(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
818
426
|
try {
|
|
819
|
-
if (!
|
|
820
|
-
const content = await
|
|
427
|
+
if (!existsSync2(ledgerPath)) return [];
|
|
428
|
+
const content = await readFile(ledgerPath, "utf-8");
|
|
821
429
|
const parsed = JSON.parse(content);
|
|
822
430
|
if (!Array.isArray(parsed)) return [];
|
|
823
431
|
return parsed;
|
|
@@ -830,40 +438,40 @@ async function getLedgerBlocks(workDir) {
|
|
|
830
438
|
return loadLedger(projectDir);
|
|
831
439
|
}
|
|
832
440
|
async function saveLedger(blocks, projectDir) {
|
|
833
|
-
const ledgerPath =
|
|
441
|
+
const ledgerPath = join(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
834
442
|
await atomicWriteJSON(ledgerPath, blocks);
|
|
835
443
|
}
|
|
836
444
|
function sha256(input) {
|
|
837
445
|
return createHash("sha256").update(input).digest("hex");
|
|
838
446
|
}
|
|
839
447
|
function getSharedLedgerDir(projectDir) {
|
|
840
|
-
return
|
|
448
|
+
return join(projectDir, SHARED_LEDGER_DIR);
|
|
841
449
|
}
|
|
842
450
|
function getActiveBlocksDir(projectDir) {
|
|
843
|
-
return
|
|
451
|
+
return join(getSharedLedgerDir(projectDir), "active");
|
|
844
452
|
}
|
|
845
453
|
function getArchivedBlocksDir(projectDir) {
|
|
846
|
-
return
|
|
454
|
+
return join(getSharedLedgerDir(projectDir), "archived");
|
|
847
455
|
}
|
|
848
456
|
function getManifestPath(projectDir) {
|
|
849
|
-
return
|
|
457
|
+
return join(getSharedLedgerDir(projectDir), MANIFEST_FILENAME);
|
|
850
458
|
}
|
|
851
459
|
function getSyncStatePath(projectDir) {
|
|
852
|
-
return
|
|
460
|
+
return join(getTrieDirectory(projectDir), "memory", SYNC_STATE_FILENAME);
|
|
853
461
|
}
|
|
854
462
|
async function ensureSharedStorageStructure(projectDir) {
|
|
855
463
|
const sharedDir = getSharedLedgerDir(projectDir);
|
|
856
464
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
857
465
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
858
|
-
await
|
|
859
|
-
await
|
|
860
|
-
await
|
|
466
|
+
await mkdir(sharedDir, { recursive: true });
|
|
467
|
+
await mkdir(activeDir, { recursive: true });
|
|
468
|
+
await mkdir(archivedDir, { recursive: true });
|
|
861
469
|
}
|
|
862
470
|
async function loadManifest(projectDir) {
|
|
863
471
|
const manifestPath = getManifestPath(projectDir);
|
|
864
472
|
try {
|
|
865
|
-
if (!
|
|
866
|
-
const content = await
|
|
473
|
+
if (!existsSync2(manifestPath)) return null;
|
|
474
|
+
const content = await readFile(manifestPath, "utf-8");
|
|
867
475
|
return JSON.parse(content);
|
|
868
476
|
} catch {
|
|
869
477
|
return null;
|
|
@@ -900,8 +508,8 @@ async function createDefaultManifest(projectDir) {
|
|
|
900
508
|
async function loadSyncState(projectDir) {
|
|
901
509
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
902
510
|
try {
|
|
903
|
-
if (!
|
|
904
|
-
const content = await
|
|
511
|
+
if (!existsSync2(syncStatePath)) return null;
|
|
512
|
+
const content = await readFile(syncStatePath, "utf-8");
|
|
905
513
|
return JSON.parse(content);
|
|
906
514
|
} catch {
|
|
907
515
|
return null;
|
|
@@ -909,8 +517,8 @@ async function loadSyncState(projectDir) {
|
|
|
909
517
|
}
|
|
910
518
|
async function saveSyncState(syncState, projectDir) {
|
|
911
519
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
912
|
-
const memoryDir =
|
|
913
|
-
await
|
|
520
|
+
const memoryDir = join(getTrieDirectory(projectDir), "memory");
|
|
521
|
+
await mkdir(memoryDir, { recursive: true });
|
|
914
522
|
await atomicWriteJSON(syncStatePath, syncState);
|
|
915
523
|
}
|
|
916
524
|
async function initializeSharedLedger(workDir) {
|
|
@@ -961,8 +569,8 @@ async function pushLedgerToShared(workDir) {
|
|
|
961
569
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
962
570
|
for (const block of localBlocks) {
|
|
963
571
|
const blockFilename = `${block.date}.json`;
|
|
964
|
-
const blockPath =
|
|
965
|
-
if (!
|
|
572
|
+
const blockPath = join(activeDir, blockFilename);
|
|
573
|
+
if (!existsSync2(blockPath) || block.updatedAt > manifest.lastSync) {
|
|
966
574
|
await atomicWriteJSON(blockPath, block);
|
|
967
575
|
manifest.index.byDate[block.date] = `active/${blockFilename}`;
|
|
968
576
|
if (block.author) {
|
|
@@ -989,10 +597,10 @@ async function loadSharedBlocks(projectDir) {
|
|
|
989
597
|
const blocks = [];
|
|
990
598
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
991
599
|
for (const filename of manifest.activeBlocks) {
|
|
992
|
-
const blockPath =
|
|
600
|
+
const blockPath = join(activeDir, filename);
|
|
993
601
|
try {
|
|
994
|
-
if (
|
|
995
|
-
const content = await
|
|
602
|
+
if (existsSync2(blockPath)) {
|
|
603
|
+
const content = await readFile(blockPath, "utf-8");
|
|
996
604
|
const block = JSON.parse(content);
|
|
997
605
|
blocks.push(block);
|
|
998
606
|
}
|
|
@@ -1124,8 +732,8 @@ function mergeBlockEntries(localBlock, remoteBlock) {
|
|
|
1124
732
|
}
|
|
1125
733
|
async function migrateLegacyLedger(workDir) {
|
|
1126
734
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1127
|
-
const legacyLedgerPath =
|
|
1128
|
-
if (!
|
|
735
|
+
const legacyLedgerPath = join(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
736
|
+
if (!existsSync2(legacyLedgerPath)) {
|
|
1129
737
|
return false;
|
|
1130
738
|
}
|
|
1131
739
|
try {
|
|
@@ -1159,8 +767,8 @@ async function migrateLegacyLedger(workDir) {
|
|
|
1159
767
|
}
|
|
1160
768
|
async function detectLegacyLedger(workDir) {
|
|
1161
769
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1162
|
-
const legacyLedgerPath =
|
|
1163
|
-
if (!
|
|
770
|
+
const legacyLedgerPath = join(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
771
|
+
if (!existsSync2(legacyLedgerPath)) {
|
|
1164
772
|
return false;
|
|
1165
773
|
}
|
|
1166
774
|
try {
|
|
@@ -1202,7 +810,7 @@ async function compressOldBlocks(workDir) {
|
|
|
1202
810
|
let compressedSize = 0;
|
|
1203
811
|
const blocksByMonth = /* @__PURE__ */ new Map();
|
|
1204
812
|
for (const blockFile of manifest.activeBlocks) {
|
|
1205
|
-
const blockPath =
|
|
813
|
+
const blockPath = join(activeDir, blockFile);
|
|
1206
814
|
const blockDate = blockFile.replace(".json", "");
|
|
1207
815
|
if (new Date(blockDate) < cutoffDate) {
|
|
1208
816
|
const monthKey = blockDate.slice(0, 7);
|
|
@@ -1213,18 +821,18 @@ async function compressOldBlocks(workDir) {
|
|
|
1213
821
|
}
|
|
1214
822
|
}
|
|
1215
823
|
for (const [monthKey, blockFiles] of blocksByMonth) {
|
|
1216
|
-
const archivePath =
|
|
1217
|
-
if (
|
|
824
|
+
const archivePath = join(archivedDir, `${monthKey}.tar.gz`);
|
|
825
|
+
if (existsSync2(archivePath)) {
|
|
1218
826
|
continue;
|
|
1219
827
|
}
|
|
1220
828
|
console.log(`Archiving ${blockFiles.length} blocks for ${monthKey}...`);
|
|
1221
829
|
const monthlyBlocks = [];
|
|
1222
830
|
for (const blockFile of blockFiles) {
|
|
1223
|
-
const blockPath =
|
|
831
|
+
const blockPath = join(activeDir, blockFile);
|
|
1224
832
|
try {
|
|
1225
833
|
const stats = await stat(blockPath);
|
|
1226
834
|
originalSize += stats.size;
|
|
1227
|
-
const content = await
|
|
835
|
+
const content = await readFile(blockPath, "utf-8");
|
|
1228
836
|
const block = JSON.parse(content);
|
|
1229
837
|
monthlyBlocks.push(block);
|
|
1230
838
|
} catch (error) {
|
|
@@ -1241,10 +849,10 @@ async function compressOldBlocks(workDir) {
|
|
|
1241
849
|
);
|
|
1242
850
|
const compressedStats = await stat(tempPath);
|
|
1243
851
|
compressedSize += compressedStats.size;
|
|
1244
|
-
await writeFile(archivePath, await
|
|
852
|
+
await writeFile(archivePath, await readFile(tempPath));
|
|
1245
853
|
await unlink(tempPath);
|
|
1246
854
|
for (const blockFile of blockFiles) {
|
|
1247
|
-
const blockPath =
|
|
855
|
+
const blockPath = join(activeDir, blockFile);
|
|
1248
856
|
await unlink(blockPath);
|
|
1249
857
|
const index = manifest.activeBlocks.indexOf(blockFile);
|
|
1250
858
|
if (index > -1) {
|
|
@@ -1281,9 +889,9 @@ async function getStorageStats(workDir) {
|
|
|
1281
889
|
let activeSize = 0;
|
|
1282
890
|
let archivedSize = 0;
|
|
1283
891
|
for (const blockFile of manifest.activeBlocks) {
|
|
1284
|
-
const blockPath =
|
|
892
|
+
const blockPath = join(activeDir, blockFile);
|
|
1285
893
|
try {
|
|
1286
|
-
if (
|
|
894
|
+
if (existsSync2(blockPath)) {
|
|
1287
895
|
const stats = await stat(blockPath);
|
|
1288
896
|
activeSize += stats.size;
|
|
1289
897
|
}
|
|
@@ -1291,9 +899,9 @@ async function getStorageStats(workDir) {
|
|
|
1291
899
|
}
|
|
1292
900
|
}
|
|
1293
901
|
for (const archiveFile of manifest.archivedBlocks) {
|
|
1294
|
-
const archivePath =
|
|
902
|
+
const archivePath = join(archivedDir, archiveFile);
|
|
1295
903
|
try {
|
|
1296
|
-
if (
|
|
904
|
+
if (existsSync2(archivePath)) {
|
|
1297
905
|
const stats = await stat(archivePath);
|
|
1298
906
|
archivedSize += stats.size;
|
|
1299
907
|
}
|
|
@@ -1328,562 +936,176 @@ async function shouldCompress(workDir) {
|
|
|
1328
936
|
});
|
|
1329
937
|
return exceedsSize || hasOldBlocks;
|
|
1330
938
|
}
|
|
1331
|
-
async function
|
|
1332
|
-
if (
|
|
939
|
+
async function correctLedgerEntries(entryIds, reason, correctionType = "corrected", workDir, author) {
|
|
940
|
+
if (entryIds.length === 0) {
|
|
1333
941
|
return {
|
|
1334
942
|
success: false,
|
|
1335
|
-
|
|
1336
|
-
error: "
|
|
943
|
+
correctedEntries: 0,
|
|
944
|
+
error: "No entry IDs provided"
|
|
1337
945
|
};
|
|
1338
946
|
}
|
|
1339
|
-
|
|
1340
|
-
const blocks = await loadLedger(projectDir);
|
|
1341
|
-
if (blocks.length === 0) {
|
|
947
|
+
if (!reason || reason.trim().length === 0) {
|
|
1342
948
|
return {
|
|
1343
949
|
success: false,
|
|
1344
|
-
|
|
1345
|
-
error: "
|
|
950
|
+
correctedEntries: 0,
|
|
951
|
+
error: "Correction reason is required"
|
|
1346
952
|
};
|
|
1347
953
|
}
|
|
1348
|
-
const
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
954
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
955
|
+
const blocks = await loadLedger(projectDir);
|
|
956
|
+
const entriesToCorrect = [];
|
|
957
|
+
for (const block of blocks) {
|
|
958
|
+
for (const entry of block.entries) {
|
|
959
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
960
|
+
entriesToCorrect.push(entry);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
1356
963
|
}
|
|
1357
|
-
|
|
1358
|
-
if (integrityIssue) {
|
|
964
|
+
if (entriesToCorrect.length === 0) {
|
|
1359
965
|
return {
|
|
1360
966
|
success: false,
|
|
1361
|
-
|
|
1362
|
-
error:
|
|
967
|
+
correctedEntries: 0,
|
|
968
|
+
error: "No active entries found with the provided IDs"
|
|
1363
969
|
};
|
|
1364
970
|
}
|
|
1365
971
|
try {
|
|
1366
|
-
const
|
|
1367
|
-
await
|
|
1368
|
-
await
|
|
972
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
973
|
+
const isRepo = await isGitRepo(projectDir);
|
|
974
|
+
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
975
|
+
const correctionAuthor = author || lastCommit?.author || "unknown";
|
|
976
|
+
const correctionEntries = entriesToCorrect.map((entry) => {
|
|
977
|
+
const correctionId = `correction-${entry.id}-${Date.now()}`;
|
|
978
|
+
return {
|
|
979
|
+
id: correctionId,
|
|
980
|
+
hash: sha256(`${correctionId}:${entry.hash}:${reason}:${now}`),
|
|
981
|
+
severity: "info",
|
|
982
|
+
file: entry.file,
|
|
983
|
+
agent: "ledger-correction",
|
|
984
|
+
timestamp: now,
|
|
985
|
+
status: "active",
|
|
986
|
+
correction: `Correcting entry ${entry.id}: ${reason}`,
|
|
987
|
+
correctedBy: entry.id
|
|
988
|
+
};
|
|
989
|
+
});
|
|
990
|
+
for (const block of blocks) {
|
|
991
|
+
let blockModified = false;
|
|
992
|
+
for (const entry of block.entries) {
|
|
993
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
994
|
+
entry.status = correctionType;
|
|
995
|
+
entry.correctionTimestamp = now;
|
|
996
|
+
entry.correction = reason;
|
|
997
|
+
blockModified = true;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (blockModified) {
|
|
1001
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((e) => e.hash));
|
|
1002
|
+
block.blockHash = computeBlockHash(
|
|
1003
|
+
block.previousHash,
|
|
1004
|
+
block.merkleRoot,
|
|
1005
|
+
block.date,
|
|
1006
|
+
block.version
|
|
1007
|
+
);
|
|
1008
|
+
block.updatedAt = now;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
await saveLedger(blocks, projectDir);
|
|
1012
|
+
const correctionBlock = await appendIssuesToLedger(
|
|
1013
|
+
correctionEntries.map((entry) => ({
|
|
1014
|
+
id: entry.id,
|
|
1015
|
+
hash: entry.hash,
|
|
1016
|
+
severity: entry.severity,
|
|
1017
|
+
file: entry.file,
|
|
1018
|
+
agent: entry.agent,
|
|
1019
|
+
timestamp: entry.timestamp,
|
|
1020
|
+
description: entry.correction || "",
|
|
1021
|
+
category: "correction",
|
|
1022
|
+
line: 0,
|
|
1023
|
+
column: 0,
|
|
1024
|
+
context: "",
|
|
1025
|
+
createdAt: entry.timestamp,
|
|
1026
|
+
lastSeen: entry.timestamp
|
|
1027
|
+
})),
|
|
1028
|
+
projectDir,
|
|
1029
|
+
correctionAuthor
|
|
1030
|
+
);
|
|
1369
1031
|
return {
|
|
1370
1032
|
success: true,
|
|
1371
|
-
|
|
1372
|
-
|
|
1033
|
+
correctedEntries: entriesToCorrect.length,
|
|
1034
|
+
correctionBlock: correctionBlock || void 0
|
|
1373
1035
|
};
|
|
1374
1036
|
} catch (error) {
|
|
1375
1037
|
return {
|
|
1376
1038
|
success: false,
|
|
1377
|
-
|
|
1378
|
-
error: `Failed to
|
|
1039
|
+
correctedEntries: 0,
|
|
1040
|
+
error: `Failed to correct entries: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1379
1041
|
};
|
|
1380
1042
|
}
|
|
1381
1043
|
}
|
|
1382
|
-
async function
|
|
1383
|
-
if (!confirmDeletion) {
|
|
1384
|
-
return {
|
|
1385
|
-
success: false,
|
|
1386
|
-
deletedBlocks: 0,
|
|
1387
|
-
error: "Deletion not confirmed. This will permanently delete ALL blocks and cannot be undone."
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1044
|
+
async function getLedgerEntries(workDir, includeStatus) {
|
|
1390
1045
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1391
1046
|
const blocks = await loadLedger(projectDir);
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
try {
|
|
1400
|
-
await saveLedger([], projectDir);
|
|
1401
|
-
await cleanupAllSharedStorage(projectDir);
|
|
1402
|
-
return {
|
|
1403
|
-
success: true,
|
|
1404
|
-
deletedBlocks: blocks.length,
|
|
1405
|
-
warning: "All blocks have been permanently deleted. Ledger is now empty."
|
|
1406
|
-
};
|
|
1407
|
-
} catch (error) {
|
|
1408
|
-
return {
|
|
1409
|
-
success: false,
|
|
1410
|
-
deletedBlocks: 0,
|
|
1411
|
-
error: `Failed to delete all blocks: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
function checkChainIntegrityAfterDeletion(blocks, datesToDelete) {
|
|
1416
|
-
const sortedBlocks = blocks.sort((a, b) => a.date.localeCompare(b.date));
|
|
1417
|
-
const deleteIndices = /* @__PURE__ */ new Set();
|
|
1418
|
-
sortedBlocks.forEach((block, index) => {
|
|
1419
|
-
if (datesToDelete.includes(block.date)) {
|
|
1420
|
-
deleteIndices.add(index);
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
if (deleteIndices.has(0) && sortedBlocks.length > 1) {
|
|
1424
|
-
return "Cannot delete the genesis block when other blocks exist. This would break the chain.";
|
|
1425
|
-
}
|
|
1426
|
-
for (let i = 1; i < sortedBlocks.length; i++) {
|
|
1427
|
-
const currentBlock = sortedBlocks[i];
|
|
1428
|
-
const previousBlock = sortedBlocks[i - 1];
|
|
1429
|
-
if (!deleteIndices.has(i) && deleteIndices.has(i - 1)) {
|
|
1430
|
-
let lastValidIndex = i - 2;
|
|
1431
|
-
while (lastValidIndex >= 0 && deleteIndices.has(lastValidIndex)) {
|
|
1432
|
-
lastValidIndex--;
|
|
1433
|
-
}
|
|
1434
|
-
if (lastValidIndex >= 0) {
|
|
1435
|
-
const expectedPreviousHash = sortedBlocks[lastValidIndex].blockHash;
|
|
1436
|
-
if (currentBlock.previousHash !== expectedPreviousHash) {
|
|
1437
|
-
return `Deleting block ${previousBlock.date} would break the chain. Block ${currentBlock.date} references it.`;
|
|
1438
|
-
}
|
|
1047
|
+
const statusFilter = includeStatus || ["active"];
|
|
1048
|
+
const entries = [];
|
|
1049
|
+
for (const block of blocks) {
|
|
1050
|
+
for (const entry of block.entries) {
|
|
1051
|
+
const entryStatus = entry.status || "active";
|
|
1052
|
+
if (statusFilter.includes(entryStatus)) {
|
|
1053
|
+
entries.push(entry);
|
|
1439
1054
|
}
|
|
1440
1055
|
}
|
|
1441
1056
|
}
|
|
1442
|
-
return
|
|
1057
|
+
return entries;
|
|
1443
1058
|
}
|
|
1444
|
-
function
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
);
|
|
1459
|
-
block.chainHeight = i;
|
|
1460
|
-
block.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1461
|
-
}
|
|
1462
|
-
return sortedBlocks;
|
|
1463
|
-
}
|
|
1464
|
-
async function cleanupSharedStorage(deletedDates, projectDir) {
|
|
1465
|
-
const manifest = await loadManifest(projectDir);
|
|
1466
|
-
if (!manifest) return;
|
|
1467
|
-
const activeDir = getActiveBlocksDir(projectDir);
|
|
1468
|
-
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
1469
|
-
for (const date of deletedDates) {
|
|
1470
|
-
const blockFile = `${date}.json`;
|
|
1471
|
-
const blockPath = join2(activeDir, blockFile);
|
|
1472
|
-
if (existsSync3(blockPath)) {
|
|
1473
|
-
try {
|
|
1474
|
-
await unlink(blockPath);
|
|
1475
|
-
} catch {
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
const index = manifest.activeBlocks.indexOf(blockFile);
|
|
1479
|
-
if (index > -1) {
|
|
1480
|
-
manifest.activeBlocks.splice(index, 1);
|
|
1481
|
-
}
|
|
1482
|
-
delete manifest.index.byDate[date];
|
|
1483
|
-
}
|
|
1484
|
-
manifest.totalBlocks = manifest.activeBlocks.length + manifest.archivedBlocks.length;
|
|
1485
|
-
await saveManifest(manifest, projectDir);
|
|
1486
|
-
}
|
|
1487
|
-
async function cleanupAllSharedStorage(projectDir) {
|
|
1488
|
-
const sharedDir = getSharedLedgerDir(projectDir);
|
|
1489
|
-
if (existsSync3(sharedDir)) {
|
|
1490
|
-
try {
|
|
1491
|
-
const manifest = await createDefaultManifest(projectDir);
|
|
1492
|
-
await saveManifest(manifest, projectDir);
|
|
1493
|
-
const activeDir = getActiveBlocksDir(projectDir);
|
|
1494
|
-
if (existsSync3(activeDir)) {
|
|
1495
|
-
const files = await readdir(activeDir).catch(() => []);
|
|
1496
|
-
for (const file of files) {
|
|
1497
|
-
if (file.endsWith(".json")) {
|
|
1498
|
-
try {
|
|
1499
|
-
await unlink(join2(activeDir, file));
|
|
1500
|
-
} catch {
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1059
|
+
async function getEntryCorrectionHistory(entryIds, workDir) {
|
|
1060
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1061
|
+
const blocks = await loadLedger(projectDir);
|
|
1062
|
+
const history = /* @__PURE__ */ new Map();
|
|
1063
|
+
for (const entryId of entryIds) {
|
|
1064
|
+
const corrections = [];
|
|
1065
|
+
let original = null;
|
|
1066
|
+
for (const block of blocks) {
|
|
1067
|
+
for (const entry of block.entries) {
|
|
1068
|
+
if (entry.id === entryId) {
|
|
1069
|
+
original = entry;
|
|
1070
|
+
}
|
|
1071
|
+
if (entry.correctedBy === entryId) {
|
|
1072
|
+
corrections.push(entry);
|
|
1503
1073
|
}
|
|
1504
1074
|
}
|
|
1505
|
-
} catch {
|
|
1506
1075
|
}
|
|
1507
|
-
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
// src/memory/issue-store.ts
|
|
1511
|
-
async function storeIssues(issues, project, workDir) {
|
|
1512
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1513
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1514
|
-
await mkdir3(memoryDir, { recursive: true });
|
|
1515
|
-
const stored = [];
|
|
1516
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1517
|
-
const seenHashes = /* @__PURE__ */ new Set();
|
|
1518
|
-
let duplicates = 0;
|
|
1519
|
-
for (const issue of issues) {
|
|
1520
|
-
const hash = hashIssue(issue);
|
|
1521
|
-
if (seenHashes.has(hash)) {
|
|
1522
|
-
duplicates++;
|
|
1523
|
-
continue;
|
|
1076
|
+
if (original) {
|
|
1077
|
+
history.set(entryId, { original, corrections });
|
|
1524
1078
|
}
|
|
1525
|
-
seenHashes.add(hash);
|
|
1526
|
-
const storedIssue = {
|
|
1527
|
-
id: issue.id,
|
|
1528
|
-
hash,
|
|
1529
|
-
severity: issue.severity,
|
|
1530
|
-
issue: issue.issue,
|
|
1531
|
-
fix: issue.fix,
|
|
1532
|
-
file: issue.file,
|
|
1533
|
-
line: issue.line,
|
|
1534
|
-
agent: issue.agent,
|
|
1535
|
-
category: issue.category,
|
|
1536
|
-
timestamp: now,
|
|
1537
|
-
project,
|
|
1538
|
-
resolved: false,
|
|
1539
|
-
resolvedAt: void 0
|
|
1540
|
-
};
|
|
1541
|
-
stored.push(storedIssue);
|
|
1542
1079
|
}
|
|
1543
|
-
|
|
1544
|
-
await appendIssuesToLedger(stored, projectDir);
|
|
1545
|
-
const dedupedCount = await updateIssueIndex(stored, projectDir);
|
|
1546
|
-
return { stored: dedupedCount, duplicates: duplicates + (stored.length - dedupedCount) };
|
|
1080
|
+
return history;
|
|
1547
1081
|
}
|
|
1548
|
-
async function
|
|
1549
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
1550
|
-
const limit = options.limit || 10;
|
|
1551
|
-
const allIssues = await loadIssueIndex(projectDir);
|
|
1552
|
-
if (allIssues.length === 0) {
|
|
1553
|
-
return [];
|
|
1554
|
-
}
|
|
1555
|
-
const filteredIssues = allIssues.filter((issue) => {
|
|
1556
|
-
if (options.project && issue.project !== options.project) return false;
|
|
1557
|
-
if (options.severity && !options.severity.includes(issue.severity)) return false;
|
|
1558
|
-
if (options.agent && issue.agent !== options.agent) return false;
|
|
1559
|
-
if (!options.includeResolved && issue.resolved) return false;
|
|
1560
|
-
return true;
|
|
1561
|
-
});
|
|
1562
|
-
if (filteredIssues.length === 0) {
|
|
1563
|
-
return [];
|
|
1564
|
-
}
|
|
1565
|
-
const bm25 = new BM25Index();
|
|
1566
|
-
const issueMap = /* @__PURE__ */ new Map();
|
|
1567
|
-
for (const issue of filteredIssues) {
|
|
1568
|
-
const searchText = `${issue.issue} ${issue.fix} ${issue.file} ${issue.agent} ${issue.category || ""} ${issue.severity}`;
|
|
1569
|
-
bm25.addDocument({
|
|
1570
|
-
id: issue.id,
|
|
1571
|
-
text: searchText
|
|
1572
|
-
});
|
|
1573
|
-
issueMap.set(issue.id, issue);
|
|
1574
|
-
}
|
|
1575
|
-
const bm25Results = bm25.search(query, limit);
|
|
1576
|
-
return bm25Results.map((result) => ({
|
|
1577
|
-
issue: issueMap.get(result.id),
|
|
1578
|
-
score: result.score,
|
|
1579
|
-
matchType: "bm25"
|
|
1580
|
-
}));
|
|
1581
|
-
}
|
|
1582
|
-
async function findSimilarIssues(issue, options = {}) {
|
|
1583
|
-
const query = `${issue.issue} ${issue.fix} ${issue.agent}`;
|
|
1584
|
-
const searchOptions = {
|
|
1585
|
-
limit: (options.limit || 5) + 5,
|
|
1586
|
-
// Get extra to account for filtering
|
|
1587
|
-
includeResolved: true
|
|
1588
|
-
};
|
|
1589
|
-
if (options.workDir !== void 0) {
|
|
1590
|
-
searchOptions.workDir = options.workDir;
|
|
1591
|
-
}
|
|
1592
|
-
const results = await searchIssues(query, searchOptions);
|
|
1593
|
-
let filtered = results.filter((r) => r.issue.id !== issue.id);
|
|
1594
|
-
if (options.excludeSameFile) {
|
|
1595
|
-
filtered = filtered.filter((r) => r.issue.file !== issue.file);
|
|
1596
|
-
}
|
|
1597
|
-
return filtered.slice(0, options.limit || 5);
|
|
1598
|
-
}
|
|
1599
|
-
async function markIssueResolved(issueId, workDir) {
|
|
1600
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1601
|
-
const index = await loadIssueIndex(projectDir);
|
|
1602
|
-
const issue = index.find((i) => i.id === issueId);
|
|
1603
|
-
if (!issue) return false;
|
|
1604
|
-
issue.resolved = true;
|
|
1605
|
-
issue.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1606
|
-
await saveIssueIndex(index, projectDir);
|
|
1607
|
-
return true;
|
|
1608
|
-
}
|
|
1609
|
-
async function autoResolveIssues(newIssueHashes, scannedFiles, workDir) {
|
|
1082
|
+
async function getCorrectionStats(workDir) {
|
|
1610
1083
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1611
|
-
const
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
let
|
|
1615
|
-
let
|
|
1616
|
-
for (const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
if (!newIssueHashes.has(issue.hash)) {
|
|
1624
|
-
issue.resolved = true;
|
|
1625
|
-
issue.resolvedAt = now;
|
|
1626
|
-
resolvedCount++;
|
|
1627
|
-
} else {
|
|
1628
|
-
stillActiveCount++;
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
if (resolvedCount > 0) {
|
|
1632
|
-
await saveIssueIndex(index, projectDir);
|
|
1633
|
-
}
|
|
1634
|
-
return { resolved: resolvedCount, stillActive: stillActiveCount };
|
|
1635
|
-
}
|
|
1636
|
-
async function resolveGoalViolation(file, goalDescription, workDir) {
|
|
1637
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1638
|
-
const index = await loadIssueIndex(projectDir);
|
|
1639
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1640
|
-
let resolvedCount = 0;
|
|
1641
|
-
for (const issue of index) {
|
|
1642
|
-
if (issue.resolved) continue;
|
|
1643
|
-
if (issue.agent !== "goal-violation") continue;
|
|
1644
|
-
const normalizedFile = issue.file.replace(/\\/g, "/");
|
|
1645
|
-
const normalizedTarget = file.replace(/\\/g, "/");
|
|
1646
|
-
if (normalizedFile === normalizedTarget && issue.issue.includes(`Goal "${goalDescription}"`)) {
|
|
1647
|
-
issue.resolved = true;
|
|
1648
|
-
issue.resolvedAt = now;
|
|
1649
|
-
resolvedCount++;
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
if (resolvedCount > 0) {
|
|
1653
|
-
await saveIssueIndex(index, projectDir);
|
|
1654
|
-
}
|
|
1655
|
-
return resolvedCount;
|
|
1656
|
-
}
|
|
1657
|
-
function getIssueHash(issue) {
|
|
1658
|
-
return hashIssue(issue);
|
|
1659
|
-
}
|
|
1660
|
-
async function getMemoryStats(workDir) {
|
|
1661
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1662
|
-
const index = await loadIssueIndex(projectDir);
|
|
1663
|
-
const historical = await getHistoricalInsights(projectDir);
|
|
1664
|
-
const MAX_ISSUES = 1e4;
|
|
1665
|
-
const uniqueHashes = new Set(index.map((i) => i.hash));
|
|
1666
|
-
const stats = {
|
|
1667
|
-
totalIssues: index.length,
|
|
1668
|
-
activeIssues: 0,
|
|
1669
|
-
issuesByAgent: {},
|
|
1670
|
-
issuesBySeverity: {},
|
|
1671
|
-
activeIssuesBySeverity: {},
|
|
1672
|
-
oldestIssue: void 0,
|
|
1673
|
-
newestIssue: void 0,
|
|
1674
|
-
resolvedCount: 0,
|
|
1675
|
-
historicalIssues: historical.totalHistoricalIssues,
|
|
1676
|
-
improvementTrend: historical.improvementTrend,
|
|
1677
|
-
capacityInfo: {
|
|
1678
|
-
current: index.length,
|
|
1679
|
-
max: MAX_ISSUES,
|
|
1680
|
-
percentFull: Math.round(index.length / MAX_ISSUES * 100),
|
|
1681
|
-
isAtCap: index.length >= MAX_ISSUES
|
|
1682
|
-
},
|
|
1683
|
-
deduplicationStats: {
|
|
1684
|
-
duplicatesAvoided: index.length - uniqueHashes.size,
|
|
1685
|
-
uniquePatterns: uniqueHashes.size
|
|
1686
|
-
}
|
|
1687
|
-
};
|
|
1688
|
-
for (const issue of index) {
|
|
1689
|
-
stats.issuesByAgent[issue.agent] = (stats.issuesByAgent[issue.agent] || 0) + 1;
|
|
1690
|
-
stats.issuesBySeverity[issue.severity] = (stats.issuesBySeverity[issue.severity] || 0) + 1;
|
|
1691
|
-
if (issue.resolved) {
|
|
1692
|
-
stats.resolvedCount++;
|
|
1693
|
-
} else {
|
|
1694
|
-
stats.activeIssues++;
|
|
1695
|
-
stats.activeIssuesBySeverity[issue.severity] = (stats.activeIssuesBySeverity[issue.severity] || 0) + 1;
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
if (index.length > 0) {
|
|
1699
|
-
const sorted = [...index].sort(
|
|
1700
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
1701
|
-
);
|
|
1702
|
-
const oldest = sorted[0]?.timestamp;
|
|
1703
|
-
const newest = sorted[sorted.length - 1]?.timestamp;
|
|
1704
|
-
if (oldest !== void 0) {
|
|
1705
|
-
stats.oldestIssue = oldest;
|
|
1706
|
-
}
|
|
1707
|
-
if (newest !== void 0) {
|
|
1708
|
-
stats.newestIssue = newest;
|
|
1084
|
+
const blocks = await loadLedger(projectDir);
|
|
1085
|
+
let totalEntries = 0;
|
|
1086
|
+
let activeEntries = 0;
|
|
1087
|
+
let correctedEntries = 0;
|
|
1088
|
+
let falsePositives = 0;
|
|
1089
|
+
for (const block of blocks) {
|
|
1090
|
+
for (const entry of block.entries) {
|
|
1091
|
+
totalEntries++;
|
|
1092
|
+
const status = entry.status || "active";
|
|
1093
|
+
if (status === "active") activeEntries++;
|
|
1094
|
+
else if (status === "corrected") correctedEntries++;
|
|
1095
|
+
else if (status === "false-positive") falsePositives++;
|
|
1709
1096
|
}
|
|
1710
1097
|
}
|
|
1711
|
-
|
|
1712
|
-
}
|
|
1713
|
-
async function getRecentIssues(options = {}) {
|
|
1714
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
1715
|
-
const index = await loadIssueIndex(projectDir);
|
|
1716
|
-
const limit = options.limit || 20;
|
|
1717
|
-
const daysBack = options.daysBack || 7;
|
|
1718
|
-
const includeResolved = options.includeResolved ?? false;
|
|
1719
|
-
const cutoff = /* @__PURE__ */ new Date();
|
|
1720
|
-
cutoff.setDate(cutoff.getDate() - daysBack);
|
|
1721
|
-
return index.filter((i) => {
|
|
1722
|
-
if (new Date(i.timestamp) < cutoff) return false;
|
|
1723
|
-
if (!includeResolved && i.resolved) return false;
|
|
1724
|
-
return true;
|
|
1725
|
-
}).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
|
|
1726
|
-
}
|
|
1727
|
-
async function purgeIssues(strategy, options = {}) {
|
|
1728
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
1729
|
-
const index = await loadIssueIndex(projectDir);
|
|
1730
|
-
const originalCount = index.length;
|
|
1731
|
-
let remaining = [];
|
|
1732
|
-
switch (strategy) {
|
|
1733
|
-
case "smart":
|
|
1734
|
-
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
1735
|
-
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
1736
|
-
remaining = index.filter((i) => {
|
|
1737
|
-
const isRecent = new Date(i.timestamp) >= thirtyDaysAgo;
|
|
1738
|
-
const isImportant = ["critical", "high"].includes(i.severity);
|
|
1739
|
-
const isUnresolved = !i.resolved;
|
|
1740
|
-
return isRecent || isImportant || isUnresolved;
|
|
1741
|
-
});
|
|
1742
|
-
break;
|
|
1743
|
-
case "resolved":
|
|
1744
|
-
remaining = index.filter((i) => !i.resolved);
|
|
1745
|
-
break;
|
|
1746
|
-
case "old":
|
|
1747
|
-
const daysOld = options.daysOld || 90;
|
|
1748
|
-
const cutoffDate = /* @__PURE__ */ new Date();
|
|
1749
|
-
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
1750
|
-
remaining = index.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
1751
|
-
break;
|
|
1752
|
-
case "all":
|
|
1753
|
-
remaining = [];
|
|
1754
|
-
break;
|
|
1755
|
-
}
|
|
1756
|
-
await saveIssueIndex(remaining, projectDir);
|
|
1098
|
+
const correctionRate = totalEntries > 0 ? (correctedEntries + falsePositives) / totalEntries * 100 : 0;
|
|
1757
1099
|
return {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1100
|
+
totalEntries,
|
|
1101
|
+
activeEntries,
|
|
1102
|
+
correctedEntries,
|
|
1103
|
+
falsePositives,
|
|
1104
|
+
correctionRate: Math.round(correctionRate * 100) / 100
|
|
1761
1105
|
};
|
|
1762
1106
|
}
|
|
1763
|
-
async function getDailyLogs(workDir) {
|
|
1764
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1765
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1766
|
-
try {
|
|
1767
|
-
if (!existsSync4(memoryDir)) return [];
|
|
1768
|
-
const files = await readdir2(memoryDir);
|
|
1769
|
-
return files.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort().reverse();
|
|
1770
|
-
} catch {
|
|
1771
|
-
return [];
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
async function appendToDailyLog(issues, projectDir) {
|
|
1775
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1776
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1777
|
-
const logPath = join3(memoryDir, `${today}.md`);
|
|
1778
|
-
let content = "";
|
|
1779
|
-
try {
|
|
1780
|
-
if (existsSync4(logPath)) {
|
|
1781
|
-
content = await readFile3(logPath, "utf-8");
|
|
1782
|
-
} else {
|
|
1783
|
-
content = `# Issue Log: ${today}
|
|
1784
|
-
|
|
1785
|
-
`;
|
|
1786
|
-
}
|
|
1787
|
-
} catch {
|
|
1788
|
-
content = `# Issue Log: ${today}
|
|
1789
|
-
|
|
1790
|
-
`;
|
|
1791
|
-
}
|
|
1792
|
-
const time = (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0];
|
|
1793
|
-
const newEntries = issues.map(
|
|
1794
|
-
(i) => `## [${time}] ${i.severity.toUpperCase()}: ${i.issue.slice(0, 80)}${i.issue.length > 80 ? "..." : ""}
|
|
1795
|
-
- **File:** \`${i.file}\`${i.line ? `:${i.line}` : ""}
|
|
1796
|
-
- **Agent:** ${i.agent}
|
|
1797
|
-
- **Fix:** ${i.fix.slice(0, 200)}${i.fix.length > 200 ? "..." : ""}
|
|
1798
|
-
`
|
|
1799
|
-
).join("\n");
|
|
1800
|
-
content += newEntries + "\n";
|
|
1801
|
-
await writeFile2(logPath, content);
|
|
1802
|
-
}
|
|
1803
|
-
async function loadIssueIndex(projectDir) {
|
|
1804
|
-
const indexPath = join3(getTrieDirectory(projectDir), "memory", "issues.json");
|
|
1805
|
-
try {
|
|
1806
|
-
if (existsSync4(indexPath)) {
|
|
1807
|
-
const content = await readFile3(indexPath, "utf-8");
|
|
1808
|
-
const result = safeParseAndValidate(content, IssueIndexSchema);
|
|
1809
|
-
if (result.success) {
|
|
1810
|
-
return result.data;
|
|
1811
|
-
}
|
|
1812
|
-
console.error(` Issue index corrupted: ${result.error}`);
|
|
1813
|
-
const backupManager = new BackupManager(indexPath);
|
|
1814
|
-
if (await backupManager.recoverFromBackup()) {
|
|
1815
|
-
console.error(" \u2705 Recovered from backup");
|
|
1816
|
-
const recovered = await readFile3(indexPath, "utf-8");
|
|
1817
|
-
const recoveredResult = safeParseAndValidate(recovered, IssueIndexSchema);
|
|
1818
|
-
if (recoveredResult.success) {
|
|
1819
|
-
return recoveredResult.data;
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
console.error(" No valid backup found, starting fresh");
|
|
1823
|
-
}
|
|
1824
|
-
} catch {
|
|
1825
|
-
}
|
|
1826
|
-
return [];
|
|
1827
|
-
}
|
|
1828
|
-
async function updateIssueIndex(newIssues, projectDir) {
|
|
1829
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1830
|
-
await mkdir3(memoryDir, { recursive: true });
|
|
1831
|
-
let existing = await loadIssueIndex(projectDir);
|
|
1832
|
-
const hashSet = new Set(existing.map((i) => i.hash));
|
|
1833
|
-
const toAdd = newIssues.filter((i) => !hashSet.has(i.hash));
|
|
1834
|
-
const dedupedCount = toAdd.length;
|
|
1835
|
-
existing = [...existing, ...toAdd];
|
|
1836
|
-
if (existing.length > 500) {
|
|
1837
|
-
const { summary, remaining } = await compactOldIssues(existing, {
|
|
1838
|
-
keepDays: 30,
|
|
1839
|
-
minIssuesToCompact: 100
|
|
1840
|
-
});
|
|
1841
|
-
if (summary) {
|
|
1842
|
-
await saveCompactedSummary(summary, projectDir);
|
|
1843
|
-
existing = remaining;
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
if (existing.length > 1e4) {
|
|
1847
|
-
existing = intelligentPrune(existing, 1e4);
|
|
1848
|
-
}
|
|
1849
|
-
await saveIssueIndex(existing, projectDir);
|
|
1850
|
-
return dedupedCount;
|
|
1851
|
-
}
|
|
1852
|
-
function intelligentPrune(issues, targetCount) {
|
|
1853
|
-
const severityWeight = {
|
|
1854
|
-
critical: 100,
|
|
1855
|
-
high: 50,
|
|
1856
|
-
moderate: 20,
|
|
1857
|
-
low: 10,
|
|
1858
|
-
info: 5
|
|
1859
|
-
};
|
|
1860
|
-
const scored = issues.map((issue) => {
|
|
1861
|
-
const ageInDays = (Date.now() - new Date(issue.timestamp).getTime()) / (1e3 * 60 * 60 * 24);
|
|
1862
|
-
const recencyScore = Math.max(0, 100 - ageInDays * 2);
|
|
1863
|
-
const severityScore = severityWeight[issue.severity] || 10;
|
|
1864
|
-
const resolvedPenalty = issue.resolved ? -50 : 0;
|
|
1865
|
-
return {
|
|
1866
|
-
issue,
|
|
1867
|
-
score: recencyScore + severityScore + resolvedPenalty
|
|
1868
|
-
};
|
|
1869
|
-
});
|
|
1870
|
-
return scored.sort((a, b) => b.score - a.score).slice(0, targetCount).map((s) => s.issue);
|
|
1871
|
-
}
|
|
1872
|
-
async function saveIssueIndex(issues, projectDir) {
|
|
1873
|
-
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
1874
|
-
await mkdir3(memoryDir, { recursive: true });
|
|
1875
|
-
const indexPath = join3(memoryDir, "issues.json");
|
|
1876
|
-
const backupManager = new BackupManager(indexPath);
|
|
1877
|
-
await backupManager.createBackup();
|
|
1878
|
-
await atomicWriteJSON(indexPath, issues);
|
|
1879
|
-
}
|
|
1880
|
-
function hashIssue(issue) {
|
|
1881
|
-
const content = `${issue.issue}|${issue.file}|${issue.severity}|${issue.agent}`;
|
|
1882
|
-
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
1883
|
-
}
|
|
1884
1107
|
|
|
1885
1108
|
export {
|
|
1886
|
-
getHistoricalInsights,
|
|
1887
1109
|
formatAuditLog,
|
|
1888
1110
|
getAuditStatistics,
|
|
1889
1111
|
getRecentAuditLogs,
|
|
@@ -1897,7 +1119,9 @@ export {
|
|
|
1897
1119
|
getWorkingTreeDiff,
|
|
1898
1120
|
isGitRepo,
|
|
1899
1121
|
getChangedFilesSinceTimestamp,
|
|
1122
|
+
appendIssuesToLedger,
|
|
1900
1123
|
verifyLedger,
|
|
1124
|
+
computeMerkleRoot,
|
|
1901
1125
|
getLedgerBlocks,
|
|
1902
1126
|
initializeSharedLedger,
|
|
1903
1127
|
syncLedgerFromShared,
|
|
@@ -1908,18 +1132,9 @@ export {
|
|
|
1908
1132
|
compressOldBlocks,
|
|
1909
1133
|
getStorageStats,
|
|
1910
1134
|
shouldCompress,
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
findSimilarIssues,
|
|
1916
|
-
markIssueResolved,
|
|
1917
|
-
autoResolveIssues,
|
|
1918
|
-
resolveGoalViolation,
|
|
1919
|
-
getIssueHash,
|
|
1920
|
-
getMemoryStats,
|
|
1921
|
-
getRecentIssues,
|
|
1922
|
-
purgeIssues,
|
|
1923
|
-
getDailyLogs
|
|
1135
|
+
correctLedgerEntries,
|
|
1136
|
+
getLedgerEntries,
|
|
1137
|
+
getEntryCorrectionHistory,
|
|
1138
|
+
getCorrectionStats
|
|
1924
1139
|
};
|
|
1925
|
-
//# sourceMappingURL=chunk-
|
|
1140
|
+
//# sourceMappingURL=chunk-ZUEAHFSY.js.map
|