@gethmy/mcp 2.3.1 → 2.3.3
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/lib/api-client.js +2099 -648
- package/dist/lib/config.js +217 -201
- package/package.json +9 -5
- package/src/memory-cleanup.ts +2 -4
- package/dist/lib/__tests__/active-learning.test.js +0 -386
- package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
- package/dist/lib/__tests__/auto-session.test.js +0 -661
- package/dist/lib/__tests__/context-assembly.test.js +0 -362
- package/dist/lib/__tests__/graph-expansion.test.js +0 -150
- package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
- package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
- package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
- package/dist/lib/__tests__/pattern-detection.test.js +0 -295
- package/dist/lib/__tests__/prompt-builder.test.js +0 -418
- package/dist/lib/active-learning.js +0 -822
- package/dist/lib/auto-session.js +0 -214
- package/dist/lib/cli.js +0 -138
- package/dist/lib/consolidation.js +0 -303
- package/dist/lib/context-assembly.js +0 -884
- package/dist/lib/graph-expansion.js +0 -163
- package/dist/lib/http.js +0 -175
- package/dist/lib/index.js +0 -7
- package/dist/lib/lifecycle-maintenance.js +0 -88
- package/dist/lib/memory-cleanup.js +0 -455
- package/dist/lib/onboard.js +0 -36
- package/dist/lib/prompt-builder.js +0 -488
- package/dist/lib/remote.js +0 -166
- package/dist/lib/server.js +0 -3365
- package/dist/lib/skills.js +0 -593
- package/dist/lib/tui/agents.js +0 -116
- package/dist/lib/tui/docs.js +0 -744
- package/dist/lib/tui/setup.js +0 -934
- package/dist/lib/tui/theme.js +0 -95
- package/dist/lib/tui/writer.js +0 -200
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified Memory Cleanup
|
|
3
|
-
*
|
|
4
|
-
* Orchestrates a 5-stage cleanup pipeline: prune stale drafts, consolidate
|
|
5
|
-
* similar memories, detect orphans, detect duplicates, and backfill embeddings.
|
|
6
|
-
*
|
|
7
|
-
* All stages are non-fatal — individual failures are collected but never block
|
|
8
|
-
* the remaining stages. Defaults to dry-run mode (preview only).
|
|
9
|
-
*/
|
|
10
|
-
import { evaluateLifecycle } from "@harmony/memory";
|
|
11
|
-
import { consolidateMemories, } from "./consolidation.js";
|
|
12
|
-
import { findSimilarEntities } from "./graph-expansion.js";
|
|
13
|
-
const ALL_STEPS = [
|
|
14
|
-
"prune",
|
|
15
|
-
"consolidate",
|
|
16
|
-
"orphans",
|
|
17
|
-
"duplicates",
|
|
18
|
-
"backfill",
|
|
19
|
-
];
|
|
20
|
-
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
21
|
-
const MAX_ENTITIES_FETCH = 200;
|
|
22
|
-
const DUPLICATE_SIMILARITY_THRESHOLD = 0.85;
|
|
23
|
-
const CONCURRENCY_LIMIT = 5;
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Main orchestrator
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
export async function runMemoryCleanup(client, workspaceId, projectId, options) {
|
|
28
|
-
const dryRun = options?.dryRun !== false;
|
|
29
|
-
const steps = options?.steps ?? ALL_STEPS;
|
|
30
|
-
const maxAgeDays = options?.maxAgeDays ?? 30;
|
|
31
|
-
const minClusterSize = options?.minClusterSize ?? 3;
|
|
32
|
-
const orphanAgeDays = options?.orphanAgeDays ?? 14;
|
|
33
|
-
const report = {
|
|
34
|
-
success: true,
|
|
35
|
-
dryRun,
|
|
36
|
-
timestamp: new Date().toISOString(),
|
|
37
|
-
workspace: { id: workspaceId, projectId },
|
|
38
|
-
summary: { totalEntities: 0, issuesFound: 0, actionsTaken: 0 },
|
|
39
|
-
steps: {},
|
|
40
|
-
errors: [],
|
|
41
|
-
healthReport: "",
|
|
42
|
-
};
|
|
43
|
-
// Fetch all entities once (shared across steps)
|
|
44
|
-
let entities = [];
|
|
45
|
-
try {
|
|
46
|
-
const listResult = await client.listMemoryEntities({
|
|
47
|
-
workspace_id: workspaceId,
|
|
48
|
-
project_id: projectId,
|
|
49
|
-
limit: MAX_ENTITIES_FETCH,
|
|
50
|
-
});
|
|
51
|
-
entities = (listResult.entities || []);
|
|
52
|
-
report.summary.totalEntities = entities.length;
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
report.errors.push({
|
|
56
|
-
step: "init",
|
|
57
|
-
message: `Failed to fetch entities: ${err.message}`,
|
|
58
|
-
});
|
|
59
|
-
report.success = false;
|
|
60
|
-
report.healthReport = generateHealthReport(report);
|
|
61
|
-
return report;
|
|
62
|
-
}
|
|
63
|
-
// Stage 1: Prune stale drafts
|
|
64
|
-
if (steps.includes("prune")) {
|
|
65
|
-
try {
|
|
66
|
-
report.steps.prune = runPruneStep(entities, maxAgeDays);
|
|
67
|
-
if (!dryRun) {
|
|
68
|
-
for (const item of report.steps.prune.items) {
|
|
69
|
-
try {
|
|
70
|
-
await client.deleteMemoryEntity(item.id);
|
|
71
|
-
report.steps.prune.pruned++;
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
report.errors.push({
|
|
75
|
-
step: "prune",
|
|
76
|
-
message: `Failed to delete ${item.id}: ${err.message}`,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
report.summary.actionsTaken += report.steps.prune.pruned;
|
|
81
|
-
}
|
|
82
|
-
report.summary.issuesFound += report.steps.prune.staleDraftsFound;
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
report.errors.push({
|
|
86
|
-
step: "prune",
|
|
87
|
-
message: err.message,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// Stage 2: Consolidate similar memories
|
|
92
|
-
if (steps.includes("consolidate")) {
|
|
93
|
-
try {
|
|
94
|
-
const result = await consolidateMemories(client, workspaceId, projectId, {
|
|
95
|
-
dryRun,
|
|
96
|
-
minClusterSize,
|
|
97
|
-
});
|
|
98
|
-
report.steps.consolidate = {
|
|
99
|
-
clustersFound: result.clustersFound,
|
|
100
|
-
entitiesProcessed: result.entitiesProcessed,
|
|
101
|
-
consolidated: result.consolidated,
|
|
102
|
-
details: result.details,
|
|
103
|
-
};
|
|
104
|
-
report.summary.issuesFound += result.clustersFound;
|
|
105
|
-
if (!dryRun)
|
|
106
|
-
report.summary.actionsTaken += result.consolidated;
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
report.errors.push({
|
|
110
|
-
step: "consolidate",
|
|
111
|
-
message: err.message,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Stage 3: Detect orphans
|
|
116
|
-
if (steps.includes("orphans")) {
|
|
117
|
-
try {
|
|
118
|
-
report.steps.orphans = await runOrphanStep(client, entities, orphanAgeDays);
|
|
119
|
-
if (!dryRun) {
|
|
120
|
-
for (const item of report.steps.orphans.items) {
|
|
121
|
-
try {
|
|
122
|
-
await client.deleteMemoryEntity(item.id);
|
|
123
|
-
report.steps.orphans.removed++;
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
report.errors.push({
|
|
127
|
-
step: "orphans",
|
|
128
|
-
message: `Failed to delete ${item.id}: ${err.message}`,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
report.summary.actionsTaken += report.steps.orphans.removed;
|
|
133
|
-
}
|
|
134
|
-
report.summary.issuesFound += report.steps.orphans.orphansFound;
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
report.errors.push({
|
|
138
|
-
step: "orphans",
|
|
139
|
-
message: err.message,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Stage 4: Detect duplicates
|
|
144
|
-
if (steps.includes("duplicates")) {
|
|
145
|
-
try {
|
|
146
|
-
report.steps.duplicates = await runDuplicateStep(client, entities, workspaceId, projectId);
|
|
147
|
-
if (!dryRun) {
|
|
148
|
-
for (const pair of report.steps.duplicates.pairs) {
|
|
149
|
-
try {
|
|
150
|
-
await client.deleteMemoryEntity(pair.removeId);
|
|
151
|
-
report.steps.duplicates.resolved++;
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
report.errors.push({
|
|
155
|
-
step: "duplicates",
|
|
156
|
-
message: `Failed to delete ${pair.removeId}: ${err.message}`,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
report.summary.actionsTaken += report.steps.duplicates.resolved;
|
|
161
|
-
}
|
|
162
|
-
report.summary.issuesFound += report.steps.duplicates.duplicatePairsFound;
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
report.errors.push({
|
|
166
|
-
step: "duplicates",
|
|
167
|
-
message: err.message,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// Stage 5: Backfill embeddings
|
|
172
|
-
if (steps.includes("backfill")) {
|
|
173
|
-
try {
|
|
174
|
-
if (dryRun) {
|
|
175
|
-
// In dry-run, just report that backfill would run
|
|
176
|
-
report.steps.backfill = {
|
|
177
|
-
processed: 0,
|
|
178
|
-
remaining: -1,
|
|
179
|
-
errors: [],
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
const result = await client.backfillEmbeddings(workspaceId);
|
|
184
|
-
report.steps.backfill = {
|
|
185
|
-
processed: result.processed,
|
|
186
|
-
remaining: result.remaining,
|
|
187
|
-
errors: result.errors || [],
|
|
188
|
-
};
|
|
189
|
-
report.summary.actionsTaken += result.processed;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
catch (err) {
|
|
193
|
-
report.errors.push({
|
|
194
|
-
step: "backfill",
|
|
195
|
-
message: err.message,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
report.healthReport = generateHealthReport(report);
|
|
200
|
-
return report;
|
|
201
|
-
}
|
|
202
|
-
// ---------------------------------------------------------------------------
|
|
203
|
-
// Step implementations
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
function runPruneStep(entities, maxAgeDays) {
|
|
206
|
-
const now = Date.now();
|
|
207
|
-
const drafts = entities.filter((e) => e.memory_tier === "draft");
|
|
208
|
-
const stale = [];
|
|
209
|
-
for (const entity of drafts) {
|
|
210
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
211
|
-
if (ageDays < maxAgeDays)
|
|
212
|
-
continue;
|
|
213
|
-
const lifecycle = evaluateLifecycle(entity);
|
|
214
|
-
stale.push({
|
|
215
|
-
id: entity.id,
|
|
216
|
-
title: entity.title,
|
|
217
|
-
ageDays: Math.round(ageDays),
|
|
218
|
-
decayScore: Math.round(lifecycle.decay.score * 100) / 100,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
return { staleDraftsFound: stale.length, pruned: 0, items: stale };
|
|
222
|
-
}
|
|
223
|
-
async function runOrphanStep(client, entities, orphanAgeDays) {
|
|
224
|
-
const now = Date.now();
|
|
225
|
-
const result = { orphansFound: 0, removed: 0, items: [] };
|
|
226
|
-
// Pre-filter: only check entities that look like orphan candidates
|
|
227
|
-
const candidates = entities.filter((e) => {
|
|
228
|
-
if (e.memory_tier === "reference")
|
|
229
|
-
return false;
|
|
230
|
-
if (e.access_count >= 2)
|
|
231
|
-
return false;
|
|
232
|
-
const ageDays = (now - new Date(e.created_at).getTime()) / MS_PER_DAY;
|
|
233
|
-
return ageDays >= orphanAgeDays;
|
|
234
|
-
});
|
|
235
|
-
// Check relations in concurrent batches
|
|
236
|
-
for (let i = 0; i < candidates.length; i += CONCURRENCY_LIMIT) {
|
|
237
|
-
const batch = candidates.slice(i, i + CONCURRENCY_LIMIT);
|
|
238
|
-
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
239
|
-
const related = await client.getRelatedEntities(entity.id);
|
|
240
|
-
const totalRelations = (related.outgoing?.length || 0) + (related.incoming?.length || 0);
|
|
241
|
-
if (totalRelations > 0)
|
|
242
|
-
return null;
|
|
243
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
244
|
-
return {
|
|
245
|
-
id: entity.id,
|
|
246
|
-
title: entity.title,
|
|
247
|
-
type: entity.type,
|
|
248
|
-
tier: entity.memory_tier,
|
|
249
|
-
ageDays: Math.round(ageDays),
|
|
250
|
-
accessCount: entity.access_count,
|
|
251
|
-
};
|
|
252
|
-
}));
|
|
253
|
-
for (const r of results) {
|
|
254
|
-
if (r.status === "fulfilled" && r.value) {
|
|
255
|
-
result.items.push(r.value);
|
|
256
|
-
result.orphansFound++;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return result;
|
|
261
|
-
}
|
|
262
|
-
async function runDuplicateStep(client, entities, workspaceId, projectId) {
|
|
263
|
-
const result = {
|
|
264
|
-
duplicatePairsFound: 0,
|
|
265
|
-
resolved: 0,
|
|
266
|
-
pairs: [],
|
|
267
|
-
};
|
|
268
|
-
const seenPairs = new Set();
|
|
269
|
-
const flaggedForRemoval = new Set();
|
|
270
|
-
const entityMap = new Map(entities.map((e) => [e.id, e]));
|
|
271
|
-
const similarityMap = new Map();
|
|
272
|
-
for (let i = 0; i < entities.length; i += CONCURRENCY_LIMIT) {
|
|
273
|
-
const batch = entities.slice(i, i + CONCURRENCY_LIMIT);
|
|
274
|
-
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
275
|
-
const similar = await findSimilarEntities(client, entity.title, entity.content, workspaceId, { projectId, limit: 5, minRrfScore: 0.05, excludeIds: [entity.id] });
|
|
276
|
-
return { entityId: entity.id, similar };
|
|
277
|
-
}));
|
|
278
|
-
for (const r of results) {
|
|
279
|
-
if (r.status === "fulfilled") {
|
|
280
|
-
similarityMap.set(r.value.entityId, r.value.similar);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// Process pairs sequentially (flaggedForRemoval creates dependencies)
|
|
285
|
-
for (const entity of entities) {
|
|
286
|
-
if (flaggedForRemoval.has(entity.id))
|
|
287
|
-
continue;
|
|
288
|
-
const similar = similarityMap.get(entity.id) || [];
|
|
289
|
-
for (const match of similar) {
|
|
290
|
-
if (flaggedForRemoval.has(match.id))
|
|
291
|
-
continue;
|
|
292
|
-
const pairKey = [entity.id, match.id].sort().join(":");
|
|
293
|
-
if (seenPairs.has(pairKey))
|
|
294
|
-
continue;
|
|
295
|
-
seenPairs.add(pairKey);
|
|
296
|
-
const sim = titleSimilarity(entity.title, match.title);
|
|
297
|
-
if (sim < DUPLICATE_SIMILARITY_THRESHOLD)
|
|
298
|
-
continue;
|
|
299
|
-
// Keep the one with higher confidence, more accesses, or higher tier
|
|
300
|
-
const entityScore = entityQualityScore(entity);
|
|
301
|
-
const matchEntity = entityMap.get(match.id);
|
|
302
|
-
const matchScore = matchEntity
|
|
303
|
-
? entityQualityScore(matchEntity)
|
|
304
|
-
: match.confidence;
|
|
305
|
-
const [keep, remove] = entityScore >= matchScore
|
|
306
|
-
? [entity, { id: match.id, title: match.title }]
|
|
307
|
-
: [{ id: match.id, title: match.title }, entity];
|
|
308
|
-
flaggedForRemoval.add(remove.id);
|
|
309
|
-
result.pairs.push({
|
|
310
|
-
keepId: keep.id,
|
|
311
|
-
keepTitle: keep.title,
|
|
312
|
-
removeId: remove.id,
|
|
313
|
-
removeTitle: remove.title,
|
|
314
|
-
similarity: Math.round(sim * 100) / 100,
|
|
315
|
-
});
|
|
316
|
-
result.duplicatePairsFound++;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
// ---------------------------------------------------------------------------
|
|
322
|
-
// Helpers
|
|
323
|
-
// ---------------------------------------------------------------------------
|
|
324
|
-
const TIER_WEIGHTS = {
|
|
325
|
-
reference: 3,
|
|
326
|
-
episode: 2,
|
|
327
|
-
draft: 1,
|
|
328
|
-
};
|
|
329
|
-
function entityQualityScore(entity) {
|
|
330
|
-
return (entity.confidence +
|
|
331
|
-
(TIER_WEIGHTS[entity.memory_tier] || 0) +
|
|
332
|
-
Math.min(entity.access_count, 10) * 0.1);
|
|
333
|
-
}
|
|
334
|
-
function titleSimilarity(a, b) {
|
|
335
|
-
const na = a.toLowerCase().trim();
|
|
336
|
-
const nb = b.toLowerCase().trim();
|
|
337
|
-
if (na === nb)
|
|
338
|
-
return 1;
|
|
339
|
-
const wordsA = new Set(na.split(/\W+/).filter(Boolean));
|
|
340
|
-
const wordsB = new Set(nb.split(/\W+/).filter(Boolean));
|
|
341
|
-
if (wordsA.size === 0 || wordsB.size === 0)
|
|
342
|
-
return 0;
|
|
343
|
-
let intersection = 0;
|
|
344
|
-
for (const w of wordsA) {
|
|
345
|
-
if (wordsB.has(w))
|
|
346
|
-
intersection++;
|
|
347
|
-
}
|
|
348
|
-
// Jaccard similarity
|
|
349
|
-
const union = wordsA.size + wordsB.size - intersection;
|
|
350
|
-
return union > 0 ? intersection / union : 0;
|
|
351
|
-
}
|
|
352
|
-
// ---------------------------------------------------------------------------
|
|
353
|
-
// Health report renderer
|
|
354
|
-
// ---------------------------------------------------------------------------
|
|
355
|
-
function generateHealthReport(report) {
|
|
356
|
-
const mode = report.dryRun ? "Dry Run (preview)" : "Executed";
|
|
357
|
-
const lines = [
|
|
358
|
-
"# Memory Health Report\n",
|
|
359
|
-
`**Mode:** ${mode} | **Entities:** ${report.summary.totalEntities} | **Issues:** ${report.summary.issuesFound} | **Actions:** ${report.summary.actionsTaken}`,
|
|
360
|
-
"",
|
|
361
|
-
];
|
|
362
|
-
if (report.summary.totalEntities >= MAX_ENTITIES_FETCH) {
|
|
363
|
-
lines.push(`> **Note:** Entity count hit the ${MAX_ENTITIES_FETCH} fetch limit. Some entities may not have been analyzed.\n`);
|
|
364
|
-
}
|
|
365
|
-
// Prune
|
|
366
|
-
if (report.steps.prune) {
|
|
367
|
-
const p = report.steps.prune;
|
|
368
|
-
lines.push("## Stale Drafts");
|
|
369
|
-
if (p.staleDraftsFound === 0) {
|
|
370
|
-
lines.push("No stale drafts found.\n");
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
lines.push(`Found **${p.staleDraftsFound}** stale drafts${!report.dryRun ? ` (pruned ${p.pruned})` : ""}:`);
|
|
374
|
-
lines.push("| Title | Age | Decay |");
|
|
375
|
-
lines.push("|-------|-----|-------|");
|
|
376
|
-
for (const item of p.items.slice(0, 20)) {
|
|
377
|
-
lines.push(`| ${item.title} | ${item.ageDays}d | ${item.decayScore} |`);
|
|
378
|
-
}
|
|
379
|
-
lines.push("");
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// Consolidate
|
|
383
|
-
if (report.steps.consolidate) {
|
|
384
|
-
const c = report.steps.consolidate;
|
|
385
|
-
lines.push("## Consolidation");
|
|
386
|
-
if (c.clustersFound === 0) {
|
|
387
|
-
lines.push(`Scanned ${c.entitiesProcessed} draft/episode entities — no clusters found.\n`);
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
lines.push(`Found **${c.clustersFound}** clusters across ${c.entitiesProcessed} entities:`);
|
|
391
|
-
for (const d of c.details.slice(0, 10)) {
|
|
392
|
-
lines.push(`- **${d.mergedTitle}** — ${d.clusterSize} entities`);
|
|
393
|
-
}
|
|
394
|
-
lines.push("");
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
// Orphans
|
|
398
|
-
if (report.steps.orphans) {
|
|
399
|
-
const o = report.steps.orphans;
|
|
400
|
-
lines.push("## Orphaned Entities");
|
|
401
|
-
if (o.orphansFound === 0) {
|
|
402
|
-
lines.push("No orphans found.\n");
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
lines.push(`Found **${o.orphansFound}** orphans${!report.dryRun ? ` (removed ${o.removed})` : ""}:`);
|
|
406
|
-
lines.push("| Title | Type | Tier | Age | Accesses |");
|
|
407
|
-
lines.push("|-------|------|------|-----|----------|");
|
|
408
|
-
for (const item of o.items.slice(0, 20)) {
|
|
409
|
-
lines.push(`| ${item.title} | ${item.type} | ${item.tier} | ${item.ageDays}d | ${item.accessCount} |`);
|
|
410
|
-
}
|
|
411
|
-
lines.push("");
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
// Duplicates
|
|
415
|
-
if (report.steps.duplicates) {
|
|
416
|
-
const d = report.steps.duplicates;
|
|
417
|
-
lines.push("## Near-Duplicates");
|
|
418
|
-
if (d.duplicatePairsFound === 0) {
|
|
419
|
-
lines.push("No duplicates found.\n");
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
lines.push(`Found **${d.duplicatePairsFound}** duplicate pairs${!report.dryRun ? ` (resolved ${d.resolved})` : ""}:`);
|
|
423
|
-
for (const pair of d.pairs.slice(0, 20)) {
|
|
424
|
-
lines.push(`- "${pair.keepTitle}" ~ "${pair.removeTitle}" (${Math.round(pair.similarity * 100)}% similar, keep first)`);
|
|
425
|
-
}
|
|
426
|
-
lines.push("");
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
// Backfill
|
|
430
|
-
if (report.steps.backfill) {
|
|
431
|
-
const b = report.steps.backfill;
|
|
432
|
-
lines.push("## Embedding Coverage");
|
|
433
|
-
if (report.dryRun) {
|
|
434
|
-
lines.push("Backfill will run when executed with `dryRun: false`.\n");
|
|
435
|
-
}
|
|
436
|
-
else if (b.remaining === 0) {
|
|
437
|
-
lines.push(`All embeddings up to date (processed ${b.processed}).\n`);
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
lines.push(`Processed ${b.processed} entities. ${b.remaining} still need embeddings.\n`);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
// Errors
|
|
444
|
-
if (report.errors.length > 0) {
|
|
445
|
-
lines.push("## Errors");
|
|
446
|
-
for (const e of report.errors) {
|
|
447
|
-
lines.push(`- **${e.step}:** ${e.message}`);
|
|
448
|
-
}
|
|
449
|
-
lines.push("");
|
|
450
|
-
}
|
|
451
|
-
if (report.dryRun) {
|
|
452
|
-
lines.push("---\n*Run with `dryRun: false` to execute cleanup.*");
|
|
453
|
-
}
|
|
454
|
-
return lines.join("\n");
|
|
455
|
-
}
|
package/dist/lib/onboard.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { requestWithBearer, signupUser } from "./api-client.js";
|
|
2
|
-
import { getApiUrl } from "./config.js";
|
|
3
|
-
export async function onboardNewUser(params) {
|
|
4
|
-
const { email, password, fullName, workspaceName = `${fullName}'s Workspace`, projectName = "My First Board", template = "kanban", keyName = "mcp-agent", apiUrl = getApiUrl(), } = params;
|
|
5
|
-
// 1. Signup
|
|
6
|
-
const signupResult = await signupUser(apiUrl, {
|
|
7
|
-
email,
|
|
8
|
-
password,
|
|
9
|
-
full_name: fullName,
|
|
10
|
-
});
|
|
11
|
-
const token = signupResult.session.access_token;
|
|
12
|
-
// 2. Create workspace
|
|
13
|
-
const workspaceResult = await requestWithBearer(apiUrl, token, "POST", "/workspaces", {
|
|
14
|
-
name: workspaceName,
|
|
15
|
-
});
|
|
16
|
-
// 3. Create project
|
|
17
|
-
const projectResult = await requestWithBearer(apiUrl, token, "POST", "/projects", {
|
|
18
|
-
workspaceId: workspaceResult.workspace.id,
|
|
19
|
-
name: projectName,
|
|
20
|
-
template,
|
|
21
|
-
});
|
|
22
|
-
// 4. Generate API key
|
|
23
|
-
const keyResult = await requestWithBearer(apiUrl, token, "POST", "/api-keys", {
|
|
24
|
-
name: keyName,
|
|
25
|
-
});
|
|
26
|
-
return {
|
|
27
|
-
user: signupResult.user,
|
|
28
|
-
workspace: workspaceResult.workspace,
|
|
29
|
-
project: projectResult.project,
|
|
30
|
-
columns: projectResult.columns,
|
|
31
|
-
apiKey: {
|
|
32
|
-
rawKey: keyResult.rawKey,
|
|
33
|
-
prefix: keyResult.apiKey.prefix,
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|