@membank/cli 0.3.0 → 0.4.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/README.md +13 -5
- package/dist/index.mjs +136 -232
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Options:
|
|
|
33
33
|
--json Machine-readable output
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Supported harnesses: `claude-code`, `copilot`, `codex`, `opencode`
|
|
36
|
+
Supported harnesses: `claude-code`, `copilot`, `codex`, `opencode` (see `membank setup` for harness-specific setup instructions)
|
|
37
37
|
|
|
38
38
|
## Commands
|
|
39
39
|
|
|
@@ -125,6 +125,16 @@ Output session context formatted for a harness. Called automatically by session
|
|
|
125
125
|
membank inject --harness claude-code --scope <project-scope>
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
+
### `membank dashboard`
|
|
129
|
+
|
|
130
|
+
Start the web dashboard for browsing and managing memories.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
membank dashboard
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Opens http://localhost:3847 by default. Features: full-text search, filtering by type/scope/pin status, edit memory metadata, view dedup reviews, and storage statistics.
|
|
137
|
+
|
|
128
138
|
## Global flags
|
|
129
139
|
|
|
130
140
|
```
|
|
@@ -143,11 +153,9 @@ Starts the stdio MCP server. This is what harnesses connect to — `setup` write
|
|
|
143
153
|
|
|
144
154
|
## Session hooks
|
|
145
155
|
|
|
146
|
-
`setup` installs
|
|
147
|
-
|
|
148
|
-
**Session start** — calls `membank inject` to prepend pinned memories into the LLM context at the beginning of every session.
|
|
156
|
+
`setup` installs a hook for Claude Code that injects memories at the start of each session:
|
|
149
157
|
|
|
150
|
-
**
|
|
158
|
+
**SessionStart** — calls `membank inject` to prepend pinned memories into the LLM context at the beginning of every session.
|
|
151
159
|
|
|
152
160
|
## Requirements
|
|
153
161
|
|
package/dist/index.mjs
CHANGED
|
@@ -134,7 +134,7 @@ async function importCommand(filePath, db, formatter, prompt) {
|
|
|
134
134
|
}
|
|
135
135
|
//#endregion
|
|
136
136
|
//#region src/commands/inject.ts
|
|
137
|
-
const MEMORY_GUIDANCE = "[Memory Guidance]:
|
|
137
|
+
const MEMORY_GUIDANCE = "[Memory Guidance]: Persistent memory is available via query_memory, save_memory, update_memory, delete_memory. Skipping save_memory when the user gives a correction or preference means they have to repeat themselves next session — that is the failure mode to avoid. Skipping query_memory on topics that touch prior decisions means contradicting yourself. Default to saving (type: correction|preference|decision|learning|fact) when in doubt; rely on dedup to handle redundancy. Pin anything that should appear at every session start.";
|
|
138
138
|
function formatContext(ctx) {
|
|
139
139
|
const lines = [];
|
|
140
140
|
const statParts = Object.entries(ctx.stats).filter(([, count]) => count > 0).map(([type, count]) => `${count} ${type}${count !== 1 ? "s" : ""}`);
|
|
@@ -160,51 +160,6 @@ function outputAdditionalContext(text, harness, eventName) {
|
|
|
160
160
|
}
|
|
161
161
|
process.stdout.write(`${text}\n`);
|
|
162
162
|
}
|
|
163
|
-
async function readStdin() {
|
|
164
|
-
if (process.stdin.isTTY) return "";
|
|
165
|
-
return new Promise((resolve) => {
|
|
166
|
-
const chunks = [];
|
|
167
|
-
const timeout = setTimeout(() => resolve(""), 1e3);
|
|
168
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
169
|
-
process.stdin.on("end", () => {
|
|
170
|
-
clearTimeout(timeout);
|
|
171
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
172
|
-
});
|
|
173
|
-
process.stdin.on("error", () => {
|
|
174
|
-
clearTimeout(timeout);
|
|
175
|
-
resolve("");
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
const FEEDBACK_PATTERNS = [
|
|
180
|
-
/\bdon'?t\b/i,
|
|
181
|
-
/\bstop\b/i,
|
|
182
|
-
/\bnever\b/i,
|
|
183
|
-
/\balways\b/i,
|
|
184
|
-
/\bremember\b/i,
|
|
185
|
-
/\bprefer\b/i,
|
|
186
|
-
/\bi (like|want|hate|dislike)\b/i,
|
|
187
|
-
/\bfrom now on\b/i,
|
|
188
|
-
/\bkeep in mind\b/i,
|
|
189
|
-
/\bnote that\b/i,
|
|
190
|
-
/\bstop doing\b/i,
|
|
191
|
-
/\bstop using\b/i,
|
|
192
|
-
/\bthat'?s wrong\b/i,
|
|
193
|
-
/\bno,?\s+(actually|that'?s)\b/i,
|
|
194
|
-
/\bplease (don'?t|stop|always|never)\b/i
|
|
195
|
-
];
|
|
196
|
-
function looksLikeFeedback(prompt) {
|
|
197
|
-
return FEEDBACK_PATTERNS.some((p) => p.test(prompt));
|
|
198
|
-
}
|
|
199
|
-
function isToolFailure(data) {
|
|
200
|
-
if (data.hook_event_name === "PostToolUseFailure") return true;
|
|
201
|
-
if (typeof data.error_message === "string" && data.error_message.length > 0) return true;
|
|
202
|
-
const response = data.tool_result ?? data.tool_response;
|
|
203
|
-
if (typeof response === "object" && response !== null) {
|
|
204
|
-
if (response.is_error === true) return true;
|
|
205
|
-
}
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
163
|
async function handleSessionStart(opts) {
|
|
209
164
|
const projectScope = opts.scope ?? await resolveScope();
|
|
210
165
|
const db = DatabaseManager.open();
|
|
@@ -218,41 +173,8 @@ async function handleSessionStart(opts) {
|
|
|
218
173
|
const harness = opts.harness;
|
|
219
174
|
outputAdditionalContext(text, harness, "SessionStart");
|
|
220
175
|
}
|
|
221
|
-
async function handleUserPrompt(harness) {
|
|
222
|
-
const raw = await readStdin();
|
|
223
|
-
if (!raw) process.exit(0);
|
|
224
|
-
let data;
|
|
225
|
-
try {
|
|
226
|
-
data = JSON.parse(raw);
|
|
227
|
-
} catch {
|
|
228
|
-
process.exit(0);
|
|
229
|
-
}
|
|
230
|
-
if (!looksLikeFeedback(typeof data.prompt === "string" ? data.prompt : "")) process.exit(0);
|
|
231
|
-
outputAdditionalContext("User prompt may contain a correction, preference, or decision worth saving. After responding, evaluate: should this be saved as a memory? If yes, call save_memory with the appropriate type (correction/preference/decision/learning) and scope (global or project).", harness, "UserPromptSubmit");
|
|
232
|
-
}
|
|
233
|
-
async function handleToolFailure(harness) {
|
|
234
|
-
const raw = await readStdin();
|
|
235
|
-
if (!raw) process.exit(0);
|
|
236
|
-
let data;
|
|
237
|
-
try {
|
|
238
|
-
data = JSON.parse(raw);
|
|
239
|
-
} catch {
|
|
240
|
-
process.exit(0);
|
|
241
|
-
}
|
|
242
|
-
if (!isToolFailure(data)) process.exit(0);
|
|
243
|
-
outputAdditionalContext(`Tool "${typeof data.tool_name === "string" ? data.tool_name : "unknown"}" failed. If this reveals a non-obvious constraint, environment issue, or repeatable failure pattern, call save_memory with type "learning" to prevent repeating it.`, harness, "PostToolUseFailure");
|
|
244
|
-
}
|
|
245
176
|
async function injectCommand(opts) {
|
|
246
|
-
|
|
247
|
-
const event = opts.event ?? "session-start";
|
|
248
|
-
if (event === "user-prompt") {
|
|
249
|
-
await handleUserPrompt(harness);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (event === "tool-failure") {
|
|
253
|
-
await handleToolFailure(harness);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
177
|
+
if (opts.event !== void 0 && opts.event !== "session-start") process.exit(0);
|
|
256
178
|
await handleSessionStart(opts);
|
|
257
179
|
}
|
|
258
180
|
//#endregion
|
|
@@ -632,10 +554,10 @@ function findMembankHookCommand(hooks, pattern) {
|
|
|
632
554
|
return "";
|
|
633
555
|
}
|
|
634
556
|
function containsMembankInject(hooks) {
|
|
635
|
-
return findMembankHookCommand(hooks, "@membank/cli
|
|
557
|
+
return findMembankHookCommand(hooks, "@membank/cli") !== "";
|
|
636
558
|
}
|
|
637
559
|
function extractInjectCommand(hooks) {
|
|
638
|
-
return findMembankHookCommand(hooks, "@membank/cli
|
|
560
|
+
return findMembankHookCommand(hooks, "@membank/cli");
|
|
639
561
|
}
|
|
640
562
|
function filterOutMembank(groups) {
|
|
641
563
|
return groups.filter((g) => !containsMembankInject(getHooksArray(g)));
|
|
@@ -643,154 +565,139 @@ function filterOutMembank(groups) {
|
|
|
643
565
|
function filterOutMembankFlat(hooks) {
|
|
644
566
|
return hooks.filter((h) => !containsMembankInject([h]));
|
|
645
567
|
}
|
|
568
|
+
function pruneNestedEvent(hooks, eventKey) {
|
|
569
|
+
const existing = hooks[eventKey];
|
|
570
|
+
if (!Array.isArray(existing)) return;
|
|
571
|
+
const cleaned = filterOutMembank(existing);
|
|
572
|
+
if (cleaned.length === 0) delete hooks[eventKey];
|
|
573
|
+
else hooks[eventKey] = cleaned;
|
|
574
|
+
}
|
|
575
|
+
function pruneFlatEvent(hooks, eventKey) {
|
|
576
|
+
const existing = hooks[eventKey];
|
|
577
|
+
if (!Array.isArray(existing)) return;
|
|
578
|
+
const cleaned = filterOutMembankFlat(existing);
|
|
579
|
+
if (cleaned.length === 0) delete hooks[eventKey];
|
|
580
|
+
else hooks[eventKey] = cleaned;
|
|
581
|
+
}
|
|
646
582
|
const writers = {
|
|
647
583
|
"claude-code": {
|
|
648
|
-
|
|
649
|
-
|
|
584
|
+
inspect(resolver) {
|
|
585
|
+
const hooks = readJson(join(resolver.home(), ".claude", "settings.json")).hooks ?? {};
|
|
586
|
+
return {
|
|
587
|
+
status: "ready",
|
|
588
|
+
hooks: [{
|
|
589
|
+
event: "SessionStart",
|
|
590
|
+
command: "npx @membank/cli@latest inject --harness claude-code",
|
|
591
|
+
existingCommand: extractInjectCommand((Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []).flatMap(getHooksArray)) || null
|
|
592
|
+
}]
|
|
593
|
+
};
|
|
594
|
+
},
|
|
595
|
+
write(resolver, events) {
|
|
650
596
|
const cfgPath = join(resolver.home(), ".claude", "settings.json");
|
|
651
597
|
const cfg = readJson(cfgPath);
|
|
652
|
-
const hooks = cfg.hooks;
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
598
|
+
const hooks = cfg.hooks ?? {};
|
|
599
|
+
const newHooks = { ...hooks };
|
|
600
|
+
pruneNestedEvent(newHooks, "UserPromptSubmit");
|
|
601
|
+
pruneNestedEvent(newHooks, "PostToolUseFailure");
|
|
602
|
+
if (events.includes("SessionStart")) newHooks.SessionStart = [...filterOutMembank(Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []), {
|
|
603
|
+
matcher: "",
|
|
604
|
+
hooks: [{
|
|
605
|
+
type: "command",
|
|
606
|
+
command: "npx @membank/cli@latest inject --harness claude-code"
|
|
607
|
+
}]
|
|
608
|
+
}];
|
|
663
609
|
writeJsonAtomic(cfgPath, {
|
|
664
610
|
...cfg,
|
|
665
|
-
hooks:
|
|
666
|
-
...hooks ?? {},
|
|
667
|
-
SessionStart: [...filteredSessionStart, {
|
|
668
|
-
matcher: "",
|
|
669
|
-
hooks: [{
|
|
670
|
-
type: "command",
|
|
671
|
-
command: "npx @membank/cli inject --harness claude-code"
|
|
672
|
-
}]
|
|
673
|
-
}],
|
|
674
|
-
UserPromptSubmit: [...filterOutMembank(existingUserPrompt), {
|
|
675
|
-
matcher: "",
|
|
676
|
-
hooks: [{
|
|
677
|
-
type: "command",
|
|
678
|
-
command: "npx @membank/cli inject --event user-prompt --harness claude-code"
|
|
679
|
-
}]
|
|
680
|
-
}],
|
|
681
|
-
PostToolUseFailure: [...filterOutMembank(existingToolFailure), {
|
|
682
|
-
matcher: "",
|
|
683
|
-
hooks: [{
|
|
684
|
-
type: "command",
|
|
685
|
-
command: "npx @membank/cli inject --event tool-failure --harness claude-code"
|
|
686
|
-
}]
|
|
687
|
-
}]
|
|
688
|
-
}
|
|
611
|
+
hooks: newHooks
|
|
689
612
|
});
|
|
690
613
|
return { status: "written" };
|
|
691
614
|
}
|
|
692
615
|
},
|
|
693
616
|
"copilot-cli": {
|
|
694
|
-
|
|
695
|
-
|
|
617
|
+
inspect(resolver) {
|
|
618
|
+
const hooks = readJson(join(resolver.home(), ".copilot", "settings.json")).hooks ?? {};
|
|
619
|
+
return {
|
|
620
|
+
status: "ready",
|
|
621
|
+
hooks: [{
|
|
622
|
+
event: "sessionStart",
|
|
623
|
+
command: "npx @membank/cli@latest inject --harness copilot-cli",
|
|
624
|
+
existingCommand: extractInjectCommand(Array.isArray(hooks.sessionStart) ? hooks.sessionStart : []) || null
|
|
625
|
+
}]
|
|
626
|
+
};
|
|
627
|
+
},
|
|
628
|
+
write(resolver, events) {
|
|
696
629
|
const cfgPath = join(resolver.home(), ".copilot", "settings.json");
|
|
697
630
|
const cfg = readJson(cfgPath);
|
|
698
|
-
const hooks = cfg.hooks;
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const existingToolFailure = Array.isArray(hooks?.postToolUseFailure) ? hooks.postToolUseFailure : [];
|
|
631
|
+
const hooks = cfg.hooks ?? {};
|
|
632
|
+
const newHooks = { ...hooks };
|
|
633
|
+
pruneFlatEvent(newHooks, "userPromptSubmitted");
|
|
634
|
+
pruneFlatEvent(newHooks, "postToolUseFailure");
|
|
635
|
+
if (events.includes("sessionStart")) newHooks.sessionStart = [...filterOutMembankFlat(Array.isArray(hooks.sessionStart) ? hooks.sessionStart : []), {
|
|
636
|
+
type: "command",
|
|
637
|
+
bash: "npx @membank/cli@latest inject --harness copilot-cli",
|
|
638
|
+
timeoutSec: 30
|
|
639
|
+
}];
|
|
708
640
|
writeJsonAtomic(cfgPath, {
|
|
709
641
|
version: cfg.version ?? 1,
|
|
710
642
|
...cfg,
|
|
711
|
-
hooks:
|
|
712
|
-
...hooks ?? {},
|
|
713
|
-
sessionStart: [...filteredSessionStart, {
|
|
714
|
-
type: "command",
|
|
715
|
-
bash: "npx @membank/cli inject --harness copilot-cli",
|
|
716
|
-
timeoutSec: 30
|
|
717
|
-
}],
|
|
718
|
-
userPromptSubmitted: [...filterOutMembankFlat(existingUserPrompt), {
|
|
719
|
-
type: "command",
|
|
720
|
-
bash: "npx @membank/cli inject --event user-prompt --harness copilot-cli",
|
|
721
|
-
timeoutSec: 30
|
|
722
|
-
}],
|
|
723
|
-
postToolUseFailure: [...filterOutMembankFlat(existingToolFailure), {
|
|
724
|
-
type: "command",
|
|
725
|
-
bash: "npx @membank/cli inject --event tool-failure --harness copilot-cli",
|
|
726
|
-
timeoutSec: 30
|
|
727
|
-
}]
|
|
728
|
-
}
|
|
643
|
+
hooks: newHooks
|
|
729
644
|
});
|
|
730
645
|
return { status: "written" };
|
|
731
646
|
}
|
|
732
647
|
},
|
|
733
648
|
codex: {
|
|
734
|
-
|
|
735
|
-
|
|
649
|
+
inspect(resolver) {
|
|
650
|
+
const hooks = readJson(join(resolver.home(), ".codex", "hooks.json")).hooks ?? {};
|
|
651
|
+
return {
|
|
652
|
+
status: "ready",
|
|
653
|
+
hooks: [{
|
|
654
|
+
event: "SessionStart",
|
|
655
|
+
command: "npx @membank/cli@latest inject --harness codex",
|
|
656
|
+
existingCommand: extractInjectCommand((Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []).flatMap(getHooksArray)) || null
|
|
657
|
+
}]
|
|
658
|
+
};
|
|
659
|
+
},
|
|
660
|
+
write(resolver, events) {
|
|
736
661
|
const cfgPath = join(resolver.home(), ".codex", "hooks.json");
|
|
737
662
|
const cfg = readJson(cfgPath);
|
|
738
|
-
const hooks = cfg.hooks;
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
663
|
+
const hooks = cfg.hooks ?? {};
|
|
664
|
+
const newHooks = { ...hooks };
|
|
665
|
+
pruneNestedEvent(newHooks, "UserPromptSubmit");
|
|
666
|
+
pruneNestedEvent(newHooks, "PostToolUse");
|
|
667
|
+
if (events.includes("SessionStart")) newHooks.SessionStart = [...filterOutMembank(Array.isArray(hooks.SessionStart) ? hooks.SessionStart : []), {
|
|
668
|
+
matcher: "",
|
|
669
|
+
hooks: [{
|
|
670
|
+
type: "command",
|
|
671
|
+
command: "npx @membank/cli@latest inject --harness codex",
|
|
672
|
+
timeout: 30
|
|
673
|
+
}]
|
|
674
|
+
}];
|
|
749
675
|
writeJsonAtomic(cfgPath, {
|
|
750
676
|
...cfg,
|
|
751
|
-
hooks:
|
|
752
|
-
...hooks ?? {},
|
|
753
|
-
SessionStart: [...filteredSessionStart, {
|
|
754
|
-
matcher: "",
|
|
755
|
-
hooks: [{
|
|
756
|
-
type: "command",
|
|
757
|
-
command: "npx @membank/cli inject --harness codex",
|
|
758
|
-
timeout: 30
|
|
759
|
-
}]
|
|
760
|
-
}],
|
|
761
|
-
UserPromptSubmit: [...filterOutMembank(existingUserPrompt), {
|
|
762
|
-
matcher: "",
|
|
763
|
-
hooks: [{
|
|
764
|
-
type: "command",
|
|
765
|
-
command: "npx @membank/cli inject --event user-prompt --harness codex",
|
|
766
|
-
timeout: 30
|
|
767
|
-
}]
|
|
768
|
-
}],
|
|
769
|
-
PostToolUse: [...filterOutMembank(existingToolFailure), {
|
|
770
|
-
matcher: "",
|
|
771
|
-
hooks: [{
|
|
772
|
-
type: "command",
|
|
773
|
-
command: "npx @membank/cli inject --event tool-failure --harness codex",
|
|
774
|
-
timeout: 30
|
|
775
|
-
}]
|
|
776
|
-
}]
|
|
777
|
-
}
|
|
677
|
+
hooks: newHooks
|
|
778
678
|
});
|
|
779
679
|
return { status: "written" };
|
|
780
680
|
}
|
|
781
681
|
},
|
|
782
682
|
opencode: {
|
|
783
|
-
|
|
784
|
-
write(resolver, overwrite = false) {
|
|
683
|
+
inspect(resolver) {
|
|
785
684
|
const pluginPath = join(resolver.home(), ".config", "opencode", "plugins", "membank.js");
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
if (
|
|
789
|
-
status: "already-configured",
|
|
790
|
-
existing: existing.trim(),
|
|
791
|
-
replacement: newOpencodePlugin()
|
|
792
|
-
};
|
|
685
|
+
let existingCommand = null;
|
|
686
|
+
if (existsSync(pluginPath)) {
|
|
687
|
+
if (readFileSync(pluginPath, "utf8").includes("@membank/cli")) existingCommand = pluginPath;
|
|
793
688
|
}
|
|
689
|
+
return {
|
|
690
|
+
status: "ready",
|
|
691
|
+
hooks: [{
|
|
692
|
+
event: "plugin",
|
|
693
|
+
command: pluginPath,
|
|
694
|
+
existingCommand
|
|
695
|
+
}]
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
write(resolver, events) {
|
|
699
|
+
if (events.length === 0) return { status: "written" };
|
|
700
|
+
const pluginPath = join(resolver.home(), ".config", "opencode", "plugins", "membank.js");
|
|
794
701
|
mkdirSync(dirname(pluginPath), { recursive: true });
|
|
795
702
|
writeFileSync(pluginPath, `${newOpencodePlugin()}\n`, "utf8");
|
|
796
703
|
return { status: "written" };
|
|
@@ -802,19 +709,7 @@ function newOpencodePlugin() {
|
|
|
802
709
|
"export default {",
|
|
803
710
|
" hooks: {",
|
|
804
711
|
" \"session.start\": async ({ $ }) => {",
|
|
805
|
-
" return await $`npx @membank/cli inject`.text();",
|
|
806
|
-
" },",
|
|
807
|
-
" \"chat.message\": async ({ $, message }) => {",
|
|
808
|
-
" const input = JSON.stringify({ prompt: message?.content ?? \"\" });",
|
|
809
|
-
" return await $`npx @membank/cli inject --event user-prompt`.stdin(input).text();",
|
|
810
|
-
" },",
|
|
811
|
-
" \"tool.execute.after\": async ({ $, result }) => {",
|
|
812
|
-
" if (!result?.exitCode && !result?.error) return;",
|
|
813
|
-
" const payload = JSON.stringify({",
|
|
814
|
-
" tool_name: result.tool ?? \"unknown\",",
|
|
815
|
-
" error_message: result.error ?? (\"exit code \" + result.exitCode),",
|
|
816
|
-
" });",
|
|
817
|
-
" return await $`npx @membank/cli inject --event tool-failure`.stdin(payload).text();",
|
|
712
|
+
" return await $`npx @membank/cli@latest inject`.text();",
|
|
818
713
|
" },",
|
|
819
714
|
" },",
|
|
820
715
|
"};"
|
|
@@ -825,10 +720,15 @@ var InjectionHookWriter = class {
|
|
|
825
720
|
constructor(resolver = defaultPathResolver) {
|
|
826
721
|
this.#resolver = resolver;
|
|
827
722
|
}
|
|
828
|
-
|
|
723
|
+
inspect(harness) {
|
|
829
724
|
const writer = writers[harness];
|
|
830
725
|
if (!writer) return { status: "not-supported" };
|
|
831
|
-
return writer.
|
|
726
|
+
return writer.inspect(this.#resolver);
|
|
727
|
+
}
|
|
728
|
+
write(harness, events) {
|
|
729
|
+
const writer = writers[harness];
|
|
730
|
+
if (!writer) return { status: "not-supported" };
|
|
731
|
+
return writer.write(this.#resolver, events);
|
|
832
732
|
}
|
|
833
733
|
};
|
|
834
734
|
//#endregion
|
|
@@ -1062,8 +962,7 @@ var SetupOrchestrator = class {
|
|
|
1062
962
|
out("");
|
|
1063
963
|
const injectionHooksConfigured = [];
|
|
1064
964
|
if (this.#hookWriter) {
|
|
1065
|
-
|
|
1066
|
-
injectionHooksConfigured.push(...await this.#runHookLoop(detected, "injection hook", (h, ow) => w.write(h, ow), yes, out));
|
|
965
|
+
injectionHooksConfigured.push(...await this.#runHookSetup(detected, yes, out));
|
|
1067
966
|
out("");
|
|
1068
967
|
}
|
|
1069
968
|
let modelDownloaded = false;
|
|
@@ -1086,28 +985,33 @@ var SetupOrchestrator = class {
|
|
|
1086
985
|
}
|
|
1087
986
|
return results;
|
|
1088
987
|
}
|
|
1089
|
-
async #
|
|
988
|
+
async #runHookSetup(detected, yes, out) {
|
|
1090
989
|
const configured = [];
|
|
990
|
+
const w = this.#hookWriter;
|
|
1091
991
|
for (const h of detected) try {
|
|
1092
|
-
const
|
|
1093
|
-
if (
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
992
|
+
const inspected = w.inspect(h.name);
|
|
993
|
+
if (inspected.status === "not-supported") continue;
|
|
994
|
+
const toWrite = [];
|
|
995
|
+
for (const hook of inspected.hooks) if (hook.existingCommand === null) {
|
|
996
|
+
out(` ${h.name}: ${hook.event} injection hook`);
|
|
997
|
+
out(` Command: ${hook.command}`);
|
|
998
|
+
if (yes || await this.#prompter(` Configure ${hook.event} injection hook for ${h.name}?`)) toWrite.push(hook.event);
|
|
1097
999
|
} else {
|
|
1098
|
-
out(` ⚠ ${h.name}: ${
|
|
1099
|
-
out(` Current: ${
|
|
1100
|
-
out(` New: ${
|
|
1101
|
-
if (yes || await this.#prompter(` Replace ${
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1000
|
+
out(` ⚠ ${h.name}: ${hook.event} injection hook already configured`);
|
|
1001
|
+
out(` Current: ${hook.existingCommand}`);
|
|
1002
|
+
if (hook.existingCommand !== hook.command) out(` New: ${hook.command}`);
|
|
1003
|
+
if (yes || await this.#prompter(` Replace ${hook.event} injection hook for ${h.name}?`)) toWrite.push(hook.event);
|
|
1004
|
+
}
|
|
1005
|
+
if (toWrite.length > 0) {
|
|
1006
|
+
w.write(h.name, toWrite);
|
|
1007
|
+
const skippedCount = inspected.hooks.length - toWrite.length;
|
|
1008
|
+
const label = skippedCount > 0 ? `${toWrite.length} injection hook(s) written, ${skippedCount} skipped` : `${toWrite.length} injection hook(s) written`;
|
|
1009
|
+
out(` ✓ ${h.name}: ${label}`);
|
|
1010
|
+
configured.push(h.name);
|
|
1107
1011
|
}
|
|
1108
1012
|
} catch (err) {
|
|
1109
1013
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1110
|
-
out(` ✗ ${h.name}
|
|
1014
|
+
out(` ✗ ${h.name} injection hooks: ${msg}`);
|
|
1111
1015
|
}
|
|
1112
1016
|
return configured;
|
|
1113
1017
|
}
|
|
@@ -1241,7 +1145,7 @@ program.command("import <file>").description("import memories from a JSON export
|
|
|
1241
1145
|
db.close();
|
|
1242
1146
|
}
|
|
1243
1147
|
});
|
|
1244
|
-
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)").option("--event <event>", "hook event type (session-start
|
|
1148
|
+
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)").option("--event <event>", "hook event type (only session-start is supported; other values no-op for legacy hook compatibility)", "session-start").action(async (cmdOptions) => {
|
|
1245
1149
|
try {
|
|
1246
1150
|
await injectCommand(cmdOptions);
|
|
1247
1151
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@membank/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"@huggingface/transformers": "^4.2.0",
|
|
18
18
|
"commander": "^14.0.3",
|
|
19
19
|
"ora": "^9.4.0",
|
|
20
|
-
"@membank/
|
|
21
|
-
"@membank/
|
|
22
|
-
"@membank/
|
|
20
|
+
"@membank/core": "0.4.0",
|
|
21
|
+
"@membank/dashboard": "0.2.0",
|
|
22
|
+
"@membank/mcp": "0.4.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^25.6.0",
|