@mycontxt/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +720 -0
- package/dist/index.js +1036 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var ValidationError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ValidationError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var NotFoundError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "NotFoundError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var ConflictError = class extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "ConflictError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var AuthError = class extends Error {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "AuthError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/engine/memory.ts
|
|
28
|
+
var MemoryEngine = class {
|
|
29
|
+
constructor(db) {
|
|
30
|
+
this.db = db;
|
|
31
|
+
}
|
|
32
|
+
// ==================
|
|
33
|
+
// Decision Management
|
|
34
|
+
// ==================
|
|
35
|
+
async addDecision(projectId, input) {
|
|
36
|
+
if (!input.title?.trim()) {
|
|
37
|
+
throw new ValidationError("Decision title is required");
|
|
38
|
+
}
|
|
39
|
+
if (!input.rationale?.trim()) {
|
|
40
|
+
throw new ValidationError("Decision rationale is required");
|
|
41
|
+
}
|
|
42
|
+
const entry = await this.db.createEntry({
|
|
43
|
+
projectId,
|
|
44
|
+
type: "decision",
|
|
45
|
+
title: input.title.trim(),
|
|
46
|
+
content: input.rationale.trim(),
|
|
47
|
+
metadata: {
|
|
48
|
+
alternatives: input.alternatives || [],
|
|
49
|
+
consequences: input.consequences || [],
|
|
50
|
+
tags: input.tags || []
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
async listDecisions(projectId, branch) {
|
|
56
|
+
return this.db.listEntries({
|
|
57
|
+
projectId,
|
|
58
|
+
type: "decision",
|
|
59
|
+
branch,
|
|
60
|
+
isArchived: false
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async getDecision(id) {
|
|
64
|
+
const entry = await this.db.getEntry(id);
|
|
65
|
+
if (!entry || entry.type !== "decision") {
|
|
66
|
+
throw new NotFoundError(`Decision with id ${id} not found`);
|
|
67
|
+
}
|
|
68
|
+
return entry;
|
|
69
|
+
}
|
|
70
|
+
async updateDecision(id, updates) {
|
|
71
|
+
const existing = await this.getDecision(id);
|
|
72
|
+
return this.db.updateEntry(id, {
|
|
73
|
+
title: updates.title?.trim() || existing.title,
|
|
74
|
+
content: updates.rationale?.trim() || existing.content,
|
|
75
|
+
metadata: {
|
|
76
|
+
...existing.metadata,
|
|
77
|
+
alternatives: updates.alternatives || existing.metadata.alternatives,
|
|
78
|
+
consequences: updates.consequences || existing.metadata.consequences,
|
|
79
|
+
tags: updates.tags || existing.metadata.tags
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// ==================
|
|
84
|
+
// Pattern Management
|
|
85
|
+
// ==================
|
|
86
|
+
async addPattern(projectId, input) {
|
|
87
|
+
if (!input.title?.trim()) {
|
|
88
|
+
throw new ValidationError("Pattern title is required");
|
|
89
|
+
}
|
|
90
|
+
if (!input.content?.trim()) {
|
|
91
|
+
throw new ValidationError("Pattern content is required");
|
|
92
|
+
}
|
|
93
|
+
return this.db.createEntry({
|
|
94
|
+
projectId,
|
|
95
|
+
type: "pattern",
|
|
96
|
+
title: input.title.trim(),
|
|
97
|
+
content: input.content.trim(),
|
|
98
|
+
metadata: {
|
|
99
|
+
category: input.category || "general",
|
|
100
|
+
tags: input.tags || []
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async listPatterns(projectId, branch) {
|
|
105
|
+
return this.db.listEntries({
|
|
106
|
+
projectId,
|
|
107
|
+
type: "pattern",
|
|
108
|
+
branch,
|
|
109
|
+
isArchived: false
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async getPattern(id) {
|
|
113
|
+
const entry = await this.db.getEntry(id);
|
|
114
|
+
if (!entry || entry.type !== "pattern") {
|
|
115
|
+
throw new NotFoundError(`Pattern with id ${id} not found`);
|
|
116
|
+
}
|
|
117
|
+
return entry;
|
|
118
|
+
}
|
|
119
|
+
async updatePattern(id, updates) {
|
|
120
|
+
const existing = await this.getPattern(id);
|
|
121
|
+
return this.db.updateEntry(id, {
|
|
122
|
+
title: updates.title?.trim() || existing.title,
|
|
123
|
+
content: updates.content?.trim() || existing.content,
|
|
124
|
+
metadata: {
|
|
125
|
+
...existing.metadata,
|
|
126
|
+
category: updates.category || existing.metadata.category,
|
|
127
|
+
tags: updates.tags || existing.metadata.tags
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// ==================
|
|
132
|
+
// Context Management
|
|
133
|
+
// ==================
|
|
134
|
+
async setContext(projectId, input) {
|
|
135
|
+
const existing = await this.db.listEntries({
|
|
136
|
+
projectId,
|
|
137
|
+
type: "context",
|
|
138
|
+
isArchived: false
|
|
139
|
+
});
|
|
140
|
+
const contextData = {
|
|
141
|
+
feature: input.feature || "",
|
|
142
|
+
blockers: input.blockers || [],
|
|
143
|
+
nextSteps: input.nextSteps || [],
|
|
144
|
+
activeFiles: input.activeFiles || []
|
|
145
|
+
};
|
|
146
|
+
if (existing.length > 0) {
|
|
147
|
+
return this.db.updateEntry(existing[0].id, {
|
|
148
|
+
title: "Project Context",
|
|
149
|
+
content: JSON.stringify(contextData, null, 2),
|
|
150
|
+
metadata: contextData
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
return this.db.createEntry({
|
|
154
|
+
projectId,
|
|
155
|
+
type: "context",
|
|
156
|
+
title: "Project Context",
|
|
157
|
+
content: JSON.stringify(contextData, null, 2),
|
|
158
|
+
metadata: contextData
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async getContext(projectId) {
|
|
163
|
+
const entries = await this.db.listEntries({
|
|
164
|
+
projectId,
|
|
165
|
+
type: "context",
|
|
166
|
+
isArchived: false
|
|
167
|
+
});
|
|
168
|
+
return entries[0] || null;
|
|
169
|
+
}
|
|
170
|
+
// ==================
|
|
171
|
+
// Document Management
|
|
172
|
+
// ==================
|
|
173
|
+
async addDocument(projectId, input) {
|
|
174
|
+
if (!input.title?.trim()) {
|
|
175
|
+
throw new ValidationError("Document title is required");
|
|
176
|
+
}
|
|
177
|
+
if (!input.content?.trim()) {
|
|
178
|
+
throw new ValidationError("Document content is required");
|
|
179
|
+
}
|
|
180
|
+
return this.db.createEntry({
|
|
181
|
+
projectId,
|
|
182
|
+
type: "document",
|
|
183
|
+
title: input.title.trim(),
|
|
184
|
+
content: input.content.trim(),
|
|
185
|
+
metadata: {
|
|
186
|
+
url: input.url,
|
|
187
|
+
tags: input.tags || []
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async listDocuments(projectId, branch) {
|
|
192
|
+
return this.db.listEntries({
|
|
193
|
+
projectId,
|
|
194
|
+
type: "document",
|
|
195
|
+
branch,
|
|
196
|
+
isArchived: false
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async getDocument(id) {
|
|
200
|
+
const entry = await this.db.getEntry(id);
|
|
201
|
+
if (!entry || entry.type !== "document") {
|
|
202
|
+
throw new NotFoundError(`Document with id ${id} not found`);
|
|
203
|
+
}
|
|
204
|
+
return entry;
|
|
205
|
+
}
|
|
206
|
+
// ==================
|
|
207
|
+
// Session Management
|
|
208
|
+
// ==================
|
|
209
|
+
async startSession(projectId, input) {
|
|
210
|
+
if (!input.feature?.trim()) {
|
|
211
|
+
throw new ValidationError("Session feature is required");
|
|
212
|
+
}
|
|
213
|
+
const active = await this.getActiveSession(projectId);
|
|
214
|
+
if (active) {
|
|
215
|
+
throw new ValidationError(
|
|
216
|
+
"An active session already exists. End it first or use a different branch."
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return this.db.createEntry({
|
|
220
|
+
projectId,
|
|
221
|
+
type: "session",
|
|
222
|
+
title: `Session: ${input.feature}`,
|
|
223
|
+
content: input.description || "",
|
|
224
|
+
metadata: {
|
|
225
|
+
feature: input.feature,
|
|
226
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
227
|
+
endedAt: null
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
async endSession(projectId, summary) {
|
|
232
|
+
const active = await this.getActiveSession(projectId);
|
|
233
|
+
if (!active) {
|
|
234
|
+
throw new NotFoundError("No active session found");
|
|
235
|
+
}
|
|
236
|
+
return this.db.updateEntry(active.id, {
|
|
237
|
+
content: summary || active.content,
|
|
238
|
+
metadata: {
|
|
239
|
+
...active.metadata,
|
|
240
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async getActiveSession(projectId) {
|
|
245
|
+
const sessions = await this.db.listEntries({
|
|
246
|
+
projectId,
|
|
247
|
+
type: "session",
|
|
248
|
+
isArchived: false
|
|
249
|
+
});
|
|
250
|
+
return sessions.find((s) => !s.metadata.endedAt) || null;
|
|
251
|
+
}
|
|
252
|
+
async listSessions(projectId, branch) {
|
|
253
|
+
return this.db.listEntries({
|
|
254
|
+
projectId,
|
|
255
|
+
type: "session",
|
|
256
|
+
branch,
|
|
257
|
+
isArchived: false
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
// ==================
|
|
261
|
+
// Generic Operations
|
|
262
|
+
// ==================
|
|
263
|
+
async deleteEntry(id, hard = false) {
|
|
264
|
+
return this.db.deleteEntry(id, hard);
|
|
265
|
+
}
|
|
266
|
+
async searchEntries(projectId, searchTerm, options) {
|
|
267
|
+
return this.db.searchEntries(projectId, searchTerm, options);
|
|
268
|
+
}
|
|
269
|
+
async getAllEntries(query) {
|
|
270
|
+
return this.db.listEntries(query);
|
|
271
|
+
}
|
|
272
|
+
async getEntryCount(projectId, branch) {
|
|
273
|
+
return this.db.countEntries({
|
|
274
|
+
projectId,
|
|
275
|
+
branch,
|
|
276
|
+
isArchived: false
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/engine/relevance.ts
|
|
282
|
+
function calculateRelevance(entry, options) {
|
|
283
|
+
let score = 0;
|
|
284
|
+
if (options.taskDescription) {
|
|
285
|
+
score += keywordOverlap(entry, options.taskDescription) * 0.4;
|
|
286
|
+
}
|
|
287
|
+
const daysOld = (Date.now() - entry.updatedAt.getTime()) / (1e3 * 60 * 60 * 24);
|
|
288
|
+
const recencyScore = Math.exp(-daysOld / 30);
|
|
289
|
+
score += recencyScore * 0.25;
|
|
290
|
+
const typePriority = getTypePriority(entry.type);
|
|
291
|
+
score += typePriority * 0.2;
|
|
292
|
+
if (options.activeFiles && options.activeFiles.length > 0) {
|
|
293
|
+
const hasFileMatch = options.activeFiles.some(
|
|
294
|
+
(file) => entry.content.toLowerCase().includes(file.toLowerCase()) || entry.title.toLowerCase().includes(file.toLowerCase())
|
|
295
|
+
);
|
|
296
|
+
if (hasFileMatch) {
|
|
297
|
+
score += 0.15;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return Math.min(score, 1);
|
|
301
|
+
}
|
|
302
|
+
function keywordOverlap(entry, query) {
|
|
303
|
+
const entryText = `${entry.title} ${entry.content}`.toLowerCase();
|
|
304
|
+
const queryWords = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
305
|
+
if (queryWords.length === 0) return 0;
|
|
306
|
+
const matchCount = queryWords.filter((word) => entryText.includes(word)).length;
|
|
307
|
+
return matchCount / queryWords.length;
|
|
308
|
+
}
|
|
309
|
+
function getTypePriority(type) {
|
|
310
|
+
const priorities = {
|
|
311
|
+
context: 1,
|
|
312
|
+
// Highest priority
|
|
313
|
+
decision: 0.8,
|
|
314
|
+
pattern: 0.8,
|
|
315
|
+
session: 0.5,
|
|
316
|
+
document: 0.3
|
|
317
|
+
// Lowest priority
|
|
318
|
+
};
|
|
319
|
+
return priorities[type] || 0.5;
|
|
320
|
+
}
|
|
321
|
+
function getMatchReasons(entry, options) {
|
|
322
|
+
const reasons = [];
|
|
323
|
+
if (options.taskDescription) {
|
|
324
|
+
const overlap = keywordOverlap(entry, options.taskDescription);
|
|
325
|
+
if (overlap > 0.3) {
|
|
326
|
+
reasons.push(`${Math.round(overlap * 100)}% keyword match`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const daysOld = (Date.now() - entry.updatedAt.getTime()) / (1e3 * 60 * 60 * 24);
|
|
330
|
+
if (daysOld < 7) {
|
|
331
|
+
reasons.push("Recently updated");
|
|
332
|
+
}
|
|
333
|
+
if (entry.type === "context") {
|
|
334
|
+
reasons.push("Current context");
|
|
335
|
+
}
|
|
336
|
+
if (options.activeFiles) {
|
|
337
|
+
const matchedFiles = options.activeFiles.filter(
|
|
338
|
+
(file) => entry.content.toLowerCase().includes(file.toLowerCase())
|
|
339
|
+
);
|
|
340
|
+
if (matchedFiles.length > 0) {
|
|
341
|
+
reasons.push(`References ${matchedFiles.join(", ")}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return reasons;
|
|
345
|
+
}
|
|
346
|
+
function rankEntries(entries, options) {
|
|
347
|
+
const minRelevance = options.minRelevance || 0.3;
|
|
348
|
+
return entries.map((entry) => ({
|
|
349
|
+
entry,
|
|
350
|
+
score: calculateRelevance(entry, options),
|
|
351
|
+
reasons: getMatchReasons(entry, options)
|
|
352
|
+
})).filter((ranked) => ranked.score >= minRelevance).sort((a, b) => b.score - a.score);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/engine/sync.ts
|
|
356
|
+
var SyncEngine = class {
|
|
357
|
+
constructor(local, remote) {
|
|
358
|
+
this.local = local;
|
|
359
|
+
this.remote = remote;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Push local changes to remote
|
|
363
|
+
*/
|
|
364
|
+
async push(projectId, options = {}) {
|
|
365
|
+
const result = {
|
|
366
|
+
pushed: 0,
|
|
367
|
+
pulled: 0,
|
|
368
|
+
conflicts: 0,
|
|
369
|
+
errors: []
|
|
370
|
+
};
|
|
371
|
+
try {
|
|
372
|
+
const project = await this.local.getProject(projectId);
|
|
373
|
+
if (!project) {
|
|
374
|
+
throw new Error("Project not found locally");
|
|
375
|
+
}
|
|
376
|
+
if (!options.dryRun) {
|
|
377
|
+
await this.remote.upsertProject(project);
|
|
378
|
+
}
|
|
379
|
+
const unsyncedEntries = await this.local.getUnsyncedEntries(projectId);
|
|
380
|
+
if (unsyncedEntries.length === 0) {
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
const conflicts = [];
|
|
384
|
+
if (!options.force) {
|
|
385
|
+
const lastPull = await this.local.getLastPull(projectId);
|
|
386
|
+
if (lastPull) {
|
|
387
|
+
const remoteChanges = await this.remote.pullEntries(projectId, lastPull);
|
|
388
|
+
const unsyncedIds = new Set(unsyncedEntries.map((e) => e.id));
|
|
389
|
+
for (const remoteEntry of remoteChanges) {
|
|
390
|
+
if (unsyncedIds.has(remoteEntry.id)) {
|
|
391
|
+
const localEntry = unsyncedEntries.find((e) => e.id === remoteEntry.id);
|
|
392
|
+
if (localEntry && localEntry.updatedAt.getTime() !== remoteEntry.updatedAt.getTime()) {
|
|
393
|
+
conflicts.push(localEntry);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (conflicts.length > 0 && !options.force) {
|
|
400
|
+
result.conflicts = conflicts.length;
|
|
401
|
+
result.errors.push(
|
|
402
|
+
`${conflicts.length} conflict(s) found. Use --force to push anyway (last-write-wins).`
|
|
403
|
+
);
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
if (!options.dryRun) {
|
|
407
|
+
await this.remote.pushEntries(unsyncedEntries);
|
|
408
|
+
await this.local.markSynced(unsyncedEntries.map((e) => e.id));
|
|
409
|
+
}
|
|
410
|
+
result.pushed = unsyncedEntries.length;
|
|
411
|
+
const branches = await this.local.listBranches(projectId);
|
|
412
|
+
for (const branch of branches) {
|
|
413
|
+
if (!options.dryRun) {
|
|
414
|
+
await this.remote.upsertBranch(branch);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return result;
|
|
418
|
+
} catch (error) {
|
|
419
|
+
result.errors.push(
|
|
420
|
+
error instanceof Error ? error.message : "Unknown error during push"
|
|
421
|
+
);
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Pull remote changes to local
|
|
427
|
+
*/
|
|
428
|
+
async pull(projectId, options = {}) {
|
|
429
|
+
const result = {
|
|
430
|
+
pushed: 0,
|
|
431
|
+
pulled: 0,
|
|
432
|
+
conflicts: 0,
|
|
433
|
+
errors: []
|
|
434
|
+
};
|
|
435
|
+
try {
|
|
436
|
+
const project = await this.local.getProject(projectId);
|
|
437
|
+
if (!project) {
|
|
438
|
+
throw new Error("Project not found locally");
|
|
439
|
+
}
|
|
440
|
+
const lastPull = await this.local.getLastPull(projectId);
|
|
441
|
+
const since = lastPull || /* @__PURE__ */ new Date(0);
|
|
442
|
+
const remoteEntries = await this.remote.pullEntries(projectId, since);
|
|
443
|
+
if (remoteEntries.length === 0) {
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
const unsyncedEntries = await this.local.getUnsyncedEntries(projectId);
|
|
447
|
+
const unsyncedIds = new Set(unsyncedEntries.map((e) => e.id));
|
|
448
|
+
const conflicts = [];
|
|
449
|
+
for (const remoteEntry of remoteEntries) {
|
|
450
|
+
if (unsyncedIds.has(remoteEntry.id)) {
|
|
451
|
+
const localEntry = unsyncedEntries.find((e) => e.id === remoteEntry.id);
|
|
452
|
+
if (localEntry && localEntry.updatedAt.getTime() !== remoteEntry.updatedAt.getTime()) {
|
|
453
|
+
conflicts.push(remoteEntry);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (conflicts.length > 0 && !options.force) {
|
|
458
|
+
result.conflicts = conflicts.length;
|
|
459
|
+
result.errors.push(
|
|
460
|
+
`${conflicts.length} conflict(s) found. Use --force to pull anyway (last-write-wins).`
|
|
461
|
+
);
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
if (!options.dryRun) {
|
|
465
|
+
for (const entry of remoteEntries) {
|
|
466
|
+
const localEntry = await this.local.getEntry(entry.id);
|
|
467
|
+
if (!localEntry) {
|
|
468
|
+
await this.local.createEntry({
|
|
469
|
+
id: entry.id,
|
|
470
|
+
projectId: entry.projectId,
|
|
471
|
+
type: entry.type,
|
|
472
|
+
title: entry.title,
|
|
473
|
+
content: entry.content,
|
|
474
|
+
metadata: entry.metadata,
|
|
475
|
+
branch: entry.branch
|
|
476
|
+
});
|
|
477
|
+
} else if (entry.updatedAt > localEntry.updatedAt || options.force) {
|
|
478
|
+
await this.local.updateEntry(entry.id, {
|
|
479
|
+
title: entry.title,
|
|
480
|
+
content: entry.content,
|
|
481
|
+
metadata: entry.metadata,
|
|
482
|
+
updatedAt: entry.updatedAt,
|
|
483
|
+
isSynced: true
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
await this.local.updateLastPull(projectId, /* @__PURE__ */ new Date());
|
|
488
|
+
}
|
|
489
|
+
result.pulled = remoteEntries.length;
|
|
490
|
+
return result;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
result.errors.push(
|
|
493
|
+
error instanceof Error ? error.message : "Unknown error during pull"
|
|
494
|
+
);
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Full bidirectional sync (pull then push)
|
|
500
|
+
*/
|
|
501
|
+
async sync(projectId, options = {}) {
|
|
502
|
+
const pullResult = await this.pull(projectId, options);
|
|
503
|
+
if (pullResult.errors.length > 0) {
|
|
504
|
+
return pullResult;
|
|
505
|
+
}
|
|
506
|
+
const pushResult = await this.push(projectId, options);
|
|
507
|
+
return {
|
|
508
|
+
pushed: pushResult.pushed,
|
|
509
|
+
pulled: pullResult.pulled,
|
|
510
|
+
conflicts: pullResult.conflicts + pushResult.conflicts,
|
|
511
|
+
errors: [...pullResult.errors, ...pushResult.errors]
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// src/utils/tokens.ts
|
|
517
|
+
import { get_encoding } from "tiktoken";
|
|
518
|
+
var encoding = get_encoding("cl100k_base");
|
|
519
|
+
function countTokens(text) {
|
|
520
|
+
const tokens = encoding.encode(text);
|
|
521
|
+
return tokens.length;
|
|
522
|
+
}
|
|
523
|
+
function countEntryTokens(entry) {
|
|
524
|
+
const text = formatEntryForContext(entry);
|
|
525
|
+
return countTokens(text);
|
|
526
|
+
}
|
|
527
|
+
function formatEntryForContext(entry) {
|
|
528
|
+
const lines = [];
|
|
529
|
+
lines.push(`## ${entry.type.toUpperCase()}: ${entry.title}`);
|
|
530
|
+
lines.push("");
|
|
531
|
+
lines.push(entry.content);
|
|
532
|
+
if (entry.metadata && Object.keys(entry.metadata).length > 0) {
|
|
533
|
+
lines.push("");
|
|
534
|
+
lines.push("**Metadata:**");
|
|
535
|
+
for (const [key, value] of Object.entries(entry.metadata)) {
|
|
536
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
537
|
+
lines.push(`- ${key}: ${value.join(", ")}`);
|
|
538
|
+
} else if (value && typeof value === "string") {
|
|
539
|
+
lines.push(`- ${key}: ${value}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
lines.push("");
|
|
544
|
+
lines.push("---");
|
|
545
|
+
lines.push("");
|
|
546
|
+
return lines.join("\n");
|
|
547
|
+
}
|
|
548
|
+
function fitToBudget(rankedEntries, maxTokens) {
|
|
549
|
+
const result = [];
|
|
550
|
+
let currentTokens = 0;
|
|
551
|
+
const headerTokens = countTokens("# MemoCore Context\n\n");
|
|
552
|
+
currentTokens += headerTokens;
|
|
553
|
+
for (const ranked of rankedEntries) {
|
|
554
|
+
const entryTokens = countEntryTokens(ranked.entry);
|
|
555
|
+
if (currentTokens + entryTokens <= maxTokens) {
|
|
556
|
+
result.push(ranked);
|
|
557
|
+
currentTokens += entryTokens;
|
|
558
|
+
} else {
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
function buildContext(rankedEntries, options) {
|
|
565
|
+
const lines = [];
|
|
566
|
+
lines.push("# MemoCore Context");
|
|
567
|
+
lines.push("");
|
|
568
|
+
if (options?.includeStats) {
|
|
569
|
+
lines.push(`Found ${rankedEntries.length} relevant entries:`);
|
|
570
|
+
lines.push("");
|
|
571
|
+
}
|
|
572
|
+
for (const ranked of rankedEntries) {
|
|
573
|
+
if (options?.includeReasons && ranked.reasons.length > 0) {
|
|
574
|
+
lines.push(`<!-- Match reasons: ${ranked.reasons.join(", ")} -->`);
|
|
575
|
+
}
|
|
576
|
+
lines.push(formatEntryForContext(ranked.entry));
|
|
577
|
+
}
|
|
578
|
+
return lines.join("\n");
|
|
579
|
+
}
|
|
580
|
+
function cleanup() {
|
|
581
|
+
encoding.free();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/engine/context-builder.ts
|
|
585
|
+
function toRankedEntries(entries) {
|
|
586
|
+
return entries.map((entry) => ({
|
|
587
|
+
entry,
|
|
588
|
+
score: 1,
|
|
589
|
+
reasons: []
|
|
590
|
+
}));
|
|
591
|
+
}
|
|
592
|
+
function buildContextPayload(entries, options) {
|
|
593
|
+
const budget = options.maxTokens || 4e3;
|
|
594
|
+
let rankedEntries = [];
|
|
595
|
+
if (options.type === "task" && options.taskDescription) {
|
|
596
|
+
const suggestOpts = {
|
|
597
|
+
projectId: options.projectId,
|
|
598
|
+
taskDescription: options.taskDescription,
|
|
599
|
+
activeFiles: options.activeFiles,
|
|
600
|
+
maxTokens: budget
|
|
601
|
+
};
|
|
602
|
+
rankedEntries = rankEntries(entries, suggestOpts);
|
|
603
|
+
} else if (options.type === "files" && options.activeFiles && options.activeFiles.length > 0) {
|
|
604
|
+
const filtered = entries.filter(
|
|
605
|
+
(entry) => options.activeFiles.some(
|
|
606
|
+
(file) => entry.content.includes(file) || entry.metadata?.files?.includes(file)
|
|
607
|
+
)
|
|
608
|
+
);
|
|
609
|
+
filtered.sort(
|
|
610
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
611
|
+
);
|
|
612
|
+
rankedEntries = toRankedEntries(filtered);
|
|
613
|
+
} else {
|
|
614
|
+
const sorted = [...entries];
|
|
615
|
+
sorted.sort(
|
|
616
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
617
|
+
);
|
|
618
|
+
rankedEntries = toRankedEntries(sorted);
|
|
619
|
+
}
|
|
620
|
+
if (options.includeTypes && options.includeTypes.length > 0) {
|
|
621
|
+
rankedEntries = rankedEntries.filter(
|
|
622
|
+
(ranked) => options.includeTypes.includes(ranked.entry.type)
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
const fitted = fitToBudget(rankedEntries, budget);
|
|
626
|
+
const context = buildContext(fitted);
|
|
627
|
+
const tokensUsed = countTokens(context);
|
|
628
|
+
return {
|
|
629
|
+
context,
|
|
630
|
+
entriesIncluded: fitted.length,
|
|
631
|
+
tokensUsed,
|
|
632
|
+
budget
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function buildContextSummary(entries) {
|
|
636
|
+
const byType = {};
|
|
637
|
+
const recentActivity = [];
|
|
638
|
+
let oldest = null;
|
|
639
|
+
let newest = null;
|
|
640
|
+
for (const entry of entries) {
|
|
641
|
+
byType[entry.type] = (byType[entry.type] || 0) + 1;
|
|
642
|
+
if (!oldest || entry.createdAt < oldest) {
|
|
643
|
+
oldest = entry.createdAt;
|
|
644
|
+
}
|
|
645
|
+
if (!newest || entry.updatedAt > newest) {
|
|
646
|
+
newest = entry.updatedAt;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const recent = [...entries].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, 5);
|
|
650
|
+
for (const entry of recent) {
|
|
651
|
+
const age = Date.now() - entry.updatedAt.getTime();
|
|
652
|
+
const daysAgo = Math.floor(age / (1e3 * 60 * 60 * 24));
|
|
653
|
+
let timeStr = "";
|
|
654
|
+
if (daysAgo === 0) {
|
|
655
|
+
timeStr = "today";
|
|
656
|
+
} else if (daysAgo === 1) {
|
|
657
|
+
timeStr = "yesterday";
|
|
658
|
+
} else if (daysAgo < 7) {
|
|
659
|
+
timeStr = `${daysAgo} days ago`;
|
|
660
|
+
} else {
|
|
661
|
+
timeStr = `${Math.floor(daysAgo / 7)} weeks ago`;
|
|
662
|
+
}
|
|
663
|
+
recentActivity.push(`${entry.type}: ${entry.title} (${timeStr})`);
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
totalEntries: entries.length,
|
|
667
|
+
byType,
|
|
668
|
+
recentActivity,
|
|
669
|
+
oldestEntry: oldest,
|
|
670
|
+
newestEntry: newest
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/scanner.ts
|
|
675
|
+
import { createHash } from "crypto";
|
|
676
|
+
var COMMENT_PATTERNS = [
|
|
677
|
+
// Single line comments: // @tag
|
|
678
|
+
{ pattern: /^(\s*)\/\/\s*/, continuation: /^(\s*)\/\/\s+(?!@)/ },
|
|
679
|
+
// Python/Ruby/Shell: # @tag
|
|
680
|
+
{ pattern: /^(\s*)#\s*/, continuation: /^(\s*)#\s+(?!@)/ },
|
|
681
|
+
// SQL: -- @tag
|
|
682
|
+
{ pattern: /^(\s*)--\s*/, continuation: /^(\s*)--\s+(?!@)/ }
|
|
683
|
+
];
|
|
684
|
+
var TAG_PATTERN = /@(decision|pattern|context)\s*/i;
|
|
685
|
+
var CATEGORY_PATTERN = /^\[([^\]]+)\]\s*/;
|
|
686
|
+
var FIELD_PATTERN = /\|\s*(\w+):\s*([^|]+)/g;
|
|
687
|
+
function parseFile(content, filePath) {
|
|
688
|
+
const lines = content.split("\n");
|
|
689
|
+
const comments = [];
|
|
690
|
+
let currentComment = null;
|
|
691
|
+
let currentIndent = "";
|
|
692
|
+
for (let i = 0; i < lines.length; i++) {
|
|
693
|
+
const line = lines[i];
|
|
694
|
+
const lineNum = i + 1;
|
|
695
|
+
const tagMatch = detectTag(line);
|
|
696
|
+
if (tagMatch) {
|
|
697
|
+
if (currentComment && currentComment.title) {
|
|
698
|
+
comments.push(finalizeComment(currentComment, filePath));
|
|
699
|
+
}
|
|
700
|
+
const { tag, content: content2, category } = tagMatch;
|
|
701
|
+
const { title, fields } = extractTitleAndFields(content2);
|
|
702
|
+
currentComment = {
|
|
703
|
+
tag,
|
|
704
|
+
category,
|
|
705
|
+
title,
|
|
706
|
+
content: title,
|
|
707
|
+
fields,
|
|
708
|
+
file: filePath,
|
|
709
|
+
line: lineNum
|
|
710
|
+
};
|
|
711
|
+
currentIndent = getIndent(line);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (currentComment && isContinuationLine(line, currentIndent)) {
|
|
715
|
+
const continuationText = extractContinuationText(line);
|
|
716
|
+
if (continuationText) {
|
|
717
|
+
currentComment.content += " " + continuationText;
|
|
718
|
+
const moreFields = extractFields(continuationText);
|
|
719
|
+
Object.assign(currentComment.fields, moreFields);
|
|
720
|
+
}
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
if (currentComment && currentComment.title) {
|
|
724
|
+
comments.push(finalizeComment(currentComment, filePath));
|
|
725
|
+
currentComment = null;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (currentComment && currentComment.title) {
|
|
729
|
+
comments.push(finalizeComment(currentComment, filePath));
|
|
730
|
+
}
|
|
731
|
+
return comments;
|
|
732
|
+
}
|
|
733
|
+
function detectTag(line) {
|
|
734
|
+
let commentContent = null;
|
|
735
|
+
for (const { pattern } of COMMENT_PATTERNS) {
|
|
736
|
+
const match = line.match(pattern);
|
|
737
|
+
if (match) {
|
|
738
|
+
commentContent = line.substring(match[0].length);
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (!commentContent) return null;
|
|
743
|
+
const tagMatch = commentContent.match(TAG_PATTERN);
|
|
744
|
+
if (!tagMatch) return null;
|
|
745
|
+
const tag = tagMatch[1].toLowerCase();
|
|
746
|
+
let content = commentContent.substring(tagMatch[0].length).trim();
|
|
747
|
+
let category;
|
|
748
|
+
const categoryMatch = content.match(CATEGORY_PATTERN);
|
|
749
|
+
if (categoryMatch) {
|
|
750
|
+
category = categoryMatch[1];
|
|
751
|
+
content = content.substring(categoryMatch[0].length).trim();
|
|
752
|
+
}
|
|
753
|
+
return { tag, content, category };
|
|
754
|
+
}
|
|
755
|
+
function isContinuationLine(line, expectedIndent) {
|
|
756
|
+
for (const { continuation } of COMMENT_PATTERNS) {
|
|
757
|
+
const match = line.match(continuation);
|
|
758
|
+
if (match && match[1] === expectedIndent) {
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
function extractContinuationText(line) {
|
|
765
|
+
for (const { continuation } of COMMENT_PATTERNS) {
|
|
766
|
+
const match = line.match(continuation);
|
|
767
|
+
if (match) {
|
|
768
|
+
return line.substring(match[0].length).trim();
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
function getIndent(line) {
|
|
774
|
+
const match = line.match(/^(\s*)/);
|
|
775
|
+
return match ? match[1] : "";
|
|
776
|
+
}
|
|
777
|
+
function extractTitleAndFields(content) {
|
|
778
|
+
const fields = extractFields(content);
|
|
779
|
+
const pipeIndex = content.indexOf("|");
|
|
780
|
+
const title = pipeIndex >= 0 ? content.substring(0, pipeIndex).trim() : content.trim();
|
|
781
|
+
return { title, fields };
|
|
782
|
+
}
|
|
783
|
+
function extractFields(content) {
|
|
784
|
+
const fields = {};
|
|
785
|
+
let match;
|
|
786
|
+
FIELD_PATTERN.lastIndex = 0;
|
|
787
|
+
while ((match = FIELD_PATTERN.exec(content)) !== null) {
|
|
788
|
+
const key = match[1];
|
|
789
|
+
const value = match[2].trim();
|
|
790
|
+
fields[key] = value;
|
|
791
|
+
}
|
|
792
|
+
return fields;
|
|
793
|
+
}
|
|
794
|
+
function finalizeComment(partial, filePath) {
|
|
795
|
+
const content = partial.content || partial.title || "";
|
|
796
|
+
const hash = createContentHash(partial.tag, partial.title, content);
|
|
797
|
+
return {
|
|
798
|
+
tag: partial.tag,
|
|
799
|
+
category: partial.category,
|
|
800
|
+
title: partial.title,
|
|
801
|
+
content,
|
|
802
|
+
fields: partial.fields || {},
|
|
803
|
+
file: filePath,
|
|
804
|
+
line: partial.line,
|
|
805
|
+
hash
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
function createContentHash(tag, title, content) {
|
|
809
|
+
const normalized = `${tag}:${title}:${content}`.toLowerCase().replace(/\s+/g, " ");
|
|
810
|
+
return createHash("sha256").update(normalized).digest("hex").substring(0, 16);
|
|
811
|
+
}
|
|
812
|
+
function scanCommentToEntry(comment, projectId) {
|
|
813
|
+
const metadata = {
|
|
814
|
+
source: "scan",
|
|
815
|
+
file: comment.file,
|
|
816
|
+
line: comment.line,
|
|
817
|
+
hash: comment.hash,
|
|
818
|
+
category: comment.category,
|
|
819
|
+
...comment.fields
|
|
820
|
+
};
|
|
821
|
+
if (comment.tag === "decision") {
|
|
822
|
+
return {
|
|
823
|
+
type: "decision",
|
|
824
|
+
title: comment.title,
|
|
825
|
+
content: comment.fields.rationale || comment.content,
|
|
826
|
+
metadata: {
|
|
827
|
+
...metadata,
|
|
828
|
+
alternatives: comment.fields.alternatives,
|
|
829
|
+
consequences: comment.fields.consequences,
|
|
830
|
+
status: comment.fields.status || "active"
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
if (comment.tag === "pattern") {
|
|
835
|
+
return {
|
|
836
|
+
type: "pattern",
|
|
837
|
+
title: comment.title,
|
|
838
|
+
content: comment.fields.template || comment.content,
|
|
839
|
+
metadata: {
|
|
840
|
+
...metadata,
|
|
841
|
+
when: comment.fields.when
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
type: "context",
|
|
847
|
+
title: "Active Context",
|
|
848
|
+
content: comment.content,
|
|
849
|
+
metadata
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/rules-parser.ts
|
|
854
|
+
function parseRulesFile(content) {
|
|
855
|
+
const lines = content.split("\n");
|
|
856
|
+
const result = {
|
|
857
|
+
stack: [],
|
|
858
|
+
decisions: [],
|
|
859
|
+
patterns: [],
|
|
860
|
+
context: null,
|
|
861
|
+
documents: []
|
|
862
|
+
};
|
|
863
|
+
let currentSection = null;
|
|
864
|
+
let currentContent = [];
|
|
865
|
+
for (let i = 0; i < lines.length; i++) {
|
|
866
|
+
const line = lines[i];
|
|
867
|
+
const headingMatch = line.match(/^#+\s+(.+)/);
|
|
868
|
+
if (headingMatch) {
|
|
869
|
+
if (currentSection && currentContent.length > 0) {
|
|
870
|
+
processSection(currentSection, currentContent.join("\n"), result);
|
|
871
|
+
}
|
|
872
|
+
currentSection = headingMatch[1].trim();
|
|
873
|
+
currentContent = [];
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (currentSection && line.trim()) {
|
|
877
|
+
currentContent.push(line);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (currentSection && currentContent.length > 0) {
|
|
881
|
+
processSection(currentSection, currentContent.join("\n"), result);
|
|
882
|
+
}
|
|
883
|
+
return result;
|
|
884
|
+
}
|
|
885
|
+
function processSection(heading, content, result) {
|
|
886
|
+
const headingLower = heading.toLowerCase();
|
|
887
|
+
if (headingLower === "stack" || headingLower === "tech stack") {
|
|
888
|
+
result.stack = parseListItems(content);
|
|
889
|
+
} else if (headingLower === "decisions") {
|
|
890
|
+
result.decisions = parseDecisions(content);
|
|
891
|
+
} else if (headingLower === "patterns") {
|
|
892
|
+
result.patterns = parsePatterns(content);
|
|
893
|
+
} else if (headingLower === "context") {
|
|
894
|
+
result.context = parseContext(content);
|
|
895
|
+
} else {
|
|
896
|
+
result.documents.push({
|
|
897
|
+
type: "document",
|
|
898
|
+
title: heading,
|
|
899
|
+
content: content.trim(),
|
|
900
|
+
metadata: { source: "rules", section: heading }
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
function parseListItems(content) {
|
|
905
|
+
const items = [];
|
|
906
|
+
const lines = content.split("\n");
|
|
907
|
+
for (const line of lines) {
|
|
908
|
+
const match = line.match(/^[-*]\s+(.+)/);
|
|
909
|
+
if (match) {
|
|
910
|
+
items.push(match[1].trim());
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return items;
|
|
914
|
+
}
|
|
915
|
+
function parseDecisions(content) {
|
|
916
|
+
const decisions = [];
|
|
917
|
+
const items = parseListItems(content);
|
|
918
|
+
for (const item of items) {
|
|
919
|
+
const match = item.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
|
|
920
|
+
if (match) {
|
|
921
|
+
const title = match[1].trim();
|
|
922
|
+
const rationale = match[2].trim();
|
|
923
|
+
decisions.push({
|
|
924
|
+
type: "decision",
|
|
925
|
+
title,
|
|
926
|
+
content: rationale,
|
|
927
|
+
metadata: {
|
|
928
|
+
source: "rules",
|
|
929
|
+
rationale
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
} else {
|
|
933
|
+
decisions.push({
|
|
934
|
+
type: "decision",
|
|
935
|
+
title: item,
|
|
936
|
+
content: item,
|
|
937
|
+
metadata: { source: "rules" }
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return decisions;
|
|
942
|
+
}
|
|
943
|
+
function parsePatterns(content) {
|
|
944
|
+
const patterns = [];
|
|
945
|
+
const items = parseListItems(content);
|
|
946
|
+
for (const item of items) {
|
|
947
|
+
const colonIndex = item.indexOf(":");
|
|
948
|
+
if (colonIndex >= 0) {
|
|
949
|
+
const name = item.substring(0, colonIndex).trim();
|
|
950
|
+
const description = item.substring(colonIndex + 1).trim();
|
|
951
|
+
patterns.push({
|
|
952
|
+
type: "pattern",
|
|
953
|
+
title: name,
|
|
954
|
+
content: description,
|
|
955
|
+
metadata: { source: "rules" }
|
|
956
|
+
});
|
|
957
|
+
} else {
|
|
958
|
+
patterns.push({
|
|
959
|
+
type: "pattern",
|
|
960
|
+
title: item,
|
|
961
|
+
content: item,
|
|
962
|
+
metadata: { source: "rules" }
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return patterns;
|
|
967
|
+
}
|
|
968
|
+
function parseContext(content) {
|
|
969
|
+
return {
|
|
970
|
+
type: "context",
|
|
971
|
+
title: "Active Context",
|
|
972
|
+
content: content.trim(),
|
|
973
|
+
metadata: { source: "rules" }
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
function generateRulesFile(entries) {
|
|
977
|
+
const sections = [];
|
|
978
|
+
if (entries.stack.length > 0) {
|
|
979
|
+
sections.push("# Stack\n");
|
|
980
|
+
for (const item of entries.stack) {
|
|
981
|
+
sections.push(`- ${item}`);
|
|
982
|
+
}
|
|
983
|
+
sections.push("");
|
|
984
|
+
}
|
|
985
|
+
if (entries.decisions.length > 0) {
|
|
986
|
+
sections.push("# Decisions\n");
|
|
987
|
+
for (const decision of entries.decisions) {
|
|
988
|
+
const rationale = decision.metadata.rationale || decision.content;
|
|
989
|
+
sections.push(`- ${decision.title} (${rationale})`);
|
|
990
|
+
}
|
|
991
|
+
sections.push("");
|
|
992
|
+
}
|
|
993
|
+
if (entries.patterns.length > 0) {
|
|
994
|
+
sections.push("# Patterns\n");
|
|
995
|
+
for (const pattern of entries.patterns) {
|
|
996
|
+
sections.push(`- ${pattern.title}: ${pattern.content}`);
|
|
997
|
+
}
|
|
998
|
+
sections.push("");
|
|
999
|
+
}
|
|
1000
|
+
if (entries.context.length > 0) {
|
|
1001
|
+
sections.push("# Context\n");
|
|
1002
|
+
sections.push(entries.context[0].content);
|
|
1003
|
+
sections.push("");
|
|
1004
|
+
}
|
|
1005
|
+
for (const doc of entries.documents) {
|
|
1006
|
+
sections.push(`# ${doc.title}
|
|
1007
|
+
`);
|
|
1008
|
+
sections.push(doc.content);
|
|
1009
|
+
sections.push("");
|
|
1010
|
+
}
|
|
1011
|
+
return sections.join("\n");
|
|
1012
|
+
}
|
|
1013
|
+
export {
|
|
1014
|
+
AuthError,
|
|
1015
|
+
ConflictError,
|
|
1016
|
+
MemoryEngine,
|
|
1017
|
+
NotFoundError,
|
|
1018
|
+
SyncEngine,
|
|
1019
|
+
ValidationError,
|
|
1020
|
+
buildContext,
|
|
1021
|
+
buildContextPayload,
|
|
1022
|
+
buildContextSummary,
|
|
1023
|
+
calculateRelevance,
|
|
1024
|
+
cleanup,
|
|
1025
|
+
countEntryTokens,
|
|
1026
|
+
countTokens,
|
|
1027
|
+
fitToBudget,
|
|
1028
|
+
formatEntryForContext,
|
|
1029
|
+
generateRulesFile,
|
|
1030
|
+
getMatchReasons,
|
|
1031
|
+
parseFile,
|
|
1032
|
+
parseRulesFile,
|
|
1033
|
+
rankEntries,
|
|
1034
|
+
scanCommentToEntry
|
|
1035
|
+
};
|
|
1036
|
+
//# sourceMappingURL=index.js.map
|