@membank/cli 0.14.2 → 0.16.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.mjs +331 -56
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { cancel, confirm, intro, isCancel, multiselect, note, outro } from "@clack/prompts";
|
|
3
|
-
import { ACTIVITY_EVENT_TYPE_VALUES, ActivityEventTypeSchema, DatabaseManager, EmbeddingService, GLOBAL_SCOPE_HASH, MIGRATIONS, MODEL_NAME, MemoryTypeSchema, MemoryTypeSchema as MemoryTypeSchema$1, ModelDownloader, PIN_BUDGET_THRESHOLD, QueryEngine, SessionContextBuilder, createActivityLogger, createActivityRepository, createMemoryRepository, createProjectRepository, createSynthesisRepository, deleteMemory, listEvents, resolveProject, runScopeToProjectsMigration, saveMemory } from "@membank/core";
|
|
3
|
+
import { ACTIVITY_EVENT_TYPE_VALUES, ActivityEventTypeSchema, DatabaseManager, EmbeddingService, GLOBAL_PROJECT_NAME, GLOBAL_SCOPE_HASH, MIGRATIONS, MODEL_NAME, MemoryTypeSchema, MemoryTypeSchema as MemoryTypeSchema$1, ModelDownloader, PIN_BUDGET_THRESHOLD, QueryEngine, SessionContextBuilder, createActivityLogger, createActivityRepository, createMemoryRepository, createProjectRepository, createSynthesisRepository, deleteMemory, listEvents, resolveProject, revertMemory, revertSynthesis, runScopeToProjectsMigration, saveMemory } from "@membank/core";
|
|
4
4
|
import { runExtraction, runSynthesis, startServer } from "@membank/mcp";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { Command } from "commander";
|
|
@@ -9,6 +9,7 @@ import { z } from "zod";
|
|
|
9
9
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { homedir, tmpdir } from "node:os";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
|
+
import { diffLines } from "@membank/core/client";
|
|
12
13
|
import Table from "cli-table3";
|
|
13
14
|
import { execFile } from "node:child_process";
|
|
14
15
|
import { promisify } from "node:util";
|
|
@@ -43,8 +44,8 @@ async function activityCommand(options, formatter) {
|
|
|
43
44
|
}
|
|
44
45
|
const events = listEvents({
|
|
45
46
|
scope,
|
|
46
|
-
type: validatedType,
|
|
47
|
-
since: options.since,
|
|
47
|
+
...validatedType !== void 0 && { type: validatedType },
|
|
48
|
+
...options.since !== void 0 && { since: options.since },
|
|
48
49
|
limit: options.limit !== void 0 ? parseInt(options.limit, 10) : 50
|
|
49
50
|
}, activityRepo);
|
|
50
51
|
if (formatter.isJson) {
|
|
@@ -380,11 +381,16 @@ async function importCommand(filePath, db, formatter, prompt) {
|
|
|
380
381
|
}
|
|
381
382
|
//#endregion
|
|
382
383
|
//#region src/commands/inject.ts
|
|
383
|
-
const
|
|
384
|
+
const SAVE_GUIDANCE = "Save (call save_memory) when: (1) user states a preference or makes a decision; (2) user corrects you; (3) you discover a working fix after a tool error; (4) you learn a non-obvious project fact. Type ∈ correction|preference|decision|learning|fact. When unsure, save.";
|
|
385
|
+
const QUERY_GUIDANCE = "Query (call query_memory) before: answering anything that touches prior decisions, and before exploration tasks (file reads, searches, web lookups) where past corrections or preferences may apply. Skip when clearly irrelevant (e.g. trivial arithmetic). Soft guideline, not a hard rule.";
|
|
386
|
+
const MEMORY_GUIDANCE = [SAVE_GUIDANCE, QUERY_GUIDANCE].join("\n");
|
|
387
|
+
function buildGuidance(harness) {
|
|
388
|
+
return harness === "claude-code" ? QUERY_GUIDANCE : MEMORY_GUIDANCE;
|
|
389
|
+
}
|
|
384
390
|
function xmlEscape(s) {
|
|
385
391
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
386
392
|
}
|
|
387
|
-
function formatContext(ctx) {
|
|
393
|
+
function formatContext(ctx, guidance) {
|
|
388
394
|
const parts = [];
|
|
389
395
|
const statParts = Object.entries(ctx.stats).filter(([, count]) => count > 0).map(([type, count]) => `${count} ${type}${count !== 1 ? "s" : ""}`);
|
|
390
396
|
if (statParts.length > 0) parts.push(`<memory-stats>\n${statParts.join(", ")}\n</memory-stats>`);
|
|
@@ -396,7 +402,7 @@ function formatContext(ctx) {
|
|
|
396
402
|
parts.push(`<pinned-memories>\n${memLines.join("\n")}\n</pinned-memories>`);
|
|
397
403
|
}
|
|
398
404
|
}
|
|
399
|
-
parts.push(`<memory-guidance>\n${
|
|
405
|
+
parts.push(`<memory-guidance>\n${guidance}\n</memory-guidance>`);
|
|
400
406
|
return parts.join("\n");
|
|
401
407
|
}
|
|
402
408
|
function outputAdditionalContext(text, harness, eventName) {
|
|
@@ -416,7 +422,7 @@ function outputAdditionalContext(text, harness, eventName) {
|
|
|
416
422
|
function pickBestSynthesis(globalSynthesis, projectSynthesis) {
|
|
417
423
|
return projectSynthesis ?? globalSynthesis;
|
|
418
424
|
}
|
|
419
|
-
async function buildText() {
|
|
425
|
+
async function buildText(harness) {
|
|
420
426
|
const resolved = await resolveProject();
|
|
421
427
|
const db = DatabaseManager.open();
|
|
422
428
|
try {
|
|
@@ -425,13 +431,13 @@ async function buildText() {
|
|
|
425
431
|
const globalRow = synthRepo.getSynthesis(GLOBAL_SCOPE_HASH);
|
|
426
432
|
const projectRow = synthRepo.getSynthesis(resolved.hash);
|
|
427
433
|
const synthesis = pickBestSynthesis(globalRow?.inFlightSince === null ? globalRow.content : void 0, projectRow?.inFlightSince === null ? projectRow.content : void 0);
|
|
428
|
-
return formatContext(builder.getSessionContext(resolved.hash, synthesis));
|
|
434
|
+
return formatContext(builder.getSessionContext(resolved.hash, synthesis), buildGuidance(harness));
|
|
429
435
|
} finally {
|
|
430
436
|
db.close();
|
|
431
437
|
}
|
|
432
438
|
}
|
|
433
439
|
async function handleEvent(harness, eventName) {
|
|
434
|
-
const text = await buildText().catch((err) => {
|
|
440
|
+
const text = await buildText(harness).catch((err) => {
|
|
435
441
|
const msg = err instanceof Error ? err.message : String(err);
|
|
436
442
|
process.stderr.write(`membank inject: ${msg}\n`);
|
|
437
443
|
return null;
|
|
@@ -447,6 +453,7 @@ async function injectCommand(opts) {
|
|
|
447
453
|
return;
|
|
448
454
|
}
|
|
449
455
|
if (opts.event === "user-prompt-submit") {
|
|
456
|
+
if (harness === "claude-code") process.exit(0);
|
|
450
457
|
await handleEvent(harness, "UserPromptSubmit");
|
|
451
458
|
return;
|
|
452
459
|
}
|
|
@@ -458,8 +465,8 @@ async function listCommand(options, formatter) {
|
|
|
458
465
|
const db = DatabaseManager.open();
|
|
459
466
|
try {
|
|
460
467
|
const memories = createMemoryRepository(db, createProjectRepository(db)).list({
|
|
461
|
-
|
|
462
|
-
pinned: options.pinned
|
|
468
|
+
...options.type !== void 0 && { type: MemoryTypeSchema$1.parse(options.type) },
|
|
469
|
+
...options.pinned !== void 0 && { pinned: options.pinned }
|
|
463
470
|
});
|
|
464
471
|
formatter.outputMemories(memories);
|
|
465
472
|
} finally {
|
|
@@ -467,6 +474,102 @@ async function listCommand(options, formatter) {
|
|
|
467
474
|
}
|
|
468
475
|
}
|
|
469
476
|
//#endregion
|
|
477
|
+
//#region src/commands/memory/diff.ts
|
|
478
|
+
function memoryDiffCommand(id, v1, v2, db, formatter) {
|
|
479
|
+
const repo = createMemoryRepository(db, createProjectRepository(db));
|
|
480
|
+
const version1 = repo.getVersion(id, v1);
|
|
481
|
+
if (version1 === void 0) {
|
|
482
|
+
formatter.error(`Version ${v1} not found for memory: ${id}`);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
const version2 = repo.getVersion(id, v2);
|
|
486
|
+
if (version2 === void 0) {
|
|
487
|
+
formatter.error(`Version ${v2} not found for memory: ${id}`);
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
const diff = diffLines(version1.content, version2.content);
|
|
491
|
+
if (formatter.isJson) {
|
|
492
|
+
process.stdout.write(`${JSON.stringify(diff)}\n`);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
process.stdout.write(`--- version ${v1}\n+++ version ${v2}\n`);
|
|
496
|
+
for (const entry of diff) if (entry.kind === "added") process.stdout.write(chalk.green(`+ ${entry.line}\n`));
|
|
497
|
+
else if (entry.kind === "removed") process.stdout.write(chalk.red(`- ${entry.line}\n`));
|
|
498
|
+
else process.stdout.write(` ${entry.line}\n`);
|
|
499
|
+
}
|
|
500
|
+
//#endregion
|
|
501
|
+
//#region src/commands/memory/history.ts
|
|
502
|
+
function truncate$2(str, max) {
|
|
503
|
+
return str.length > max ? `${str.slice(0, max - 1)}…` : str;
|
|
504
|
+
}
|
|
505
|
+
function memoryHistoryCommand(id, db, formatter) {
|
|
506
|
+
const versions = createMemoryRepository(db, createProjectRepository(db)).listVersions(id);
|
|
507
|
+
if (versions.length === 0) {
|
|
508
|
+
formatter.error(`No version history found for memory: ${id}`);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
if (formatter.isJson) {
|
|
512
|
+
process.stdout.write(`${JSON.stringify(versions)}\n`);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const table = new Table({
|
|
516
|
+
head: [
|
|
517
|
+
chalk.bold("version"),
|
|
518
|
+
chalk.bold("created_at"),
|
|
519
|
+
chalk.bold("preview")
|
|
520
|
+
],
|
|
521
|
+
style: { head: [] }
|
|
522
|
+
});
|
|
523
|
+
for (const v of versions) table.push([
|
|
524
|
+
String(v.version),
|
|
525
|
+
v.createdAt,
|
|
526
|
+
truncate$2(v.content.replace(/\n/g, " "), 60)
|
|
527
|
+
]);
|
|
528
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/commands/memory/revert.ts
|
|
532
|
+
async function memoryRevertCommand(id, version, db, formatter, prompt) {
|
|
533
|
+
const repo = createMemoryRepository(db, createProjectRepository(db));
|
|
534
|
+
if (repo.findById(id) === void 0) {
|
|
535
|
+
formatter.error(`Memory not found: ${id}`);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
if (repo.getVersion(id, version) === void 0) {
|
|
539
|
+
formatter.error(`Version ${version} not found for memory: ${id}`);
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
if (!await prompt.confirm(`Revert memory ${id} to version ${version}?`)) return;
|
|
543
|
+
await revertMemory(id, version, {
|
|
544
|
+
repo,
|
|
545
|
+
embedder: new EmbeddingService(),
|
|
546
|
+
activityLogger: createActivityLogger(db)
|
|
547
|
+
});
|
|
548
|
+
process.stdout.write(`${chalk.green("✓")} Reverted memory ${chalk.dim(id)} to version ${version}\n`);
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/commands/memory/show.ts
|
|
552
|
+
function memoryShowCommand(id, db, formatter, opts) {
|
|
553
|
+
const repo = createMemoryRepository(db, createProjectRepository(db));
|
|
554
|
+
if (opts.version !== void 0) {
|
|
555
|
+
const v = repo.getVersion(id, opts.version);
|
|
556
|
+
if (v === void 0) {
|
|
557
|
+
formatter.error(`Version ${opts.version} not found for memory: ${id}`);
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
if (formatter.isJson) process.stdout.write(`${JSON.stringify(v)}\n`);
|
|
561
|
+
else process.stdout.write(`${v.content}\n`);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const memory = repo.findById(id);
|
|
565
|
+
if (memory === void 0) {
|
|
566
|
+
formatter.error(`Memory not found: ${id}`);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
if (formatter.isJson) process.stdout.write(`${JSON.stringify(memory)}\n`);
|
|
570
|
+
else process.stdout.write(`${memory.content}\n`);
|
|
571
|
+
}
|
|
572
|
+
//#endregion
|
|
470
573
|
//#region src/commands/migrate.ts
|
|
471
574
|
async function migrateCommand(mode, name, formatter) {
|
|
472
575
|
if (mode === "list") {
|
|
@@ -568,9 +671,85 @@ async function statsCommand(formatter) {
|
|
|
568
671
|
}
|
|
569
672
|
}
|
|
570
673
|
//#endregion
|
|
571
|
-
//#region src/commands/synthesize.ts
|
|
674
|
+
//#region src/commands/synthesize/resolve-scope.ts
|
|
675
|
+
function resolveScope(scope, db) {
|
|
676
|
+
if (scope === GLOBAL_PROJECT_NAME) return GLOBAL_SCOPE_HASH;
|
|
677
|
+
if (/^[0-9a-f]{16}$/.test(scope)) return scope;
|
|
678
|
+
const project = createProjectRepository(db).getByName(scope);
|
|
679
|
+
return project !== void 0 ? project.scopeHash : scope;
|
|
680
|
+
}
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/commands/synthesize/diff.ts
|
|
683
|
+
function synthesizeDiffCommand(v1, v2, opts, formatter) {
|
|
684
|
+
const db = DatabaseManager.open();
|
|
685
|
+
try {
|
|
686
|
+
const scope = opts.scope ?? GLOBAL_PROJECT_NAME;
|
|
687
|
+
const resolvedScope = resolveScope(scope, db);
|
|
688
|
+
const repo = createSynthesisRepository(db);
|
|
689
|
+
const version1 = repo.getVersion(resolvedScope, v1);
|
|
690
|
+
if (version1 === void 0) {
|
|
691
|
+
formatter.error(`Version ${v1} not found for scope: ${scope}`);
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
const version2 = repo.getVersion(resolvedScope, v2);
|
|
695
|
+
if (version2 === void 0) {
|
|
696
|
+
formatter.error(`Version ${v2} not found for scope: ${scope}`);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
const diff = diffLines(version1.content, version2.content);
|
|
700
|
+
if (formatter.isJson) {
|
|
701
|
+
process.stdout.write(`${JSON.stringify(diff)}\n`);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
process.stdout.write(`--- version ${v1}\n+++ version ${v2}\n`);
|
|
705
|
+
for (const entry of diff) if (entry.kind === "added") process.stdout.write(chalk.green(`+ ${entry.line}\n`));
|
|
706
|
+
else if (entry.kind === "removed") process.stdout.write(chalk.red(`- ${entry.line}\n`));
|
|
707
|
+
else process.stdout.write(` ${entry.line}\n`);
|
|
708
|
+
} finally {
|
|
709
|
+
db.close();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/commands/synthesize/history.ts
|
|
714
|
+
function truncate$1(str, max) {
|
|
715
|
+
return str.length > max ? `${str.slice(0, max - 1)}…` : str;
|
|
716
|
+
}
|
|
717
|
+
function synthesizeHistoryCommand(opts, formatter) {
|
|
718
|
+
const db = DatabaseManager.open();
|
|
719
|
+
try {
|
|
720
|
+
const scope = opts.scope ?? GLOBAL_PROJECT_NAME;
|
|
721
|
+
const resolvedScope = resolveScope(scope, db);
|
|
722
|
+
const versions = createSynthesisRepository(db).listVersions(resolvedScope);
|
|
723
|
+
if (versions.length === 0) {
|
|
724
|
+
formatter.error(`No version history found for scope: ${scope}`);
|
|
725
|
+
process.exit(1);
|
|
726
|
+
}
|
|
727
|
+
if (formatter.isJson) {
|
|
728
|
+
process.stdout.write(`${JSON.stringify(versions)}\n`);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const table = new Table({
|
|
732
|
+
head: [
|
|
733
|
+
chalk.bold("version"),
|
|
734
|
+
chalk.bold("synthesized_at"),
|
|
735
|
+
chalk.bold("preview")
|
|
736
|
+
],
|
|
737
|
+
style: { head: [] }
|
|
738
|
+
});
|
|
739
|
+
for (const v of versions) table.push([
|
|
740
|
+
String(v.version),
|
|
741
|
+
new Date(v.synthesizedAt).toLocaleString(),
|
|
742
|
+
truncate$1(v.content.replace(/\n/g, " "), 60)
|
|
743
|
+
]);
|
|
744
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
745
|
+
} finally {
|
|
746
|
+
db.close();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
//#endregion
|
|
750
|
+
//#region src/commands/synthesize/index.ts
|
|
572
751
|
async function synthesizeRunCommand(opts, formatter) {
|
|
573
|
-
const scope = opts.scope ??
|
|
752
|
+
const scope = opts.scope ?? GLOBAL_PROJECT_NAME;
|
|
574
753
|
if (!formatter.isJson) process.stdout.write(`Running synthesis for scope: ${scope}\n`);
|
|
575
754
|
const content = await runSynthesis(scope);
|
|
576
755
|
if (formatter.isJson) process.stdout.write(`${JSON.stringify({
|
|
@@ -582,14 +761,26 @@ async function synthesizeRunCommand(opts, formatter) {
|
|
|
582
761
|
function synthesizeShowCommand(opts, formatter) {
|
|
583
762
|
const db = DatabaseManager.open();
|
|
584
763
|
try {
|
|
585
|
-
const scope = opts.scope ??
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
764
|
+
const scope = opts.scope ?? GLOBAL_PROJECT_NAME;
|
|
765
|
+
const resolvedScope = resolveScope(scope, db);
|
|
766
|
+
const repo = createSynthesisRepository(db);
|
|
767
|
+
if (opts.version !== void 0) {
|
|
768
|
+
const version = repo.getVersion(resolvedScope, opts.version);
|
|
769
|
+
if (version === void 0) {
|
|
770
|
+
if (formatter.isJson) process.stdout.write(`${JSON.stringify(null)}\n`);
|
|
771
|
+
else process.stdout.write(`Version ${opts.version} not found for scope: ${scope}\n`);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (formatter.isJson) process.stdout.write(`${JSON.stringify(version)}\n`);
|
|
775
|
+
else {
|
|
776
|
+
process.stdout.write(`\nScope: ${resolvedScope} (version ${version.version})\n`);
|
|
777
|
+
process.stdout.write(`Synthesized: ${new Date(version.synthesizedAt).toLocaleString()}\n`);
|
|
778
|
+
process.stdout.write(`Archived: ${new Date(version.createdAt).toLocaleString()}\n`);
|
|
779
|
+
process.stdout.write(`\n${version.content}\n\n`);
|
|
780
|
+
}
|
|
781
|
+
return;
|
|
591
782
|
}
|
|
592
|
-
const synthesis =
|
|
783
|
+
const synthesis = repo.getSynthesis(resolvedScope);
|
|
593
784
|
if (synthesis === void 0) {
|
|
594
785
|
if (formatter.isJson) process.stdout.write(`${JSON.stringify(null)}\n`);
|
|
595
786
|
else process.stdout.write(`No synthesis found for scope: ${scope}\n`);
|
|
@@ -622,7 +813,7 @@ function synthesizeStatusCommand(formatter) {
|
|
|
622
813
|
}
|
|
623
814
|
process.stdout.write("\n");
|
|
624
815
|
for (const s of syntheses) {
|
|
625
|
-
const displayScope = (s.scope !== GLOBAL_SCOPE_HASH ? projectRepo.getByHash(s.scope) : void 0)?.name ?? (s.scope === GLOBAL_SCOPE_HASH ?
|
|
816
|
+
const displayScope = (s.scope !== GLOBAL_SCOPE_HASH ? projectRepo.getByHash(s.scope) : void 0)?.name ?? (s.scope === GLOBAL_SCOPE_HASH ? GLOBAL_PROJECT_NAME : s.scope);
|
|
626
817
|
const inFlight = s.inFlightSince !== null ? " [in-flight]" : "";
|
|
627
818
|
const synthesized = new Date(s.synthesizedAt).toLocaleString();
|
|
628
819
|
const expires = new Date(s.expiresAt).toLocaleString();
|
|
@@ -636,6 +827,25 @@ function synthesizeStatusCommand(formatter) {
|
|
|
636
827
|
}
|
|
637
828
|
}
|
|
638
829
|
//#endregion
|
|
830
|
+
//#region src/commands/synthesize/revert.ts
|
|
831
|
+
async function synthesizeRevertCommand(version, opts, formatter, prompt) {
|
|
832
|
+
const db = DatabaseManager.open();
|
|
833
|
+
try {
|
|
834
|
+
const scope = opts.scope ?? GLOBAL_PROJECT_NAME;
|
|
835
|
+
const resolvedScope = resolveScope(scope, db);
|
|
836
|
+
const repo = createSynthesisRepository(db);
|
|
837
|
+
if (repo.getVersion(resolvedScope, version) === void 0) {
|
|
838
|
+
formatter.error(`Version ${version} not found for scope: ${scope}`);
|
|
839
|
+
process.exit(1);
|
|
840
|
+
}
|
|
841
|
+
if (!await prompt.confirm(`Revert synthesis for scope "${scope}" to version ${version}?`)) return;
|
|
842
|
+
revertSynthesis(resolvedScope, version, repo);
|
|
843
|
+
process.stdout.write(`${chalk.green("✓")} Reverted synthesis for scope ${chalk.dim(scope)} to version ${version}\n`);
|
|
844
|
+
} finally {
|
|
845
|
+
db.close();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
//#endregion
|
|
639
849
|
//#region src/commands/unpin.ts
|
|
640
850
|
function unpinCommand(id, db) {
|
|
641
851
|
const ownDb = db === void 0;
|
|
@@ -1133,28 +1343,19 @@ const writers = {
|
|
|
1133
1343
|
const cfg = readJson(cfgPath);
|
|
1134
1344
|
const hooks = MaybeJsonObjectSchema.parse(cfg.hooks) ?? {};
|
|
1135
1345
|
const sessionStartInner = (Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []).flatMap(getHooksArray);
|
|
1136
|
-
const userPromptSubmitInner = (Array.isArray(hooks.UserPromptSubmit) ? hooks.UserPromptSubmit : []).flatMap(getHooksArray);
|
|
1137
1346
|
const sessionEndInner = (Array.isArray(hooks.SessionEnd) ? hooks.SessionEnd : []).flatMap(getHooksArray);
|
|
1138
1347
|
return {
|
|
1139
1348
|
status: "ready",
|
|
1140
1349
|
configPath: cfgPath,
|
|
1141
|
-
hooks: [
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
existingCommand: extractInjectCommand(userPromptSubmitInner) || null
|
|
1151
|
-
},
|
|
1152
|
-
{
|
|
1153
|
-
event: "SessionEnd",
|
|
1154
|
-
command: "npx -y @membank/cli extract --harness claude-code",
|
|
1155
|
-
existingCommand: extractInjectCommand(sessionEndInner) || null
|
|
1156
|
-
}
|
|
1157
|
-
]
|
|
1350
|
+
hooks: [{
|
|
1351
|
+
event: "SessionStart",
|
|
1352
|
+
command: "npx -y @membank/cli inject --harness claude-code",
|
|
1353
|
+
existingCommand: extractInjectCommand(sessionStartInner) || null
|
|
1354
|
+
}, {
|
|
1355
|
+
event: "SessionEnd",
|
|
1356
|
+
command: "npx -y @membank/cli extract --harness claude-code",
|
|
1357
|
+
existingCommand: extractInjectCommand(sessionEndInner) || null
|
|
1358
|
+
}]
|
|
1158
1359
|
};
|
|
1159
1360
|
},
|
|
1160
1361
|
write(resolver, events) {
|
|
@@ -1164,6 +1365,7 @@ const writers = {
|
|
|
1164
1365
|
const newHooks = { ...hooks };
|
|
1165
1366
|
pruneNestedEvent(newHooks, "PostToolUseFailure");
|
|
1166
1367
|
pruneNestedEvent(newHooks, "Stop");
|
|
1368
|
+
pruneNestedEvent(newHooks, "UserPromptSubmit");
|
|
1167
1369
|
if (events.includes("SessionStart")) newHooks.SessionStart = [...filterOutMembank(Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []), {
|
|
1168
1370
|
matcher: "",
|
|
1169
1371
|
hooks: [{
|
|
@@ -1171,13 +1373,6 @@ const writers = {
|
|
|
1171
1373
|
command: "npx -y @membank/cli inject --harness claude-code"
|
|
1172
1374
|
}]
|
|
1173
1375
|
}];
|
|
1174
|
-
if (events.includes("UserPromptSubmit")) newHooks.UserPromptSubmit = [...filterOutMembank(Array.isArray(hooks.UserPromptSubmit) ? hooks.UserPromptSubmit : []), {
|
|
1175
|
-
matcher: "",
|
|
1176
|
-
hooks: [{
|
|
1177
|
-
type: "command",
|
|
1178
|
-
command: "npx -y @membank/cli inject --harness claude-code --event user-prompt-submit"
|
|
1179
|
-
}]
|
|
1180
|
-
}];
|
|
1181
1376
|
if (events.includes("SessionEnd")) newHooks.SessionEnd = [...filterOutMembank(Array.isArray(hooks.SessionEnd) ? hooks.SessionEnd : []), {
|
|
1182
1377
|
matcher: "clear|resume|logout|prompt_input_exit|other",
|
|
1183
1378
|
hooks: [{
|
|
@@ -1750,9 +1945,9 @@ program.command("inject").description("output session context for harness inject
|
|
|
1750
1945
|
program.command("extract").description("(internal) run session-end memory extraction; reads the harness's SessionEnd hook payload from stdin").option("--harness <name>", "harness whose session-end hook payload is on stdin (only claude-code is supported today)", "claude-code").option("--session <id>", "session id (otherwise read from stdin)").option("--transcript <path>", "transcript JSONL path (otherwise read from stdin)").action(async (cmdOptions) => {
|
|
1751
1946
|
try {
|
|
1752
1947
|
await extractCommand({
|
|
1753
|
-
harness: cmdOptions.harness,
|
|
1754
|
-
sessionId: cmdOptions.session,
|
|
1755
|
-
transcript: cmdOptions.transcript
|
|
1948
|
+
...cmdOptions.harness !== void 0 && { harness: cmdOptions.harness },
|
|
1949
|
+
...cmdOptions.session !== void 0 && { sessionId: cmdOptions.session },
|
|
1950
|
+
...cmdOptions.transcript !== void 0 && { transcript: cmdOptions.transcript }
|
|
1756
1951
|
});
|
|
1757
1952
|
} catch (err) {
|
|
1758
1953
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -1834,16 +2029,16 @@ setupCmd.action(async (cmdOptions) => {
|
|
|
1834
2029
|
writer,
|
|
1835
2030
|
hookWriter,
|
|
1836
2031
|
prompter: (question) => promptHelper.confirm(question),
|
|
1837
|
-
harnessSelector,
|
|
2032
|
+
...harnessSelector !== void 0 && { harnessSelector },
|
|
1838
2033
|
modelDownloader: new ModelDownloader(),
|
|
1839
|
-
|
|
2034
|
+
...!formatter.isJson && { out: decoratedOut },
|
|
1840
2035
|
synthesisOptIn: true
|
|
1841
2036
|
});
|
|
1842
2037
|
try {
|
|
1843
2038
|
const results = await orchestrator.run({
|
|
1844
2039
|
yes: autoYes,
|
|
1845
|
-
dryRun: cmdOptions.dryRun,
|
|
1846
|
-
harness: cmdOptions.harness,
|
|
2040
|
+
...cmdOptions.dryRun !== void 0 && { dryRun: cmdOptions.dryRun },
|
|
2041
|
+
...cmdOptions.harness !== void 0 && { harness: cmdOptions.harness },
|
|
1847
2042
|
json: formatter.isJson
|
|
1848
2043
|
});
|
|
1849
2044
|
if (!formatter.isJson && !cmdOptions.dryRun && results.length > 0) {
|
|
@@ -1909,11 +2104,14 @@ synthesizeCmd.command("run").description("trigger a synthesis run for a scope").
|
|
|
1909
2104
|
process.exit(2);
|
|
1910
2105
|
}
|
|
1911
2106
|
});
|
|
1912
|
-
synthesizeCmd.command("show").description("display current synthesis for a scope").option("--scope <scope>", "scope to show (default: global)").action((cmdOptions) => {
|
|
2107
|
+
synthesizeCmd.command("show").description("display current synthesis for a scope, or a specific archived version").option("--scope <scope>", "scope to show (default: global)").option("--version <n>", "show this archived version number instead of the active synthesis").action((cmdOptions) => {
|
|
1913
2108
|
const globalOpts = program.opts();
|
|
1914
2109
|
const formatter = Formatter.create(globalOpts.json === true);
|
|
1915
2110
|
try {
|
|
1916
|
-
synthesizeShowCommand(
|
|
2111
|
+
synthesizeShowCommand({
|
|
2112
|
+
...cmdOptions.scope !== void 0 && { scope: cmdOptions.scope },
|
|
2113
|
+
...cmdOptions.version !== void 0 && { version: Number(cmdOptions.version) }
|
|
2114
|
+
}, formatter);
|
|
1917
2115
|
} catch (err) {
|
|
1918
2116
|
formatter.error(err instanceof Error ? err.message : String(err));
|
|
1919
2117
|
process.exit(2);
|
|
@@ -1929,13 +2127,90 @@ synthesizeCmd.command("status").description("show all scopes with synthesis stat
|
|
|
1929
2127
|
process.exit(2);
|
|
1930
2128
|
}
|
|
1931
2129
|
});
|
|
2130
|
+
synthesizeCmd.command("history").description("list archived synthesis versions for a scope").option("--scope <scope>", "scope to list history for (default: global)").action((cmdOptions) => {
|
|
2131
|
+
const globalOpts = program.opts();
|
|
2132
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2133
|
+
try {
|
|
2134
|
+
synthesizeHistoryCommand(cmdOptions, formatter);
|
|
2135
|
+
} catch (err) {
|
|
2136
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2137
|
+
process.exit(2);
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
synthesizeCmd.command("diff <v1> <v2>").description("show a line diff between two archived synthesis versions").option("--scope <scope>", "scope to diff (default: global)").action((v1, v2, cmdOptions) => {
|
|
2141
|
+
const globalOpts = program.opts();
|
|
2142
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2143
|
+
try {
|
|
2144
|
+
synthesizeDiffCommand(Number(v1), Number(v2), cmdOptions, formatter);
|
|
2145
|
+
} catch (err) {
|
|
2146
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2147
|
+
process.exit(2);
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
synthesizeCmd.command("revert <version>").description("revert the active synthesis to a previous archived version (records a new version)").option("--scope <scope>", "scope to revert (default: global)").action(async (version, cmdOptions) => {
|
|
2151
|
+
const globalOpts = program.opts();
|
|
2152
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2153
|
+
const prompt = new PromptHelper(globalOpts.yes === true);
|
|
2154
|
+
try {
|
|
2155
|
+
await synthesizeRevertCommand(Number(version), cmdOptions, formatter, prompt);
|
|
2156
|
+
} catch (err) {
|
|
2157
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2158
|
+
process.exit(2);
|
|
2159
|
+
}
|
|
2160
|
+
});
|
|
2161
|
+
const memoryCmd = program.command("memory").description("view and manage memory version history");
|
|
2162
|
+
memoryCmd.command("history <id>").description("list version history for a memory").action((id) => {
|
|
2163
|
+
const globalOpts = program.opts();
|
|
2164
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2165
|
+
const db = DatabaseManager.open();
|
|
2166
|
+
try {
|
|
2167
|
+
memoryHistoryCommand(id, db, formatter);
|
|
2168
|
+
} catch (err) {
|
|
2169
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2170
|
+
process.exit(2);
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
memoryCmd.command("show <id>").description("show content of a memory, optionally at a specific version").option("--version <n>", "show this version number instead of current content").action((id, cmdOptions) => {
|
|
2174
|
+
const globalOpts = program.opts();
|
|
2175
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2176
|
+
const db = DatabaseManager.open();
|
|
2177
|
+
try {
|
|
2178
|
+
memoryShowCommand(id, db, formatter, { ...cmdOptions.version !== void 0 && { version: Number(cmdOptions.version) } });
|
|
2179
|
+
} catch (err) {
|
|
2180
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2181
|
+
process.exit(2);
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
memoryCmd.command("diff <id> <v1> <v2>").description("show a line diff between two versions of a memory").action((id, v1, v2) => {
|
|
2185
|
+
const globalOpts = program.opts();
|
|
2186
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2187
|
+
const db = DatabaseManager.open();
|
|
2188
|
+
try {
|
|
2189
|
+
memoryDiffCommand(id, Number(v1), Number(v2), db, formatter);
|
|
2190
|
+
} catch (err) {
|
|
2191
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2192
|
+
process.exit(2);
|
|
2193
|
+
}
|
|
2194
|
+
});
|
|
2195
|
+
memoryCmd.command("revert <id> <version>").description("revert a memory to a previous version (records the revert as a new version)").action(async (id, version) => {
|
|
2196
|
+
const globalOpts = program.opts();
|
|
2197
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
2198
|
+
const db = DatabaseManager.open();
|
|
2199
|
+
const prompt = new PromptHelper(globalOpts.yes === true);
|
|
2200
|
+
try {
|
|
2201
|
+
await memoryRevertCommand(id, Number(version), db, formatter, prompt);
|
|
2202
|
+
} catch (err) {
|
|
2203
|
+
formatter.error(err instanceof Error ? err.message : String(err));
|
|
2204
|
+
process.exit(2);
|
|
2205
|
+
}
|
|
2206
|
+
});
|
|
1932
2207
|
program.command("activity").description("list activity events for the current project (or --global for global memories)").option("--type <event_type>", "filter by event type (memory.created|updated|deleted|flagged|queried)").option("--since <date>", "return events after this ISO date/time").option("--memory-id <id>", "filter by memory id").option("--limit <n>", "maximum number of results (default 50)").option("--global", "show activity for global (sentinel) project").option("--scope <hash>", "show activity for a specific scope hash (advanced)").action(async (cmdOptions) => {
|
|
1933
2208
|
const globalOpts = program.opts();
|
|
1934
2209
|
const formatter = Formatter.create(globalOpts.json === true);
|
|
1935
2210
|
try {
|
|
1936
2211
|
await activityCommand({
|
|
1937
2212
|
...cmdOptions,
|
|
1938
|
-
json: globalOpts.json
|
|
2213
|
+
...globalOpts.json !== void 0 && { json: globalOpts.json }
|
|
1939
2214
|
}, formatter);
|
|
1940
2215
|
} catch (err) {
|
|
1941
2216
|
formatter.error(err instanceof Error ? err.message : String(err));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@membank/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"commander": "^14.0.3",
|
|
22
22
|
"ora": "^9.4.0",
|
|
23
23
|
"zod": "^4.4.3",
|
|
24
|
-
"@membank/
|
|
25
|
-
"@membank/
|
|
24
|
+
"@membank/core": "0.14.0",
|
|
25
|
+
"@membank/mcp": "0.16.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^25.6.0",
|