hmem-mcp 5.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 -214
- package/dist/cli-checkpoint.js +102 -40
- package/dist/cli-checkpoint.js.map +1 -1
- package/dist/cli-context-inject.d.ts +7 -6
- package/dist/cli-context-inject.js +27 -130
- package/dist/cli-context-inject.js.map +1 -1
- 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 +97 -188
- package/dist/cli-init.js.map +1 -1
- package/dist/cli-log-exchange.js +63 -3
- 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 +18 -2
- package/dist/cli.js.map +1 -1
- package/dist/hmem-config.d.ts +10 -0
- package/dist/hmem-config.js +63 -13
- package/dist/hmem-config.js.map +1 -1
- package/dist/hmem-store.d.ts +30 -1
- package/dist/hmem-store.js +219 -48
- package/dist/hmem-store.js.map +1 -1
- package/dist/mcp-server.js +202 -75
- package/dist/mcp-server.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 +47 -21
- package/skills/hmem-write/SKILL.md +38 -14
package/dist/cli-checkpoint.js
CHANGED
|
@@ -78,62 +78,111 @@ export async function checkpoint() {
|
|
|
78
78
|
const activeProject = store.getActiveProject();
|
|
79
79
|
const projectName = activeProject?.title?.split("|")[0]?.trim() ?? "unknown";
|
|
80
80
|
const projectId = activeProject?.id ?? "";
|
|
81
|
-
// 3.
|
|
82
|
-
const
|
|
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) => {
|
|
83
101
|
const user = ex.userText.length > 800 ? ex.userText.substring(0, 800) + "..." : ex.userText;
|
|
84
102
|
const agent = ex.agentText.length > 1200 ? ex.agentText.substring(0, 1200) + "..." : ex.agentText;
|
|
85
|
-
|
|
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}`;
|
|
86
105
|
}).join("\n\n");
|
|
87
|
-
//
|
|
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)
|
|
88
116
|
store.close();
|
|
89
117
|
// 5. Build MCP config for subagent
|
|
90
118
|
mcpConfigPath = buildMcpConfig(projectDir, agentId);
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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}
|
|
101
142
|
${formattedExchanges}
|
|
102
143
|
|
|
103
|
-
##
|
|
144
|
+
## Tasks (execute ALL in order):
|
|
104
145
|
|
|
105
|
-
|
|
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}
|
|
106
150
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- BAD: "Stop Hook logs exchanges" (that's a feature description, not a lesson)
|
|
110
|
-
- GOOD: "HMEM_AGENT_ID must be set in hook scripts, otherwise resolveHmemPath falls back to memory.hmem instead of Agents/NAME/NAME.hmem"
|
|
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")
|
|
111
153
|
|
|
112
|
-
2.
|
|
113
|
-
|
|
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.
|
|
114
160
|
|
|
115
|
-
3.
|
|
116
|
-
|
|
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.
|
|
117
169
|
|
|
118
|
-
|
|
119
|
-
- 2-3 sentences: what was accomplished, what's in progress, next step
|
|
120
|
-
- Use: append_memory(id="${projectId}.7", content="Handoff (YYYY-MM-DD HH:MM): ...")
|
|
170
|
+
${summaryTask}
|
|
121
171
|
|
|
122
|
-
###
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- Skip if nothing noteworthy — don't write garbage entries
|
|
127
|
-
- Max 2-3 entries total. Quality over quantity
|
|
128
|
-
- Each L1 must be a complete, self-contained sentence (~15-20 tokens)
|
|
129
|
-
- Title (~50 chars) should be specific, not vague
|
|
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)")
|
|
130
176
|
|
|
131
|
-
###
|
|
132
|
-
|
|
133
|
-
2. Check if a similar L/D/E already exists before creating a new one
|
|
134
|
-
3. If it does, use append_memory to extend it instead
|
|
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.
|
|
135
179
|
|
|
136
|
-
|
|
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`;
|
|
137
186
|
// 7. Spawn Haiku with MCP access
|
|
138
187
|
const allowedTools = [
|
|
139
188
|
"mcp__hmem__read_memory",
|
|
@@ -149,6 +198,19 @@ Now analyze the conversation and save what's worth keeping. If nothing is notewo
|
|
|
149
198
|
timeout: 120_000,
|
|
150
199
|
}).trim();
|
|
151
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
|
+
}
|
|
152
214
|
}
|
|
153
215
|
catch (e) {
|
|
154
216
|
const stdout = e.stdout?.toString()?.substring(0, 200) || "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-checkpoint.js","sourceRoot":"","sources":["../src/cli-checkpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,oEAAoE;AACpE,SAAS,cAAc,CAAC,UAAkB,EAAE,OAAe;IACzD,gCAAgC;IAChC,IAAI,cAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,cAAc,GAAG,QAAQ,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;QACjD,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;QACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YACnC,qEAAqE;YACrE,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;QAC5B,cAAc,GAAG,IAAI,CAAC,IAAI,CACxB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAC3B,oBAAoB,EACpB,OAAO,CAAC,OAAO,EACf,8CAA8C,CAC/C,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG;QACb,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,CAAC,cAAc,CAAC;gBACtB,GAAG,EAAE;oBACH,gBAAgB,EAAE,UAAU;oBAC5B,aAAa,EAAE,OAAO;oBACtB,eAAe,EAAE,GAAG;iBACrB;aACF;SACF;KACF,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,uBAAuB,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;IAC7E,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACnF,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEjC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;IAChF,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE9C,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,IAAI,CAAC;QACH,6CAA6C;QAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;QACvC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,SAAS,GAAG,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,qBAAqB;QAEvD,wBAAwB;QACxB,MAAM,aAAa,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC/C,MAAM,WAAW,GAAG,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;QAC7E,MAAM,SAAS,GAAG,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC;QAE1C,8DAA8D;QAC9D,MAAM,kBAAkB,GAAG,
|
|
1
|
+
{"version":3,"file":"cli-checkpoint.js","sourceRoot":"","sources":["../src/cli-checkpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* cli-context-inject.ts
|
|
3
3
|
*
|
|
4
4
|
* Called by Claude Code's SessionStart[clear] hook after /clear.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
11
12
|
*
|
|
12
13
|
* Usage: hmem context-inject (reads stdin JSON from Claude Code hook)
|
|
13
14
|
*
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* cli-context-inject.ts
|
|
3
3
|
*
|
|
4
4
|
* Called by Claude Code's SessionStart[clear] hook after /clear.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
11
12
|
*
|
|
12
13
|
* Usage: hmem context-inject (reads stdin JSON from Claude Code hook)
|
|
13
14
|
*
|
|
@@ -18,73 +19,20 @@
|
|
|
18
19
|
import fs from "node:fs";
|
|
19
20
|
import { openAgentMemory } from "./hmem-store.js";
|
|
20
21
|
import { loadHmemConfig } from "./hmem-config.js";
|
|
21
|
-
|
|
22
|
-
const RECENT_MESSAGES = 20;
|
|
23
|
-
/** Read the last N user/assistant messages from a JSONL transcript. */
|
|
24
|
-
function readRecentTranscript(transcriptPath, count) {
|
|
25
|
-
if (!transcriptPath || !fs.existsSync(transcriptPath))
|
|
26
|
-
return [];
|
|
27
|
-
const stat = fs.statSync(transcriptPath);
|
|
28
|
-
const TAIL_BYTES = 5 * 1024 * 1024; // 5MB tail
|
|
29
|
-
const start = Math.max(0, stat.size - TAIL_BYTES);
|
|
30
|
-
const fd = fs.openSync(transcriptPath, "r");
|
|
31
|
-
const buf = Buffer.alloc(Math.min(stat.size, TAIL_BYTES));
|
|
32
|
-
fs.readSync(fd, buf, 0, buf.length, start);
|
|
33
|
-
fs.closeSync(fd);
|
|
34
|
-
const text = buf.toString("utf-8");
|
|
35
|
-
const lines = text.split("\n").filter(l => l.trim());
|
|
36
|
-
const messages = [];
|
|
37
|
-
for (const line of lines) {
|
|
38
|
-
try {
|
|
39
|
-
const entry = JSON.parse(line);
|
|
40
|
-
// Claude Code transcript format: { type: "human"|"assistant", message: { role, content } }
|
|
41
|
-
const role = entry.type || entry.role || entry.message?.role;
|
|
42
|
-
if (role !== "human" && role !== "assistant" && role !== "user")
|
|
43
|
-
continue;
|
|
44
|
-
const content = entry.message?.content || entry.content;
|
|
45
|
-
if (!content)
|
|
46
|
-
continue;
|
|
47
|
-
let text;
|
|
48
|
-
if (typeof content === "string") {
|
|
49
|
-
text = content;
|
|
50
|
-
}
|
|
51
|
-
else if (Array.isArray(content)) {
|
|
52
|
-
text = content
|
|
53
|
-
.filter(c => c.type === "text" && c.text)
|
|
54
|
-
.map(c => c.text)
|
|
55
|
-
.join("\n");
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (!text.trim())
|
|
61
|
-
continue;
|
|
62
|
-
const label = (role === "human" || role === "user") ? "USER" : "ASSISTANT";
|
|
63
|
-
// Truncate very long messages (e.g. tool outputs)
|
|
64
|
-
const maxLen = 500;
|
|
65
|
-
const truncated = text.length > maxLen ? text.substring(0, maxLen) + "..." : text;
|
|
66
|
-
messages.push(`${label}: ${truncated}`);
|
|
67
|
-
}
|
|
68
|
-
catch { /* skip malformed lines */ }
|
|
69
|
-
}
|
|
70
|
-
return messages.slice(-count);
|
|
71
|
-
}
|
|
22
|
+
import { resolveEnvDefaults } from "./cli-env.js";
|
|
72
23
|
export async function contextInject() {
|
|
73
|
-
|
|
24
|
+
// Resolve env defaults (HMEM_PROJECT_DIR, HMEM_AGENT_ID)
|
|
25
|
+
resolveEnvDefaults();
|
|
26
|
+
const projectDir = process.env.HMEM_PROJECT_DIR || "";
|
|
74
27
|
if (!projectDir) {
|
|
75
28
|
process.stderr.write("HMEM_PROJECT_DIR not set\n");
|
|
76
29
|
return;
|
|
77
30
|
}
|
|
78
|
-
// Read hook input from stdin
|
|
79
|
-
let transcriptPath = "";
|
|
31
|
+
// Read hook input from stdin (required by Claude Code hook protocol)
|
|
80
32
|
try {
|
|
81
|
-
|
|
82
|
-
if (stdin) {
|
|
83
|
-
const input = JSON.parse(stdin);
|
|
84
|
-
transcriptPath = input.transcript_path || "";
|
|
85
|
-
}
|
|
33
|
+
fs.readFileSync(0, "utf-8");
|
|
86
34
|
}
|
|
87
|
-
catch { /* no stdin
|
|
35
|
+
catch { /* no stdin — OK */ }
|
|
88
36
|
const agentId = process.env.HMEM_AGENT_ID || process.env.COUNCIL_AGENT_ID || "";
|
|
89
37
|
const templateName = agentId.replace(/_\d+$/, "");
|
|
90
38
|
const config = loadHmemConfig(projectDir);
|
|
@@ -98,79 +46,28 @@ export async function contextInject() {
|
|
|
98
46
|
}
|
|
99
47
|
try {
|
|
100
48
|
const lines = [];
|
|
101
|
-
// 1.
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
lines.push("");
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const activeProject = allEntries.find(e => e.active && !e.obsolete && !e.irrelevant);
|
|
111
|
-
if (activeProject) {
|
|
112
|
-
const projectEntries = store.read({
|
|
113
|
-
id: activeProject.id,
|
|
114
|
-
depth: 3,
|
|
115
|
-
expand: true,
|
|
116
|
-
agentRole: "worker",
|
|
117
|
-
});
|
|
118
|
-
if (projectEntries.length > 0) {
|
|
119
|
-
const p = projectEntries[0];
|
|
120
|
-
lines.push(`## Active project: ${p.id} ${p.title}`);
|
|
121
|
-
if (p.level_1 && p.level_1 !== p.title)
|
|
122
|
-
lines.push(` ${p.level_1}`);
|
|
123
|
-
// Overview node (.1) — expand with full content
|
|
124
|
-
if (p.children) {
|
|
125
|
-
const overview = p.children.find((c) => c.seq === 1);
|
|
126
|
-
if (overview) {
|
|
127
|
-
lines.push(` Overview:`);
|
|
128
|
-
if (overview.children) {
|
|
129
|
-
for (const gc of overview.children.filter((g) => !g.irrelevant)) {
|
|
130
|
-
lines.push(` ${gc.content}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// 3. Recent O-entries linked to project (full exchanges for all)
|
|
136
|
-
if (config.recentOEntries > 0) {
|
|
137
|
-
const recentO = store.getRecentOEntries(config.recentOEntries, activeProject.id);
|
|
138
|
-
if (recentO.length > 0) {
|
|
139
|
-
lines.push(`\n Recent sessions:`);
|
|
140
|
-
for (let i = 0; i < recentO.length; i++) {
|
|
141
|
-
const o = recentO[i];
|
|
142
|
-
lines.push(` ${o.id} ${o.created_at.substring(0, 10)} ${o.title}`);
|
|
143
|
-
const exLimit = i === 0 ? 10 : 5;
|
|
144
|
-
const exchanges = store.getOEntryExchanges(o.id, exLimit);
|
|
145
|
-
for (const ex of exchanges) {
|
|
146
|
-
const userShort = ex.userText.length > 300 ? ex.userText.substring(0, 300) + "..." : ex.userText;
|
|
147
|
-
const agentShort = ex.agentText.length > 500 ? ex.agentText.substring(0, 500) + "..." : ex.agentText;
|
|
148
|
-
lines.push(` USER: ${userShort}`);
|
|
149
|
-
if (agentShort)
|
|
150
|
-
lines.push(` AGENT: ${agentShort}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
const projects = allEntries.filter(e => !e.obsolete && !e.irrelevant);
|
|
159
|
-
lines.push("## No active project. Available:");
|
|
160
|
-
for (const p of projects) {
|
|
161
|
-
lines.push(` ${p.id} ${p.title}`);
|
|
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}`);
|
|
162
58
|
}
|
|
163
59
|
}
|
|
164
|
-
//
|
|
60
|
+
// 2. R-entries (rules) — compact, one line each
|
|
165
61
|
const rules = store.read({ prefix: "R", depth: 1, agentRole: "worker" })
|
|
166
62
|
.filter(r => !r.obsolete && !r.irrelevant);
|
|
167
63
|
if (rules.length > 0) {
|
|
168
64
|
lines.push("\n## Rules:");
|
|
169
65
|
for (const r of rules) {
|
|
170
|
-
|
|
66
|
+
const body = r.level_1 && r.level_1 !== r.title ? `\n> ${r.level_1}` : "";
|
|
67
|
+
lines.push(` ${r.id} ${r.title}${body}`);
|
|
171
68
|
}
|
|
172
69
|
}
|
|
173
|
-
lines.push(
|
|
70
|
+
lines.push(`\n(Context re-injected after /clear. Use load_project for full briefing, read_memory(id) to drill into specific entries.)`);
|
|
174
71
|
process.stdout.write(lines.join("\n") + "\n");
|
|
175
72
|
}
|
|
176
73
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-context-inject.js","sourceRoot":"","sources":["../src/cli-context-inject.ts"],"names":[],"mappings":"AAAA
|
|
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
|