hmem-mcp 4.0.0 → 5.1.21

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.
Files changed (47) hide show
  1. package/README.md +161 -205
  2. package/dist/cli-checkpoint.d.ts +16 -0
  3. package/dist/cli-checkpoint.js +233 -0
  4. package/dist/cli-checkpoint.js.map +1 -0
  5. package/dist/cli-context-inject.d.ts +19 -0
  6. package/dist/cli-context-inject.js +77 -0
  7. package/dist/cli-context-inject.js.map +1 -0
  8. package/dist/cli-env.d.ts +16 -0
  9. package/dist/cli-env.js +40 -0
  10. package/dist/cli-env.js.map +1 -0
  11. package/dist/cli-hook-startup.d.ts +20 -0
  12. package/dist/cli-hook-startup.js +101 -0
  13. package/dist/cli-hook-startup.js.map +1 -0
  14. package/dist/cli-init.js +148 -10
  15. package/dist/cli-init.js.map +1 -1
  16. package/dist/cli-log-exchange.js +87 -23
  17. package/dist/cli-log-exchange.js.map +1 -1
  18. package/dist/cli-statusline.d.ts +14 -0
  19. package/dist/cli-statusline.js +172 -0
  20. package/dist/cli-statusline.js.map +1 -0
  21. package/dist/cli.js +30 -2
  22. package/dist/cli.js.map +1 -1
  23. package/dist/hmem-config.d.ts +31 -0
  24. package/dist/hmem-config.js +76 -12
  25. package/dist/hmem-config.js.map +1 -1
  26. package/dist/hmem-store.d.ts +62 -1
  27. package/dist/hmem-store.js +364 -46
  28. package/dist/hmem-store.js.map +1 -1
  29. package/dist/mcp-server.js +405 -99
  30. package/dist/mcp-server.js.map +1 -1
  31. package/dist/session-cache.d.ts +11 -0
  32. package/dist/session-cache.js +25 -0
  33. package/dist/session-cache.js.map +1 -1
  34. package/package.json +1 -1
  35. package/scripts/autoresearch-nightly.sh +84 -0
  36. package/scripts/hmem-statusline.sh +4 -0
  37. package/skills/hmem-config/SKILL.md +112 -147
  38. package/skills/hmem-curate/SKILL.md +56 -6
  39. package/skills/hmem-new-project/SKILL.md +164 -0
  40. package/skills/hmem-read/SKILL.md +174 -146
  41. package/skills/hmem-release/SKILL.md +141 -0
  42. package/skills/hmem-self-curate/SKILL.md +49 -7
  43. package/skills/hmem-setup/SKILL.md +169 -87
  44. package/skills/hmem-sync-setup/SKILL.md +16 -3
  45. package/skills/hmem-update/SKILL.md +254 -0
  46. package/skills/hmem-wipe/SKILL.md +75 -0
  47. package/skills/hmem-write/SKILL.md +113 -61
@@ -0,0 +1,233 @@
1
+ /**
2
+ * cli-checkpoint.ts
3
+ *
4
+ * Automatic checkpoint: reads recent exchanges from the active O-entry,
5
+ * then spawns a Haiku subagent WITH MCP tool access that writes L/D/E entries
6
+ * and updates the project handoff. The subagent follows the hmem-write skill rules.
7
+ *
8
+ * Designed to run in the background (spawned by the Stop hook when checkpointMode is "auto").
9
+ *
10
+ * Usage: hmem checkpoint
11
+ *
12
+ * Requires env:
13
+ * HMEM_PROJECT_DIR — root directory for .hmem files
14
+ * HMEM_AGENT_ID — agent identifier (optional, auto-detected)
15
+ */
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ import { execSync } from "node:child_process";
19
+ import { HmemStore, resolveHmemPath } from "./hmem-store.js";
20
+ import { loadHmemConfig } from "./hmem-config.js";
21
+ /** Build a minimal MCP config JSON for the subagent (hmem only). */
22
+ function buildMcpConfig(projectDir, agentId) {
23
+ // Find the hmem-mcp entry point
24
+ let hmemServerPath;
25
+ try {
26
+ hmemServerPath = execSync("which hmem", { encoding: "utf8" }).trim();
27
+ // Resolve symlink to find the actual JS file
28
+ const realPath = fs.realpathSync(hmemServerPath);
29
+ hmemServerPath = path.join(path.dirname(realPath), "mcp-server.js");
30
+ if (!fs.existsSync(hmemServerPath)) {
31
+ // Fallback: look in the dist directory relative to the resolved path
32
+ hmemServerPath = path.join(path.dirname(path.dirname(realPath)), "dist", "mcp-server.js");
33
+ }
34
+ }
35
+ catch {
36
+ // Fallback: global npm path
37
+ hmemServerPath = path.join(process.env.HOME || "/home", ".nvm/versions/node", process.version, "lib/node_modules/hmem-mcp/dist/mcp-server.js");
38
+ }
39
+ const nodePath = process.execPath;
40
+ const config = {
41
+ mcpServers: {
42
+ hmem: {
43
+ command: nodePath,
44
+ args: [hmemServerPath],
45
+ env: {
46
+ HMEM_PROJECT_DIR: projectDir,
47
+ HMEM_AGENT_ID: agentId,
48
+ HMEM_NO_SESSION: "1",
49
+ },
50
+ },
51
+ },
52
+ };
53
+ const tmpPath = path.join("/tmp", `hmem-checkpoint-mcp-${process.pid}.json`);
54
+ fs.writeFileSync(tmpPath, JSON.stringify(config), "utf8");
55
+ return tmpPath;
56
+ }
57
+ export async function checkpoint() {
58
+ const projectDir = process.env.HMEM_PROJECT_DIR || process.env.COUNCIL_PROJECT_DIR;
59
+ if (!projectDir)
60
+ process.exit(0);
61
+ const agentId = process.env.HMEM_AGENT_ID || process.env.COUNCIL_AGENT_ID || "";
62
+ const templateName = agentId.replace(/_\d+$/, "");
63
+ const hmemPath = resolveHmemPath(projectDir, templateName);
64
+ if (!fs.existsSync(hmemPath))
65
+ process.exit(0);
66
+ const config = loadHmemConfig(path.dirname(hmemPath));
67
+ const store = new HmemStore(hmemPath, config);
68
+ let mcpConfigPath = "";
69
+ try {
70
+ // 1. Get active O-entry and recent exchanges
71
+ const activeOId = store.getActiveOId();
72
+ if (!activeOId)
73
+ return;
74
+ const exchanges = store.getOEntryExchanges(activeOId, 20);
75
+ if (exchanges.length < 3)
76
+ return; // Not enough context
77
+ // 2. Get active project
78
+ const activeProject = store.getActiveProject();
79
+ const projectName = activeProject?.title?.split("|")[0]?.trim() ?? "unknown";
80
+ const projectId = activeProject?.id ?? "";
81
+ // 3. Tag skill-dialog exchanges (brainstorming, debugging, TDD, etc.)
82
+ const skillMarker = "Base directory for this skill:";
83
+ for (const ex of exchanges) {
84
+ if (ex.userText.includes(skillMarker)) {
85
+ store.addTag(ex.nodeId, "#skill-dialog");
86
+ }
87
+ }
88
+ // 4. Get previous checkpoint summaries for rolling compression
89
+ const prevSummaries = store.getCheckpointSummaries(activeOId, 2);
90
+ const lastSummarySeq = prevSummaries.length > 0 ? prevSummaries[0].seq : 0;
91
+ // Only format exchanges AFTER the last summary (those are new)
92
+ const newExchanges = exchanges.filter(ex => ex.seq > lastSummarySeq);
93
+ // Split: last 5 stay verbatim, the rest get summarized
94
+ const VERBATIM_WINDOW = 5;
95
+ const toSummarize = newExchanges.length > VERBATIM_WINDOW
96
+ ? newExchanges.slice(0, -VERBATIM_WINDOW)
97
+ : [];
98
+ const verbatimExchanges = newExchanges.slice(-VERBATIM_WINDOW);
99
+ // 5. Format exchanges (generous limits — Haiku needs context)
100
+ const formattedExchanges = newExchanges.map((ex, i) => {
101
+ const user = ex.userText.length > 800 ? ex.userText.substring(0, 800) + "..." : ex.userText;
102
+ const agent = ex.agentText.length > 1200 ? ex.agentText.substring(0, 1200) + "..." : ex.agentText;
103
+ const currentTitle = ex.userText.split("\n")[0].replace(/[<>\[\]]/g, "").substring(0, 40);
104
+ return `--- Exchange ${i + 1} (${ex.nodeId}) [title: "${currentTitle}"] ---\nUSER: ${user}\nAGENT: ${agent}`;
105
+ }).join("\n\n");
106
+ // Format previous summaries for rolling compression
107
+ let prevSummaryText = "";
108
+ if (prevSummaries.length > 0) {
109
+ const parts = prevSummaries.reverse().map((s, i) => {
110
+ const label = i === prevSummaries.length - 1 ? "Most recent summary" : "Older summary";
111
+ return `[${label} — ${s.created_at.substring(0, 16)}]\n${s.content}`;
112
+ });
113
+ prevSummaryText = parts.join("\n\n");
114
+ }
115
+ // 6. Close store before spawning subagent (avoid DB lock)
116
+ store.close();
117
+ // 5. Build MCP config for subagent
118
+ mcpConfigPath = buildMcpConfig(projectDir, agentId);
119
+ // 7. Build the prompt
120
+ const summarySection = prevSummaryText
121
+ ? `\n## Previous checkpoint summaries (oldest first):\n\n${prevSummaryText}\n`
122
+ : "";
123
+ // Build exchange listing for titling
124
+ const exchangeListing = newExchanges.map(ex => {
125
+ const currentTitle = ex.userText.split("\n")[0].replace(/[<>\[\]]/g, "").substring(0, 40);
126
+ return ` ${ex.nodeId}: "${currentTitle}"`;
127
+ }).join("\n");
128
+ // Build conditional summary task
129
+ const hasSummaryWork = toSummarize.length > 0;
130
+ const summaryNodeIds = toSummarize.map(e => e.nodeId).join(", ");
131
+ const verbatimNodeIds = verbatimExchanges.map(e => e.nodeId).join(", ");
132
+ const summaryTask = hasSummaryWork
133
+ ? `### 4. Checkpoint summary (REQUIRED — ${toSummarize.length} exchanges to compress)
134
+ Summarize exchanges ${summaryNodeIds} into a rolling summary.
135
+ ${prevSummaryText ? "IMPORTANT: Incorporate the previous summary into your new one — it covers older exchanges. The new summary should be a CUMULATIVE summary of everything so far." : "This is the first summary for this session."}
136
+ append_memory(id="${activeOId}", content="\\t[CP] Rolling summary covering all exchanges through ${toSummarize[toSummarize.length - 1].nodeId}. 3-8 sentences. Match conversation language.")
137
+ The last ${verbatimExchanges.length} exchanges (${verbatimNodeIds}) stay verbatim — do NOT include them in the summary.`
138
+ : `### 4. Checkpoint summary — SKIP
139
+ Only ${newExchanges.length} exchanges since last summary — all within the verbatim window (last ${VERBATIM_WINDOW}). No summary needed.`;
140
+ const prompt = `You are a checkpoint agent for "${projectName}" (${projectId}). Process ${newExchanges.length} new exchanges from ${activeOId}.
141
+ ${summarySection}
142
+ ${formattedExchanges}
143
+
144
+ ## Tasks (execute ALL in order):
145
+
146
+ ### 1. Title each exchange (REQUIRED)
147
+ Each exchange node needs a descriptive title (max 50 chars, match conversation language).
148
+ Current titles (auto-extracted, usually bad):
149
+ ${exchangeListing}
150
+
151
+ For each: update_memory(id="<nodeId>", content="Descriptive title summarizing the exchange")
152
+ Example: update_memory(id="${newExchanges[0]?.nodeId || "O0XXX.1"}", content="Fix hmem-sync path resolution for HMEM_AGENT_ID")
153
+
154
+ ### 2. Extract knowledge (non-obvious only, max 2-3)
155
+ - L: Root-cause lesson (not surface symptoms)
156
+ - E: Bug + root cause + fix
157
+ - D: Architecture decision + rationale
158
+ write_memory(prefix="L/E/D", content="...", tags=[3-5 tags], links=["${projectId}"])
159
+ Skip if nothing non-obvious happened.
160
+
161
+ ### 3. Update project P-entry (IMPORTANT)
162
+ Keep ${projectName} (${projectId}) current:
163
+ - **Protocol** (.7): append_memory(id="${projectId}.7", content="Session YYYY-MM-DD: what happened, what was decided/shipped")
164
+ - **Bugs** (.6): new bugs found → append with reference to E-entry
165
+ - **Open tasks** (.8): tasks completed → update as done; new tasks → append
166
+ - **Overview** (.1): if project state, architecture, or goals changed significantly
167
+ - **Codebase** (.2): if new files/modules added or entry points changed
168
+ Read the P-entry first: read_memory(id="${projectId}") to see current state before updating.
169
+
170
+ ${summaryTask}
171
+
172
+ ### 5. Title the O-entry root
173
+ If the O-entry root (${activeOId}) still has a generic title like "unassigned" or just the project name,
174
+ give it a proper session title based on what happened so far:
175
+ update_memory(id="${activeOId}", content="Session title summarizing key topics (max 60 chars)")
176
+
177
+ ### 6. Project relevance check
178
+ Are these exchanges about ${projectName}? If conversation drifted to a different project, note "[DRIFT: topic X]" in the summary.
179
+
180
+ ## Rules:
181
+ - read_memory() FIRST to avoid duplicates and see current P-entry state
182
+ - Match language of existing entries (likely German)
183
+ - Tags: 3-5 per entry, lowercase with #
184
+ - Only save what's valuable in 6 months
185
+ - Do NOT skip titling — every exchange needs a proper title`;
186
+ // 7. Spawn Haiku with MCP access
187
+ const allowedTools = [
188
+ "mcp__hmem__read_memory",
189
+ "mcp__hmem__write_memory",
190
+ "mcp__hmem__append_memory",
191
+ "mcp__hmem__update_memory",
192
+ ].join(" ");
193
+ const disallowedTools = "mcp__hmem__flush_context";
194
+ try {
195
+ const output = execSync(`claude -p --model haiku --mcp-config "${mcpConfigPath}" --allowedTools "${allowedTools}" --disallowedTools "${disallowedTools}" --dangerously-skip-permissions 2>/dev/null`, {
196
+ input: prompt,
197
+ encoding: "utf8",
198
+ timeout: 120_000,
199
+ }).trim();
200
+ console.log(`[hmem checkpoint] Haiku: ${output.substring(0, 300)}`);
201
+ // Tag the checkpoint summary node that Haiku wrote via append_memory
202
+ // Haiku writes it as a [CP] prefixed L2 node under the O-entry
203
+ try {
204
+ const postStore = new HmemStore(hmemPath, config);
205
+ const tagged = postStore.tagNewCheckpointSummaries(activeOId);
206
+ if (tagged.length > 0) {
207
+ console.log(`[hmem checkpoint] Tagged checkpoint summaries: ${tagged.join(", ")}`);
208
+ }
209
+ postStore.close();
210
+ }
211
+ catch (tagErr) {
212
+ console.error(`[hmem checkpoint] Failed to tag summary: ${tagErr}`);
213
+ }
214
+ }
215
+ catch (e) {
216
+ const stdout = e.stdout?.toString()?.substring(0, 200) || "";
217
+ console.error(`[hmem checkpoint] Failed (exit ${e.status}): ${stdout}`);
218
+ }
219
+ }
220
+ catch (e) {
221
+ console.error(`[hmem checkpoint] ${e}`);
222
+ }
223
+ finally {
224
+ // Cleanup temp MCP config
225
+ if (mcpConfigPath) {
226
+ try {
227
+ fs.unlinkSync(mcpConfigPath);
228
+ }
229
+ catch { }
230
+ }
231
+ }
232
+ }
233
+ //# sourceMappingURL=cli-checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-checkpoint.js","sourceRoot":"","sources":["../src/cli-checkpoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * cli-context-inject.ts
3
+ *
4
+ * Called by Claude Code's SessionStart[clear] hook after /clear.
5
+ * Outputs a compact context summary to stdout for re-injection:
6
+ * - Compact project overview (all P-entries, one line each, active marked)
7
+ * - R-entries (rules, one line each)
8
+ * - Hint to use load_project for full briefing
9
+ *
10
+ * Deliberately lightweight (~200 tokens). Full context comes from
11
+ * load_project() or read_memory() which the agent calls next.
12
+ *
13
+ * Usage: hmem context-inject (reads stdin JSON from Claude Code hook)
14
+ *
15
+ * Requires env:
16
+ * HMEM_PROJECT_DIR — root directory for .hmem files
17
+ * HMEM_AGENT_ID — agent identifier (optional)
18
+ */
19
+ export declare function contextInject(): Promise<void>;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * cli-context-inject.ts
3
+ *
4
+ * Called by Claude Code's SessionStart[clear] hook after /clear.
5
+ * Outputs a compact context summary to stdout for re-injection:
6
+ * - Compact project overview (all P-entries, one line each, active marked)
7
+ * - R-entries (rules, one line each)
8
+ * - Hint to use load_project for full briefing
9
+ *
10
+ * Deliberately lightweight (~200 tokens). Full context comes from
11
+ * load_project() or read_memory() which the agent calls next.
12
+ *
13
+ * Usage: hmem context-inject (reads stdin JSON from Claude Code hook)
14
+ *
15
+ * Requires env:
16
+ * HMEM_PROJECT_DIR — root directory for .hmem files
17
+ * HMEM_AGENT_ID — agent identifier (optional)
18
+ */
19
+ import fs from "node:fs";
20
+ import { openAgentMemory } from "./hmem-store.js";
21
+ import { loadHmemConfig } from "./hmem-config.js";
22
+ import { resolveEnvDefaults } from "./cli-env.js";
23
+ export async function contextInject() {
24
+ // Resolve env defaults (HMEM_PROJECT_DIR, HMEM_AGENT_ID)
25
+ resolveEnvDefaults();
26
+ const projectDir = process.env.HMEM_PROJECT_DIR || "";
27
+ if (!projectDir) {
28
+ process.stderr.write("HMEM_PROJECT_DIR not set\n");
29
+ return;
30
+ }
31
+ // Read hook input from stdin (required by Claude Code hook protocol)
32
+ try {
33
+ fs.readFileSync(0, "utf-8");
34
+ }
35
+ catch { /* no stdin — OK */ }
36
+ const agentId = process.env.HMEM_AGENT_ID || process.env.COUNCIL_AGENT_ID || "";
37
+ const templateName = agentId.replace(/_\d+$/, "");
38
+ const config = loadHmemConfig(projectDir);
39
+ let store;
40
+ try {
41
+ store = openAgentMemory(projectDir, templateName, config);
42
+ }
43
+ catch (e) {
44
+ process.stderr.write(`Failed to open memory: ${e}\n`);
45
+ return;
46
+ }
47
+ try {
48
+ const lines = [];
49
+ // 1. Compact project overview — one line per P-entry
50
+ const allProjects = store.read({ prefix: "P", depth: 1, agentRole: "worker" })
51
+ .filter(e => !e.obsolete && !e.irrelevant);
52
+ const activeProject = allProjects.find(e => e.active);
53
+ if (allProjects.length > 0) {
54
+ lines.push("## Projects:");
55
+ for (const p of allProjects) {
56
+ const marker = p.active ? " [*]" : "";
57
+ lines.push(` ${p.id}${marker} ${p.title}`);
58
+ }
59
+ }
60
+ // 2. R-entries (rules) — compact, one line each
61
+ const rules = store.read({ prefix: "R", depth: 1, agentRole: "worker" })
62
+ .filter(r => !r.obsolete && !r.irrelevant);
63
+ if (rules.length > 0) {
64
+ lines.push("\n## Rules:");
65
+ for (const r of rules) {
66
+ const body = r.level_1 && r.level_1 !== r.title ? `\n> ${r.level_1}` : "";
67
+ lines.push(` ${r.id} ${r.title}${body}`);
68
+ }
69
+ }
70
+ lines.push(`\n(Context re-injected after /clear. Use load_project for full briefing, read_memory(id) to drill into specific entries.)`);
71
+ process.stdout.write(lines.join("\n") + "\n");
72
+ }
73
+ finally {
74
+ store.close();
75
+ }
76
+ }
77
+ //# sourceMappingURL=cli-context-inject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-context-inject.js","sourceRoot":"","sources":["../src/cli-context-inject.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * cli-env.ts
3
+ *
4
+ * Shared environment variable resolution for all hmem CLI commands.
5
+ * Sets HMEM_PROJECT_DIR and HMEM_AGENT_ID defaults so CLI commands
6
+ * work without a bash wrapper script (cross-platform, no Git Bash needed on Windows).
7
+ */
8
+ /**
9
+ * Resolve HMEM_PROJECT_DIR and HMEM_AGENT_ID environment variables.
10
+ *
11
+ * - HMEM_PROJECT_DIR defaults to ~/.hmem
12
+ * - HMEM_AGENT_ID is auto-detected from Agents/ directory if not set
13
+ *
14
+ * Call this early in any CLI command that needs these env vars.
15
+ */
16
+ export declare function resolveEnvDefaults(): void;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * cli-env.ts
3
+ *
4
+ * Shared environment variable resolution for all hmem CLI commands.
5
+ * Sets HMEM_PROJECT_DIR and HMEM_AGENT_ID defaults so CLI commands
6
+ * work without a bash wrapper script (cross-platform, no Git Bash needed on Windows).
7
+ */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import os from "node:os";
11
+ /**
12
+ * Resolve HMEM_PROJECT_DIR and HMEM_AGENT_ID environment variables.
13
+ *
14
+ * - HMEM_PROJECT_DIR defaults to ~/.hmem
15
+ * - HMEM_AGENT_ID is auto-detected from Agents/ directory if not set
16
+ *
17
+ * Call this early in any CLI command that needs these env vars.
18
+ */
19
+ export function resolveEnvDefaults() {
20
+ // HMEM_PROJECT_DIR: default to ~/.hmem
21
+ if (!process.env.HMEM_PROJECT_DIR) {
22
+ process.env.HMEM_PROJECT_DIR = process.env.COUNCIL_PROJECT_DIR || path.join(os.homedir(), ".hmem");
23
+ }
24
+ const projectDir = process.env.HMEM_PROJECT_DIR;
25
+ // HMEM_AGENT_ID: auto-detect from Agents/ directory
26
+ if (!process.env.HMEM_AGENT_ID && !process.env.COUNCIL_AGENT_ID) {
27
+ const agentsDir = path.join(projectDir, "Agents");
28
+ try {
29
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
30
+ for (const entry of entries) {
31
+ if (entry.isDirectory()) {
32
+ process.env.HMEM_AGENT_ID = entry.name;
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ catch { }
38
+ }
39
+ }
40
+ //# sourceMappingURL=cli-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-env.js","sourceRoot":"","sources":["../src/cli-env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,uCAAuC;IACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IACrG,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAEhD,oDAAoD;IACpD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC;oBACvC,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * cli-hook-startup.ts
3
+ *
4
+ * Called by Claude Code's UserPromptSubmit hook on every user message.
5
+ * Replaces the former hmem-startup.sh bash script — works cross-platform (no Git Bash needed on Windows).
6
+ *
7
+ * Behavior:
8
+ * - First message: remind agent to call read_memory()
9
+ * - Every N messages: checkpoint reminder (remind mode only)
10
+ * - After 60 messages: context warning (every 5 messages)
11
+ *
12
+ * Reads hook JSON from stdin, outputs hook JSON to stdout.
13
+ *
14
+ * Usage: hmem hook-startup
15
+ *
16
+ * Requires env:
17
+ * HMEM_PROJECT_DIR — root directory for .hmem files (default: ~/.hmem)
18
+ * HMEM_AGENT_ID — agent identifier (optional, auto-detected)
19
+ */
20
+ export declare function hookStartup(): Promise<void>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * cli-hook-startup.ts
3
+ *
4
+ * Called by Claude Code's UserPromptSubmit hook on every user message.
5
+ * Replaces the former hmem-startup.sh bash script — works cross-platform (no Git Bash needed on Windows).
6
+ *
7
+ * Behavior:
8
+ * - First message: remind agent to call read_memory()
9
+ * - Every N messages: checkpoint reminder (remind mode only)
10
+ * - After 60 messages: context warning (every 5 messages)
11
+ *
12
+ * Reads hook JSON from stdin, outputs hook JSON to stdout.
13
+ *
14
+ * Usage: hmem hook-startup
15
+ *
16
+ * Requires env:
17
+ * HMEM_PROJECT_DIR — root directory for .hmem files (default: ~/.hmem)
18
+ * HMEM_AGENT_ID — agent identifier (optional, auto-detected)
19
+ */
20
+ import fs from "node:fs";
21
+ import os from "node:os";
22
+ import path from "node:path";
23
+ import { resolveEnvDefaults } from "./cli-env.js";
24
+ import { loadHmemConfig } from "./hmem-config.js";
25
+ export async function hookStartup() {
26
+ // Read hook JSON from stdin
27
+ let input;
28
+ try {
29
+ const data = fs.readFileSync(0, "utf8");
30
+ input = JSON.parse(data || "{}");
31
+ }
32
+ catch {
33
+ process.exit(0);
34
+ }
35
+ // Extract session_id
36
+ const sessionId = input?.session_id || "global";
37
+ // Skip subagents
38
+ if (input?.parentUuid)
39
+ process.exit(0);
40
+ // Resolve env defaults
41
+ resolveEnvDefaults();
42
+ // Read config
43
+ let interval = 20;
44
+ let mode = "remind";
45
+ const projectDir = process.env.HMEM_PROJECT_DIR;
46
+ if (projectDir) {
47
+ try {
48
+ const agentId = process.env.HMEM_AGENT_ID || "";
49
+ const templateName = agentId.replace(/_\d+$/, "");
50
+ let configDir = projectDir;
51
+ if (templateName && templateName !== "UNKNOWN") {
52
+ const agentDir = path.join(projectDir, "Agents", templateName);
53
+ if (fs.existsSync(agentDir))
54
+ configDir = agentDir;
55
+ }
56
+ const config = loadHmemConfig(configDir);
57
+ interval = config.checkpointInterval;
58
+ mode = config.checkpointMode;
59
+ }
60
+ catch { }
61
+ }
62
+ // Counter file (session-scoped)
63
+ const counterFile = path.join(os.tmpdir(), `claude-hmem-counter-${sessionId}`);
64
+ let count = 0;
65
+ try {
66
+ count = parseInt(fs.readFileSync(counterFile, "utf8").trim(), 10) || 0;
67
+ }
68
+ catch { }
69
+ count++;
70
+ fs.writeFileSync(counterFile, String(count), "utf8");
71
+ // First message: load memory
72
+ if (count === 1) {
73
+ process.stdout.write(JSON.stringify({
74
+ hookSpecificOutput: {
75
+ hookEventName: "UserPromptSubmit",
76
+ additionalContext: "IMPORTANT: This is the first message of the session. Load your memory context silently \u2014 do not mention it to the user.\n\n" +
77
+ "- If the user\u2019s message names a specific project (e.g. \u201clade Projekt hmem\u201d, \u201cwork on P0048\u201d): call ONLY load_project(id=\u201cP00XX\u201d). Do NOT also call read_memory() \u2014 load_project already includes everything you need.\n" +
78
+ "- Otherwise: call read_memory() (no parameters) to get the full L1 overview, then decide.",
79
+ },
80
+ }));
81
+ }
82
+ else if (mode === "remind" && interval > 0 && count % interval === 0) {
83
+ // Checkpoint reminder (remind mode only — auto mode is handled by Stop hook)
84
+ process.stdout.write(JSON.stringify({
85
+ hookSpecificOutput: {
86
+ hookEventName: "UserPromptSubmit",
87
+ additionalContext: "CHECKPOINT: You have been working for a while. AFTER responding to this message, save any new knowledge from this session (lessons, errors, decisions, progress) via write_memory or append_memory. You MUST do this \u2014 it is your only way to remember across sessions.",
88
+ },
89
+ }));
90
+ }
91
+ else if (count >= 60 && count % 5 === 0) {
92
+ // Context warning for long sessions
93
+ process.stdout.write(JSON.stringify({
94
+ hookSpecificOutput: {
95
+ hookEventName: "UserPromptSubmit",
96
+ additionalContext: "CONTEXT WARNING: This session has been running for a long time. Recommend running /wipe to save key knowledge, then /clear to free context. Performance degrades significantly in very long sessions.",
97
+ },
98
+ }));
99
+ }
100
+ }
101
+ //# sourceMappingURL=cli-hook-startup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-hook-startup.js","sourceRoot":"","sources":["../src/cli-hook-startup.ts"],"names":[],"mappings":"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"}