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.
- package/README.md +161 -205
- package/dist/cli-checkpoint.d.ts +16 -0
- package/dist/cli-checkpoint.js +233 -0
- package/dist/cli-checkpoint.js.map +1 -0
- package/dist/cli-context-inject.d.ts +19 -0
- package/dist/cli-context-inject.js +77 -0
- package/dist/cli-context-inject.js.map +1 -0
- package/dist/cli-env.d.ts +16 -0
- package/dist/cli-env.js +40 -0
- package/dist/cli-env.js.map +1 -0
- package/dist/cli-hook-startup.d.ts +20 -0
- package/dist/cli-hook-startup.js +101 -0
- package/dist/cli-hook-startup.js.map +1 -0
- package/dist/cli-init.js +148 -10
- package/dist/cli-init.js.map +1 -1
- package/dist/cli-log-exchange.js +87 -23
- package/dist/cli-log-exchange.js.map +1 -1
- package/dist/cli-statusline.d.ts +14 -0
- package/dist/cli-statusline.js +172 -0
- package/dist/cli-statusline.js.map +1 -0
- package/dist/cli.js +30 -2
- package/dist/cli.js.map +1 -1
- package/dist/hmem-config.d.ts +31 -0
- package/dist/hmem-config.js +76 -12
- package/dist/hmem-config.js.map +1 -1
- package/dist/hmem-store.d.ts +62 -1
- package/dist/hmem-store.js +364 -46
- package/dist/hmem-store.js.map +1 -1
- package/dist/mcp-server.js +405 -99
- package/dist/mcp-server.js.map +1 -1
- package/dist/session-cache.d.ts +11 -0
- package/dist/session-cache.js +25 -0
- package/dist/session-cache.js.map +1 -1
- package/package.json +1 -1
- package/scripts/autoresearch-nightly.sh +84 -0
- package/scripts/hmem-statusline.sh +4 -0
- package/skills/hmem-config/SKILL.md +112 -147
- package/skills/hmem-curate/SKILL.md +56 -6
- package/skills/hmem-new-project/SKILL.md +164 -0
- package/skills/hmem-read/SKILL.md +174 -146
- package/skills/hmem-release/SKILL.md +141 -0
- package/skills/hmem-self-curate/SKILL.md +49 -7
- package/skills/hmem-setup/SKILL.md +169 -87
- package/skills/hmem-sync-setup/SKILL.md +16 -3
- package/skills/hmem-update/SKILL.md +254 -0
- package/skills/hmem-wipe/SKILL.md +75 -0
- 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;
|
package/dist/cli-env.js
ADDED
|
@@ -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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"}
|