@triedotdev/mcp 1.0.148 → 1.0.151
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/LICENSE +21 -0
- package/README.md +225 -78
- package/dist/{autonomy-config-ZCOSTMPD.js → autonomy-config-3APNC6QF.js} +3 -3
- package/dist/{chat-store-OJLJCJFI.js → chat-store-HMTDL7I2.js} +3 -3
- package/dist/{chunk-LD7ZEFNY.js → chunk-3KZBC3RJ.js} +2 -2
- package/dist/{chunk-SH7H3WRU.js → chunk-4TQQP7JD.js} +3 -3
- package/dist/{chunk-SH7H3WRU.js.map → chunk-4TQQP7JD.js.map} +1 -1
- package/dist/{chunk-N2EDZTKG.js → chunk-4UDBGYI3.js} +13 -13
- package/dist/{chunk-XTTZAQWJ.js → chunk-74R4XSFB.js} +5 -5
- package/dist/{chunk-T6PS3MXJ.js → chunk-7HYOJ4Q7.js} +8 -8
- package/dist/{chunk-T6PS3MXJ.js.map → chunk-7HYOJ4Q7.js.map} +1 -1
- package/dist/{chunk-3MUCUZ46.js → chunk-ABY2R7OK.js} +3 -3
- package/dist/chunk-ED7PLRQA.js +782 -0
- package/dist/chunk-ED7PLRQA.js.map +1 -0
- package/dist/{chunk-4MJ52WBH.js → chunk-EFWVF6TI.js} +4 -2
- package/dist/chunk-EFWVF6TI.js.map +1 -0
- package/dist/{chunk-23RJT5WT.js → chunk-F7BMFOZ6.js} +2 -2
- package/dist/{chunk-4PAAGLKO.js → chunk-G7Q23IGF.js} +36 -9
- package/dist/{chunk-4PAAGLKO.js.map → chunk-G7Q23IGF.js.map} +1 -1
- package/dist/{chunk-LT6VUZG2.js → chunk-GLY76TSI.js} +3 -3
- package/dist/{chunk-WMDFK7LI.js → chunk-IFBEAOHH.js} +5 -5
- package/dist/{chunk-YEIJW6X6.js → chunk-JIS2OCZR.js} +4 -4
- package/dist/{chunk-FG467PDD.js → chunk-LNUMECBJ.js} +2 -2
- package/dist/{chunk-FPEMP54L.js → chunk-OJXFQRUE.js} +2 -2
- package/dist/{chunk-5KJ4UJOY.js → chunk-QQG42HCI.js} +2 -2
- package/dist/{chunk-7OJ6JIPL.js → chunk-REHKDCI6.js} +7 -150
- package/dist/chunk-REHKDCI6.js.map +1 -0
- package/dist/{chunk-J7CEBSEB.js → chunk-SU3WCAC4.js} +102 -163
- package/dist/chunk-SU3WCAC4.js.map +1 -0
- package/dist/{chunk-4C67GV3O.js → chunk-TCNCNWGV.js} +2 -2
- package/dist/{chunk-NKHO34UZ.js → chunk-TU7D5DEW.js} +2 -2
- package/dist/{chunk-62POBLFC.js → chunk-TWPX6PHF.js} +453 -986
- package/dist/chunk-TWPX6PHF.js.map +1 -0
- package/dist/{chunk-FH335WL5.js → chunk-TWQPOVRA.js} +2 -2
- package/dist/{chunk-YOJGSRZK.js → chunk-V3O7C2LY.js} +2 -2
- package/dist/{chunk-V7AY2EJO.js → chunk-WOTLY5NA.js} +2 -2
- package/dist/cli/create-agent.js +8 -40
- package/dist/cli/create-agent.js.map +1 -1
- package/dist/cli/main.js +121 -164
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +31 -155
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/{client-INNE2GGZ.js → client-5L64D5SQ.js} +3 -3
- package/dist/{codebase-index-FMIULFZQ.js → codebase-index-OOE7OAHP.js} +3 -3
- package/dist/{fast-analyzer-MWKCDRGD.js → fast-analyzer-FMU3X4AZ.js} +5 -5
- package/dist/github-ingester-C66ZRUYC.js +11 -0
- package/dist/{goal-manager-ZBWKWEML.js → goal-manager-VTBFFYN4.js} +8 -7
- package/dist/{goal-validator-DA3JQ6JN.js → goal-validator-EM5XVWVC.js} +7 -6
- package/dist/{graph-J4OGTYCO.js → graph-26JPZ3DF.js} +3 -3
- package/dist/{hypothesis-JCUMZKTG.js → hypothesis-4UPE7KXU.js} +8 -7
- package/dist/{incident-index-BWW2UEY7.js → incident-index-H6APJ4S3.js} +3 -3
- package/dist/index.js +116 -397
- package/dist/index.js.map +1 -1
- package/dist/{insight-store-A5XXMFD6.js → insight-store-QEEUQR5L.js} +4 -4
- package/dist/{issue-store-LZWZIGM7.js → issue-store-C6XYENE5.js} +7 -4
- package/dist/ledger-VNA4DX3Z.js +51 -0
- package/dist/linear-ingester-WIUBWF55.js +11 -0
- package/dist/{tiered-storage-VZL7KK64.js → tiered-storage-P6Z3NV2Q.js} +3 -3
- package/dist/trie-agent-GJJJCL6P.js +27 -0
- package/dist/trie-agent-GJJJCL6P.js.map +1 -0
- package/package.json +8 -6
- package/dist/chunk-4MJ52WBH.js.map +0 -1
- package/dist/chunk-62POBLFC.js.map +0 -1
- package/dist/chunk-7OJ6JIPL.js.map +0 -1
- package/dist/chunk-G76DYVGX.js +0 -136
- package/dist/chunk-G76DYVGX.js.map +0 -1
- package/dist/chunk-J7CEBSEB.js.map +0 -1
- package/dist/comprehension-46F7ZNKL.js +0 -821
- package/dist/comprehension-46F7ZNKL.js.map +0 -1
- package/dist/github-ingester-J2ZFYXVE.js +0 -11
- package/dist/linear-ingester-JRDQAIAA.js +0 -11
- package/dist/trie-agent-6A7YBNTQ.js +0 -26
- package/dist/workers/agent-worker.d.ts +0 -2
- package/dist/workers/agent-worker.js +0 -28
- package/dist/workers/agent-worker.js.map +0 -1
- /package/dist/{autonomy-config-ZCOSTMPD.js.map → autonomy-config-3APNC6QF.js.map} +0 -0
- /package/dist/{chat-store-OJLJCJFI.js.map → chat-store-HMTDL7I2.js.map} +0 -0
- /package/dist/{chunk-LD7ZEFNY.js.map → chunk-3KZBC3RJ.js.map} +0 -0
- /package/dist/{chunk-N2EDZTKG.js.map → chunk-4UDBGYI3.js.map} +0 -0
- /package/dist/{chunk-XTTZAQWJ.js.map → chunk-74R4XSFB.js.map} +0 -0
- /package/dist/{chunk-3MUCUZ46.js.map → chunk-ABY2R7OK.js.map} +0 -0
- /package/dist/{chunk-23RJT5WT.js.map → chunk-F7BMFOZ6.js.map} +0 -0
- /package/dist/{chunk-LT6VUZG2.js.map → chunk-GLY76TSI.js.map} +0 -0
- /package/dist/{chunk-WMDFK7LI.js.map → chunk-IFBEAOHH.js.map} +0 -0
- /package/dist/{chunk-YEIJW6X6.js.map → chunk-JIS2OCZR.js.map} +0 -0
- /package/dist/{chunk-FG467PDD.js.map → chunk-LNUMECBJ.js.map} +0 -0
- /package/dist/{chunk-FPEMP54L.js.map → chunk-OJXFQRUE.js.map} +0 -0
- /package/dist/{chunk-5KJ4UJOY.js.map → chunk-QQG42HCI.js.map} +0 -0
- /package/dist/{chunk-4C67GV3O.js.map → chunk-TCNCNWGV.js.map} +0 -0
- /package/dist/{chunk-NKHO34UZ.js.map → chunk-TU7D5DEW.js.map} +0 -0
- /package/dist/{chunk-FH335WL5.js.map → chunk-TWQPOVRA.js.map} +0 -0
- /package/dist/{chunk-YOJGSRZK.js.map → chunk-V3O7C2LY.js.map} +0 -0
- /package/dist/{chunk-V7AY2EJO.js.map → chunk-WOTLY5NA.js.map} +0 -0
- /package/dist/{client-INNE2GGZ.js.map → client-5L64D5SQ.js.map} +0 -0
- /package/dist/{codebase-index-FMIULFZQ.js.map → codebase-index-OOE7OAHP.js.map} +0 -0
- /package/dist/{fast-analyzer-MWKCDRGD.js.map → fast-analyzer-FMU3X4AZ.js.map} +0 -0
- /package/dist/{github-ingester-J2ZFYXVE.js.map → github-ingester-C66ZRUYC.js.map} +0 -0
- /package/dist/{goal-manager-ZBWKWEML.js.map → goal-manager-VTBFFYN4.js.map} +0 -0
- /package/dist/{goal-validator-DA3JQ6JN.js.map → goal-validator-EM5XVWVC.js.map} +0 -0
- /package/dist/{graph-J4OGTYCO.js.map → graph-26JPZ3DF.js.map} +0 -0
- /package/dist/{hypothesis-JCUMZKTG.js.map → hypothesis-4UPE7KXU.js.map} +0 -0
- /package/dist/{incident-index-BWW2UEY7.js.map → incident-index-H6APJ4S3.js.map} +0 -0
- /package/dist/{insight-store-A5XXMFD6.js.map → insight-store-QEEUQR5L.js.map} +0 -0
- /package/dist/{issue-store-LZWZIGM7.js.map → issue-store-C6XYENE5.js.map} +0 -0
- /package/dist/{linear-ingester-JRDQAIAA.js.map → ledger-VNA4DX3Z.js.map} +0 -0
- /package/dist/{tiered-storage-VZL7KK64.js.map → linear-ingester-WIUBWF55.js.map} +0 -0
- /package/dist/{trie-agent-6A7YBNTQ.js.map → tiered-storage-P6Z3NV2Q.js.map} +0 -0
|
@@ -1,422 +1,29 @@
|
|
|
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";
|
|
10
4
|
import {
|
|
11
5
|
getTrieDirectory,
|
|
12
6
|
getWorkingDirectory
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
|
|
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
|
-
}
|
|
7
|
+
} from "./chunk-4TQQP7JD.js";
|
|
401
8
|
|
|
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 existsSync4 } 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 as
|
|
16
|
+
import { join as join3 } 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
|
|
416
23
|
import { exec, execFile, execSync } from "child_process";
|
|
417
24
|
import { promisify } from "util";
|
|
418
25
|
|
|
419
|
-
// src/
|
|
26
|
+
// src/utils/audit-logger.ts
|
|
420
27
|
function formatAuditLog(_entry) {
|
|
421
28
|
return "Audit logging has been integrated into the decision ledger";
|
|
422
29
|
}
|
|
@@ -464,9 +71,6 @@ async function logSkillExecution(_execution) {
|
|
|
464
71
|
async function getRecentAuditLogs(_limit = 10) {
|
|
465
72
|
return [];
|
|
466
73
|
}
|
|
467
|
-
async function getSkillAuditLogs(_skillName) {
|
|
468
|
-
return [];
|
|
469
|
-
}
|
|
470
74
|
|
|
471
75
|
// src/utils/command-runner.ts
|
|
472
76
|
var execAsync = promisify(exec);
|
|
@@ -712,6 +316,176 @@ async function getChangedFilesSinceTimestamp(projectPath, timestamp) {
|
|
|
712
316
|
}
|
|
713
317
|
}
|
|
714
318
|
|
|
319
|
+
// src/memory/crypto-keys.ts
|
|
320
|
+
import * as ed25519 from "@noble/ed25519";
|
|
321
|
+
import { randomBytes } from "crypto";
|
|
322
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
323
|
+
import { join } from "path";
|
|
324
|
+
function getKeysDirectory(workDir) {
|
|
325
|
+
const trieDir = getTrieDirectory(workDir || process.cwd());
|
|
326
|
+
return join(trieDir, "keys");
|
|
327
|
+
}
|
|
328
|
+
function getDefaultKeyPath(workDir) {
|
|
329
|
+
return join(getKeysDirectory(workDir), "signing-key.json");
|
|
330
|
+
}
|
|
331
|
+
async function generateKeyPair() {
|
|
332
|
+
const privateKeyBytes = randomBytes(32);
|
|
333
|
+
const publicKeyBytes = await ed25519.getPublicKeyAsync(privateKeyBytes);
|
|
334
|
+
return {
|
|
335
|
+
publicKey: Buffer.from(publicKeyBytes).toString("hex"),
|
|
336
|
+
privateKey: Buffer.from(privateKeyBytes).toString("hex")
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function saveKeyPair(keyPair, workDir) {
|
|
340
|
+
const keysDir = getKeysDirectory(workDir);
|
|
341
|
+
if (!existsSync2(keysDir)) {
|
|
342
|
+
mkdirSync(keysDir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
const keyPath = getDefaultKeyPath(workDir);
|
|
345
|
+
const keyData = {
|
|
346
|
+
...keyPair,
|
|
347
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
348
|
+
version: 1
|
|
349
|
+
};
|
|
350
|
+
writeFileSync(keyPath, JSON.stringify(keyData, null, 2), "utf-8");
|
|
351
|
+
console.error("\u26A0\uFE0F Signing key created at:", keyPath);
|
|
352
|
+
console.error(" Keep this file secure and add to .gitignore");
|
|
353
|
+
}
|
|
354
|
+
function loadKeyPair(workDir) {
|
|
355
|
+
const keyPath = getDefaultKeyPath(workDir);
|
|
356
|
+
if (!existsSync2(keyPath)) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
const keyData = JSON.parse(readFileSync(keyPath, "utf-8"));
|
|
361
|
+
return {
|
|
362
|
+
publicKey: keyData.publicKey,
|
|
363
|
+
privateKey: keyData.privateKey
|
|
364
|
+
};
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error("Failed to load signing key:", error);
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function getOrCreateKeyPair(workDir) {
|
|
371
|
+
const existing = loadKeyPair(workDir);
|
|
372
|
+
if (existing) {
|
|
373
|
+
return existing;
|
|
374
|
+
}
|
|
375
|
+
const newKeyPair = await generateKeyPair();
|
|
376
|
+
saveKeyPair(newKeyPair, workDir);
|
|
377
|
+
return newKeyPair;
|
|
378
|
+
}
|
|
379
|
+
async function signData(data, privateKey) {
|
|
380
|
+
const dataBytes = Buffer.from(data, "utf-8");
|
|
381
|
+
const privateKeyBytes = Buffer.from(privateKey, "hex");
|
|
382
|
+
const signatureBytes = await ed25519.signAsync(dataBytes, privateKeyBytes);
|
|
383
|
+
return Buffer.from(signatureBytes).toString("hex");
|
|
384
|
+
}
|
|
385
|
+
async function verifySignature(data, signature, publicKey) {
|
|
386
|
+
try {
|
|
387
|
+
const dataBytes = Buffer.from(data, "utf-8");
|
|
388
|
+
const signatureBytes = Buffer.from(signature, "hex");
|
|
389
|
+
const publicKeyBytes = Buffer.from(publicKey, "hex");
|
|
390
|
+
return await ed25519.verifyAsync(signatureBytes, dataBytes, publicKeyBytes);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error("Signature verification failed:", error);
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function signHash(hash, workDir) {
|
|
397
|
+
const keyPair = await getOrCreateKeyPair(workDir);
|
|
398
|
+
const signature = await signData(hash, keyPair.privateKey);
|
|
399
|
+
return {
|
|
400
|
+
signature,
|
|
401
|
+
publicKey: keyPair.publicKey,
|
|
402
|
+
algorithm: "Ed25519",
|
|
403
|
+
signedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async function verifyHashSignature(hash, signatureData) {
|
|
407
|
+
if (signatureData.algorithm !== "Ed25519") {
|
|
408
|
+
console.error("Unsupported signature algorithm:", signatureData.algorithm);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
return await verifySignature(hash, signatureData.signature, signatureData.publicKey);
|
|
412
|
+
}
|
|
413
|
+
function hasSigningKey(workDir) {
|
|
414
|
+
const keyPath = getDefaultKeyPath(workDir);
|
|
415
|
+
return existsSync2(keyPath);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/memory/git-integration.ts
|
|
419
|
+
import { exec as exec2 } from "child_process";
|
|
420
|
+
import { promisify as promisify2 } from "util";
|
|
421
|
+
import { existsSync as existsSync3 } from "fs";
|
|
422
|
+
import { join as join2 } from "path";
|
|
423
|
+
var execAsync2 = promisify2(exec2);
|
|
424
|
+
async function isGitIntegrationEnabled(workDir) {
|
|
425
|
+
try {
|
|
426
|
+
const gitDir = join2(workDir, ".git");
|
|
427
|
+
if (!existsSync3(gitDir)) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
const configPath = join2(getTrieDirectory(workDir), "config.json");
|
|
431
|
+
if (!existsSync3(configPath)) {
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
const config = JSON.parse(await import("fs/promises").then((fs) => fs.readFile(configPath, "utf-8")));
|
|
435
|
+
return config.gitIntegration !== false;
|
|
436
|
+
} catch {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async function autoCommitLedger(workDir, message) {
|
|
441
|
+
try {
|
|
442
|
+
const enabled = await isGitIntegrationEnabled(workDir);
|
|
443
|
+
if (!enabled) {
|
|
444
|
+
return { committed: false, error: "Git integration disabled" };
|
|
445
|
+
}
|
|
446
|
+
const ledgerPath = join2(getTrieDirectory(workDir), "memory", "ledger.json");
|
|
447
|
+
const { stdout: statusOutput } = await execAsync2("git status --porcelain", { cwd: workDir });
|
|
448
|
+
const hasLedgerChanges = statusOutput.includes("ledger.json");
|
|
449
|
+
if (!hasLedgerChanges) {
|
|
450
|
+
return { committed: false, error: "No ledger changes to commit" };
|
|
451
|
+
}
|
|
452
|
+
await execAsync2(`git add ${ledgerPath}`, { cwd: workDir });
|
|
453
|
+
const commitMessage = message || "ledger: append entries";
|
|
454
|
+
await execAsync2(
|
|
455
|
+
`git commit -m "${commitMessage}"`,
|
|
456
|
+
{ cwd: workDir }
|
|
457
|
+
);
|
|
458
|
+
const { stdout: hashOutput } = await execAsync2("git rev-parse HEAD", { cwd: workDir });
|
|
459
|
+
const commitHash = hashOutput.trim();
|
|
460
|
+
return {
|
|
461
|
+
committed: true,
|
|
462
|
+
commitHash
|
|
463
|
+
};
|
|
464
|
+
} catch (error) {
|
|
465
|
+
return {
|
|
466
|
+
committed: false,
|
|
467
|
+
error: error instanceof Error ? error.message : String(error)
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function ensureKeysIgnored(workDir) {
|
|
472
|
+
try {
|
|
473
|
+
const gitignorePath = join2(workDir, ".gitignore");
|
|
474
|
+
const fs = await import("fs/promises");
|
|
475
|
+
let gitignore = "";
|
|
476
|
+
if (existsSync3(gitignorePath)) {
|
|
477
|
+
gitignore = await fs.readFile(gitignorePath, "utf-8");
|
|
478
|
+
}
|
|
479
|
+
if (gitignore.includes(".trie/keys/")) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const addition = "\n# Trie signing keys (keep secure, do not commit)\n.trie/keys/\n";
|
|
483
|
+
await fs.appendFile(gitignorePath, addition, "utf-8");
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error("Failed to update .gitignore:", error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
715
489
|
// src/memory/ledger.ts
|
|
716
490
|
var LEDGER_FILENAME = "ledger.json";
|
|
717
491
|
var SHARED_LEDGER_DIR = ".trie-shared";
|
|
@@ -719,25 +493,74 @@ var MANIFEST_FILENAME = "ledger-manifest.json";
|
|
|
719
493
|
var SYNC_STATE_FILENAME = "ledger-sync.json";
|
|
720
494
|
var GENESIS_HASH = "0".repeat(64);
|
|
721
495
|
var LEDGER_VERSION = 2;
|
|
496
|
+
async function signLedgerEntry(entry, workDir) {
|
|
497
|
+
try {
|
|
498
|
+
const signatureData = await signHash(entry.hash, workDir);
|
|
499
|
+
return {
|
|
500
|
+
...entry,
|
|
501
|
+
signature: signatureData.signature,
|
|
502
|
+
publicKey: signatureData.publicKey,
|
|
503
|
+
signedAt: signatureData.signedAt,
|
|
504
|
+
signatureAlgorithm: "Ed25519"
|
|
505
|
+
};
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error("Failed to sign ledger entry:", error);
|
|
508
|
+
return entry;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function verifyLedgerEntry(entry) {
|
|
512
|
+
if (!entry.signature || !entry.publicKey) {
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
if (entry.signatureAlgorithm !== "Ed25519") {
|
|
516
|
+
console.error("Unsupported signature algorithm:", entry.signatureAlgorithm);
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
const signatureData = {
|
|
520
|
+
signature: entry.signature,
|
|
521
|
+
publicKey: entry.publicKey,
|
|
522
|
+
algorithm: "Ed25519",
|
|
523
|
+
signedAt: entry.signedAt || ""
|
|
524
|
+
};
|
|
525
|
+
return await verifyHashSignature(entry.hash, signatureData);
|
|
526
|
+
}
|
|
527
|
+
async function verifyBlockSignatures(block) {
|
|
528
|
+
const invalidEntries = [];
|
|
529
|
+
for (const entry of block.entries) {
|
|
530
|
+
const isValid = await verifyLedgerEntry(entry);
|
|
531
|
+
if (!isValid) {
|
|
532
|
+
invalidEntries.push(entry.id);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
valid: invalidEntries.length === 0,
|
|
537
|
+
invalidEntries
|
|
538
|
+
};
|
|
539
|
+
}
|
|
722
540
|
async function appendIssuesToLedger(issues, workDir, author) {
|
|
723
541
|
if (issues.length === 0) return null;
|
|
724
542
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
725
|
-
const memoryDir =
|
|
726
|
-
await
|
|
543
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
544
|
+
await mkdir(memoryDir, { recursive: true });
|
|
727
545
|
const isRepo = await isGitRepo(projectDir);
|
|
728
546
|
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
729
547
|
const blockAuthor = author || lastCommit?.author || "unknown";
|
|
730
548
|
const blocks = await loadLedger(projectDir);
|
|
731
549
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
732
550
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
733
|
-
const
|
|
551
|
+
const shouldSign = hasSigningKey(projectDir);
|
|
552
|
+
let entries = issues.map((issue) => ({
|
|
734
553
|
id: issue.id,
|
|
735
554
|
hash: issue.hash,
|
|
736
555
|
severity: issue.severity,
|
|
737
556
|
file: issue.file,
|
|
738
557
|
agent: issue.agent,
|
|
739
|
-
timestamp: issue.timestamp
|
|
558
|
+
timestamp: issue.timestamp,
|
|
559
|
+
status: "active"
|
|
740
560
|
}));
|
|
561
|
+
if (shouldSign) {
|
|
562
|
+
entries = await Promise.all(entries.map((entry) => signLedgerEntry(entry, projectDir)));
|
|
563
|
+
}
|
|
741
564
|
const previousBlock = blocks[blocks.length - 1];
|
|
742
565
|
const block = previousBlock && previousBlock.date === today ? previousBlock : createSyncableBlock(today, now, previousBlock?.blockHash ?? GENESIS_HASH, blockAuthor, lastCommit?.hash, blocks.length);
|
|
743
566
|
if (block !== previousBlock) {
|
|
@@ -748,6 +571,11 @@ async function appendIssuesToLedger(issues, workDir, author) {
|
|
|
748
571
|
block.blockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
749
572
|
block.updatedAt = now;
|
|
750
573
|
await saveLedger(blocks, projectDir);
|
|
574
|
+
if (await isGitRepo(projectDir)) {
|
|
575
|
+
await ensureKeysIgnored(projectDir);
|
|
576
|
+
const commitMessage = `ledger: append ${entries.length} ${entries.length === 1 ? "entry" : "entries"}`;
|
|
577
|
+
await autoCommitLedger(projectDir, commitMessage);
|
|
578
|
+
}
|
|
751
579
|
return block;
|
|
752
580
|
}
|
|
753
581
|
async function verifyLedger(workDir) {
|
|
@@ -774,7 +602,14 @@ async function verifyLedger(workDir) {
|
|
|
774
602
|
}
|
|
775
603
|
const computedBlockHash = computeBlockHash(block.previousHash, block.merkleRoot, block.date, block.version);
|
|
776
604
|
if (block.blockHash !== computedBlockHash) {
|
|
777
|
-
return { valid: false, error: `Block ${i} hash mismatch` };
|
|
605
|
+
return { valid: false, error: `Block ${i} block hash mismatch` };
|
|
606
|
+
}
|
|
607
|
+
const signatureVerification = await verifyBlockSignatures(block);
|
|
608
|
+
if (!signatureVerification.valid) {
|
|
609
|
+
return {
|
|
610
|
+
valid: false,
|
|
611
|
+
error: `Block ${i} has invalid signatures: ${signatureVerification.invalidEntries.join(", ")}`
|
|
612
|
+
};
|
|
778
613
|
}
|
|
779
614
|
}
|
|
780
615
|
return { valid: true };
|
|
@@ -809,15 +644,15 @@ function createSyncableBlock(date, now, previousHash, author, gitCommit, chainHe
|
|
|
809
644
|
createdAt: now,
|
|
810
645
|
updatedAt: now,
|
|
811
646
|
author,
|
|
812
|
-
|
|
813
|
-
|
|
647
|
+
chainHeight,
|
|
648
|
+
...gitCommit && { gitCommit }
|
|
814
649
|
};
|
|
815
650
|
}
|
|
816
651
|
async function loadLedger(projectDir) {
|
|
817
|
-
const ledgerPath =
|
|
652
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
818
653
|
try {
|
|
819
|
-
if (!
|
|
820
|
-
const content = await
|
|
654
|
+
if (!existsSync4(ledgerPath)) return [];
|
|
655
|
+
const content = await readFile(ledgerPath, "utf-8");
|
|
821
656
|
const parsed = JSON.parse(content);
|
|
822
657
|
if (!Array.isArray(parsed)) return [];
|
|
823
658
|
return parsed;
|
|
@@ -830,40 +665,40 @@ async function getLedgerBlocks(workDir) {
|
|
|
830
665
|
return loadLedger(projectDir);
|
|
831
666
|
}
|
|
832
667
|
async function saveLedger(blocks, projectDir) {
|
|
833
|
-
const ledgerPath =
|
|
668
|
+
const ledgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
834
669
|
await atomicWriteJSON(ledgerPath, blocks);
|
|
835
670
|
}
|
|
836
671
|
function sha256(input) {
|
|
837
672
|
return createHash("sha256").update(input).digest("hex");
|
|
838
673
|
}
|
|
839
674
|
function getSharedLedgerDir(projectDir) {
|
|
840
|
-
return
|
|
675
|
+
return join3(projectDir, SHARED_LEDGER_DIR);
|
|
841
676
|
}
|
|
842
677
|
function getActiveBlocksDir(projectDir) {
|
|
843
|
-
return
|
|
678
|
+
return join3(getSharedLedgerDir(projectDir), "active");
|
|
844
679
|
}
|
|
845
680
|
function getArchivedBlocksDir(projectDir) {
|
|
846
|
-
return
|
|
681
|
+
return join3(getSharedLedgerDir(projectDir), "archived");
|
|
847
682
|
}
|
|
848
683
|
function getManifestPath(projectDir) {
|
|
849
|
-
return
|
|
684
|
+
return join3(getSharedLedgerDir(projectDir), MANIFEST_FILENAME);
|
|
850
685
|
}
|
|
851
686
|
function getSyncStatePath(projectDir) {
|
|
852
|
-
return
|
|
687
|
+
return join3(getTrieDirectory(projectDir), "memory", SYNC_STATE_FILENAME);
|
|
853
688
|
}
|
|
854
689
|
async function ensureSharedStorageStructure(projectDir) {
|
|
855
690
|
const sharedDir = getSharedLedgerDir(projectDir);
|
|
856
691
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
857
692
|
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
858
|
-
await
|
|
859
|
-
await
|
|
860
|
-
await
|
|
693
|
+
await mkdir(sharedDir, { recursive: true });
|
|
694
|
+
await mkdir(activeDir, { recursive: true });
|
|
695
|
+
await mkdir(archivedDir, { recursive: true });
|
|
861
696
|
}
|
|
862
697
|
async function loadManifest(projectDir) {
|
|
863
698
|
const manifestPath = getManifestPath(projectDir);
|
|
864
699
|
try {
|
|
865
|
-
if (!
|
|
866
|
-
const content = await
|
|
700
|
+
if (!existsSync4(manifestPath)) return null;
|
|
701
|
+
const content = await readFile(manifestPath, "utf-8");
|
|
867
702
|
return JSON.parse(content);
|
|
868
703
|
} catch {
|
|
869
704
|
return null;
|
|
@@ -873,7 +708,7 @@ async function saveManifest(manifest, projectDir) {
|
|
|
873
708
|
const manifestPath = getManifestPath(projectDir);
|
|
874
709
|
await atomicWriteJSON(manifestPath, manifest);
|
|
875
710
|
}
|
|
876
|
-
async function createDefaultManifest(
|
|
711
|
+
async function createDefaultManifest(_projectDir) {
|
|
877
712
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
878
713
|
return {
|
|
879
714
|
version: LEDGER_VERSION,
|
|
@@ -900,8 +735,8 @@ async function createDefaultManifest(projectDir) {
|
|
|
900
735
|
async function loadSyncState(projectDir) {
|
|
901
736
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
902
737
|
try {
|
|
903
|
-
if (!
|
|
904
|
-
const content = await
|
|
738
|
+
if (!existsSync4(syncStatePath)) return null;
|
|
739
|
+
const content = await readFile(syncStatePath, "utf-8");
|
|
905
740
|
return JSON.parse(content);
|
|
906
741
|
} catch {
|
|
907
742
|
return null;
|
|
@@ -909,8 +744,8 @@ async function loadSyncState(projectDir) {
|
|
|
909
744
|
}
|
|
910
745
|
async function saveSyncState(syncState, projectDir) {
|
|
911
746
|
const syncStatePath = getSyncStatePath(projectDir);
|
|
912
|
-
const memoryDir =
|
|
913
|
-
await
|
|
747
|
+
const memoryDir = join3(getTrieDirectory(projectDir), "memory");
|
|
748
|
+
await mkdir(memoryDir, { recursive: true });
|
|
914
749
|
await atomicWriteJSON(syncStatePath, syncState);
|
|
915
750
|
}
|
|
916
751
|
async function initializeSharedLedger(workDir) {
|
|
@@ -961,16 +796,17 @@ async function pushLedgerToShared(workDir) {
|
|
|
961
796
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
962
797
|
for (const block of localBlocks) {
|
|
963
798
|
const blockFilename = `${block.date}.json`;
|
|
964
|
-
const blockPath =
|
|
965
|
-
if (!
|
|
799
|
+
const blockPath = join3(activeDir, blockFilename);
|
|
800
|
+
if (!existsSync4(blockPath) || block.updatedAt > manifest.lastSync) {
|
|
966
801
|
await atomicWriteJSON(blockPath, block);
|
|
967
802
|
manifest.index.byDate[block.date] = `active/${blockFilename}`;
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
803
|
+
const blockAuthor = block.author;
|
|
804
|
+
if (blockAuthor) {
|
|
805
|
+
if (!manifest.index.byAuthor[blockAuthor]) {
|
|
806
|
+
manifest.index.byAuthor[blockAuthor] = [];
|
|
971
807
|
}
|
|
972
|
-
if (!manifest.index.byAuthor[
|
|
973
|
-
manifest.index.byAuthor[
|
|
808
|
+
if (!manifest.index.byAuthor[blockAuthor].includes(`active/${blockFilename}`)) {
|
|
809
|
+
manifest.index.byAuthor[blockAuthor].push(`active/${blockFilename}`);
|
|
974
810
|
}
|
|
975
811
|
}
|
|
976
812
|
if (!manifest.activeBlocks.includes(blockFilename)) {
|
|
@@ -989,10 +825,10 @@ async function loadSharedBlocks(projectDir) {
|
|
|
989
825
|
const blocks = [];
|
|
990
826
|
const activeDir = getActiveBlocksDir(projectDir);
|
|
991
827
|
for (const filename of manifest.activeBlocks) {
|
|
992
|
-
const blockPath =
|
|
828
|
+
const blockPath = join3(activeDir, filename);
|
|
993
829
|
try {
|
|
994
|
-
if (
|
|
995
|
-
const content = await
|
|
830
|
+
if (existsSync4(blockPath)) {
|
|
831
|
+
const content = await readFile(blockPath, "utf-8");
|
|
996
832
|
const block = JSON.parse(content);
|
|
997
833
|
blocks.push(block);
|
|
998
834
|
}
|
|
@@ -1063,8 +899,6 @@ function detectBlockConflict(localBlock, remoteBlock) {
|
|
|
1063
899
|
remoteBlock
|
|
1064
900
|
};
|
|
1065
901
|
}
|
|
1066
|
-
const localEntryHashes = new Set(localBlock.entries.map((e) => e.hash));
|
|
1067
|
-
const remoteEntryHashes = new Set(remoteBlock.entries.map((e) => e.hash));
|
|
1068
902
|
const hasConflictingEntries = localBlock.entries.some((localEntry) => {
|
|
1069
903
|
const conflictingRemoteEntry = remoteBlock.entries.find(
|
|
1070
904
|
(remoteEntry) => remoteEntry.id === localEntry.id && remoteEntry.hash !== localEntry.hash
|
|
@@ -1124,8 +958,8 @@ function mergeBlockEntries(localBlock, remoteBlock) {
|
|
|
1124
958
|
}
|
|
1125
959
|
async function migrateLegacyLedger(workDir) {
|
|
1126
960
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1127
|
-
const legacyLedgerPath =
|
|
1128
|
-
if (!
|
|
961
|
+
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
962
|
+
if (!existsSync4(legacyLedgerPath)) {
|
|
1129
963
|
return false;
|
|
1130
964
|
}
|
|
1131
965
|
try {
|
|
@@ -1141,9 +975,9 @@ async function migrateLegacyLedger(workDir) {
|
|
|
1141
975
|
...block,
|
|
1142
976
|
version: LEDGER_VERSION,
|
|
1143
977
|
author: defaultAuthor,
|
|
1144
|
-
gitCommit: lastCommit?.hash,
|
|
1145
978
|
chainHeight: index,
|
|
1146
|
-
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
979
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
980
|
+
...lastCommit?.hash && { gitCommit: lastCommit.hash }
|
|
1147
981
|
}));
|
|
1148
982
|
await initializeSharedLedger(projectDir);
|
|
1149
983
|
await saveLedger(migratedBlocks, projectDir);
|
|
@@ -1159,8 +993,8 @@ async function migrateLegacyLedger(workDir) {
|
|
|
1159
993
|
}
|
|
1160
994
|
async function detectLegacyLedger(workDir) {
|
|
1161
995
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1162
|
-
const legacyLedgerPath =
|
|
1163
|
-
if (!
|
|
996
|
+
const legacyLedgerPath = join3(getTrieDirectory(projectDir), "memory", LEDGER_FILENAME);
|
|
997
|
+
if (!existsSync4(legacyLedgerPath)) {
|
|
1164
998
|
return false;
|
|
1165
999
|
}
|
|
1166
1000
|
try {
|
|
@@ -1202,7 +1036,6 @@ async function compressOldBlocks(workDir) {
|
|
|
1202
1036
|
let compressedSize = 0;
|
|
1203
1037
|
const blocksByMonth = /* @__PURE__ */ new Map();
|
|
1204
1038
|
for (const blockFile of manifest.activeBlocks) {
|
|
1205
|
-
const blockPath = join2(activeDir, blockFile);
|
|
1206
1039
|
const blockDate = blockFile.replace(".json", "");
|
|
1207
1040
|
if (new Date(blockDate) < cutoffDate) {
|
|
1208
1041
|
const monthKey = blockDate.slice(0, 7);
|
|
@@ -1213,18 +1046,18 @@ async function compressOldBlocks(workDir) {
|
|
|
1213
1046
|
}
|
|
1214
1047
|
}
|
|
1215
1048
|
for (const [monthKey, blockFiles] of blocksByMonth) {
|
|
1216
|
-
const archivePath =
|
|
1217
|
-
if (
|
|
1049
|
+
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1050
|
+
if (existsSync4(archivePath)) {
|
|
1218
1051
|
continue;
|
|
1219
1052
|
}
|
|
1220
1053
|
console.log(`Archiving ${blockFiles.length} blocks for ${monthKey}...`);
|
|
1221
1054
|
const monthlyBlocks = [];
|
|
1222
1055
|
for (const blockFile of blockFiles) {
|
|
1223
|
-
const blockPath =
|
|
1056
|
+
const blockPath = join3(activeDir, blockFile);
|
|
1224
1057
|
try {
|
|
1225
1058
|
const stats = await stat(blockPath);
|
|
1226
1059
|
originalSize += stats.size;
|
|
1227
|
-
const content = await
|
|
1060
|
+
const content = await readFile(blockPath, "utf-8");
|
|
1228
1061
|
const block = JSON.parse(content);
|
|
1229
1062
|
monthlyBlocks.push(block);
|
|
1230
1063
|
} catch (error) {
|
|
@@ -1241,10 +1074,10 @@ async function compressOldBlocks(workDir) {
|
|
|
1241
1074
|
);
|
|
1242
1075
|
const compressedStats = await stat(tempPath);
|
|
1243
1076
|
compressedSize += compressedStats.size;
|
|
1244
|
-
await writeFile(archivePath, await
|
|
1077
|
+
await writeFile(archivePath, await readFile(tempPath));
|
|
1245
1078
|
await unlink(tempPath);
|
|
1246
1079
|
for (const blockFile of blockFiles) {
|
|
1247
|
-
const blockPath =
|
|
1080
|
+
const blockPath = join3(activeDir, blockFile);
|
|
1248
1081
|
await unlink(blockPath);
|
|
1249
1082
|
const index = manifest.activeBlocks.indexOf(blockFile);
|
|
1250
1083
|
if (index > -1) {
|
|
@@ -1263,6 +1096,31 @@ async function compressOldBlocks(workDir) {
|
|
|
1263
1096
|
sizeReduction: originalSize > 0 ? Math.round((originalSize - compressedSize) / originalSize * 100) : 0
|
|
1264
1097
|
};
|
|
1265
1098
|
}
|
|
1099
|
+
async function loadArchivedBlocks(projectDir, monthKey) {
|
|
1100
|
+
const archivedDir = getArchivedBlocksDir(projectDir);
|
|
1101
|
+
const archivePath = join3(archivedDir, `${monthKey}.tar.gz`);
|
|
1102
|
+
if (!existsSync4(archivePath)) {
|
|
1103
|
+
return [];
|
|
1104
|
+
}
|
|
1105
|
+
try {
|
|
1106
|
+
const chunks = [];
|
|
1107
|
+
await pipeline(
|
|
1108
|
+
createReadStream(archivePath),
|
|
1109
|
+
createGunzip(),
|
|
1110
|
+
async function* (source) {
|
|
1111
|
+
for await (const chunk of source) {
|
|
1112
|
+
chunks.push(chunk);
|
|
1113
|
+
}
|
|
1114
|
+
yield Buffer.concat(chunks);
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
const decompressedData = Buffer.concat(chunks).toString("utf-8");
|
|
1118
|
+
return JSON.parse(decompressedData);
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
console.error(`Failed to load archived blocks for ${monthKey}:`, error);
|
|
1121
|
+
return [];
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1266
1124
|
async function getStorageStats(workDir) {
|
|
1267
1125
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1268
1126
|
const manifest = await loadManifest(projectDir);
|
|
@@ -1281,9 +1139,9 @@ async function getStorageStats(workDir) {
|
|
|
1281
1139
|
let activeSize = 0;
|
|
1282
1140
|
let archivedSize = 0;
|
|
1283
1141
|
for (const blockFile of manifest.activeBlocks) {
|
|
1284
|
-
const blockPath =
|
|
1142
|
+
const blockPath = join3(activeDir, blockFile);
|
|
1285
1143
|
try {
|
|
1286
|
-
if (
|
|
1144
|
+
if (existsSync4(blockPath)) {
|
|
1287
1145
|
const stats = await stat(blockPath);
|
|
1288
1146
|
activeSize += stats.size;
|
|
1289
1147
|
}
|
|
@@ -1291,16 +1149,15 @@ async function getStorageStats(workDir) {
|
|
|
1291
1149
|
}
|
|
1292
1150
|
}
|
|
1293
1151
|
for (const archiveFile of manifest.archivedBlocks) {
|
|
1294
|
-
const archivePath =
|
|
1152
|
+
const archivePath = join3(archivedDir, archiveFile);
|
|
1295
1153
|
try {
|
|
1296
|
-
if (
|
|
1154
|
+
if (existsSync4(archivePath)) {
|
|
1297
1155
|
const stats = await stat(archivePath);
|
|
1298
1156
|
archivedSize += stats.size;
|
|
1299
1157
|
}
|
|
1300
1158
|
} catch {
|
|
1301
1159
|
}
|
|
1302
1160
|
}
|
|
1303
|
-
const totalSize = activeSize + archivedSize;
|
|
1304
1161
|
const estimatedUncompressed = archivedSize * 5;
|
|
1305
1162
|
const compressionRatio = estimatedUncompressed > 0 ? Math.round((1 - archivedSize / estimatedUncompressed) * 100) : 0;
|
|
1306
1163
|
return {
|
|
@@ -1328,566 +1185,179 @@ async function shouldCompress(workDir) {
|
|
|
1328
1185
|
});
|
|
1329
1186
|
return exceedsSize || hasOldBlocks;
|
|
1330
1187
|
}
|
|
1331
|
-
async function
|
|
1332
|
-
if (
|
|
1188
|
+
async function correctLedgerEntries(entryIds, reason, correctionType = "corrected", workDir, author) {
|
|
1189
|
+
if (entryIds.length === 0) {
|
|
1333
1190
|
return {
|
|
1334
1191
|
success: false,
|
|
1335
|
-
|
|
1336
|
-
error: "
|
|
1192
|
+
correctedEntries: 0,
|
|
1193
|
+
error: "No entry IDs provided"
|
|
1337
1194
|
};
|
|
1338
1195
|
}
|
|
1339
|
-
|
|
1340
|
-
const blocks = await loadLedger(projectDir);
|
|
1341
|
-
if (blocks.length === 0) {
|
|
1196
|
+
if (!reason || reason.trim().length === 0) {
|
|
1342
1197
|
return {
|
|
1343
1198
|
success: false,
|
|
1344
|
-
|
|
1345
|
-
error: "
|
|
1199
|
+
correctedEntries: 0,
|
|
1200
|
+
error: "Correction reason is required"
|
|
1346
1201
|
};
|
|
1347
1202
|
}
|
|
1348
|
-
const
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1203
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1204
|
+
const blocks = await loadLedger(projectDir);
|
|
1205
|
+
const entriesToCorrect = [];
|
|
1206
|
+
for (const block of blocks) {
|
|
1207
|
+
for (const entry of block.entries) {
|
|
1208
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1209
|
+
entriesToCorrect.push(entry);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1356
1212
|
}
|
|
1357
|
-
|
|
1358
|
-
if (integrityIssue) {
|
|
1213
|
+
if (entriesToCorrect.length === 0) {
|
|
1359
1214
|
return {
|
|
1360
1215
|
success: false,
|
|
1361
|
-
|
|
1362
|
-
error:
|
|
1216
|
+
correctedEntries: 0,
|
|
1217
|
+
error: "No active entries found with the provided IDs"
|
|
1363
1218
|
};
|
|
1364
1219
|
}
|
|
1365
1220
|
try {
|
|
1366
|
-
const
|
|
1367
|
-
await
|
|
1368
|
-
await
|
|
1221
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1222
|
+
const isRepo = await isGitRepo(projectDir);
|
|
1223
|
+
const lastCommit = isRepo ? await getLastCommit(projectDir) : null;
|
|
1224
|
+
const correctionAuthor = author || lastCommit?.author || "unknown";
|
|
1225
|
+
const correctionEntries = entriesToCorrect.map((entry) => {
|
|
1226
|
+
const correctionId = `correction-${entry.id}-${Date.now()}`;
|
|
1227
|
+
return {
|
|
1228
|
+
id: correctionId,
|
|
1229
|
+
hash: sha256(`${correctionId}:${entry.hash}:${reason}:${now}`),
|
|
1230
|
+
severity: "info",
|
|
1231
|
+
file: entry.file,
|
|
1232
|
+
agent: "ledger-correction",
|
|
1233
|
+
timestamp: now,
|
|
1234
|
+
status: "active",
|
|
1235
|
+
correction: `Correcting entry ${entry.id}: ${reason}`,
|
|
1236
|
+
correctedBy: entry.id
|
|
1237
|
+
};
|
|
1238
|
+
});
|
|
1239
|
+
for (const block of blocks) {
|
|
1240
|
+
let blockModified = false;
|
|
1241
|
+
for (const entry of block.entries) {
|
|
1242
|
+
if (entryIds.includes(entry.id) && entry.status === "active") {
|
|
1243
|
+
entry.status = correctionType;
|
|
1244
|
+
entry.correctionTimestamp = now;
|
|
1245
|
+
entry.correction = reason;
|
|
1246
|
+
blockModified = true;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
if (blockModified) {
|
|
1250
|
+
block.merkleRoot = computeMerkleRoot(block.entries.map((e) => e.hash));
|
|
1251
|
+
block.blockHash = computeBlockHash(
|
|
1252
|
+
block.previousHash,
|
|
1253
|
+
block.merkleRoot,
|
|
1254
|
+
block.date,
|
|
1255
|
+
block.version
|
|
1256
|
+
);
|
|
1257
|
+
block.updatedAt = now;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
await saveLedger(blocks, projectDir);
|
|
1261
|
+
const correctionBlock = await appendIssuesToLedger(
|
|
1262
|
+
correctionEntries.map((entry) => ({
|
|
1263
|
+
id: entry.id,
|
|
1264
|
+
hash: entry.hash,
|
|
1265
|
+
severity: entry.severity,
|
|
1266
|
+
issue: entry.correction || "",
|
|
1267
|
+
fix: "",
|
|
1268
|
+
file: entry.file,
|
|
1269
|
+
line: 0,
|
|
1270
|
+
agent: entry.agent,
|
|
1271
|
+
category: "correction",
|
|
1272
|
+
timestamp: entry.timestamp,
|
|
1273
|
+
project: "",
|
|
1274
|
+
resolved: false,
|
|
1275
|
+
resolvedAt: void 0
|
|
1276
|
+
})),
|
|
1277
|
+
projectDir,
|
|
1278
|
+
correctionAuthor
|
|
1279
|
+
);
|
|
1369
1280
|
return {
|
|
1370
1281
|
success: true,
|
|
1371
|
-
|
|
1372
|
-
|
|
1282
|
+
correctedEntries: entriesToCorrect.length,
|
|
1283
|
+
...correctionBlock && { correctionBlock }
|
|
1373
1284
|
};
|
|
1374
1285
|
} catch (error) {
|
|
1375
1286
|
return {
|
|
1376
1287
|
success: false,
|
|
1377
|
-
|
|
1378
|
-
error: `Failed to
|
|
1288
|
+
correctedEntries: 0,
|
|
1289
|
+
error: `Failed to correct entries: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1379
1290
|
};
|
|
1380
1291
|
}
|
|
1381
1292
|
}
|
|
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
|
-
}
|
|
1293
|
+
async function getLedgerEntries(workDir, includeStatus) {
|
|
1390
1294
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1391
1295
|
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
|
-
}
|
|
1296
|
+
const statusFilter = includeStatus || ["active"];
|
|
1297
|
+
const entries = [];
|
|
1298
|
+
for (const block of blocks) {
|
|
1299
|
+
for (const entry of block.entries) {
|
|
1300
|
+
const entryStatus = entry.status || "active";
|
|
1301
|
+
if (statusFilter.includes(entryStatus)) {
|
|
1302
|
+
entries.push(entry);
|
|
1439
1303
|
}
|
|
1440
1304
|
}
|
|
1441
1305
|
}
|
|
1442
|
-
return
|
|
1443
|
-
}
|
|
1444
|
-
function repairChainAfterDeletion(remainingBlocks) {
|
|
1445
|
-
if (remainingBlocks.length === 0) {
|
|
1446
|
-
return [];
|
|
1447
|
-
}
|
|
1448
|
-
const sortedBlocks = remainingBlocks.sort((a, b) => a.date.localeCompare(b.date));
|
|
1449
|
-
for (let i = 0; i < sortedBlocks.length; i++) {
|
|
1450
|
-
const block = sortedBlocks[i];
|
|
1451
|
-
const previousHash = i === 0 ? GENESIS_HASH : sortedBlocks[i - 1].blockHash;
|
|
1452
|
-
block.previousHash = previousHash;
|
|
1453
|
-
block.blockHash = computeBlockHash(
|
|
1454
|
-
block.previousHash,
|
|
1455
|
-
block.merkleRoot,
|
|
1456
|
-
block.date,
|
|
1457
|
-
block.version
|
|
1458
|
-
);
|
|
1459
|
-
block.chainHeight = i;
|
|
1460
|
-
block.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1461
|
-
}
|
|
1462
|
-
return sortedBlocks;
|
|
1306
|
+
return entries;
|
|
1463
1307
|
}
|
|
1464
|
-
async function
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
const
|
|
1468
|
-
const
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
-
}
|
|
1308
|
+
async function getEntryCorrectionHistory(entryIds, workDir) {
|
|
1309
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1310
|
+
const blocks = await loadLedger(projectDir);
|
|
1311
|
+
const history = /* @__PURE__ */ new Map();
|
|
1312
|
+
for (const entryId of entryIds) {
|
|
1313
|
+
const corrections = [];
|
|
1314
|
+
let original = null;
|
|
1315
|
+
for (const block of blocks) {
|
|
1316
|
+
for (const entry of block.entries) {
|
|
1317
|
+
if (entry.id === entryId) {
|
|
1318
|
+
original = entry;
|
|
1319
|
+
}
|
|
1320
|
+
if (entry.correctedBy === entryId) {
|
|
1321
|
+
corrections.push(entry);
|
|
1503
1322
|
}
|
|
1504
1323
|
}
|
|
1505
|
-
} catch {
|
|
1506
|
-
}
|
|
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;
|
|
1524
|
-
}
|
|
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
|
-
}
|
|
1543
|
-
await appendToDailyLog(stored, projectDir);
|
|
1544
|
-
await appendIssuesToLedger(stored, projectDir);
|
|
1545
|
-
const dedupedCount = await updateIssueIndex(stored, projectDir);
|
|
1546
|
-
return { stored: dedupedCount, duplicates: duplicates + (stored.length - dedupedCount) };
|
|
1547
|
-
}
|
|
1548
|
-
async function searchIssues(query, options = {}) {
|
|
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) {
|
|
1610
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1611
|
-
const index = await loadIssueIndex(projectDir);
|
|
1612
|
-
const scannedFileSet = new Set(scannedFiles.map((f) => f.replace(/\\/g, "/")));
|
|
1613
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1614
|
-
let resolvedCount = 0;
|
|
1615
|
-
let stillActiveCount = 0;
|
|
1616
|
-
for (const issue of index) {
|
|
1617
|
-
if (issue.resolved) continue;
|
|
1618
|
-
const normalizedFile = issue.file.replace(/\\/g, "/");
|
|
1619
|
-
if (!scannedFileSet.has(normalizedFile)) {
|
|
1620
|
-
stillActiveCount++;
|
|
1621
|
-
continue;
|
|
1622
1324
|
}
|
|
1623
|
-
if (
|
|
1624
|
-
|
|
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++;
|
|
1325
|
+
if (original) {
|
|
1326
|
+
history.set(entryId, { original, corrections });
|
|
1650
1327
|
}
|
|
1651
1328
|
}
|
|
1652
|
-
|
|
1653
|
-
await saveIssueIndex(index, projectDir);
|
|
1654
|
-
}
|
|
1655
|
-
return resolvedCount;
|
|
1329
|
+
return history;
|
|
1656
1330
|
}
|
|
1657
|
-
function
|
|
1658
|
-
return hashIssue(issue);
|
|
1659
|
-
}
|
|
1660
|
-
async function getMemoryStats(workDir) {
|
|
1331
|
+
async function getCorrectionStats(workDir) {
|
|
1661
1332
|
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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;
|
|
1333
|
+
const blocks = await loadLedger(projectDir);
|
|
1334
|
+
let totalEntries = 0;
|
|
1335
|
+
let activeEntries = 0;
|
|
1336
|
+
let correctedEntries = 0;
|
|
1337
|
+
let falsePositives = 0;
|
|
1338
|
+
for (const block of blocks) {
|
|
1339
|
+
for (const entry of block.entries) {
|
|
1340
|
+
totalEntries++;
|
|
1341
|
+
const status = entry.status || "active";
|
|
1342
|
+
if (status === "active") activeEntries++;
|
|
1343
|
+
else if (status === "corrected") correctedEntries++;
|
|
1344
|
+
else if (status === "false-positive") falsePositives++;
|
|
1709
1345
|
}
|
|
1710
1346
|
}
|
|
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);
|
|
1347
|
+
const correctionRate = totalEntries > 0 ? (correctedEntries + falsePositives) / totalEntries * 100 : 0;
|
|
1757
1348
|
return {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1349
|
+
totalEntries,
|
|
1350
|
+
activeEntries,
|
|
1351
|
+
correctedEntries,
|
|
1352
|
+
falsePositives,
|
|
1353
|
+
correctionRate: Math.round(correctionRate * 100) / 100
|
|
1761
1354
|
};
|
|
1762
1355
|
}
|
|
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
1356
|
|
|
1885
1357
|
export {
|
|
1886
|
-
getHistoricalInsights,
|
|
1887
1358
|
formatAuditLog,
|
|
1888
1359
|
getAuditStatistics,
|
|
1889
1360
|
getRecentAuditLogs,
|
|
1890
|
-
getSkillAuditLogs,
|
|
1891
1361
|
runShellCommandSync,
|
|
1892
1362
|
getRecentCommits,
|
|
1893
1363
|
getStagedChanges,
|
|
@@ -1897,7 +1367,12 @@ export {
|
|
|
1897
1367
|
getWorkingTreeDiff,
|
|
1898
1368
|
isGitRepo,
|
|
1899
1369
|
getChangedFilesSinceTimestamp,
|
|
1370
|
+
signLedgerEntry,
|
|
1371
|
+
verifyLedgerEntry,
|
|
1372
|
+
verifyBlockSignatures,
|
|
1373
|
+
appendIssuesToLedger,
|
|
1900
1374
|
verifyLedger,
|
|
1375
|
+
computeMerkleRoot,
|
|
1901
1376
|
getLedgerBlocks,
|
|
1902
1377
|
initializeSharedLedger,
|
|
1903
1378
|
syncLedgerFromShared,
|
|
@@ -1906,20 +1381,12 @@ export {
|
|
|
1906
1381
|
detectLegacyLedger,
|
|
1907
1382
|
getLedgerSyncStatus,
|
|
1908
1383
|
compressOldBlocks,
|
|
1384
|
+
loadArchivedBlocks,
|
|
1909
1385
|
getStorageStats,
|
|
1910
1386
|
shouldCompress,
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
findSimilarIssues,
|
|
1916
|
-
markIssueResolved,
|
|
1917
|
-
autoResolveIssues,
|
|
1918
|
-
resolveGoalViolation,
|
|
1919
|
-
getIssueHash,
|
|
1920
|
-
getMemoryStats,
|
|
1921
|
-
getRecentIssues,
|
|
1922
|
-
purgeIssues,
|
|
1923
|
-
getDailyLogs
|
|
1387
|
+
correctLedgerEntries,
|
|
1388
|
+
getLedgerEntries,
|
|
1389
|
+
getEntryCorrectionHistory,
|
|
1390
|
+
getCorrectionStats
|
|
1924
1391
|
};
|
|
1925
|
-
//# sourceMappingURL=chunk-
|
|
1392
|
+
//# sourceMappingURL=chunk-TWPX6PHF.js.map
|