codemem 0.24.0 → 0.25.1
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.js +262 -26
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEFAULT_COORDINATOR_DB_PATH, MemoryStore, ObserverClient, RawEventSweeper, SyncRetentionRunner, VERSION, applyBootstrapSnapshot, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, ensureDeviceIdentity, exportMemories, fetchAllSnapshotPages, fingerprintPublicKey, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
2
|
+
import { DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MemoryStore, ObserverClient, RawEventSweeper, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, exportMemories, fetchAllSnapshotPages, fingerprintPublicKey, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -242,7 +242,7 @@ function envNotDisabled(value) {
|
|
|
242
242
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
243
243
|
return normalized !== "0" && normalized !== "false" && normalized !== "off";
|
|
244
244
|
}
|
|
245
|
-
function parsePositiveInt(value, fallback) {
|
|
245
|
+
function parsePositiveInt$1(value, fallback) {
|
|
246
246
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
247
247
|
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
248
248
|
return parsed;
|
|
@@ -269,8 +269,8 @@ function resolveInjectProject(payload) {
|
|
|
269
269
|
async function buildLocalPack(context, project, dbPath) {
|
|
270
270
|
const store = new MemoryStore(dbPath);
|
|
271
271
|
try {
|
|
272
|
-
const limit = parsePositiveInt(process.env.CODEMEM_INJECT_LIMIT, 8);
|
|
273
|
-
const budget = parsePositiveInt(process.env.CODEMEM_INJECT_TOKEN_BUDGET, 800);
|
|
272
|
+
const limit = parsePositiveInt$1(process.env.CODEMEM_INJECT_LIMIT, 8);
|
|
273
|
+
const budget = parsePositiveInt$1(process.env.CODEMEM_INJECT_TOKEN_BUDGET, 800);
|
|
274
274
|
const filters = {};
|
|
275
275
|
if (project) filters.project = project;
|
|
276
276
|
const pack = await store.buildMemoryPackAsync(context, limit, budget, filters);
|
|
@@ -281,11 +281,11 @@ async function buildLocalPack(context, project, dbPath) {
|
|
|
281
281
|
}
|
|
282
282
|
async function tryHttpPack(context, project, maxTimeMs = DEFAULT_HTTP_MAX_TIME_S * 1e3) {
|
|
283
283
|
const host = process.env.CODEMEM_VIEWER_HOST || DEFAULT_VIEWER_HOST;
|
|
284
|
-
const port = parsePositiveInt(process.env.CODEMEM_VIEWER_PORT, DEFAULT_VIEWER_PORT);
|
|
284
|
+
const port = parsePositiveInt$1(process.env.CODEMEM_VIEWER_PORT, DEFAULT_VIEWER_PORT);
|
|
285
285
|
const url = new URL(`http://${host}:${port}/api/pack`);
|
|
286
286
|
url.searchParams.set("context", context);
|
|
287
|
-
url.searchParams.set("limit", String(parsePositiveInt(process.env.CODEMEM_INJECT_LIMIT, 8)));
|
|
288
|
-
url.searchParams.set("token_budget", String(parsePositiveInt(process.env.CODEMEM_INJECT_TOKEN_BUDGET, 800)));
|
|
287
|
+
url.searchParams.set("limit", String(parsePositiveInt$1(process.env.CODEMEM_INJECT_LIMIT, 8)));
|
|
288
|
+
url.searchParams.set("token_budget", String(parsePositiveInt$1(process.env.CODEMEM_INJECT_TOKEN_BUDGET, 800)));
|
|
289
289
|
if (project) url.searchParams.set("project", project);
|
|
290
290
|
const controller = new AbortController();
|
|
291
291
|
const timeout = setTimeout(() => controller.abort(), maxTimeMs);
|
|
@@ -308,8 +308,8 @@ async function buildClaudeHookInjection(payload, opts, deps = {}) {
|
|
|
308
308
|
const httpPack = deps.httpPack ?? tryHttpPack;
|
|
309
309
|
const resolveDb = deps.resolveDb ?? resolveDbPath;
|
|
310
310
|
const project = resolveInjectProject(payload);
|
|
311
|
-
const maxChars = parsePositiveInt(process.env.CODEMEM_INJECT_MAX_CHARS, DEFAULT_MAX_CHARS);
|
|
312
|
-
const httpMaxTimeMs = parsePositiveInt(process.env.CODEMEM_INJECT_HTTP_MAX_TIME_S, DEFAULT_HTTP_MAX_TIME_S) * 1e3;
|
|
311
|
+
const maxChars = parsePositiveInt$1(process.env.CODEMEM_INJECT_MAX_CHARS, DEFAULT_MAX_CHARS);
|
|
312
|
+
const httpMaxTimeMs = parsePositiveInt$1(process.env.CODEMEM_INJECT_HTTP_MAX_TIME_S, DEFAULT_HTTP_MAX_TIME_S) * 1e3;
|
|
313
313
|
let additionalContext = "";
|
|
314
314
|
try {
|
|
315
315
|
additionalContext = await buildPack(context, project, resolveDb(resolveDbOpt(opts)));
|
|
@@ -1361,6 +1361,119 @@ pruneMemCmd.action((opts) => {
|
|
|
1361
1361
|
}
|
|
1362
1362
|
});
|
|
1363
1363
|
dbCommand.addCommand(pruneMemCmd);
|
|
1364
|
+
var dedupCmd = new Command("dedup-memories").configureHelp(helpStyle).description("Deactivate near-duplicate memories (cross-session, same normalized title within time window)").option("--window <ms>", "max time gap in milliseconds between duplicates (default: 3600000)").option("--limit <n>", "max pairs to check").option("--dry-run", "preview deactivations without writing");
|
|
1365
|
+
addDbOption(dedupCmd);
|
|
1366
|
+
addJsonOption(dedupCmd);
|
|
1367
|
+
dedupCmd.action((opts) => {
|
|
1368
|
+
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
1369
|
+
try {
|
|
1370
|
+
const windowMs = parseOptionalPositiveInt$1(opts.window);
|
|
1371
|
+
const limit = parseOptionalPositiveInt$1(opts.limit);
|
|
1372
|
+
const result = dedupNearDuplicateMemories(store.db, {
|
|
1373
|
+
windowMs,
|
|
1374
|
+
limit,
|
|
1375
|
+
dryRun: opts.dryRun === true
|
|
1376
|
+
});
|
|
1377
|
+
if (opts.json) {
|
|
1378
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const action = opts.dryRun ? "Would deactivate" : "Deactivated";
|
|
1382
|
+
p.intro("codemem db dedup-memories");
|
|
1383
|
+
if (result.pairs.length > 0 && result.pairs.length <= 20) for (const pair of result.pairs) p.log.info(`${action} [${pair.deactivated_id}] (kept [${pair.kept_id}]): ${pair.title.slice(0, 80)}`);
|
|
1384
|
+
p.outro(`${action} ${result.deactivated} duplicates from ${result.checked} pairs`);
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1387
|
+
process.exitCode = 1;
|
|
1388
|
+
} finally {
|
|
1389
|
+
store.close();
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
dbCommand.addCommand(dedupCmd);
|
|
1393
|
+
var backfillDedupKeysCmd = new Command("backfill-dedup-keys").configureHelp(helpStyle).description("Populate missing memory_items.dedup_key values for legacy rows").option("--limit <n>", "max memories to check").option("--dry-run", "preview updates without writing");
|
|
1394
|
+
addDbOption(backfillDedupKeysCmd);
|
|
1395
|
+
addJsonOption(backfillDedupKeysCmd);
|
|
1396
|
+
backfillDedupKeysCmd.action((opts) => {
|
|
1397
|
+
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
1398
|
+
try {
|
|
1399
|
+
const limit = parseOptionalPositiveInt$1(opts.limit);
|
|
1400
|
+
const result = backfillMemoryDedupKeys(store.db, {
|
|
1401
|
+
limit,
|
|
1402
|
+
dryRun: opts.dryRun === true
|
|
1403
|
+
});
|
|
1404
|
+
if (opts.json) {
|
|
1405
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const action = opts.dryRun ? "Would update" : "Updated";
|
|
1409
|
+
p.intro("codemem db backfill-dedup-keys");
|
|
1410
|
+
p.log.success(`${action} ${result.updated} memories (skipped ${result.skipped})`);
|
|
1411
|
+
p.outro(`Checked ${result.checked} memories`);
|
|
1412
|
+
} catch (error) {
|
|
1413
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1414
|
+
process.exitCode = 1;
|
|
1415
|
+
} finally {
|
|
1416
|
+
store.close();
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
dbCommand.addCommand(backfillDedupKeysCmd);
|
|
1420
|
+
var backfillNarrativeCmd = new Command("backfill-narrative").configureHelp(helpStyle).description("Extract narrative from session_summary body_text (## Completed / ## Learned sections)").option("--limit <n>", "max memories to check").option("--dry-run", "preview updates without writing");
|
|
1421
|
+
addDbOption(backfillNarrativeCmd);
|
|
1422
|
+
addJsonOption(backfillNarrativeCmd);
|
|
1423
|
+
backfillNarrativeCmd.action((opts) => {
|
|
1424
|
+
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
1425
|
+
try {
|
|
1426
|
+
const limit = parseOptionalPositiveInt$1(opts.limit);
|
|
1427
|
+
const result = backfillNarrativeFromBody(store.db, {
|
|
1428
|
+
limit,
|
|
1429
|
+
dryRun: opts.dryRun === true
|
|
1430
|
+
});
|
|
1431
|
+
if (opts.json) {
|
|
1432
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
const action = opts.dryRun ? "Would update" : "Updated";
|
|
1436
|
+
p.intro("codemem db backfill-narrative");
|
|
1437
|
+
p.log.success(`${action} ${result.updated} memories (skipped ${result.skipped})`);
|
|
1438
|
+
p.outro(`Checked ${result.checked} memories`);
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1441
|
+
process.exitCode = 1;
|
|
1442
|
+
} finally {
|
|
1443
|
+
store.close();
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
dbCommand.addCommand(backfillNarrativeCmd);
|
|
1447
|
+
var aiBackfillStructuredCmd = new Command("ai-backfill-structured").configureHelp(helpStyle).description("Use GPT-5.4 to populate missing narrative/facts/concepts for older non-session-summary memories").option("--limit <n>", "max memories to check").option("--kinds <csv>", "comma-separated kinds to target").option("--overwrite", "overwrite existing structured fields instead of only filling missing ones").option("--dry-run", "preview updates without writing");
|
|
1448
|
+
addDbOption(aiBackfillStructuredCmd);
|
|
1449
|
+
addJsonOption(aiBackfillStructuredCmd);
|
|
1450
|
+
aiBackfillStructuredCmd.action(async (opts) => {
|
|
1451
|
+
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
1452
|
+
try {
|
|
1453
|
+
const limit = parseOptionalPositiveInt$1(opts.limit);
|
|
1454
|
+
const kinds = parseKindsCsv(opts.kinds);
|
|
1455
|
+
const result = await aiBackfillStructuredContent(store.db, {
|
|
1456
|
+
limit,
|
|
1457
|
+
kinds,
|
|
1458
|
+
overwrite: opts.overwrite === true,
|
|
1459
|
+
dryRun: opts.dryRun === true
|
|
1460
|
+
});
|
|
1461
|
+
if (opts.json) {
|
|
1462
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
const action = opts.dryRun ? "Would update" : "Updated";
|
|
1466
|
+
p.intro("codemem db ai-backfill-structured");
|
|
1467
|
+
p.log.success(`${action} ${result.updated} memories (skipped ${result.skipped}, failed ${result.failed})`);
|
|
1468
|
+
p.outro(`Checked ${result.checked} memories`);
|
|
1469
|
+
} catch (error) {
|
|
1470
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1471
|
+
process.exitCode = 1;
|
|
1472
|
+
} finally {
|
|
1473
|
+
store.close();
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
dbCommand.addCommand(aiBackfillStructuredCmd);
|
|
1364
1477
|
//#endregion
|
|
1365
1478
|
//#region src/commands/embed.ts
|
|
1366
1479
|
function parseOptionalPositiveInt(value) {
|
|
@@ -1601,13 +1714,22 @@ var mcpCommand = mcpCmd.action(async (opts) => {
|
|
|
1601
1714
|
});
|
|
1602
1715
|
//#endregion
|
|
1603
1716
|
//#region src/commands/pack-shared.ts
|
|
1717
|
+
var PackUsageError = class extends Error {
|
|
1718
|
+
constructor(message) {
|
|
1719
|
+
super(message);
|
|
1720
|
+
this.name = "PackUsageError";
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1604
1723
|
function collectWorkingSetFile(value, previous) {
|
|
1605
1724
|
return [...previous, value];
|
|
1606
1725
|
}
|
|
1726
|
+
function addPackRequestOptions(command) {
|
|
1727
|
+
return command.option("-n, --limit <n>", "max items", "10").option("--budget <tokens>", "token budget").option("--token-budget <tokens>", "token budget").option("--working-set-file <path>", "recently modified file path used as ranking hint", collectWorkingSetFile, []).option("--project <project>", "project identifier (defaults to git repo root)").option("--all-projects", "search across all projects").option("--compact", "render a scannable index with full detail only for top N items").option("--compact-detail <n>", "items to show in full detail in compact mode (default 3)");
|
|
1728
|
+
}
|
|
1607
1729
|
function buildPackRequestOptions(opts, ctx = {}) {
|
|
1608
|
-
const limit =
|
|
1730
|
+
const limit = parsePositiveInt(opts.limit ?? "10", "limit");
|
|
1609
1731
|
const budgetRaw = opts.tokenBudget ?? opts.budget;
|
|
1610
|
-
const budget = budgetRaw ?
|
|
1732
|
+
const budget = budgetRaw ? parseNonNegativeInt(budgetRaw, "token budget") : void 0;
|
|
1611
1733
|
const filters = {};
|
|
1612
1734
|
if (!opts.allProjects) {
|
|
1613
1735
|
const defaultProject = ctx.envProject?.trim() || null;
|
|
@@ -1617,12 +1739,30 @@ function buildPackRequestOptions(opts, ctx = {}) {
|
|
|
1617
1739
|
if (project) filters.project = project;
|
|
1618
1740
|
}
|
|
1619
1741
|
if ((opts.workingSetFile?.length ?? 0) > 0) filters.working_set_paths = opts.workingSetFile;
|
|
1742
|
+
let renderOptions;
|
|
1743
|
+
if (opts.compact || opts.compactDetail != null) {
|
|
1744
|
+
renderOptions = { compact: true };
|
|
1745
|
+
if (opts.compactDetail != null) renderOptions.compactDetailCount = parseNonNegativeInt(opts.compactDetail, "compact detail count");
|
|
1746
|
+
}
|
|
1620
1747
|
return {
|
|
1621
1748
|
limit,
|
|
1622
1749
|
budget,
|
|
1623
|
-
filters
|
|
1750
|
+
filters,
|
|
1751
|
+
renderOptions
|
|
1624
1752
|
};
|
|
1625
1753
|
}
|
|
1754
|
+
function parsePositiveInt(value, label) {
|
|
1755
|
+
if (!/^\d+$/.test(value.trim())) throw new PackUsageError(`${label} must be a positive integer`);
|
|
1756
|
+
const parsed = Number.parseInt(value, 10);
|
|
1757
|
+
if (!Number.isFinite(parsed) || parsed < 1) throw new PackUsageError(`${label} must be a positive integer`);
|
|
1758
|
+
return parsed;
|
|
1759
|
+
}
|
|
1760
|
+
function parseNonNegativeInt(value, label) {
|
|
1761
|
+
if (!/^\d+$/.test(value.trim())) throw new PackUsageError(`${label} must be a non-negative integer`);
|
|
1762
|
+
const parsed = Number.parseInt(value, 10);
|
|
1763
|
+
if (!Number.isFinite(parsed) || parsed < 0) throw new PackUsageError(`${label} must be a non-negative integer`);
|
|
1764
|
+
return parsed;
|
|
1765
|
+
}
|
|
1626
1766
|
//#endregion
|
|
1627
1767
|
//#region src/commands/memory.ts
|
|
1628
1768
|
/**
|
|
@@ -2235,14 +2375,79 @@ memoryCommand.addCommand(createMemoryRelinkReportCommand());
|
|
|
2235
2375
|
memoryCommand.addCommand(createMemoryRelinkPlanCommand());
|
|
2236
2376
|
//#endregion
|
|
2237
2377
|
//#region src/commands/pack.ts
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2378
|
+
function describeCandidate(candidate) {
|
|
2379
|
+
const scoreParts = [
|
|
2380
|
+
candidate.scores.combined_score != null ? `combined=${candidate.scores.combined_score.toFixed(2)}` : null,
|
|
2381
|
+
candidate.scores.base_score != null ? `base=${candidate.scores.base_score.toFixed(2)}` : null,
|
|
2382
|
+
candidate.scores.text_overlap > 0 ? `text=${candidate.scores.text_overlap}` : null,
|
|
2383
|
+
candidate.scores.tag_overlap > 0 ? `tag=${candidate.scores.tag_overlap}` : null,
|
|
2384
|
+
candidate.scores.working_set_overlap > 0 ? `working_set=${candidate.scores.working_set_overlap.toFixed(2)}` : null
|
|
2385
|
+
].filter(Boolean).join(" ");
|
|
2386
|
+
const lines = [`${candidate.rank}. [${candidate.id}] (${candidate.kind}) ${candidate.title}`];
|
|
2387
|
+
if (candidate.section) lines.push(` - section: ${candidate.section}`);
|
|
2388
|
+
if (candidate.reasons.length > 0) lines.push(` - reasons: ${candidate.reasons.join(", ")}`);
|
|
2389
|
+
if (scoreParts) lines.push(` - scores: ${scoreParts}`);
|
|
2390
|
+
if (candidate.preview) lines.push(` - preview: ${candidate.preview}`);
|
|
2391
|
+
return lines;
|
|
2392
|
+
}
|
|
2393
|
+
function renderPackTrace(trace) {
|
|
2394
|
+
const workingSet = trace.inputs.working_set_files.length > 0 ? trace.inputs.working_set_files.join(", ") : "(none)";
|
|
2395
|
+
const lines = [
|
|
2396
|
+
"Pack trace",
|
|
2397
|
+
`- Query: ${trace.inputs.query}`,
|
|
2398
|
+
`- Project: ${trace.inputs.project ?? "(default)"}`,
|
|
2399
|
+
`- Working set: ${workingSet}`,
|
|
2400
|
+
`- Mode: ${trace.mode.selected}`,
|
|
2401
|
+
`- Mode reasons: ${trace.mode.reasons.join(", ") || "(none)"}`,
|
|
2402
|
+
`- Token budget: ${trace.inputs.token_budget ?? "(none)"}`,
|
|
2403
|
+
""
|
|
2404
|
+
];
|
|
2405
|
+
for (const disposition of [
|
|
2406
|
+
"selected",
|
|
2407
|
+
"dropped",
|
|
2408
|
+
"deduped",
|
|
2409
|
+
"trimmed"
|
|
2410
|
+
]) {
|
|
2411
|
+
const group = trace.retrieval.candidates.filter((candidate) => candidate.disposition === disposition);
|
|
2412
|
+
if (group.length === 0) continue;
|
|
2413
|
+
lines.push(disposition.charAt(0).toUpperCase() + disposition.slice(1));
|
|
2414
|
+
for (const candidate of group) lines.push(...describeCandidate(candidate));
|
|
2415
|
+
lines.push("");
|
|
2416
|
+
}
|
|
2417
|
+
lines.push("Assembly");
|
|
2418
|
+
lines.push(`- deduped ids: ${trace.assembly.deduped_ids.join(", ") || "(none)"}`);
|
|
2419
|
+
lines.push(`- trimmed ids: ${trace.assembly.trimmed_ids.join(", ") || "(none)"}`);
|
|
2420
|
+
lines.push(`- trim reasons: ${trace.assembly.trim_reasons.join(", ") || "(none)"}`);
|
|
2421
|
+
lines.push(`- section counts: summary=${trace.output.section_counts.summary} timeline=${trace.output.section_counts.timeline} observations=${trace.output.section_counts.observations}`);
|
|
2422
|
+
lines.push(`- estimated tokens: ${trace.output.estimated_tokens}`);
|
|
2423
|
+
lines.push(`- truncated: ${trace.output.truncated ? "yes" : "no"}`);
|
|
2424
|
+
lines.push("");
|
|
2425
|
+
lines.push("Final pack");
|
|
2426
|
+
lines.push(trace.output.pack_text);
|
|
2427
|
+
return lines.join("\n");
|
|
2428
|
+
}
|
|
2429
|
+
async function withStore(opts, errorCode, run) {
|
|
2430
|
+
let store = null;
|
|
2243
2431
|
try {
|
|
2244
|
-
|
|
2245
|
-
|
|
2432
|
+
store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
2433
|
+
await run(store);
|
|
2434
|
+
} catch (error) {
|
|
2435
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2436
|
+
const usageError = error instanceof PackUsageError;
|
|
2437
|
+
if (opts.json) {
|
|
2438
|
+
emitJsonError(usageError ? "usage_error" : errorCode, message, usageError ? 2 : 1);
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
p.log.error(message);
|
|
2442
|
+
process.exitCode = usageError ? 2 : 1;
|
|
2443
|
+
} finally {
|
|
2444
|
+
store?.close();
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
async function packAction(context, opts) {
|
|
2448
|
+
await withStore(opts, "pack_failed", async (store) => {
|
|
2449
|
+
const { limit, budget, filters, renderOptions } = buildPackRequestOptions(opts, { envProject: process.env.CODEMEM_PROJECT });
|
|
2450
|
+
const result = await store.buildMemoryPackAsync(context, limit, budget, filters, renderOptions);
|
|
2246
2451
|
if (opts.json) {
|
|
2247
2452
|
console.log(JSON.stringify(result, null, 2));
|
|
2248
2453
|
return;
|
|
@@ -2253,15 +2458,34 @@ var packCommand = packCmd.action(async (context, opts) => {
|
|
|
2253
2458
|
p.outro("done");
|
|
2254
2459
|
return;
|
|
2255
2460
|
}
|
|
2256
|
-
const
|
|
2257
|
-
p.log.info(`${
|
|
2461
|
+
const metrics = result.metrics;
|
|
2462
|
+
p.log.info(`${metrics.total_items} items, ~${metrics.pack_tokens} tokens` + (metrics.fallback_used ? " (fallback)" : "") + ` [fts:${metrics.sources.fts} sem:${metrics.sources.semantic} fuzzy:${metrics.sources.fuzzy}]`);
|
|
2258
2463
|
for (const item of result.items) p.log.step(`#${item.id} ${item.kind} ${item.title}`);
|
|
2259
2464
|
p.note(result.pack_text, "pack_text");
|
|
2260
2465
|
p.outro("done");
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2466
|
+
});
|
|
2467
|
+
}
|
|
2468
|
+
async function traceAction(context, opts) {
|
|
2469
|
+
await withStore(opts, "pack_trace_failed", async (store) => {
|
|
2470
|
+
const { limit, budget, filters, renderOptions } = buildPackRequestOptions(opts, { envProject: process.env.CODEMEM_PROJECT });
|
|
2471
|
+
const trace = await store.buildMemoryPackTraceAsync(context, limit, budget, filters, renderOptions);
|
|
2472
|
+
if (opts.json) {
|
|
2473
|
+
console.log(JSON.stringify(trace, null, 2));
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
console.log(renderPackTrace(trace));
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
var packCmd = addPackRequestOptions(new Command("pack").enablePositionalOptions().configureHelp(helpStyle).description("Build a context-aware memory pack").argument("<context>", "context string to search for"));
|
|
2480
|
+
addDbOption(packCmd);
|
|
2481
|
+
addJsonOption(packCmd);
|
|
2482
|
+
packCmd.action(packAction);
|
|
2483
|
+
var traceCmd = addPackRequestOptions(new Command("trace").configureHelp(helpStyle).description("Trace retrieval and assembly for a memory pack").argument("<context>", "context string to trace"));
|
|
2484
|
+
addDbOption(traceCmd);
|
|
2485
|
+
addJsonOption(traceCmd);
|
|
2486
|
+
traceCmd.action(traceAction);
|
|
2487
|
+
packCmd.addCommand(traceCmd);
|
|
2488
|
+
var packCommand = packCmd;
|
|
2265
2489
|
//#endregion
|
|
2266
2490
|
//#region src/commands/recent.ts
|
|
2267
2491
|
var cmd = new Command("recent").configureHelp(helpStyle).description("Show recent memories").option("--limit <n>", "max results", "5").option("--project <project>", "project identifier (defaults to git repo root)").option("--all-projects", "search across all projects").option("--kind <kind>", "filter by memory kind");
|
|
@@ -2683,6 +2907,8 @@ async function startForegroundViewer(invocation) {
|
|
|
2683
2907
|
dbPath: resolveDbPath(invocation.dbPath ?? void 0),
|
|
2684
2908
|
signal: retentionAbort.signal
|
|
2685
2909
|
});
|
|
2910
|
+
const vectorMigrationRunner = new VectorModelMigrationRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
2911
|
+
const dedupKeyBackfillRunner = new DedupKeyBackfillRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
2686
2912
|
const syncRuntimeStatus = {
|
|
2687
2913
|
phase: syncEnabled ? "starting" : "disabled",
|
|
2688
2914
|
detail: syncEnabled ? "Waiting for viewer startup to finish" : "Sync is disabled"
|
|
@@ -2725,6 +2951,14 @@ async function startForegroundViewer(invocation) {
|
|
|
2725
2951
|
p.log.success(`Listening on http://${info.address}:${info.port}`);
|
|
2726
2952
|
p.log.info(`Database: ${preparedDb}`);
|
|
2727
2953
|
p.log.step("Raw event sweeper started");
|
|
2954
|
+
if (!isEmbeddingDisabled()) {
|
|
2955
|
+
vectorMigrationRunner.start();
|
|
2956
|
+
p.log.step("Vector maintenance runner started");
|
|
2957
|
+
}
|
|
2958
|
+
if (hasPendingDedupKeyBackfill(store.db)) {
|
|
2959
|
+
dedupKeyBackfillRunner.start();
|
|
2960
|
+
p.log.step("Dedup-key maintenance runner started");
|
|
2961
|
+
}
|
|
2728
2962
|
if (syncConfig.syncRetentionEnabled) {
|
|
2729
2963
|
retentionRunner.start();
|
|
2730
2964
|
p.log.step("Retention maintenance runner started");
|
|
@@ -2783,6 +3017,8 @@ async function startForegroundViewer(invocation) {
|
|
|
2783
3017
|
syncAbort.abort();
|
|
2784
3018
|
retentionAbort.abort();
|
|
2785
3019
|
await sweeper.stop();
|
|
3020
|
+
await dedupKeyBackfillRunner.stop();
|
|
3021
|
+
await vectorMigrationRunner.stop();
|
|
2786
3022
|
await retentionRunner.stop();
|
|
2787
3023
|
await new Promise((resolve) => {
|
|
2788
3024
|
let remaining = syncServer ? 2 : 1;
|
|
@@ -4106,7 +4342,7 @@ function getShellCompletionScript() {
|
|
|
4106
4342
|
return completion.generateCompletionCode();
|
|
4107
4343
|
}
|
|
4108
4344
|
var program = new Command();
|
|
4109
|
-
program.name("codemem").description("codemem — persistent memory for AI coding agents").option("--install-completion", "install shell completion").option("--show-completion", "show shell completion install guidance").version(VERSION).configureHelp(helpStyle);
|
|
4345
|
+
program.name("codemem").description("codemem — persistent memory for AI coding agents").enablePositionalOptions().option("--install-completion", "install shell completion").option("--show-completion", "show shell completion install guidance").version(VERSION).configureHelp(helpStyle);
|
|
4110
4346
|
if (hasRootFlag("--setup-completion") || hasRootFlag("--install-completion")) {
|
|
4111
4347
|
completion.setupShellInitFile();
|
|
4112
4348
|
process.exit(0);
|