hippocamp 0.1.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.
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const path = require("node:path");
5
+
6
+ const COMMANDS = {
7
+ dream: "hippocamp-dream.cjs",
8
+ mcp: "hippocamp-mcp.cjs",
9
+ "install-codex": "install-codex.cjs",
10
+ "install-claude": "install-claude.cjs",
11
+ "upgrade-codex": "install-codex.cjs",
12
+ "upgrade-claude": "install-claude.cjs",
13
+ };
14
+
15
+ function printHelp() {
16
+ console.log(`Hippocamp
17
+
18
+ Usage:
19
+ hippocamp dream
20
+ hippocamp mcp
21
+ hippocamp install-codex
22
+ hippocamp install-claude
23
+ hippocamp upgrade-codex
24
+ hippocamp upgrade-claude
25
+
26
+ Commands:
27
+ dream Run offline Dream memory compaction
28
+ mcp Run the local MCP server
29
+ install-codex Install the Hippocamp skill and MCP server into Codex
30
+ install-claude Install the Hippocamp skill and MCP server into Claude Code
31
+ upgrade-codex Upgrade the Codex Hippocamp install
32
+ upgrade-claude Upgrade the Claude Code Hippocamp install
33
+ `);
34
+ }
35
+
36
+ function main() {
37
+ const [command, ...args] = process.argv.slice(2);
38
+
39
+ if (!command || command === "--help" || command === "-h") {
40
+ printHelp();
41
+ return;
42
+ }
43
+
44
+ const script = COMMANDS[command];
45
+
46
+ if (!script) {
47
+ console.error(`Unknown command: ${command}`);
48
+ printHelp();
49
+ process.exit(1);
50
+ }
51
+
52
+ const child = spawn(process.execPath, [path.join(__dirname, script), ...args], {
53
+ stdio: "inherit",
54
+ });
55
+
56
+ child.on("exit", (code, signal) => {
57
+ if (signal) {
58
+ process.kill(process.pid, signal);
59
+ return;
60
+ }
61
+
62
+ process.exit(code || 0);
63
+ });
64
+ }
65
+
66
+ main();
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs/promises");
4
+ const os = require("node:os");
5
+ const path = require("node:path");
6
+ const { execFile } = require("node:child_process");
7
+ const { promisify } = require("node:util");
8
+
9
+ const execFileAsync = promisify(execFile);
10
+
11
+ const CLAUDE_MANAGED_START = "<!-- hippocamp:managed:start -->";
12
+ const CLAUDE_MANAGED_END = "<!-- hippocamp:managed:end -->";
13
+ const HIPPOCAMP_SKILL_NAME = "hippocamp-memory";
14
+
15
+ function expandHomePath(value) {
16
+ if (!value) {
17
+ return value;
18
+ }
19
+
20
+ if (value === "~") {
21
+ return os.homedir();
22
+ }
23
+
24
+ if (value.startsWith("~/")) {
25
+ return path.join(os.homedir(), value.slice(2));
26
+ }
27
+
28
+ return value;
29
+ }
30
+
31
+ function parseArgs(argv) {
32
+ const options = {
33
+ claudeHome: expandHomePath("~/.claude"),
34
+ globalRoot: expandHomePath(process.env.HIPPOCAMP_GLOBAL_ROOT || "~/.lagoon"),
35
+ serverName: "hippocamp",
36
+ };
37
+
38
+ for (let index = 0; index < argv.length; index += 1) {
39
+ const token = argv[index];
40
+
41
+ if (token === "--claude-home") {
42
+ options.claudeHome = path.resolve(expandHomePath(argv[index + 1]));
43
+ index += 1;
44
+ continue;
45
+ }
46
+
47
+ if (token === "--global-root") {
48
+ options.globalRoot = path.resolve(expandHomePath(argv[index + 1]));
49
+ index += 1;
50
+ continue;
51
+ }
52
+
53
+ if (token === "--server-name") {
54
+ options.serverName = argv[index + 1];
55
+ index += 1;
56
+ continue;
57
+ }
58
+
59
+ if (token === "--help") {
60
+ printHelp();
61
+ process.exit(0);
62
+ }
63
+
64
+ throw new Error(`Unknown argument: ${token}`);
65
+ }
66
+
67
+ return options;
68
+ }
69
+
70
+ function printHelp() {
71
+ console.log(`Install Hippocamp into Claude Code.
72
+
73
+ Usage:
74
+ node scripts/install-claude.cjs [--claude-home PATH] [--global-root PATH] [--server-name NAME]
75
+
76
+ Defaults:
77
+ --claude-home ~/.claude
78
+ --global-root ~/.lagoon
79
+ --server-name hippocamp
80
+ `);
81
+ }
82
+
83
+ async function runClaude(args) {
84
+ try {
85
+ const result = await execFileAsync("claude", args);
86
+ return {
87
+ ok: true,
88
+ stdout: result.stdout.trim(),
89
+ stderr: result.stderr.trim(),
90
+ };
91
+ } catch (error) {
92
+ return {
93
+ ok: false,
94
+ stdout: error.stdout?.trim() || "",
95
+ stderr: error.stderr?.trim() || error.message,
96
+ };
97
+ }
98
+ }
99
+
100
+ function buildClaudeManagedBlock() {
101
+ return [
102
+ CLAUDE_MANAGED_START,
103
+ "# Hippocamp",
104
+ "",
105
+ "- For every new top-level coding task, call the Hippocamp `wake_up` MCP tool before inspecting repository files, planning, or editing.",
106
+ "- Read the returned global and project memory before using other memory tools.",
107
+ "- Do this even when the user does not explicitly mention memory.",
108
+ "- Only call `search_memory` if the wake-up files are insufficient.",
109
+ "- If `wake_up` is unavailable or fails, say so explicitly and continue without memory instead of silently skipping it.",
110
+ "- After meaningful code or content changes, checkpoint project memory before the final response.",
111
+ "- When project state changes, use `append_event` for milestones and update curated files such as `current_state.md` and `open_threads.md` before finishing the task.",
112
+ "- When appending events, include concise `Cues:` values or pass the `cues` argument so fuzzy recall can find the event later.",
113
+ "- Do not duplicate commits, PRs, issues, reviews, or CI results in memory; store references plus the missing rationale, preference, assumption, or follow-up context.",
114
+ "- If a project-memory write does not sync automatically, call `sync_memory` before finishing the task.",
115
+ "- If the user asks to upgrade Hippocamp for Claude Code, run `npx hippocamp@latest upgrade-claude`; from a source checkout, run `git pull --ff-only`, `npm install`, then `npm run upgrade:claude`.",
116
+ "- Before writing, reviewing, or refactoring code, read and follow `/Users/guillaumegay/.agents/skills/karpathy-guidelines/SKILL.md` unless the user explicitly asks to skip it.",
117
+ CLAUDE_MANAGED_END,
118
+ "",
119
+ ].join("\n");
120
+ }
121
+
122
+ function upsertManagedBlock(existingContent, block) {
123
+ if (!existingContent) {
124
+ return block;
125
+ }
126
+
127
+ const startIndex = existingContent.indexOf(CLAUDE_MANAGED_START);
128
+ const endIndex = existingContent.indexOf(CLAUDE_MANAGED_END);
129
+
130
+ if (startIndex >= 0 && endIndex >= 0 && endIndex >= startIndex) {
131
+ const before = existingContent.slice(0, startIndex).trimEnd();
132
+ const after = existingContent.slice(endIndex + CLAUDE_MANAGED_END.length).trimStart();
133
+ return [before, block.trimEnd(), after].filter(Boolean).join("\n\n").concat("\n");
134
+ }
135
+
136
+ return `${existingContent.trimEnd()}\n\n${block}`;
137
+ }
138
+
139
+ async function ensureClaudeInstructionsInstalled({ claudeHome }) {
140
+ const claudeMdPath = path.join(claudeHome, "CLAUDE.md");
141
+ const existingContent = await fs.readFile(claudeMdPath, "utf8").catch((error) => {
142
+ if (error && typeof error === "object" && error.code === "ENOENT") {
143
+ return "";
144
+ }
145
+
146
+ throw error;
147
+ });
148
+ const nextContent = upsertManagedBlock(existingContent, buildClaudeManagedBlock());
149
+
150
+ await fs.mkdir(claudeHome, { recursive: true });
151
+ await fs.writeFile(claudeMdPath, nextContent, "utf8");
152
+
153
+ return claudeMdPath;
154
+ }
155
+
156
+ async function copyDirectory(sourceDir, targetDir) {
157
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
158
+
159
+ await fs.mkdir(targetDir, { recursive: true });
160
+
161
+ for (const entry of entries) {
162
+ const sourcePath = path.join(sourceDir, entry.name);
163
+ const targetPath = path.join(targetDir, entry.name);
164
+
165
+ if (entry.isDirectory()) {
166
+ await copyDirectory(sourcePath, targetPath);
167
+ continue;
168
+ }
169
+
170
+ if (entry.isFile()) {
171
+ await fs.copyFile(sourcePath, targetPath);
172
+ }
173
+ }
174
+ }
175
+
176
+ async function ensureClaudeSkillInstalled({ claudeHome, repoRoot }) {
177
+ const sourceDir = path.join(repoRoot, "skills", HIPPOCAMP_SKILL_NAME);
178
+ const skillsDir = path.join(claudeHome, "skills");
179
+ const targetDir = path.join(skillsDir, HIPPOCAMP_SKILL_NAME);
180
+ const legacyAgentPath = path.join(claudeHome, "agents", `${HIPPOCAMP_SKILL_NAME}.md`);
181
+
182
+ await copyDirectory(sourceDir, targetDir);
183
+ await fs.rm(legacyAgentPath, { force: true }).catch((error) => {
184
+ if (error && typeof error === "object" && error.code === "ENOENT") {
185
+ return;
186
+ }
187
+
188
+ throw error;
189
+ });
190
+
191
+ return targetDir;
192
+ }
193
+
194
+ async function ensureServerInstalled({ repoRoot, globalRoot, serverName }) {
195
+ const existing = await runClaude(["mcp", "get", serverName]);
196
+
197
+ if (existing.ok) {
198
+ const removed = await runClaude(["mcp", "remove", "--scope", "user", serverName]);
199
+
200
+ if (!removed.ok) {
201
+ throw new Error(removed.stderr || `Failed to remove existing MCP server: ${serverName}`);
202
+ }
203
+ }
204
+
205
+ const scriptPath = path.join(repoRoot, "scripts", "hippocamp-mcp.cjs");
206
+ const added = await runClaude([
207
+ "mcp",
208
+ "add",
209
+ "--scope",
210
+ "user",
211
+ "--transport",
212
+ "stdio",
213
+ serverName,
214
+ "--env",
215
+ `HIPPOCAMP_GLOBAL_ROOT=${globalRoot}`,
216
+ "--",
217
+ process.execPath,
218
+ scriptPath,
219
+ ]);
220
+
221
+ if (!added.ok) {
222
+ throw new Error(added.stderr || `Failed to add MCP server: ${serverName}`);
223
+ }
224
+
225
+ return {
226
+ name: serverName,
227
+ command: process.execPath,
228
+ scriptPath,
229
+ globalRoot,
230
+ summary: added.stdout || added.stderr,
231
+ };
232
+ }
233
+
234
+ async function main() {
235
+ const options = parseArgs(process.argv.slice(2));
236
+ const repoRoot = path.resolve(__dirname, "..");
237
+
238
+ await fs.mkdir(options.claudeHome, { recursive: true });
239
+
240
+ const claudeMdPath = await ensureClaudeInstructionsInstalled({
241
+ claudeHome: options.claudeHome,
242
+ });
243
+ const claudeSkillPath = await ensureClaudeSkillInstalled({
244
+ claudeHome: options.claudeHome,
245
+ repoRoot,
246
+ });
247
+
248
+ const server = await ensureServerInstalled({
249
+ repoRoot,
250
+ globalRoot: options.globalRoot,
251
+ serverName: options.serverName,
252
+ });
253
+
254
+ console.log(
255
+ JSON.stringify(
256
+ {
257
+ ok: true,
258
+ claudeHome: options.claudeHome,
259
+ claudeMdPath,
260
+ claudeSkillPath,
261
+ server,
262
+ note: "Restart Claude Code to load the installed MCP server, Hippocamp skill, and refreshed CLAUDE.md instructions.",
263
+ },
264
+ null,
265
+ 2,
266
+ ),
267
+ );
268
+ }
269
+
270
+ main().catch((error) => {
271
+ console.error("Failed to install Hippocamp into Claude Code:", error.message);
272
+ process.exit(1);
273
+ });
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs/promises");
4
+ const os = require("node:os");
5
+ const path = require("node:path");
6
+ const { execFile } = require("node:child_process");
7
+ const { promisify } = require("node:util");
8
+
9
+ const execFileAsync = promisify(execFile);
10
+ const CODEX_MANAGED_START = "<!-- hippocamp:managed:start -->";
11
+ const CODEX_MANAGED_END = "<!-- hippocamp:managed:end -->";
12
+
13
+ function expandHomePath(value) {
14
+ if (!value) {
15
+ return value;
16
+ }
17
+
18
+ if (value === "~") {
19
+ return os.homedir();
20
+ }
21
+
22
+ if (value.startsWith("~/")) {
23
+ return path.join(os.homedir(), value.slice(2));
24
+ }
25
+
26
+ return value;
27
+ }
28
+
29
+ function parseArgs(argv) {
30
+ const options = {
31
+ codexHome: expandHomePath(process.env.CODEX_HOME || "~/.codex"),
32
+ globalRoot: expandHomePath(process.env.HIPPOCAMP_GLOBAL_ROOT || "~/.lagoon"),
33
+ serverName: "hippocamp",
34
+ };
35
+
36
+ for (let index = 0; index < argv.length; index += 1) {
37
+ const token = argv[index];
38
+
39
+ if (token === "--codex-home") {
40
+ options.codexHome = path.resolve(expandHomePath(argv[index + 1]));
41
+ index += 1;
42
+ continue;
43
+ }
44
+
45
+ if (token === "--global-root") {
46
+ options.globalRoot = path.resolve(expandHomePath(argv[index + 1]));
47
+ index += 1;
48
+ continue;
49
+ }
50
+
51
+ if (token === "--server-name") {
52
+ options.serverName = argv[index + 1];
53
+ index += 1;
54
+ continue;
55
+ }
56
+
57
+ if (token === "--help") {
58
+ printHelp();
59
+ process.exit(0);
60
+ }
61
+
62
+ throw new Error(`Unknown argument: ${token}`);
63
+ }
64
+
65
+ return options;
66
+ }
67
+
68
+ function printHelp() {
69
+ console.log(`Install Hippocamp into Codex.
70
+
71
+ Usage:
72
+ node scripts/install-codex.cjs [--codex-home PATH] [--global-root PATH] [--server-name NAME]
73
+
74
+ Defaults:
75
+ --codex-home ~/.codex
76
+ --global-root ~/.lagoon
77
+ --server-name hippocamp
78
+ `);
79
+ }
80
+
81
+ async function runCodex(args) {
82
+ try {
83
+ const result = await execFileAsync("codex", args);
84
+ return {
85
+ ok: true,
86
+ stdout: result.stdout.trim(),
87
+ stderr: result.stderr.trim(),
88
+ };
89
+ } catch (error) {
90
+ return {
91
+ ok: false,
92
+ stdout: error.stdout?.trim() || "",
93
+ stderr: error.stderr?.trim() || error.message,
94
+ };
95
+ }
96
+ }
97
+
98
+ async function ensureSkillInstalled({ repoRoot, codexHome }) {
99
+ const sourceDir = path.join(repoRoot, "skills", "hippocamp-memory");
100
+ const targetDir = path.join(codexHome, "skills", "hippocamp-memory");
101
+
102
+ await fs.mkdir(targetDir, { recursive: true });
103
+ await fs.copyFile(path.join(sourceDir, "SKILL.md"), path.join(targetDir, "SKILL.md"));
104
+
105
+ return targetDir;
106
+ }
107
+
108
+ function buildCodexManagedBlock() {
109
+ return [
110
+ CODEX_MANAGED_START,
111
+ "# Hippocamp",
112
+ "",
113
+ "- For every new top-level coding task, call the Hippocamp `wake_up` MCP tool before inspecting repository files, planning, or editing.",
114
+ "- Read the returned global and project memory before using other memory tools.",
115
+ "- Do this even when the user does not explicitly mention memory.",
116
+ "- Only call `search_memory` if the wake-up files are insufficient.",
117
+ "- If `wake_up` is unavailable or fails, say so explicitly and continue without memory instead of silently skipping it.",
118
+ "- After meaningful code or content changes, checkpoint project memory before the final response.",
119
+ "- When project state changes, use `append_event` for milestones and update curated files such as `current_state.md` and `open_threads.md` before finishing the task.",
120
+ "- When appending events, include concise `Cues:` values or pass the `cues` argument so fuzzy recall can find the event later.",
121
+ "- Do not duplicate commits, PRs, issues, reviews, or CI results in memory; store references plus the missing rationale, preference, assumption, or follow-up context.",
122
+ "- If a project-memory write does not sync automatically, call `sync_memory` before finishing the task.",
123
+ "- If the user asks to upgrade Hippocamp for Codex, run `npx hippocamp@latest upgrade-codex`; from a source checkout, run `git pull --ff-only`, `npm install`, then `npm run upgrade:codex`.",
124
+ CODEX_MANAGED_END,
125
+ "",
126
+ ].join("\n");
127
+ }
128
+
129
+ function upsertManagedBlock(existingContent, block) {
130
+ if (!existingContent) {
131
+ return block;
132
+ }
133
+
134
+ const startIndex = existingContent.indexOf(CODEX_MANAGED_START);
135
+ const endIndex = existingContent.indexOf(CODEX_MANAGED_END);
136
+
137
+ if (startIndex >= 0 && endIndex >= 0 && endIndex >= startIndex) {
138
+ const before = existingContent.slice(0, startIndex).trimEnd();
139
+ const after = existingContent.slice(endIndex + CODEX_MANAGED_END.length).trimStart();
140
+ return [before, block.trimEnd(), after].filter(Boolean).join("\n\n").concat("\n");
141
+ }
142
+
143
+ return `${existingContent.trimEnd()}\n\n${block}`;
144
+ }
145
+
146
+ async function ensureCodexInstructionsInstalled({ codexHome }) {
147
+ const agentsMdPath = path.join(codexHome, "AGENTS.md");
148
+ const existingContent = await fs.readFile(agentsMdPath, "utf8").catch((error) => {
149
+ if (error && typeof error === "object" && error.code === "ENOENT") {
150
+ return "";
151
+ }
152
+
153
+ throw error;
154
+ });
155
+ const nextContent = upsertManagedBlock(existingContent, buildCodexManagedBlock());
156
+
157
+ await fs.mkdir(codexHome, { recursive: true });
158
+ await fs.writeFile(agentsMdPath, nextContent, "utf8");
159
+
160
+ return agentsMdPath;
161
+ }
162
+
163
+ async function ensureServerInstalled({ repoRoot, globalRoot, serverName }) {
164
+ const existing = await runCodex(["mcp", "get", serverName, "--json"]);
165
+
166
+ if (existing.ok) {
167
+ const removed = await runCodex(["mcp", "remove", serverName]);
168
+
169
+ if (!removed.ok) {
170
+ throw new Error(removed.stderr || `Failed to remove existing MCP server: ${serverName}`);
171
+ }
172
+ }
173
+
174
+ const scriptPath = path.join(repoRoot, "scripts", "hippocamp-mcp.cjs");
175
+ const added = await runCodex([
176
+ "mcp",
177
+ "add",
178
+ serverName,
179
+ "--env",
180
+ `HIPPOCAMP_GLOBAL_ROOT=${globalRoot}`,
181
+ "--",
182
+ process.execPath,
183
+ scriptPath,
184
+ ]);
185
+
186
+ if (!added.ok) {
187
+ throw new Error(added.stderr || `Failed to add MCP server: ${serverName}`);
188
+ }
189
+
190
+ return {
191
+ name: serverName,
192
+ command: process.execPath,
193
+ scriptPath,
194
+ globalRoot,
195
+ summary: added.stdout || added.stderr,
196
+ };
197
+ }
198
+
199
+ async function main() {
200
+ const options = parseArgs(process.argv.slice(2));
201
+ const repoRoot = path.resolve(__dirname, "..");
202
+
203
+ await fs.mkdir(options.codexHome, { recursive: true });
204
+
205
+ const skillPath = await ensureSkillInstalled({
206
+ repoRoot,
207
+ codexHome: options.codexHome,
208
+ });
209
+ const codexInstructionsPath = await ensureCodexInstructionsInstalled({
210
+ codexHome: options.codexHome,
211
+ });
212
+
213
+ const server = await ensureServerInstalled({
214
+ repoRoot,
215
+ globalRoot: options.globalRoot,
216
+ serverName: options.serverName,
217
+ });
218
+
219
+ console.log(
220
+ JSON.stringify(
221
+ {
222
+ ok: true,
223
+ codexHome: options.codexHome,
224
+ skillPath,
225
+ codexInstructionsPath,
226
+ server,
227
+ note: "Restart Codex to load the installed skill, MCP server, and refreshed AGENTS.md instructions.",
228
+ },
229
+ null,
230
+ 2,
231
+ ),
232
+ );
233
+ }
234
+
235
+ main().catch((error) => {
236
+ console.error("Failed to install Hippocamp into Codex:", error.message);
237
+ process.exit(1);
238
+ });
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: hippocamp-memory
3
+ description: Use Hippocamp memory to load global identity and workflow context from the Lagoon clone and project context from the Lagoon projects folder. Trigger this at the start of top-level task threads, when you need durable memory recall, or when you need to checkpoint meaningful progress.
4
+ ---
5
+
6
+ # Hippocamp Memory
7
+
8
+ Use this skill when the user wants persistent memory across agent sessions.
9
+
10
+ This same skill can be installed into Codex or Claude Code.
11
+ For Codex, use `npm run install:codex`; that installer copies this skill into `~/.codex/skills/hippocamp-memory/`, registers the Hippocamp MCP server, and refreshes `~/.codex/AGENTS.md`.
12
+ For Claude Code, use `npm run install:claude`; that installer copies this skill into `~/.claude/skills/hippocamp-memory/`, registers the Hippocamp MCP server, and refreshes `~/.claude/CLAUDE.md`.
13
+ Upgrade uses the same path: run `npm run upgrade:codex` or `npm run upgrade:claude` from a source checkout, or `npx hippocamp@latest upgrade-codex` / `npx hippocamp@latest upgrade-claude` after publishing.
14
+
15
+ ## Scope
16
+
17
+ - Global memory lives at the root of the local Lagoon clone pointed to by `HIPPOCAMP_GLOBAL_ROOT`.
18
+ - Project memory lives under `projects/<slug>/` inside that same Lagoon clone.
19
+ - The current project slug is inferred from `HIPPOCAMP_PROJECT_ROOT` or the current working directory.
20
+ - The Hippocamp MCP server exposes the tools that read and write those locations.
21
+
22
+ ## Default Workflow
23
+
24
+ 1. At the start of every top-level coding task, call `wake_up` before inspecting files, planning, or editing.
25
+ 2. Read the wake-up output before searching.
26
+ 3. Only call `search_memory` if the wake-up files are insufficient.
27
+ 4. During work, use `append_event` only for meaningful milestones.
28
+ 5. When appending events, include concise `Cues:` values or pass the `cues` argument so fuzzy recall can find the event later.
29
+ 6. `write_memory_file` and `append_event` sync by default.
30
+ 7. If a sync is skipped or fails, call `sync_memory` explicitly.
31
+ 8. At the end of the task, use `write_memory_file` to update curated files such as:
32
+ - `current_state.md`
33
+ - `open_threads.md`
34
+ - `identity.md`
35
+ - `how_i_work.md`
36
+ - `preferences.md`
37
+
38
+ ## Behavior Rules
39
+
40
+ - Do not full-scan memory on every thread.
41
+ - Prefer curated summaries over raw event history.
42
+ - Use event cues as short recall handles; `search_memory` fuzzy-ranks cues and headings before reading matching event bodies.
43
+ - Keep curated files short and legible.
44
+ - Do not duplicate GitHub-owned facts such as commits, PRs, issues, reviews, or CI results. Store artifact references plus the missing rationale, preference, assumption, or follow-up context.
45
+ - Use `project` scope for project-specific state.
46
+ - Use `global` scope only for durable personal context that should follow the user across projects.
47
+ - Avoid rewriting unrelated memory files.
48
+ - Treat `sync_memory` as the recovery path when default sync cannot safely proceed.
49
+
50
+ ## Suggested Files
51
+
52
+ Global:
53
+
54
+ - `identity.md`
55
+ - `how_i_work.md`
56
+ - `preferences.md`
57
+ - `open_loops.md`
58
+
59
+ Project:
60
+
61
+ - `project.md`
62
+ - `current_state.md`
63
+ - `open_threads.md`
64
+ - `events/YYYY-MM-DD.md`
65
+ - `events/YYYY-MM-DD.index.json`
66
+
67
+ ## Tooling
68
+
69
+ Expected MCP tools:
70
+
71
+ - `wake_up`
72
+ - `read_memory_file`
73
+ - `write_memory_file`
74
+ - `append_event`
75
+ - `list_memory_files`
76
+ - `search_memory`
77
+ - `sync_memory`