@ngockhoale/ukit 3.0.12 → 3.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/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/manifests/documentation.yaml +12 -0
- package/package.json +1 -1
- package/scripts/bench/data-foundation.mjs +368 -50
- package/src/cli/commands/doctor.js +232 -3
- package/src/cli/commands/feedback.js +64 -1
- package/src/cli/commands/install.js +18 -0
- package/src/cli/commands/memory.js +42 -37
- package/src/cli/commands/telemetry.js +460 -0
- package/src/cli/index.js +7 -0
- package/src/core/agentRuntime/adapters.js +83 -2
- package/src/core/agentRuntime/diagnostics.js +104 -0
- package/src/core/agentRuntime/supervisor.js +137 -0
- package/src/core/agentRuntime/telemetry.js +204 -0
- package/src/core/memory/memoryEmit.js +131 -0
- package/src/core/memory/memoryHit.js +1 -1
- package/src/core/memory/migrate.js +18 -11
- package/src/core/memory/migrateMapping.js +15 -7
- package/src/core/memory/mutateMemory.js +22 -4
- package/src/core/memory/recordIndex.js +10 -3
- package/src/core/memory/recordStore.js +28 -3
- package/src/core/memory/retrieval.js +79 -38
- package/src/core/memory/store.js +37 -37
- package/src/core/memory/storeV2.js +28 -25
- package/src/core/memory/storeV2Loader.js +2 -2
- package/src/core/observability/adapters/ingest.js +576 -0
- package/src/core/observability/analytics/anomalies.js +415 -0
- package/src/core/observability/analytics/summary.js +16 -1
- package/src/core/observability/emit/config.js +69 -1
- package/src/core/observability/emit/crash.js +434 -0
- package/src/core/observability/emit/lifecycle.js +349 -0
- package/src/core/observability/emit/recorder.js +135 -9
- package/src/core/observability/evaluation/aiPacket.js +52 -10
- package/src/core/observability/evaluation/outcomes.js +95 -0
- package/src/core/observability/evaluation/runner.js +225 -0
- package/src/core/observability/privacy/allowlist.js +23 -3
- package/src/core/observability/schema/compatibility.js +48 -3
- package/src/core/observability/schema/constants.js +5 -0
- package/src/core/observability/schema/registry.js +57 -0
- package/src/core/observability/schema/validate.js +68 -6
- package/src/core/observability/segments/internal.js +42 -8
- package/src/core/observability/segments/readSegments.js +35 -1
- package/src/core/observability/segments/recovery.js +3 -2
- package/src/core/observability/segments/retention.js +137 -33
- package/src/core/observability/support/projector.js +88 -18
- package/src/core/observability/support/provision.js +160 -0
- package/src/core/observability/support/renderer.js +2 -2
- package/src/core/observability/support/schedule.js +174 -0
- package/template_project/.claude/hooks/auto-allow-bash.sh +7 -1
- package/template_project/.claude/hooks/auto-prune-bash.sh +16 -7
- package/template_project/.claude/hooks/verification-guard.sh +13 -4
- package/template_project/.claude/ukit/runtime/async-lock.mjs +26 -0
|
@@ -51,24 +51,24 @@ function extractFlag(args, flag) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
async function listAllPendingPatternCandidates(projectRoot, runtimePaths) {
|
|
54
|
+
async function listAllPendingPatternCandidates(projectRoot, runtimePaths, { homeDir } = {}) {
|
|
55
55
|
// v2 mode: pending pattern candidates live as derived_fact records with
|
|
56
56
|
// meta.legacyStatus === 'pending' (facade handles the conversion).
|
|
57
57
|
// Respect memoryV2.enabled — without this check loadRecords would run the
|
|
58
58
|
// lazy v1→v2 migration even when the whole v2 lane is disabled.
|
|
59
59
|
let v2Enabled = true;
|
|
60
60
|
try {
|
|
61
|
-
const { config } = await inspectRuntimeConfig(projectRoot);
|
|
61
|
+
const { config } = await inspectRuntimeConfig(projectRoot, { homeDir });
|
|
62
62
|
if (config?.memoryV2?.enabled === false) v2Enabled = false;
|
|
63
63
|
} catch {
|
|
64
64
|
// unreadable config → default enabled
|
|
65
65
|
}
|
|
66
|
-
const records = v2Enabled ? await loadRecords(projectRoot) : [];
|
|
66
|
+
const records = v2Enabled ? await loadRecords(projectRoot, { homeDir }) : [];
|
|
67
67
|
if (records.length > 0) {
|
|
68
68
|
const projectIds = new Set(records.map((r) => r.project_id).filter(Boolean));
|
|
69
69
|
const results = [];
|
|
70
70
|
for (const projectId of projectIds) {
|
|
71
|
-
const pending = await listPendingPatternCandidates(projectRoot, projectId);
|
|
71
|
+
const pending = await listPendingPatternCandidates(projectRoot, projectId, { homeDir });
|
|
72
72
|
for (const candidate of pending) {
|
|
73
73
|
results.push({ ...candidate, projectId });
|
|
74
74
|
}
|
|
@@ -87,7 +87,7 @@ async function listAllPendingPatternCandidates(projectRoot, runtimePaths) {
|
|
|
87
87
|
for (const entry of entries) {
|
|
88
88
|
if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
|
|
89
89
|
const projectId = entry.name.replace(/\.json$/, '');
|
|
90
|
-
const pending = await listPendingPatternCandidates(projectRoot, projectId);
|
|
90
|
+
const pending = await listPendingPatternCandidates(projectRoot, projectId, { homeDir });
|
|
91
91
|
for (const candidate of pending) {
|
|
92
92
|
results.push({ ...candidate, projectId });
|
|
93
93
|
}
|
|
@@ -128,7 +128,7 @@ function handleMutationResult(res, { notFoundMessage }) {
|
|
|
128
128
|
|
|
129
129
|
// ---- `ukit memory v2` ops (SPEC §11) ----
|
|
130
130
|
|
|
131
|
-
async function runMemoryV2(projectRoot, args) {
|
|
131
|
+
async function runMemoryV2(projectRoot, homeDir, args) {
|
|
132
132
|
const [opRaw, ...rest] = args;
|
|
133
133
|
const op = (opRaw ?? 'list').toLowerCase();
|
|
134
134
|
|
|
@@ -140,6 +140,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
140
140
|
type: typeFlag.value ?? undefined,
|
|
141
141
|
scope: scopeFlag.value ?? undefined,
|
|
142
142
|
status: statusFlag.value ?? undefined,
|
|
143
|
+
homeDir,
|
|
143
144
|
});
|
|
144
145
|
if (records.length === 0) {
|
|
145
146
|
console.log('[UKit] No memory v2 records found.');
|
|
@@ -157,7 +158,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
157
158
|
if (!id) {
|
|
158
159
|
throw new Error('Missing record id. Usage: ukit memory v2 show <id>');
|
|
159
160
|
}
|
|
160
|
-
const record = await getRecord(projectRoot, id);
|
|
161
|
+
const record = await getRecord(projectRoot, id, { homeDir });
|
|
161
162
|
if (!record) {
|
|
162
163
|
throw new Error(`Memory v2 record not found: ${id}`);
|
|
163
164
|
}
|
|
@@ -170,7 +171,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
170
171
|
if (!id) {
|
|
171
172
|
throw new Error('Missing record id. Usage: ukit memory v2 promote <id>');
|
|
172
173
|
}
|
|
173
|
-
const record = await getRecord(projectRoot, id);
|
|
174
|
+
const record = await getRecord(projectRoot, id, { homeDir });
|
|
174
175
|
if (!record) {
|
|
175
176
|
throw new Error(`Memory v2 record not found: ${id}`);
|
|
176
177
|
}
|
|
@@ -186,7 +187,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
186
187
|
// through mutateMemory with expectedRevision so a concurrent writer
|
|
187
188
|
// surfaces as a conflict instead of a silent clobber (FR-023).
|
|
188
189
|
const res = await mutateMemory(
|
|
189
|
-
{ kind: 'project', projectRoot },
|
|
190
|
+
{ kind: 'project', projectRoot, homeDir },
|
|
190
191
|
{
|
|
191
192
|
op: 'update',
|
|
192
193
|
payload: {
|
|
@@ -216,12 +217,12 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
216
217
|
if (!id) {
|
|
217
218
|
throw new Error('Missing record id. Usage: ukit memory v2 stale <id>');
|
|
218
219
|
}
|
|
219
|
-
const record = await getRecord(projectRoot, id);
|
|
220
|
+
const record = await getRecord(projectRoot, id, { homeDir });
|
|
220
221
|
if (!record) {
|
|
221
222
|
throw new Error(`Memory v2 record not found: ${id}`);
|
|
222
223
|
}
|
|
223
224
|
const res = await mutateMemory(
|
|
224
|
-
{ kind: 'project', projectRoot },
|
|
225
|
+
{ kind: 'project', projectRoot, homeDir },
|
|
225
226
|
{
|
|
226
227
|
op: 'update',
|
|
227
228
|
payload: { id, patch: { status: 'stale' } },
|
|
@@ -238,7 +239,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
238
239
|
|
|
239
240
|
if (op === 'migrate') {
|
|
240
241
|
const dryRun = rest.includes('--dry-run');
|
|
241
|
-
const result = await runMigration(projectRoot, { dryRun });
|
|
242
|
+
const result = await runMigration(projectRoot, { dryRun, homeDir });
|
|
242
243
|
if (dryRun) {
|
|
243
244
|
// Per-record plan: counts per action + ids only — never record bodies.
|
|
244
245
|
const counts = {};
|
|
@@ -259,7 +260,7 @@ async function runMemoryV2(projectRoot, args) {
|
|
|
259
260
|
}
|
|
260
261
|
console.log(`[UKit] Migrated ${result.migrated} record(s) to v2.`);
|
|
261
262
|
console.log(`[UKit] Backup: ${result.backupDir ?? 'none'} — marker: ${result.markerPath}`);
|
|
262
|
-
const stats = await v2Stats(projectRoot);
|
|
263
|
+
const stats = await v2Stats(projectRoot, { homeDir });
|
|
263
264
|
console.log(`[UKit] v2 store: ${stats.total} record(s) — ${JSON.stringify(stats.byType)}`);
|
|
264
265
|
return;
|
|
265
266
|
}
|
|
@@ -436,8 +437,8 @@ const MEM_REF_RE = /<!--\s*mem:(mem_[0-9a-f]{12})\s*-->/g;
|
|
|
436
437
|
// Approved promotable records: active project_rule/procedure belonging to the
|
|
437
438
|
// project, not bulk-migrated v1 leftovers (created_by 'migration'), and not
|
|
438
439
|
// still-pending candidates (meta.legacyStatus 'pending').
|
|
439
|
-
async function collectPromotableRecords(projectRoot, projectId) {
|
|
440
|
-
const records = await loadRecords(projectRoot);
|
|
440
|
+
async function collectPromotableRecords(projectRoot, projectId, { homeDir } = {}) {
|
|
441
|
+
const records = await loadRecords(projectRoot, { homeDir });
|
|
441
442
|
return records.filter((r) =>
|
|
442
443
|
(r.type === 'project_rule' || r.type === 'procedure')
|
|
443
444
|
&& r.status === 'active'
|
|
@@ -478,9 +479,9 @@ async function writeTextAtomic(filePath, text) {
|
|
|
478
479
|
await fs.rename(tmp, filePath);
|
|
479
480
|
}
|
|
480
481
|
|
|
481
|
-
async function runMemoryPromote(projectRoot, args) {
|
|
482
|
+
async function runMemoryPromote(projectRoot, homeDir, args) {
|
|
482
483
|
const dryRun = args.includes('--dry-run');
|
|
483
|
-
const projectId = (await detectProjectContext(projectRoot)).project.name;
|
|
484
|
+
const projectId = (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
484
485
|
const memoryMdPath = path.join(projectRoot, 'docs', 'MEMORY.md');
|
|
485
486
|
|
|
486
487
|
let existing = '';
|
|
@@ -491,7 +492,7 @@ async function runMemoryPromote(projectRoot, args) {
|
|
|
491
492
|
}
|
|
492
493
|
|
|
493
494
|
const outsideIds = externallyReferencedIds(existing);
|
|
494
|
-
const promotable = (await collectPromotableRecords(projectRoot, projectId))
|
|
495
|
+
const promotable = (await collectPromotableRecords(projectRoot, projectId, { homeDir }))
|
|
495
496
|
.filter((r) => !outsideIds.has(r.id));
|
|
496
497
|
|
|
497
498
|
const block = renderLearnedBlock(promotable);
|
|
@@ -567,11 +568,11 @@ function episodeText(ledger, sessionId) {
|
|
|
567
568
|
return text.slice(0, 300);
|
|
568
569
|
}
|
|
569
570
|
|
|
570
|
-
async function runMemoryEpisode(projectRoot, args) {
|
|
571
|
+
async function runMemoryEpisode(projectRoot, homeDir, args) {
|
|
571
572
|
const dryRun = args.includes('--dry-run');
|
|
572
573
|
const sessionFlag = extractFlag(args, '--session').value;
|
|
573
574
|
|
|
574
|
-
const { config } = await inspectRuntimeConfig(projectRoot);
|
|
575
|
+
const { config } = await inspectRuntimeConfig(projectRoot, { homeDir });
|
|
575
576
|
if (config?.memoryV2?.enabled === false) {
|
|
576
577
|
console.log('[UKit] episode: skipped (memoryV2 disabled)');
|
|
577
578
|
return;
|
|
@@ -592,19 +593,19 @@ async function runMemoryEpisode(projectRoot, args) {
|
|
|
592
593
|
return;
|
|
593
594
|
}
|
|
594
595
|
|
|
595
|
-
const existing = await loadRecords(projectRoot);
|
|
596
|
+
const existing = await loadRecords(projectRoot, { homeDir });
|
|
596
597
|
if (existing.some((r) => r.meta?.ledgerKey === ledgerKey)) {
|
|
597
598
|
console.log('[UKit] episode: already recorded');
|
|
598
599
|
return;
|
|
599
600
|
}
|
|
600
601
|
|
|
601
602
|
const ttlDays = Number(config?.memoryV2?.episodeTtlDays) || 90;
|
|
602
|
-
const projectId = (await detectProjectContext(projectRoot)).project.name;
|
|
603
|
+
const projectId = (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
603
604
|
// FR-023: the add goes through mutateMemory with the ledger key as the
|
|
604
605
|
// idempotency key — a hook-invoked rerun is a journal 'duplicate' even if
|
|
605
606
|
// the meta.ledgerKey fast-path above is bypassed.
|
|
606
607
|
const res = await mutateMemory(
|
|
607
|
-
{ kind: 'project', projectRoot },
|
|
608
|
+
{ kind: 'project', projectRoot, homeDir },
|
|
608
609
|
{
|
|
609
610
|
op: 'add',
|
|
610
611
|
idempotencyKey: ledgerKey,
|
|
@@ -745,7 +746,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
745
746
|
}
|
|
746
747
|
|
|
747
748
|
if (subcommand === 'v2') {
|
|
748
|
-
await runMemoryV2(projectRoot, rest);
|
|
749
|
+
await runMemoryV2(projectRoot, homeDir, rest);
|
|
749
750
|
return;
|
|
750
751
|
}
|
|
751
752
|
|
|
@@ -763,7 +764,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
763
764
|
return;
|
|
764
765
|
}
|
|
765
766
|
|
|
766
|
-
const items = await listMemoryItems(projectRoot);
|
|
767
|
+
const items = await listMemoryItems(projectRoot, { homeDir });
|
|
767
768
|
if (items.length === 0) {
|
|
768
769
|
console.log('[UKit] No memory items found.');
|
|
769
770
|
return;
|
|
@@ -783,12 +784,12 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
783
784
|
throw new Error('Missing pattern text. Usage: ukit memory propose "<text>" [--category <name>] [--project <id>]');
|
|
784
785
|
}
|
|
785
786
|
|
|
786
|
-
const projectId = projectFlag.value ?? (await detectProjectContext(projectRoot)).project.name;
|
|
787
|
+
const projectId = projectFlag.value ?? (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
787
788
|
const candidate = await proposePatternCandidate(projectRoot, projectId, {
|
|
788
789
|
text,
|
|
789
790
|
category: categoryFlag.value,
|
|
790
791
|
detectedFrom: 'cli',
|
|
791
|
-
});
|
|
792
|
+
}, { homeDir });
|
|
792
793
|
|
|
793
794
|
console.log(`[UKit] Proposed ${candidate.id} (${candidate.status}) for project ${projectId}.`);
|
|
794
795
|
console.log('[UKit] Review with `ukit memory list --pending`, then `ukit memory approve <id>`.');
|
|
@@ -809,7 +810,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
809
810
|
if (minFlag.value != null && (!Number.isFinite(minCount) || minCount < 1)) {
|
|
810
811
|
throw new Error(`Invalid --min value: ${minFlag.value}`);
|
|
811
812
|
}
|
|
812
|
-
const projectId = projectFlag.value ?? (await detectProjectContext(projectRoot)).project.name;
|
|
813
|
+
const projectId = projectFlag.value ?? (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
813
814
|
|
|
814
815
|
const { proposeFromPatterns } = await import('../../learning/patternProposals.js');
|
|
815
816
|
const result = await proposeFromPatterns(projectRoot, projectId, {
|
|
@@ -853,12 +854,13 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
853
854
|
throw new Error(`Missing candidate id. Usage: ukit memory ${subcommand} <candidateId> [--project <id>]`);
|
|
854
855
|
}
|
|
855
856
|
|
|
856
|
-
const projectId = explicitProjectId ?? (await detectProjectContext(projectRoot)).project.name;
|
|
857
|
+
const projectId = explicitProjectId ?? (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
857
858
|
const resolved = await resolvePatternCandidate(
|
|
858
859
|
projectRoot,
|
|
859
860
|
projectId,
|
|
860
861
|
candidateId,
|
|
861
862
|
subcommand === 'approve' ? 'approve' : 'reject',
|
|
863
|
+
{ homeDir },
|
|
862
864
|
);
|
|
863
865
|
|
|
864
866
|
console.log(`[UKit] ${resolved.status === 'approved' ? 'Approved' : 'Rejected'} ${candidateId}.`);
|
|
@@ -868,7 +870,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
868
870
|
if (subcommand === 'hygiene') {
|
|
869
871
|
const projectFlagIndex = rest.indexOf('--project');
|
|
870
872
|
const explicitProjectId = projectFlagIndex >= 0 ? rest[projectFlagIndex + 1] : null;
|
|
871
|
-
const projectId = explicitProjectId ?? (await detectProjectContext(projectRoot)).project.name;
|
|
873
|
+
const projectId = explicitProjectId ?? (await detectProjectContext(projectRoot, { homeDir })).project.name;
|
|
872
874
|
|
|
873
875
|
const result = await runProjectHygiene(projectRoot, projectId);
|
|
874
876
|
console.log(`[UKit] Ran hygiene for project ${projectId}.`);
|
|
@@ -890,6 +892,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
890
892
|
// silently widening to cross-repo results.
|
|
891
893
|
projectId: projectContext.project.id ?? null,
|
|
892
894
|
projectAliases: projectContext.project.aliases ?? [],
|
|
895
|
+
homeDir,
|
|
893
896
|
});
|
|
894
897
|
if (results.length === 0) {
|
|
895
898
|
console.log('[UKit] No memory matches found.');
|
|
@@ -923,6 +926,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
923
926
|
projectRoot,
|
|
924
927
|
projectId: projectContext.project.id ?? null,
|
|
925
928
|
projectAliases: projectContext.project.aliases ?? [],
|
|
929
|
+
homeDir,
|
|
926
930
|
});
|
|
927
931
|
// FR-007: the hit block carries citation/freshness/reasons — provenance
|
|
928
932
|
// surface only, never record meta. Legacy lane has no hit; its
|
|
@@ -942,7 +946,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
942
946
|
throw new Error('Missing task/query. Usage: ukit memory recall <task>');
|
|
943
947
|
}
|
|
944
948
|
|
|
945
|
-
const runtimeConfigInspection = await inspectRuntimeConfig(projectRoot);
|
|
949
|
+
const runtimeConfigInspection = await inspectRuntimeConfig(projectRoot, { homeDir });
|
|
946
950
|
if (!runtimeConfigInspection.valid) {
|
|
947
951
|
throw new Error('Runtime config is invalid. Run `ukit doctor` or rerun `ukit install`.');
|
|
948
952
|
}
|
|
@@ -959,6 +963,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
959
963
|
maxTokens: runtimeConfig.memory.maxInjectionTokens,
|
|
960
964
|
promptCache: Boolean(runtimeConfig.tokenPipeline?.promptCache),
|
|
961
965
|
inputCompression: Boolean(runtimeConfig.tokenPipeline?.inputCompression),
|
|
966
|
+
homeDir,
|
|
962
967
|
});
|
|
963
968
|
|
|
964
969
|
if (!injection) {
|
|
@@ -971,12 +976,12 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
971
976
|
}
|
|
972
977
|
|
|
973
978
|
if (subcommand === 'promote') {
|
|
974
|
-
await runMemoryPromote(projectRoot, rest);
|
|
979
|
+
await runMemoryPromote(projectRoot, homeDir, rest);
|
|
975
980
|
return;
|
|
976
981
|
}
|
|
977
982
|
|
|
978
983
|
if (subcommand === 'episode') {
|
|
979
|
-
await runMemoryEpisode(projectRoot, rest);
|
|
984
|
+
await runMemoryEpisode(projectRoot, homeDir, rest);
|
|
980
985
|
return;
|
|
981
986
|
}
|
|
982
987
|
|
|
@@ -986,7 +991,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
986
991
|
throw new Error('Missing memory id. Usage: ukit memory forget <id>');
|
|
987
992
|
}
|
|
988
993
|
|
|
989
|
-
const result = await forgetMemoryItem(projectRoot, memoryId);
|
|
994
|
+
const result = await forgetMemoryItem(projectRoot, memoryId, { homeDir });
|
|
990
995
|
if (!result.removed) {
|
|
991
996
|
throw new Error(`Memory item not found: ${memoryId}`);
|
|
992
997
|
}
|
|
@@ -1003,7 +1008,7 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
1003
1008
|
// FR-023/§9: purge removes the record, writes a tombstone, then sweeps
|
|
1004
1009
|
// prompt-cache entries that embedded the purged record's text.
|
|
1005
1010
|
const res = await mutateMemory(
|
|
1006
|
-
{ kind: 'project', projectRoot },
|
|
1011
|
+
{ kind: 'project', projectRoot, homeDir },
|
|
1007
1012
|
{ op: 'purge', payload: { id: memoryId } },
|
|
1008
1013
|
);
|
|
1009
1014
|
const handled = handleMutationResult(res, {
|
|
@@ -1016,10 +1021,10 @@ export async function runMemory({ projectRoot, homeDir = os.homedir(), argv = []
|
|
|
1016
1021
|
}
|
|
1017
1022
|
|
|
1018
1023
|
if (subcommand === 'export') {
|
|
1019
|
-
const data = await exportMemory(projectRoot);
|
|
1024
|
+
const data = await exportMemory(projectRoot, { homeDir });
|
|
1020
1025
|
// FR-023/025: defense-in-depth redaction at the CLI boundary too —
|
|
1021
1026
|
// store.js exportMemory already redacts, this catches any future lane.
|
|
1022
|
-
const { config } = await inspectRuntimeConfig(projectRoot);
|
|
1027
|
+
const { config } = await inspectRuntimeConfig(projectRoot, { homeDir });
|
|
1023
1028
|
const { payload: safe } = redactWritePayload({ meta: data }, { config });
|
|
1024
1029
|
console.log(JSON.stringify(safe.meta, null, 2));
|
|
1025
1030
|
return;
|