@membank/cli 0.0.3 → 0.0.4
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 +242 -95
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DatabaseManager, EmbeddingService, MemoryRepository, QueryEngine } from "@membank/core";
|
|
2
|
+
import { DatabaseManager, EmbeddingService, MEMORY_TYPE_VALUES, MemoryRepository, QueryEngine, SessionContextBuilder, resolveScope } from "@membank/core";
|
|
3
3
|
import { startServer } from "@membank/mcp";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -72,13 +72,7 @@ function exportCommand(db, formatter, opts) {
|
|
|
72
72
|
}
|
|
73
73
|
//#endregion
|
|
74
74
|
//#region src/commands/import.ts
|
|
75
|
-
const MEMORY_TYPES = new Set(
|
|
76
|
-
"correction",
|
|
77
|
-
"preference",
|
|
78
|
-
"decision",
|
|
79
|
-
"learning",
|
|
80
|
-
"fact"
|
|
81
|
-
]);
|
|
75
|
+
const MEMORY_TYPES = new Set(MEMORY_TYPE_VALUES);
|
|
82
76
|
function isValidRecord(r) {
|
|
83
77
|
if (typeof r !== "object" || r === null) return false;
|
|
84
78
|
const rec = r;
|
|
@@ -133,6 +127,41 @@ async function importCommand(filePath, db, formatter, prompt) {
|
|
|
133
127
|
else process.stdout.write(`Imported ${count} memories.\n`);
|
|
134
128
|
}
|
|
135
129
|
//#endregion
|
|
130
|
+
//#region src/commands/inject.ts
|
|
131
|
+
function formatContext(ctx) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
const statParts = Object.entries(ctx.stats).filter(([, count]) => count > 0).map(([type, count]) => `${count} ${type}${count !== 1 ? "s" : ""}`);
|
|
134
|
+
if (statParts.length > 0) lines.push(`[Memory Stats]: ${statParts.join(", ")}`);
|
|
135
|
+
const formatMemory = (m) => `"${m.content}" (${m.type})`;
|
|
136
|
+
for (const m of ctx.pinnedGlobal) lines.push(`[Pinned Global]: ${formatMemory(m)}`);
|
|
137
|
+
for (const m of ctx.pinnedProject) lines.push(`[Pinned Project]: ${formatMemory(m)}`);
|
|
138
|
+
return lines.join("\n");
|
|
139
|
+
}
|
|
140
|
+
async function injectCommand(opts) {
|
|
141
|
+
const projectScope = opts.scope ?? await resolveScope();
|
|
142
|
+
const db = DatabaseManager.open();
|
|
143
|
+
let text;
|
|
144
|
+
try {
|
|
145
|
+
text = formatContext(new SessionContextBuilder(db).getSessionContext(projectScope));
|
|
146
|
+
} finally {
|
|
147
|
+
db.close();
|
|
148
|
+
}
|
|
149
|
+
if (!text) process.exit(0);
|
|
150
|
+
const harness = opts.harness;
|
|
151
|
+
if (harness === "claude-code") {
|
|
152
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: {
|
|
153
|
+
hookEventName: "SessionStart",
|
|
154
|
+
additionalContext: text
|
|
155
|
+
} }));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (harness === "copilot-cli") {
|
|
159
|
+
process.stdout.write(JSON.stringify({ additionalContext: text }));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
process.stdout.write(`${text}\n`);
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
136
165
|
//#region src/commands/list.ts
|
|
137
166
|
async function listCommand(options, formatter) {
|
|
138
167
|
const db = DatabaseManager.open();
|
|
@@ -216,11 +245,8 @@ var Formatter = class Formatter {
|
|
|
216
245
|
constructor(isJson) {
|
|
217
246
|
this.#isJson = isJson;
|
|
218
247
|
}
|
|
219
|
-
static create() {
|
|
220
|
-
return new Formatter(!process.stdout.isTTY);
|
|
221
|
-
}
|
|
222
|
-
withJson(isJson) {
|
|
223
|
-
return new Formatter(isJson);
|
|
248
|
+
static create(forceJson = false) {
|
|
249
|
+
return new Formatter(forceJson || !process.stdout.isTTY);
|
|
224
250
|
}
|
|
225
251
|
get isJson() {
|
|
226
252
|
return this.#isJson;
|
|
@@ -230,13 +256,7 @@ var Formatter = class Formatter {
|
|
|
230
256
|
process.stdout.write(`${JSON.stringify(memory)}\n`);
|
|
231
257
|
return;
|
|
232
258
|
}
|
|
233
|
-
|
|
234
|
-
process.stdout.write(`\n[${memory.type}] ${memory.id}\n`);
|
|
235
|
-
process.stdout.write(` Content : ${memory.content}\n`);
|
|
236
|
-
process.stdout.write(` Tags : ${tags}\n`);
|
|
237
|
-
process.stdout.write(` Scope : ${memory.scope}\n`);
|
|
238
|
-
process.stdout.write(` Pinned : ${memory.pinned}\n`);
|
|
239
|
-
process.stdout.write("\n");
|
|
259
|
+
this.#writeMemoryBlock(memory, ` Pinned : ${memory.pinned}\n`);
|
|
240
260
|
}
|
|
241
261
|
outputMemories(memories) {
|
|
242
262
|
if (this.#isJson) {
|
|
@@ -247,13 +267,7 @@ var Formatter = class Formatter {
|
|
|
247
267
|
process.stdout.write("No memories found.\n");
|
|
248
268
|
return;
|
|
249
269
|
}
|
|
250
|
-
for (const memory of memories)
|
|
251
|
-
const tags = memory.tags.length > 0 ? memory.tags.join(", ") : "(none)";
|
|
252
|
-
process.stdout.write(`\n[${memory.type}] ${memory.id}\n`);
|
|
253
|
-
process.stdout.write(` Content : ${memory.content}\n`);
|
|
254
|
-
process.stdout.write(` Tags : ${tags}\n`);
|
|
255
|
-
process.stdout.write(` Scope : ${memory.scope}\n`);
|
|
256
|
-
}
|
|
270
|
+
for (const memory of memories) this.#writeMemoryBlock(memory);
|
|
257
271
|
process.stdout.write("\n");
|
|
258
272
|
}
|
|
259
273
|
outputStats(stats) {
|
|
@@ -280,20 +294,22 @@ var Formatter = class Formatter {
|
|
|
280
294
|
process.stdout.write("No memories found.\n");
|
|
281
295
|
return;
|
|
282
296
|
}
|
|
283
|
-
for (const result of results) {
|
|
284
|
-
const tags = result.tags.length > 0 ? result.tags.join(", ") : "(none)";
|
|
285
|
-
process.stdout.write(`\n[${result.type}] ${result.id}\n`);
|
|
286
|
-
process.stdout.write(` Content : ${result.content}\n`);
|
|
287
|
-
process.stdout.write(` Tags : ${tags}\n`);
|
|
288
|
-
process.stdout.write(` Scope : ${result.scope}\n`);
|
|
289
|
-
process.stdout.write(` Score : ${result.score.toFixed(4)}\n`);
|
|
290
|
-
}
|
|
297
|
+
for (const result of results) this.#writeMemoryBlock(result, ` Score : ${result.score.toFixed(4)}\n`);
|
|
291
298
|
process.stdout.write("\n");
|
|
292
299
|
}
|
|
293
300
|
error(msg) {
|
|
294
301
|
if (this.#isJson) process.stderr.write(`${JSON.stringify({ error: msg })}\n`);
|
|
295
302
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
296
303
|
}
|
|
304
|
+
#writeMemoryBlock(memory, extra) {
|
|
305
|
+
const tags = memory.tags.length > 0 ? memory.tags.join(", ") : "(none)";
|
|
306
|
+
process.stdout.write(`\n[${memory.type}] ${memory.id}\n`);
|
|
307
|
+
process.stdout.write(` Content : ${memory.content}\n`);
|
|
308
|
+
process.stdout.write(` Tags : ${tags}\n`);
|
|
309
|
+
process.stdout.write(` Scope : ${memory.scope}\n`);
|
|
310
|
+
if (extra !== void 0) process.stdout.write(extra);
|
|
311
|
+
process.stdout.write("\n");
|
|
312
|
+
}
|
|
297
313
|
};
|
|
298
314
|
//#endregion
|
|
299
315
|
//#region src/prompt-helper.ts
|
|
@@ -352,7 +368,7 @@ async function execFileNoThrow(cmd, args) {
|
|
|
352
368
|
}
|
|
353
369
|
//#endregion
|
|
354
370
|
//#region src/setup/harness-config-writer.ts
|
|
355
|
-
const defaultPathResolver = {
|
|
371
|
+
const defaultPathResolver$1 = {
|
|
356
372
|
home: () => {
|
|
357
373
|
const h = process.env.HOME ?? process.env.USERPROFILE;
|
|
358
374
|
if (!h) throw new Error("Cannot determine home directory");
|
|
@@ -360,14 +376,14 @@ const defaultPathResolver = {
|
|
|
360
376
|
},
|
|
361
377
|
cwd: () => process.cwd()
|
|
362
378
|
};
|
|
363
|
-
function readJson(path) {
|
|
379
|
+
function readJson$1(path) {
|
|
364
380
|
try {
|
|
365
381
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
366
382
|
} catch {
|
|
367
383
|
return {};
|
|
368
384
|
}
|
|
369
385
|
}
|
|
370
|
-
function writeJsonAtomic(path, data) {
|
|
386
|
+
function writeJsonAtomic$1(path, data) {
|
|
371
387
|
mkdirSync(dirname(path), { recursive: true });
|
|
372
388
|
const tmp = join(mkdtempSync(join(tmpdir(), "membank-")), "cfg.json");
|
|
373
389
|
writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
@@ -384,9 +400,9 @@ const MEMBANK_NPX_ARGS = [
|
|
|
384
400
|
"@membank/cli@latest",
|
|
385
401
|
"--mcp"
|
|
386
402
|
];
|
|
387
|
-
const writers = {
|
|
403
|
+
const writers$1 = {
|
|
388
404
|
"claude-code": { async write(resolver, run, { overwrite = false } = {}) {
|
|
389
|
-
const configured = hasKey(readJson(join(resolver.home(), ".claude.json")).mcpServers, "membank");
|
|
405
|
+
const configured = hasKey(readJson$1(join(resolver.home(), ".claude.json")).mcpServers, "membank");
|
|
390
406
|
if (configured && !overwrite) return { status: "already-configured" };
|
|
391
407
|
if (configured) {
|
|
392
408
|
const remove = await run("claude", [
|
|
@@ -412,37 +428,20 @@ const writers = {
|
|
|
412
428
|
if (add.exitCode !== 0) throw new Error(`claude mcp add failed: ${add.stderr || add.stdout}`);
|
|
413
429
|
return { status: "written" };
|
|
414
430
|
} },
|
|
415
|
-
|
|
416
|
-
const cfgPath = join(resolver.
|
|
417
|
-
const cfg = readJson(cfgPath);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
...cfg,
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
command: "npx",
|
|
427
|
-
args: ["@membank/cli@latest", "--mcp"]
|
|
428
|
-
}
|
|
431
|
+
copilot: { async write(resolver, _run, { overwrite = false } = {}) {
|
|
432
|
+
const cfgPath = join(resolver.home(), ".copilot", "mcp-config.json");
|
|
433
|
+
const cfg = readJson$1(cfgPath);
|
|
434
|
+
if (hasKey(cfg.mcpServers, "membank") && !overwrite) return { status: "already-configured" };
|
|
435
|
+
writeJsonAtomic$1(cfgPath, {
|
|
436
|
+
...cfg,
|
|
437
|
+
mcpServers: {
|
|
438
|
+
...cfg.mcpServers,
|
|
439
|
+
membank: {
|
|
440
|
+
command: "npx",
|
|
441
|
+
args: ["@membank/cli@latest", "--mcp"]
|
|
429
442
|
}
|
|
430
|
-
}
|
|
431
|
-
return { status: "written" };
|
|
432
|
-
}
|
|
433
|
-
const payload = JSON.stringify({
|
|
434
|
-
name: "membank",
|
|
435
|
-
command: "npx",
|
|
436
|
-
args: ["@membank/cli@latest", "--mcp"]
|
|
443
|
+
}
|
|
437
444
|
});
|
|
438
|
-
const result = await run("code", [
|
|
439
|
-
"--folder-uri",
|
|
440
|
-
resolver.cwd(),
|
|
441
|
-
"--add-mcp",
|
|
442
|
-
payload
|
|
443
|
-
]);
|
|
444
|
-
assertCliFound(result, "code");
|
|
445
|
-
if (result.exitCode !== 0) throw new Error(`code --add-mcp failed: ${result.stderr || result.stdout}`);
|
|
446
445
|
return { status: "written" };
|
|
447
446
|
} },
|
|
448
447
|
codex: { async write(_resolver, run, { overwrite = false } = {}) {
|
|
@@ -472,9 +471,9 @@ const writers = {
|
|
|
472
471
|
} },
|
|
473
472
|
opencode: { async write(resolver, _run, { overwrite = false } = {}) {
|
|
474
473
|
const cfgPath = join(resolver.home(), ".config", "opencode", "opencode.json");
|
|
475
|
-
const cfg = readJson(cfgPath);
|
|
474
|
+
const cfg = readJson$1(cfgPath);
|
|
476
475
|
if (hasKey(cfg.mcp, "membank") && !overwrite) return { status: "already-configured" };
|
|
477
|
-
writeJsonAtomic(cfgPath, {
|
|
476
|
+
writeJsonAtomic$1(cfgPath, {
|
|
478
477
|
...cfg,
|
|
479
478
|
mcp: {
|
|
480
479
|
...cfg.mcp,
|
|
@@ -491,21 +490,142 @@ const writers = {
|
|
|
491
490
|
return { status: "written" };
|
|
492
491
|
} }
|
|
493
492
|
};
|
|
494
|
-
const SUPPORTED_HARNESSES = Object.keys(writers);
|
|
493
|
+
const SUPPORTED_HARNESSES = Object.keys(writers$1);
|
|
495
494
|
var HarnessConfigWriter = class {
|
|
496
495
|
#resolver;
|
|
497
496
|
#run;
|
|
498
|
-
constructor(resolver = defaultPathResolver, run = execFileNoThrow) {
|
|
497
|
+
constructor(resolver = defaultPathResolver$1, run = execFileNoThrow) {
|
|
499
498
|
this.#resolver = resolver;
|
|
500
499
|
this.#run = run;
|
|
501
500
|
}
|
|
502
501
|
async write(harness, { overwrite = false } = {}) {
|
|
503
|
-
const writer = writers[harness];
|
|
502
|
+
const writer = writers$1[harness];
|
|
504
503
|
if (!writer) throw new Error(`Unknown harness: ${harness}`);
|
|
505
504
|
return writer.write(this.#resolver, this.#run, { overwrite });
|
|
506
505
|
}
|
|
507
506
|
};
|
|
508
507
|
//#endregion
|
|
508
|
+
//#region src/setup/injection-hook-writer.ts
|
|
509
|
+
const defaultPathResolver = { home: () => {
|
|
510
|
+
const h = process.env.HOME ?? process.env.USERPROFILE;
|
|
511
|
+
if (!h) throw new Error("Cannot determine home directory");
|
|
512
|
+
return h;
|
|
513
|
+
} };
|
|
514
|
+
function readJson(path) {
|
|
515
|
+
try {
|
|
516
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
517
|
+
} catch {
|
|
518
|
+
return {};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function writeJsonAtomic(path, data) {
|
|
522
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
523
|
+
const tmp = join(mkdtempSync(join(tmpdir(), "membank-hook-")), "cfg.json");
|
|
524
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
525
|
+
renameSync(tmp, path);
|
|
526
|
+
}
|
|
527
|
+
function containsMembankInject(hooks) {
|
|
528
|
+
if (!Array.isArray(hooks)) return false;
|
|
529
|
+
return hooks.some((h) => typeof h === "object" && h !== null && ("command" in h && typeof h.command === "string" && h.command.includes("@membank/cli inject") || "bash" in h && typeof h.bash === "string" && h.bash.includes("@membank/cli inject")));
|
|
530
|
+
}
|
|
531
|
+
const writers = {
|
|
532
|
+
"claude-code": { write(resolver) {
|
|
533
|
+
const cfgPath = join(resolver.home(), ".claude", "settings.json");
|
|
534
|
+
const cfg = readJson(cfgPath);
|
|
535
|
+
const hooks = cfg.hooks;
|
|
536
|
+
const sessionStart = hooks?.SessionStart;
|
|
537
|
+
if (Array.isArray(sessionStart) && containsMembankInject(sessionStart.flatMap((g) => Array.isArray(g.hooks) ? g.hooks : []))) return { status: "already-configured" };
|
|
538
|
+
const existingSessionStart = Array.isArray(sessionStart) ? sessionStart : [];
|
|
539
|
+
writeJsonAtomic(cfgPath, {
|
|
540
|
+
...cfg,
|
|
541
|
+
hooks: {
|
|
542
|
+
...hooks ?? {},
|
|
543
|
+
SessionStart: [...existingSessionStart, {
|
|
544
|
+
matcher: "",
|
|
545
|
+
hooks: [{
|
|
546
|
+
type: "command",
|
|
547
|
+
command: "npx @membank/cli inject --harness claude-code"
|
|
548
|
+
}]
|
|
549
|
+
}]
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return { status: "written" };
|
|
553
|
+
} },
|
|
554
|
+
"copilot-cli": { write(resolver) {
|
|
555
|
+
const cfgPath = join(resolver.home(), ".copilot", "settings.json");
|
|
556
|
+
const cfg = readJson(cfgPath);
|
|
557
|
+
const hooks = cfg.hooks;
|
|
558
|
+
const sessionStart = hooks?.sessionStart;
|
|
559
|
+
if (Array.isArray(sessionStart) && containsMembankInject(sessionStart)) return { status: "already-configured" };
|
|
560
|
+
const existingSessionStart = Array.isArray(sessionStart) ? sessionStart : [];
|
|
561
|
+
writeJsonAtomic(cfgPath, {
|
|
562
|
+
version: cfg.version ?? 1,
|
|
563
|
+
...cfg,
|
|
564
|
+
hooks: {
|
|
565
|
+
...hooks ?? {},
|
|
566
|
+
sessionStart: [...existingSessionStart, {
|
|
567
|
+
type: "command",
|
|
568
|
+
bash: "npx @membank/cli inject --harness copilot-cli",
|
|
569
|
+
timeoutSec: 30
|
|
570
|
+
}]
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
return { status: "written" };
|
|
574
|
+
} },
|
|
575
|
+
codex: { write(resolver) {
|
|
576
|
+
const cfgPath = join(resolver.home(), ".codex", "hooks.json");
|
|
577
|
+
const cfg = readJson(cfgPath);
|
|
578
|
+
const hooks = cfg.hooks;
|
|
579
|
+
const sessionStart = hooks?.SessionStart;
|
|
580
|
+
if (Array.isArray(sessionStart) && containsMembankInject(sessionStart.flatMap((g) => Array.isArray(g.hooks) ? g.hooks : []))) return { status: "already-configured" };
|
|
581
|
+
const existingSessionStart = Array.isArray(sessionStart) ? sessionStart : [];
|
|
582
|
+
writeJsonAtomic(cfgPath, {
|
|
583
|
+
...cfg,
|
|
584
|
+
hooks: {
|
|
585
|
+
...hooks ?? {},
|
|
586
|
+
SessionStart: [...existingSessionStart, {
|
|
587
|
+
matcher: "",
|
|
588
|
+
hooks: [{
|
|
589
|
+
type: "command",
|
|
590
|
+
command: "npx @membank/cli inject --harness codex",
|
|
591
|
+
timeout: 30
|
|
592
|
+
}]
|
|
593
|
+
}]
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
return { status: "written" };
|
|
597
|
+
} },
|
|
598
|
+
opencode: { write(resolver) {
|
|
599
|
+
const pluginPath = join(resolver.home(), ".config", "opencode", "plugins", "membank.js");
|
|
600
|
+
if (existsSync(pluginPath)) {
|
|
601
|
+
if (readFileSync(pluginPath, "utf8").includes("@membank/cli inject")) return { status: "already-configured" };
|
|
602
|
+
}
|
|
603
|
+
mkdirSync(dirname(pluginPath), { recursive: true });
|
|
604
|
+
writeFileSync(pluginPath, [
|
|
605
|
+
"export default {",
|
|
606
|
+
" hooks: {",
|
|
607
|
+
" \"session.start\": async ({ $ }) => {",
|
|
608
|
+
" return await $`npx @membank/cli inject`.text();",
|
|
609
|
+
" },",
|
|
610
|
+
" },",
|
|
611
|
+
"};",
|
|
612
|
+
""
|
|
613
|
+
].join("\n"), "utf8");
|
|
614
|
+
return { status: "written" };
|
|
615
|
+
} }
|
|
616
|
+
};
|
|
617
|
+
var InjectionHookWriter = class {
|
|
618
|
+
#resolver;
|
|
619
|
+
constructor(resolver = defaultPathResolver) {
|
|
620
|
+
this.#resolver = resolver;
|
|
621
|
+
}
|
|
622
|
+
write(harness) {
|
|
623
|
+
const writer = writers[harness];
|
|
624
|
+
if (!writer) return { status: "not-supported" };
|
|
625
|
+
return writer.write(this.#resolver);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
//#endregion
|
|
509
629
|
//#region src/setup/model-downloader.ts
|
|
510
630
|
const MODEL_NAME = "Xenova/bge-small-en-v1.5";
|
|
511
631
|
var ModelDownloadError = class extends Error {
|
|
@@ -571,13 +691,9 @@ var ModelDownloader = class extends EventEmitter {
|
|
|
571
691
|
};
|
|
572
692
|
//#endregion
|
|
573
693
|
//#region src/setup/harness-detector.ts
|
|
574
|
-
const defaultResolver = {
|
|
575
|
-
homeDir: homedir,
|
|
576
|
-
cwd: () => process.cwd()
|
|
577
|
-
};
|
|
694
|
+
const defaultResolver = { homeDir: homedir };
|
|
578
695
|
function harnessConfigs(resolver) {
|
|
579
696
|
const home = resolver.homeDir();
|
|
580
|
-
const cwd = resolver.cwd();
|
|
581
697
|
return [
|
|
582
698
|
{
|
|
583
699
|
name: "claude-code",
|
|
@@ -585,8 +701,8 @@ function harnessConfigs(resolver) {
|
|
|
585
701
|
fallbackPaths: [join(home, ".claude", "settings.json")]
|
|
586
702
|
},
|
|
587
703
|
{
|
|
588
|
-
name: "
|
|
589
|
-
configPath: join(
|
|
704
|
+
name: "copilot",
|
|
705
|
+
configPath: join(home, ".copilot", "mcp-config.json")
|
|
590
706
|
},
|
|
591
707
|
{
|
|
592
708
|
name: "codex",
|
|
@@ -628,6 +744,7 @@ function defaultPrompter(question) {
|
|
|
628
744
|
var SetupOrchestrator = class {
|
|
629
745
|
#detector;
|
|
630
746
|
#writer;
|
|
747
|
+
#hookWriter;
|
|
631
748
|
#prompter;
|
|
632
749
|
#modelDownloader;
|
|
633
750
|
#out;
|
|
@@ -635,6 +752,7 @@ var SetupOrchestrator = class {
|
|
|
635
752
|
constructor(deps) {
|
|
636
753
|
this.#detector = deps.detector ?? (() => detectHarnesses());
|
|
637
754
|
this.#writer = deps.writer;
|
|
755
|
+
this.#hookWriter = deps.hookWriter;
|
|
638
756
|
this.#prompter = deps.prompter ?? defaultPrompter;
|
|
639
757
|
this.#modelDownloader = deps.modelDownloader;
|
|
640
758
|
this.#out = deps.out ?? ((msg) => process.stdout.write(`${msg}\n`));
|
|
@@ -656,6 +774,7 @@ var SetupOrchestrator = class {
|
|
|
656
774
|
if (json) this.#out(JSON.stringify({
|
|
657
775
|
detectedHarnesses: [],
|
|
658
776
|
configuredHarnesses: [],
|
|
777
|
+
injectionHooksConfigured: [],
|
|
659
778
|
modelDownloaded: false
|
|
660
779
|
}));
|
|
661
780
|
return [];
|
|
@@ -667,7 +786,10 @@ var SetupOrchestrator = class {
|
|
|
667
786
|
}
|
|
668
787
|
if (dryRun) {
|
|
669
788
|
out("Planned changes (dry-run — no files written):");
|
|
670
|
-
for (const h of detected)
|
|
789
|
+
for (const h of detected) {
|
|
790
|
+
out(` ⚠ ${h.name}: would write MCP config`);
|
|
791
|
+
if (this.#hookWriter) out(` ⚠ ${h.name}: would write injection hook config`);
|
|
792
|
+
}
|
|
671
793
|
out("");
|
|
672
794
|
out(" ⚠ Model download: skipped (dry-run)");
|
|
673
795
|
return detected.map((h) => ({
|
|
@@ -732,6 +854,21 @@ var SetupOrchestrator = class {
|
|
|
732
854
|
});
|
|
733
855
|
}
|
|
734
856
|
out("");
|
|
857
|
+
const injectionHooksConfigured = [];
|
|
858
|
+
if (this.#hookWriter) {
|
|
859
|
+
for (const h of detected) try {
|
|
860
|
+
const hookResult = this.#hookWriter.write(h.name);
|
|
861
|
+
if (hookResult.status === "not-supported") continue;
|
|
862
|
+
if (hookResult.status === "written") {
|
|
863
|
+
out(` ✓ ${h.name}: injection hook written`);
|
|
864
|
+
injectionHooksConfigured.push(h.name);
|
|
865
|
+
} else out(` ⚠ ${h.name}: injection hook already configured`);
|
|
866
|
+
} catch (err) {
|
|
867
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
868
|
+
out(` ✗ ${h.name} injection hook: ${msg}`);
|
|
869
|
+
}
|
|
870
|
+
out("");
|
|
871
|
+
}
|
|
735
872
|
let modelDownloaded = false;
|
|
736
873
|
if (this.#modelDownloader) modelDownloaded = !(await this.#runModelDownload(this.#modelDownloader, out)).skipped;
|
|
737
874
|
else out("Model download step: see DRA-52");
|
|
@@ -742,6 +879,7 @@ var SetupOrchestrator = class {
|
|
|
742
879
|
const output = {
|
|
743
880
|
detectedHarnesses: detected.map((h) => h.name),
|
|
744
881
|
configuredHarnesses: results.filter((r) => r.status === "written").map((r) => r.harness),
|
|
882
|
+
injectionHooksConfigured,
|
|
745
883
|
modelDownloaded
|
|
746
884
|
};
|
|
747
885
|
this.#out(JSON.stringify(output));
|
|
@@ -782,7 +920,7 @@ const program = new Command();
|
|
|
782
920
|
program.name("membank").description("LLM memory management system").option("--json", "emit machine-readable JSON only").option("-y, --yes", "skip all confirmation prompts").option("--mcp", "start the MCP stdio server (for harness integration)");
|
|
783
921
|
program.command("query <queryText>").description("search memories by semantic similarity").option("--type <type>", "filter by memory type (correction|preference|decision|learning|fact)").option("--limit <n>", "maximum number of results", "10").action(async (queryText, cmdOptions) => {
|
|
784
922
|
const globalOpts = program.opts();
|
|
785
|
-
const formatter = Formatter.create(
|
|
923
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
786
924
|
try {
|
|
787
925
|
await queryCommand(queryText, cmdOptions, formatter);
|
|
788
926
|
} catch (err) {
|
|
@@ -792,7 +930,7 @@ program.command("query <queryText>").description("search memories by semantic si
|
|
|
792
930
|
});
|
|
793
931
|
program.command("list").description("list memories with optional filters").option("--type <type>", "filter by memory type (correction|preference|decision|learning|fact)").option("--pinned", "return only pinned memories").action(async (cmdOptions) => {
|
|
794
932
|
const globalOpts = program.opts();
|
|
795
|
-
const formatter = Formatter.create(
|
|
933
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
796
934
|
try {
|
|
797
935
|
await listCommand(cmdOptions, formatter);
|
|
798
936
|
} catch (err) {
|
|
@@ -802,7 +940,7 @@ program.command("list").description("list memories with optional filters").optio
|
|
|
802
940
|
});
|
|
803
941
|
program.command("stats").description("show memory counts by type, total, and needs_review").action(async () => {
|
|
804
942
|
const globalOpts = program.opts();
|
|
805
|
-
const formatter = Formatter.create(
|
|
943
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
806
944
|
try {
|
|
807
945
|
await statsCommand(formatter);
|
|
808
946
|
} catch (err) {
|
|
@@ -812,7 +950,7 @@ program.command("stats").description("show memory counts by type, total, and nee
|
|
|
812
950
|
});
|
|
813
951
|
program.command("delete <id>").description("delete a memory by ID").action(async (id) => {
|
|
814
952
|
const globalOpts = program.opts();
|
|
815
|
-
const formatter = Formatter.create(
|
|
953
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
816
954
|
const prompt = new PromptHelper(globalOpts.yes === true || !process.stdout.isTTY);
|
|
817
955
|
const db = DatabaseManager.open();
|
|
818
956
|
try {
|
|
@@ -826,7 +964,7 @@ program.command("delete <id>").description("delete a memory by ID").action(async
|
|
|
826
964
|
});
|
|
827
965
|
program.command("add <content>").description("save a new memory").requiredOption("--type <type>", "memory type (correction|preference|decision|learning|fact)").option("--tags <tags>", "comma-separated tags").option("--scope <scope>", "scope (global or project identifier)").action(async (content, cmdOptions) => {
|
|
828
966
|
const globalOpts = program.opts();
|
|
829
|
-
const formatter = Formatter.create(
|
|
967
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
830
968
|
try {
|
|
831
969
|
await addCommand(content, cmdOptions, formatter);
|
|
832
970
|
} catch (err) {
|
|
@@ -836,7 +974,7 @@ program.command("add <content>").description("save a new memory").requiredOption
|
|
|
836
974
|
});
|
|
837
975
|
program.command("pin <id>").description("pin a memory by ID").action((id) => {
|
|
838
976
|
const globalOpts = program.opts();
|
|
839
|
-
const formatter = Formatter.create(
|
|
977
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
840
978
|
try {
|
|
841
979
|
pinCommand(id, formatter);
|
|
842
980
|
} catch (err) {
|
|
@@ -846,7 +984,7 @@ program.command("pin <id>").description("pin a memory by ID").action((id) => {
|
|
|
846
984
|
});
|
|
847
985
|
program.command("unpin <id>").description("unpin a memory by ID").action((id) => {
|
|
848
986
|
const globalOpts = program.opts();
|
|
849
|
-
const formatter = Formatter.create(
|
|
987
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
850
988
|
try {
|
|
851
989
|
unpinCommand(id, formatter);
|
|
852
990
|
} catch (err) {
|
|
@@ -856,7 +994,7 @@ program.command("unpin <id>").description("unpin a memory by ID").action((id) =>
|
|
|
856
994
|
});
|
|
857
995
|
program.command("export").description("export all memories to a JSON file").option("--output <path>", "output file path (default: membank-export-<timestamp>.json in cwd)").action((cmdOptions) => {
|
|
858
996
|
const globalOpts = program.opts();
|
|
859
|
-
const formatter = Formatter.create(
|
|
997
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
860
998
|
const db = DatabaseManager.open();
|
|
861
999
|
try {
|
|
862
1000
|
exportCommand(db, formatter, cmdOptions);
|
|
@@ -869,7 +1007,7 @@ program.command("export").description("export all memories to a JSON file").opti
|
|
|
869
1007
|
});
|
|
870
1008
|
program.command("import <file>").description("import memories from a JSON export file").action(async (file) => {
|
|
871
1009
|
const globalOpts = program.opts();
|
|
872
|
-
const formatter = Formatter.create(
|
|
1010
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
873
1011
|
const prompt = new PromptHelper(globalOpts.yes === true || !process.stdout.isTTY);
|
|
874
1012
|
const db = DatabaseManager.open();
|
|
875
1013
|
try {
|
|
@@ -881,21 +1019,30 @@ program.command("import <file>").description("import memories from a JSON export
|
|
|
881
1019
|
db.close();
|
|
882
1020
|
}
|
|
883
1021
|
});
|
|
1022
|
+
program.command("inject").description("output session context for harness injection (used by setup hooks)").option("--harness <name>", "format output for a specific harness (claude-code|copilot-cli|codex|opencode)").option("--scope <scope>", "project scope override (default: auto-detect from git remote)").action(async (cmdOptions) => {
|
|
1023
|
+
try {
|
|
1024
|
+
await injectCommand(cmdOptions);
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
1027
|
+
process.exit(2);
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
884
1030
|
program.command("setup").description("detect installed harnesses and write MCP config for each").option("--yes", "skip all confirmation prompts").option("--dry-run", "print planned changes without writing any file").option("--harness <name>", "target only the named harness (skip detection)").action(async (cmdOptions) => {
|
|
885
1031
|
const globalOpts = program.opts();
|
|
886
1032
|
const autoYes = cmdOptions.yes === true || globalOpts.yes === true;
|
|
887
|
-
const
|
|
888
|
-
const formatter = Formatter.create().withJson(useJson);
|
|
1033
|
+
const formatter = Formatter.create(globalOpts.json === true);
|
|
889
1034
|
if (cmdOptions.harness !== void 0) {
|
|
890
|
-
if (!SUPPORTED_HARNESSES.
|
|
1035
|
+
if (!SUPPORTED_HARNESSES.some((h) => h === cmdOptions.harness)) {
|
|
891
1036
|
formatter.error(`Unknown harness: "${cmdOptions.harness}". Supported: ${SUPPORTED_HARNESSES.join(", ")}`);
|
|
892
1037
|
process.exit(1);
|
|
893
1038
|
}
|
|
894
1039
|
}
|
|
895
1040
|
const writer = new HarnessConfigWriter();
|
|
1041
|
+
const hookWriter = new InjectionHookWriter();
|
|
896
1042
|
const promptHelper = new PromptHelper(autoYes);
|
|
897
1043
|
const orchestrator = new SetupOrchestrator({
|
|
898
1044
|
writer,
|
|
1045
|
+
hookWriter,
|
|
899
1046
|
prompter: (question) => promptHelper.confirm(question),
|
|
900
1047
|
modelDownloader: new ModelDownloader()
|
|
901
1048
|
});
|
|
@@ -904,7 +1051,7 @@ program.command("setup").description("detect installed harnesses and write MCP c
|
|
|
904
1051
|
yes: autoYes,
|
|
905
1052
|
dryRun: cmdOptions.dryRun,
|
|
906
1053
|
harness: cmdOptions.harness,
|
|
907
|
-
json:
|
|
1054
|
+
json: formatter.isJson
|
|
908
1055
|
})).some((r) => r.status === "error")) process.exit(1);
|
|
909
1056
|
} catch (err) {
|
|
910
1057
|
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.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"@huggingface/transformers": "^4.2.0",
|
|
18
18
|
"commander": "^14.0.3",
|
|
19
19
|
"ora": "^9.4.0",
|
|
20
|
-
"@membank/
|
|
21
|
-
"@membank/
|
|
20
|
+
"@membank/mcp": "0.0.4",
|
|
21
|
+
"@membank/core": "0.0.4"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.6.0",
|