@smart-coder-labs/nexusmind-mcp 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +25 -0
- package/dist/client.js +67 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +121 -0
- package/dist/setup.js.map +1 -0
- package/package.json +29 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/.mcp.json +12 -0
- package/plugin/README.md +71 -0
- package/plugin/hooks/hooks.json +59 -0
- package/plugin/install.sh +227 -0
- package/plugin/scripts/_helpers.sh +30 -0
- package/plugin/scripts/post-compaction.sh +133 -0
- package/plugin/scripts/session-start.sh +131 -0
- package/plugin/scripts/session-stop.sh +6 -0
- package/plugin/scripts/subagent-stop.sh +76 -0
- package/plugin/scripts/user-prompt-submit.sh +73 -0
- package/plugin/skills/memory/SKILL.md +64 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface Memory {
|
|
2
|
+
id: string;
|
|
3
|
+
user_id: string;
|
|
4
|
+
project: string;
|
|
5
|
+
tool: string;
|
|
6
|
+
content: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
created_at: string;
|
|
9
|
+
}
|
|
10
|
+
export interface StoreMemoryInput {
|
|
11
|
+
content: string;
|
|
12
|
+
project?: string;
|
|
13
|
+
tool?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface StoreMemoryResponse {
|
|
17
|
+
id: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function storeMemory(input: StoreMemoryInput): Promise<StoreMemoryResponse>;
|
|
20
|
+
export declare function searchMemories(query: string, limit?: number): Promise<Memory[]>;
|
|
21
|
+
export declare function listMemories(params?: {
|
|
22
|
+
project?: string;
|
|
23
|
+
tool?: string;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}): Promise<Memory[]>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const BASE_URL = process.env.NEXUSMIND_BASE_URL ?? 'http://localhost:8080';
|
|
2
|
+
const API_KEY = process.env.NEXUSMIND_API_KEY ?? '';
|
|
3
|
+
if (!API_KEY) {
|
|
4
|
+
process.stderr.write('[nexusmind-mcp] Warning: NEXUSMIND_API_KEY is not set. ' +
|
|
5
|
+
'Set it to your NexusMind API key.\n');
|
|
6
|
+
}
|
|
7
|
+
async function request(path, init) {
|
|
8
|
+
let res;
|
|
9
|
+
try {
|
|
10
|
+
res = await fetch(`${BASE_URL}${path}`, {
|
|
11
|
+
...init,
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
15
|
+
...init?.headers,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
const err = new Error(`NexusMind backend not reachable at ${BASE_URL}. Is it running?`);
|
|
21
|
+
err.status = 0;
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
if (res.status === 401) {
|
|
25
|
+
const err = new Error('Invalid API key. Set NEXUSMIND_API_KEY to your NexusMind key.');
|
|
26
|
+
err.status = 401;
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
31
|
+
const err = new Error(body.error ?? res.statusText);
|
|
32
|
+
err.status = res.status;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
if (res.status === 204)
|
|
36
|
+
return undefined;
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
// ── API calls ────────────────────────────────────────────────────────────────
|
|
40
|
+
export function storeMemory(input) {
|
|
41
|
+
return request('/v1/memory/store', {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
content: input.content,
|
|
45
|
+
project: input.project ?? '',
|
|
46
|
+
tool: input.tool ?? 'claude-code',
|
|
47
|
+
tags: input.tags ?? [],
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function searchMemories(query, limit = 10) {
|
|
52
|
+
return request('/v1/memory/search', {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
body: JSON.stringify({ query, limit }),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export function listMemories(params = {}) {
|
|
58
|
+
const qs = new URLSearchParams();
|
|
59
|
+
if (params.project)
|
|
60
|
+
qs.set('project', params.project);
|
|
61
|
+
if (params.tool)
|
|
62
|
+
qs.set('tool', params.tool);
|
|
63
|
+
if (params.limit)
|
|
64
|
+
qs.set('limit', String(params.limit));
|
|
65
|
+
return request(`/v1/memory?${qs}`);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { storeMemory, searchMemories, listMemories } from './client.js';
|
|
6
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
7
|
+
function formatMemory(m) {
|
|
8
|
+
const date = new Date(m.created_at).toLocaleDateString();
|
|
9
|
+
const tags = m.tags.length > 0 ? ` [${m.tags.join(', ')}]` : '';
|
|
10
|
+
return `• [${m.tool}] ${m.project || '(no project)'} — ${m.content}${tags} (${date})`;
|
|
11
|
+
}
|
|
12
|
+
function formatList(memories) {
|
|
13
|
+
if (memories.length === 0)
|
|
14
|
+
return 'No memories found.';
|
|
15
|
+
return memories.map(formatMemory).join('\n');
|
|
16
|
+
}
|
|
17
|
+
// ── Server ───────────────────────────────────────────────────────────────────
|
|
18
|
+
const server = new McpServer({
|
|
19
|
+
name: 'nexusmind',
|
|
20
|
+
version: '0.1.0',
|
|
21
|
+
});
|
|
22
|
+
// store_memory
|
|
23
|
+
server.tool('store_memory', 'Store a memory, decision, or piece of context for later retrieval by the team.', {
|
|
24
|
+
content: z.string().describe('The memory content to store (decision, convention, finding, etc.)'),
|
|
25
|
+
project: z.string().optional().describe('Project or repo name (e.g. "nexusmind", "payments-api")'),
|
|
26
|
+
tool: z.string().optional().describe('Tool name — defaults to "claude-code"'),
|
|
27
|
+
tags: z.array(z.string()).optional().describe('Tags for categorization (e.g. ["auth", "convention"])'),
|
|
28
|
+
}, async ({ content, project, tool, tags }) => {
|
|
29
|
+
try {
|
|
30
|
+
const res = await storeMemory({ content, project, tool, tags });
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: 'text', text: `Memory stored (id: ${res.id})` }],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// search_memory
|
|
43
|
+
server.tool('search_memory', 'Search past memories and decisions stored by the team using full-text search.', {
|
|
44
|
+
query: z.string().describe('What to search for (e.g. "authentication", "database connection pool")'),
|
|
45
|
+
limit: z.number().int().min(1).max(50).optional().describe('Max results to return (default: 10)'),
|
|
46
|
+
}, async ({ query, limit }) => {
|
|
47
|
+
try {
|
|
48
|
+
const memories = await searchMemories(query, limit ?? 10);
|
|
49
|
+
const text = memories.length === 0
|
|
50
|
+
? `No memories found for query: "${query}"`
|
|
51
|
+
: `Found ${memories.length} result(s) for "${query}":\n\n${formatList(memories)}`;
|
|
52
|
+
return { content: [{ type: 'text', text }] };
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// list_memories
|
|
62
|
+
server.tool('list_memories', 'List recent memories stored by the team, optionally filtered by project or tool.', {
|
|
63
|
+
project: z.string().optional().describe('Filter by project name'),
|
|
64
|
+
tool: z.string().optional().describe('Filter by tool (e.g. "claude-code", "cursor")'),
|
|
65
|
+
limit: z.number().int().min(1).max(100).optional().describe('Max results (default: 20)'),
|
|
66
|
+
}, async ({ project, tool, limit }) => {
|
|
67
|
+
try {
|
|
68
|
+
const memories = await listMemories({ project, tool, limit: limit ?? 20 });
|
|
69
|
+
const text = memories.length === 0
|
|
70
|
+
? 'No memories found.'
|
|
71
|
+
: `${memories.length} recent memory(ies):\n\n${formatList(memories)}`;
|
|
72
|
+
return { content: [{ type: 'text', text }] };
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// ── Start ────────────────────────────────────────────────────────────────────
|
|
82
|
+
const transport = new StdioServerTransport();
|
|
83
|
+
await server.connect(transport);
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/setup.d.ts
ADDED
package/dist/setup.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import * as readline from 'node:readline/promises';
|
|
7
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const PLUGIN_DIR = join(__dirname, '..', 'plugin');
|
|
10
|
+
const CLAUDE_DIR = join(homedir(), '.claude');
|
|
11
|
+
const SETTINGS_PATH = join(CLAUDE_DIR, 'settings.json');
|
|
12
|
+
const DEFAULT_BASE_URL = 'https://nexusmind-backend.fly.dev';
|
|
13
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
14
|
+
const c = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
bold: '\x1b[1m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
blue: '\x1b[34m',
|
|
19
|
+
yellow: '\x1b[33m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
};
|
|
22
|
+
const info = (msg) => console.log(`${c.blue}[nexusmind]${c.reset} ${msg}`);
|
|
23
|
+
const success = (msg) => console.log(`${c.green}[nexusmind]${c.reset} ${msg}`);
|
|
24
|
+
const warn = (msg) => console.log(`${c.yellow}[nexusmind] WARNING:${c.reset} ${msg}`);
|
|
25
|
+
const err = (msg) => console.error(`${c.red}[nexusmind] ERROR:${c.reset} ${msg}`);
|
|
26
|
+
function readSettings() {
|
|
27
|
+
if (!existsSync(SETTINGS_PATH))
|
|
28
|
+
return {};
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(readFileSync(SETTINGS_PATH, 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function writeSettings(settings) {
|
|
37
|
+
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
38
|
+
writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
|
|
39
|
+
}
|
|
40
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
41
|
+
console.log(`\n${c.bold}NexusMind — Claude Code Setup${c.reset}`);
|
|
42
|
+
console.log('────────────────────────────────\n');
|
|
43
|
+
// 1. Collect config
|
|
44
|
+
const rl = readline.createInterface({ input, output });
|
|
45
|
+
const apiKey = process.env.NEXUSMIND_API_KEY
|
|
46
|
+
?? await rl.question('NexusMind API key: ');
|
|
47
|
+
const baseUrl = process.env.NEXUSMIND_BASE_URL
|
|
48
|
+
?? await rl.question(`Backend URL [${DEFAULT_BASE_URL}]: `).then(v => v.trim() || DEFAULT_BASE_URL);
|
|
49
|
+
rl.close();
|
|
50
|
+
if (!apiKey.trim()) {
|
|
51
|
+
warn('No API key provided — you can set NEXUSMIND_API_KEY later and re-run setup.');
|
|
52
|
+
}
|
|
53
|
+
// 2. Read current settings
|
|
54
|
+
const settings = readSettings();
|
|
55
|
+
// 3. Merge MCP server
|
|
56
|
+
const mcpServers = settings.mcpServers ?? {};
|
|
57
|
+
mcpServers['nexusmind'] = {
|
|
58
|
+
command: 'npx',
|
|
59
|
+
args: ['-y', '@smart-coder-labs/nexusmind-mcp'],
|
|
60
|
+
env: {
|
|
61
|
+
NEXUSMIND_API_KEY: '${NEXUSMIND_API_KEY}',
|
|
62
|
+
NEXUSMIND_BASE_URL: baseUrl,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
settings.mcpServers = mcpServers;
|
|
66
|
+
info('MCP server entry added.');
|
|
67
|
+
// 4. Merge hooks
|
|
68
|
+
const hooksPath = join(PLUGIN_DIR, 'hooks', 'hooks.json');
|
|
69
|
+
if (existsSync(hooksPath)) {
|
|
70
|
+
const pluginHooks = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
71
|
+
const existing = settings.hooks ?? {};
|
|
72
|
+
for (const [event, entries] of Object.entries(pluginHooks.hooks)) {
|
|
73
|
+
if (!existing[event])
|
|
74
|
+
existing[event] = [];
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
// Resolve ${CLAUDE_PLUGIN_ROOT} to actual plugin dir
|
|
77
|
+
const resolved = JSON.parse(JSON.stringify(entry).replaceAll('${CLAUDE_PLUGIN_ROOT}', PLUGIN_DIR));
|
|
78
|
+
// Dedup by command
|
|
79
|
+
const existingCmds = existing[event]
|
|
80
|
+
.flatMap(e => [e.command, ...(e.hooks ?? []).map(h => h.command)])
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
const newCmd = resolved.command ?? resolved.hooks?.[0]?.command ?? '';
|
|
83
|
+
if (!existingCmds.includes(newCmd)) {
|
|
84
|
+
existing[event].push(resolved);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
settings.hooks = existing;
|
|
89
|
+
info('Hooks merged.');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
warn(`Hooks not found at ${hooksPath} — skipping hooks installation.`);
|
|
93
|
+
}
|
|
94
|
+
// 5. Write settings
|
|
95
|
+
writeSettings(settings);
|
|
96
|
+
success(`Settings written to ${SETTINGS_PATH}`);
|
|
97
|
+
// 6. Persist env vars to shell rc files
|
|
98
|
+
function appendEnvVar(rcFile, name, value) {
|
|
99
|
+
if (!existsSync(rcFile))
|
|
100
|
+
return;
|
|
101
|
+
const content = readFileSync(rcFile, 'utf8');
|
|
102
|
+
if (content.includes(`export ${name}=`)) {
|
|
103
|
+
warn(`${name} already in ${rcFile} — skipping.`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
writeFileSync(rcFile, content + `\n# NexusMind\nexport ${name}="${value}"\n`);
|
|
107
|
+
success(`Wrote ${name} to ${rcFile}`);
|
|
108
|
+
}
|
|
109
|
+
if (apiKey.trim()) {
|
|
110
|
+
appendEnvVar(join(homedir(), '.zshrc'), 'NEXUSMIND_API_KEY', apiKey.trim());
|
|
111
|
+
appendEnvVar(join(homedir(), '.bashrc'), 'NEXUSMIND_API_KEY', apiKey.trim());
|
|
112
|
+
}
|
|
113
|
+
appendEnvVar(join(homedir(), '.zshrc'), 'NEXUSMIND_BASE_URL', baseUrl);
|
|
114
|
+
appendEnvVar(join(homedir(), '.bashrc'), 'NEXUSMIND_BASE_URL', baseUrl);
|
|
115
|
+
// ── Done ──────────────────────────────────────────────────────────────────────
|
|
116
|
+
console.log(`\n${c.bold}${c.green}Done!${c.reset}\n`);
|
|
117
|
+
console.log('Next steps:');
|
|
118
|
+
console.log(' 1. Restart your shell or run: source ~/.zshrc');
|
|
119
|
+
console.log(' 2. Open Claude Code — NexusMind connects automatically');
|
|
120
|
+
console.log(' 3. store_memory, search_memory, list_memories are now available\n');
|
|
121
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smart-coder-labs/nexusmind-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "NexusMind MCP server for Claude Code — team memory that persists across sessions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"nexusmind-mcp": "dist/index.js",
|
|
8
|
+
"nexusmind-setup": "dist/setup.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"setup": "node dist/setup.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"plugin"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
22
|
+
"zod": "^3.22.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"tsx": "^4.6.0",
|
|
27
|
+
"typescript": "^5.3.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nexusmind",
|
|
3
|
+
"description": "Team memory for AI coding agents. Store decisions, bugs, and conventions — shared across the whole team.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": { "name": "NexusMind" },
|
|
6
|
+
"homepage": "https://nexusmind.smartcoderlabs.com",
|
|
7
|
+
"repository": "https://github.com/smart-coder-labs/nexus-mind",
|
|
8
|
+
"license": "MIT"
|
|
9
|
+
}
|
package/plugin/.mcp.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"nexusmind": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["-y", "@smart-coder-labs/nexusmind-mcp"],
|
|
6
|
+
"env": {
|
|
7
|
+
"NEXUSMIND_API_KEY": "${NEXUSMIND_API_KEY}",
|
|
8
|
+
"NEXUSMIND_BASE_URL": "${NEXUSMIND_BASE_URL:-https://nexusmind-backend.fly.dev}"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/plugin/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# NexusMind — Claude Code Plugin
|
|
2
|
+
|
|
3
|
+
Team memory for AI coding agents. Store decisions, bugs, and conventions — shared across the whole team.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Injects your team's recent memories into every Claude Code session as context
|
|
8
|
+
- Provides `store_memory`, `search_memory`, and `list_memories` tools via MCP
|
|
9
|
+
- Reminds Claude Code to save decisions proactively throughout the session
|
|
10
|
+
- Passively captures subagent output for team context
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 18+
|
|
15
|
+
- A NexusMind API key (`NEXUSMIND_API_KEY`)
|
|
16
|
+
- Claude Code
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
### Option 1 — one-liner
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -fsSL https://raw.githubusercontent.com/smart-coder-labs/nexus-mind/main/plugin/claude-code/install.sh | bash
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Option 2 — clone and run
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git clone https://github.com/smart-coder-labs/nexus-mind.git
|
|
30
|
+
bash nexus-mind/plugin/claude-code/install.sh
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Option 3 — Claude plugin marketplace
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
claude plugin marketplace add smart-coder-labs/nexus-mind
|
|
37
|
+
claude plugin install nexusmind
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
|----------|---------|-------------|
|
|
44
|
+
| `NEXUSMIND_API_KEY` | — | Your NexusMind API key (required) |
|
|
45
|
+
| `NEXUSMIND_BASE_URL` | `https://nexusmind-backend.fly.dev` | Backend URL (optional, for self-hosting) |
|
|
46
|
+
|
|
47
|
+
Set these in your shell profile or pass them before running Claude Code:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export NEXUSMIND_API_KEY=your-key-here
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## What gets installed
|
|
54
|
+
|
|
55
|
+
The installer:
|
|
56
|
+
1. Adds the `nexusmind` MCP server to `~/.claude/settings.json`
|
|
57
|
+
2. Adds lifecycle hooks (SessionStart, UserPromptSubmit, SubagentStop, Stop)
|
|
58
|
+
3. Writes `NEXUSMIND_API_KEY` and `NEXUSMIND_BASE_URL` to `~/.bashrc` and `~/.zshrc`
|
|
59
|
+
|
|
60
|
+
## MCP Tools
|
|
61
|
+
|
|
62
|
+
| Tool | When to use |
|
|
63
|
+
|------|-------------|
|
|
64
|
+
| `store_memory` | Save a decision, bug fix, convention, or discovery |
|
|
65
|
+
| `search_memory` | Look up past decisions or context |
|
|
66
|
+
| `list_memories` | Browse recent team memories |
|
|
67
|
+
|
|
68
|
+
## Uninstall
|
|
69
|
+
|
|
70
|
+
Remove the `nexusmind` entry from `~/.claude/settings.json` under both `mcpServers` and `hooks`.
|
|
71
|
+
Remove the `NEXUSMIND_API_KEY` and `NEXUSMIND_BASE_URL` exports from your shell profile.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|clear",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/session-start.sh"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "compact",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-compaction.sh"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"UserPromptSubmit": [
|
|
24
|
+
{
|
|
25
|
+
"matcher": "",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/user-prompt-submit.sh"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"SubagentStop": [
|
|
35
|
+
{
|
|
36
|
+
"matcher": "",
|
|
37
|
+
"hooks": [
|
|
38
|
+
{
|
|
39
|
+
"type": "command",
|
|
40
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/subagent-stop.sh",
|
|
41
|
+
"async": true
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"Stop": [
|
|
47
|
+
{
|
|
48
|
+
"matcher": "",
|
|
49
|
+
"hooks": [
|
|
50
|
+
{
|
|
51
|
+
"type": "command",
|
|
52
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/session-stop.sh",
|
|
53
|
+
"async": true
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — NexusMind Claude Code plugin installer
|
|
3
|
+
# Sets up the MCP server entry and hooks in ~/.claude/settings.json
|
|
4
|
+
# and writes environment variables to shell rc files.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
CLAUDE_SETTINGS="${HOME}/.claude/settings.json"
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# Colors
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
GREEN='\033[0;32m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
BOLD='\033[1m'
|
|
18
|
+
RESET='\033[0m'
|
|
19
|
+
|
|
20
|
+
info() { echo -e "${BLUE}[nexusmind]${RESET} $*"; }
|
|
21
|
+
success() { echo -e "${GREEN}[nexusmind]${RESET} $*"; }
|
|
22
|
+
warn() { echo -e "${YELLOW}[nexusmind] WARNING:${RESET} $*"; }
|
|
23
|
+
error() { echo -e "${RED}[nexusmind] ERROR:${RESET} $*" >&2; }
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Dependency checks (warn, don't fail)
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
echo ""
|
|
29
|
+
echo -e "${BOLD}NexusMind — Claude Code Plugin Installer${RESET}"
|
|
30
|
+
echo "────────────────────────────────────────"
|
|
31
|
+
echo ""
|
|
32
|
+
|
|
33
|
+
if ! command -v node &>/dev/null; then
|
|
34
|
+
warn "node is not installed. The MCP server requires Node.js 18+."
|
|
35
|
+
warn "Install it from https://nodejs.org before using NexusMind."
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if ! command -v jq &>/dev/null; then
|
|
39
|
+
warn "jq is not installed. JSON merging will use python3 as fallback."
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if ! command -v python3 &>/dev/null; then
|
|
43
|
+
error "python3 is required for JSON merging but was not found."
|
|
44
|
+
error "Install python3 and re-run this installer."
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Collect configuration
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
# API key
|
|
53
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
54
|
+
echo -n "Enter your NexusMind API key: "
|
|
55
|
+
read -r NEXUSMIND_API_KEY
|
|
56
|
+
if [[ -z "$NEXUSMIND_API_KEY" ]]; then
|
|
57
|
+
warn "No API key provided. You can set it later with: export NEXUSMIND_API_KEY=<your-key>"
|
|
58
|
+
NEXUSMIND_API_KEY=""
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Base URL
|
|
63
|
+
DEFAULT_URL="https://nexusmind-backend.fly.dev"
|
|
64
|
+
if [[ -z "${NEXUSMIND_BASE_URL:-}" ]]; then
|
|
65
|
+
echo -n "NexusMind backend URL [${DEFAULT_URL}]: "
|
|
66
|
+
read -r NEXUSMIND_BASE_URL
|
|
67
|
+
NEXUSMIND_BASE_URL="${NEXUSMIND_BASE_URL:-$DEFAULT_URL}"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
info "Using backend: ${NEXUSMIND_BASE_URL}"
|
|
71
|
+
echo ""
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Ensure ~/.claude directory and settings.json exist
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
mkdir -p "${HOME}/.claude"
|
|
77
|
+
|
|
78
|
+
if [[ ! -f "$CLAUDE_SETTINGS" ]]; then
|
|
79
|
+
echo '{}' > "$CLAUDE_SETTINGS"
|
|
80
|
+
info "Created ${CLAUDE_SETTINGS}"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Helper: merge JSON via python3
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
merge_json() {
|
|
87
|
+
# merge_json <file> <python_snippet_that_modifies_variable_d>
|
|
88
|
+
local file="$1"
|
|
89
|
+
local snippet="$2"
|
|
90
|
+
python3 -c "
|
|
91
|
+
import json, sys
|
|
92
|
+
|
|
93
|
+
with open('${file}', 'r') as f:
|
|
94
|
+
d = json.load(f)
|
|
95
|
+
|
|
96
|
+
${snippet}
|
|
97
|
+
|
|
98
|
+
with open('${file}', 'w') as f:
|
|
99
|
+
json.dump(d, f, indent=2)
|
|
100
|
+
f.write('\n')
|
|
101
|
+
"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# 1. Write MCP server entry
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
info "Adding MCP server entry to ${CLAUDE_SETTINGS}..."
|
|
108
|
+
|
|
109
|
+
merge_json "$CLAUDE_SETTINGS" "
|
|
110
|
+
if 'mcpServers' not in d:
|
|
111
|
+
d['mcpServers'] = {}
|
|
112
|
+
d['mcpServers']['nexusmind'] = {
|
|
113
|
+
'command': 'npx',
|
|
114
|
+
'args': ['-y', '@smart-coder-labs/nexusmind-mcp'],
|
|
115
|
+
'env': {
|
|
116
|
+
'NEXUSMIND_API_KEY': '\${NEXUSMIND_API_KEY}',
|
|
117
|
+
'NEXUSMIND_BASE_URL': '\${NEXUSMIND_BASE_URL:-${NEXUSMIND_BASE_URL}}'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
"
|
|
121
|
+
success "MCP server entry added."
|
|
122
|
+
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
# 2. Read hooks from hooks.json and merge into settings.json
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
HOOKS_JSON="${PLUGIN_DIR}/hooks/hooks.json"
|
|
127
|
+
|
|
128
|
+
if [[ ! -f "$HOOKS_JSON" ]]; then
|
|
129
|
+
warn "hooks.json not found at ${HOOKS_JSON}. Skipping hooks installation."
|
|
130
|
+
else
|
|
131
|
+
info "Merging hooks into ${CLAUDE_SETTINGS}..."
|
|
132
|
+
|
|
133
|
+
PLUGIN_ROOT_ESCAPED="$(echo "${PLUGIN_DIR}/scripts" | sed 's/[\/&]/\\&/g')"
|
|
134
|
+
|
|
135
|
+
python3 -c "
|
|
136
|
+
import json
|
|
137
|
+
|
|
138
|
+
with open('${CLAUDE_SETTINGS}', 'r') as f:
|
|
139
|
+
settings = json.load(f)
|
|
140
|
+
|
|
141
|
+
with open('${HOOKS_JSON}', 'r') as f:
|
|
142
|
+
hooks_data = json.load(f)
|
|
143
|
+
|
|
144
|
+
plugin_root = '${PLUGIN_DIR}'
|
|
145
|
+
|
|
146
|
+
if 'hooks' not in settings:
|
|
147
|
+
settings['hooks'] = {}
|
|
148
|
+
|
|
149
|
+
# Merge each hook event
|
|
150
|
+
for event, entries in hooks_data.get('hooks', {}).items():
|
|
151
|
+
if event not in settings['hooks']:
|
|
152
|
+
settings['hooks'][event] = []
|
|
153
|
+
for entry in entries:
|
|
154
|
+
# Replace \${CLAUDE_PLUGIN_ROOT}/scripts with actual path
|
|
155
|
+
resolved_entry = json.loads(
|
|
156
|
+
json.dumps(entry).replace('\${CLAUDE_PLUGIN_ROOT}', plugin_root)
|
|
157
|
+
)
|
|
158
|
+
# Avoid duplicates based on command
|
|
159
|
+
existing_commands = [
|
|
160
|
+
h.get('command', '')
|
|
161
|
+
for group in settings['hooks'][event]
|
|
162
|
+
for h in (group.get('hooks', []) if isinstance(group, dict) and 'hooks' in group else [group])
|
|
163
|
+
]
|
|
164
|
+
new_command = resolved_entry.get('command', '') or (
|
|
165
|
+
resolved_entry.get('hooks', [{}])[0].get('command', '')
|
|
166
|
+
if isinstance(resolved_entry, dict) and 'hooks' in resolved_entry else ''
|
|
167
|
+
)
|
|
168
|
+
if new_command not in existing_commands:
|
|
169
|
+
settings['hooks'][event].append(resolved_entry)
|
|
170
|
+
|
|
171
|
+
with open('${CLAUDE_SETTINGS}', 'w') as f:
|
|
172
|
+
json.dump(settings, f, indent=2)
|
|
173
|
+
f.write('\n')
|
|
174
|
+
"
|
|
175
|
+
success "Hooks merged."
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# 3. Write env vars to shell rc files
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
write_env_var() {
|
|
182
|
+
local rc_file="$1"
|
|
183
|
+
local var_name="$2"
|
|
184
|
+
local var_value="$3"
|
|
185
|
+
|
|
186
|
+
if [[ ! -f "$rc_file" ]]; then
|
|
187
|
+
return 0
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
# Skip if already set in the file
|
|
191
|
+
if grep -q "export ${var_name}=" "$rc_file" 2>/dev/null; then
|
|
192
|
+
warn "${var_name} already set in ${rc_file} — skipping."
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
echo "" >> "$rc_file"
|
|
197
|
+
echo "# NexusMind" >> "$rc_file"
|
|
198
|
+
echo "export ${var_name}=\"${var_value}\"" >> "$rc_file"
|
|
199
|
+
success "Wrote ${var_name} to ${rc_file}"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if [[ -n "$NEXUSMIND_API_KEY" ]]; then
|
|
203
|
+
write_env_var "${HOME}/.bashrc" "NEXUSMIND_API_KEY" "$NEXUSMIND_API_KEY"
|
|
204
|
+
write_env_var "${HOME}/.zshrc" "NEXUSMIND_API_KEY" "$NEXUSMIND_API_KEY"
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
write_env_var "${HOME}/.bashrc" "NEXUSMIND_BASE_URL" "$NEXUSMIND_BASE_URL"
|
|
208
|
+
write_env_var "${HOME}/.zshrc" "NEXUSMIND_BASE_URL" "$NEXUSMIND_BASE_URL"
|
|
209
|
+
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
# Done
|
|
212
|
+
# ---------------------------------------------------------------------------
|
|
213
|
+
echo ""
|
|
214
|
+
echo -e "${BOLD}${GREEN}Installation complete!${RESET}"
|
|
215
|
+
echo ""
|
|
216
|
+
echo "Next steps:"
|
|
217
|
+
echo " 1. Restart your shell or run: source ~/.zshrc (or ~/.bashrc)"
|
|
218
|
+
echo " 2. Open Claude Code — NexusMind will connect automatically"
|
|
219
|
+
echo " 3. The MCP tools (store_memory, search_memory, list_memories) are now available"
|
|
220
|
+
echo ""
|
|
221
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
222
|
+
echo -e "${YELLOW}Remember to set your API key:${RESET}"
|
|
223
|
+
echo " export NEXUSMIND_API_KEY=<your-key>"
|
|
224
|
+
echo ""
|
|
225
|
+
fi
|
|
226
|
+
echo "Documentation: https://nexusmind.smartcoderlabs.com"
|
|
227
|
+
echo ""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# _helpers.sh — shared helper functions for NexusMind Claude Code plugin scripts
|
|
3
|
+
|
|
4
|
+
# detect_project: determines the project name from git or directory context.
|
|
5
|
+
# Priority: git remote origin repo name → git root basename → cwd basename.
|
|
6
|
+
detect_project() {
|
|
7
|
+
local project=""
|
|
8
|
+
|
|
9
|
+
# 1. Try git remote origin URL → extract repo name
|
|
10
|
+
if git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
11
|
+
local remote_url
|
|
12
|
+
remote_url="$(git remote get-url origin 2>/dev/null || true)"
|
|
13
|
+
if [[ -n "$remote_url" ]]; then
|
|
14
|
+
# Strip trailing .git, then extract last path component
|
|
15
|
+
project="$(basename "$remote_url" .git)"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 2. Fallback: git root directory basename
|
|
19
|
+
if [[ -z "$project" ]]; then
|
|
20
|
+
project="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || true)")"
|
|
21
|
+
fi
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# 3. Final fallback: current working directory basename
|
|
25
|
+
if [[ -z "$project" ]]; then
|
|
26
|
+
project="$(basename "$PWD")"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "$project"
|
|
30
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post-compaction.sh — NexusMind Claude Code plugin: SessionStart (compact matcher) hook
|
|
3
|
+
# Outputs additionalContext after a compaction event with recovery instructions.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
# shellcheck source=./_helpers.sh
|
|
8
|
+
source "${SCRIPT_DIR}/_helpers.sh"
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# Parse stdin JSON
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
INPUT="$(cat)"
|
|
14
|
+
|
|
15
|
+
session_id="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('session_id',''))" 2>/dev/null || true)"
|
|
16
|
+
cwd="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('cwd',''))" 2>/dev/null || true)"
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Config
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
NEXUSMIND_BASE_URL="${NEXUSMIND_BASE_URL:-https://nexusmind-backend.fly.dev}"
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Guard: API key required
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
27
|
+
cat <<'EOF'
|
|
28
|
+
<!-- NexusMind NOT CONNECTED after compaction: NEXUSMIND_API_KEY is not set. -->
|
|
29
|
+
EOF
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Guard: backend health check
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
if ! curl -sf --max-time 5 "${NEXUSMIND_BASE_URL}/v1/health" &>/dev/null; then
|
|
37
|
+
cat <<'EOF'
|
|
38
|
+
<!-- NexusMind NOT CONNECTED after compaction: backend is unreachable. -->
|
|
39
|
+
EOF
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Detect project
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
if [[ -n "$cwd" ]]; then
|
|
47
|
+
pushd "$cwd" &>/dev/null || true
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
PROJECT="$(detect_project)"
|
|
51
|
+
|
|
52
|
+
if [[ -n "$cwd" ]]; then
|
|
53
|
+
popd &>/dev/null || true
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Fetch recent memories
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
RECENT_MEMORIES=""
|
|
60
|
+
MEMORIES_RESPONSE="$(curl -sf --max-time 10 \
|
|
61
|
+
-H "Authorization: Bearer ${NEXUSMIND_API_KEY}" \
|
|
62
|
+
-H "Content-Type: application/json" \
|
|
63
|
+
"${NEXUSMIND_BASE_URL}/v1/memory?limit=15" 2>/dev/null || true)"
|
|
64
|
+
|
|
65
|
+
if [[ -n "$MEMORIES_RESPONSE" ]]; then
|
|
66
|
+
RECENT_MEMORIES="$(echo "$MEMORIES_RESPONSE" | python3 -c "
|
|
67
|
+
import sys, json
|
|
68
|
+
try:
|
|
69
|
+
data = json.load(sys.stdin)
|
|
70
|
+
items = data if isinstance(data, list) else data.get('memories', data.get('items', data.get('data', [])))
|
|
71
|
+
lines = []
|
|
72
|
+
for m in items[:10]:
|
|
73
|
+
project = m.get('project', '') or m.get('metadata', {}).get('project', '')
|
|
74
|
+
tool = m.get('tool', '') or m.get('metadata', {}).get('tool', '')
|
|
75
|
+
content = m.get('content', m.get('text', ''))
|
|
76
|
+
label = '/'.join(filter(None, [project, tool]))
|
|
77
|
+
snippet = content[:120].replace('\n', ' ')
|
|
78
|
+
lines.append(f'- [{label}] {snippet}')
|
|
79
|
+
print('\n'.join(lines))
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
" 2>/dev/null || true)"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Output additionalContext with compaction-specific recovery instructions
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
cat <<PROTOCOL
|
|
89
|
+
## NexusMind — ACTIVE PROTOCOL (post-compaction recovery)
|
|
90
|
+
|
|
91
|
+
Context was compacted. You MUST recover state before continuing.
|
|
92
|
+
|
|
93
|
+
**FIRST ACTION REQUIRED after compaction:**
|
|
94
|
+
1. Call store_memory with a summary of what was being worked on before compaction
|
|
95
|
+
2. Call search_memory to recover prior decisions and context
|
|
96
|
+
3. Only then continue working
|
|
97
|
+
|
|
98
|
+
**Project detected**: ${PROJECT}
|
|
99
|
+
|
|
100
|
+
### CORE TOOLS
|
|
101
|
+
store_memory — save decisions, bugs, discoveries, conventions PROACTIVELY (do not wait to be asked)
|
|
102
|
+
search_memory — find past decisions or context
|
|
103
|
+
list_memories — browse recent memories
|
|
104
|
+
|
|
105
|
+
### PROACTIVE SAVE RULE
|
|
106
|
+
Call store_memory IMMEDIATELY after ANY decision, bug fix, discovery, or convention — not just when asked.
|
|
107
|
+
Always pass tool="claude-code" and project="${PROJECT}".
|
|
108
|
+
|
|
109
|
+
### WHEN TO SEARCH
|
|
110
|
+
- User's first message references a feature or problem → search_memory with keywords
|
|
111
|
+
- Starting work on something that might have been done before → search_memory
|
|
112
|
+
- User asks to recall anything → search_memory
|
|
113
|
+
|
|
114
|
+
### SESSION CLOSE (MANDATORY)
|
|
115
|
+
Before saying "done", call store_memory with:
|
|
116
|
+
- What was accomplished
|
|
117
|
+
- Key decisions and why
|
|
118
|
+
- Files changed
|
|
119
|
+
- Next steps
|
|
120
|
+
|
|
121
|
+
This is NOT optional. If you skip this, the next session starts blind.
|
|
122
|
+
PROTOCOL
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# Append recent memories if available
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
if [[ -n "$RECENT_MEMORIES" ]]; then
|
|
128
|
+
cat <<MEMORIES
|
|
129
|
+
|
|
130
|
+
### Recent Team Memories (last 10)
|
|
131
|
+
${RECENT_MEMORIES}
|
|
132
|
+
MEMORIES
|
|
133
|
+
fi
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# session-start.sh — NexusMind Claude Code plugin: SessionStart hook
|
|
3
|
+
# Outputs additionalContext for Claude Code with the NexusMind active protocol.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
# shellcheck source=./_helpers.sh
|
|
8
|
+
source "${SCRIPT_DIR}/_helpers.sh"
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# Parse stdin JSON (Claude Code passes hook payload via stdin)
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
INPUT="$(cat)"
|
|
14
|
+
|
|
15
|
+
session_id="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('session_id',''))" 2>/dev/null || true)"
|
|
16
|
+
cwd="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('cwd',''))" 2>/dev/null || true)"
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Config
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
NEXUSMIND_BASE_URL="${NEXUSMIND_BASE_URL:-https://nexusmind-backend.fly.dev}"
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Guard: API key required
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
27
|
+
cat <<'EOF'
|
|
28
|
+
<!-- NexusMind NOT CONNECTED: NEXUSMIND_API_KEY is not set. Memory tools will not be available.
|
|
29
|
+
Run: export NEXUSMIND_API_KEY=<your-key>
|
|
30
|
+
Then restart Claude Code. -->
|
|
31
|
+
EOF
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Guard: backend health check
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
if ! curl -sf --max-time 5 "${NEXUSMIND_BASE_URL}/v1/health" &>/dev/null; then
|
|
39
|
+
cat <<'EOF'
|
|
40
|
+
<!-- NexusMind NOT CONNECTED: backend is unreachable. Memory tools will not be available.
|
|
41
|
+
Check NEXUSMIND_BASE_URL or your network connection. -->
|
|
42
|
+
EOF
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Detect project
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
if [[ -n "$cwd" ]]; then
|
|
50
|
+
pushd "$cwd" &>/dev/null || true
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
PROJECT="$(detect_project)"
|
|
54
|
+
|
|
55
|
+
if [[ -n "$cwd" ]]; then
|
|
56
|
+
popd &>/dev/null || true
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Fetch recent memories
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
RECENT_MEMORIES=""
|
|
63
|
+
MEMORIES_RESPONSE="$(curl -sf --max-time 10 \
|
|
64
|
+
-H "Authorization: Bearer ${NEXUSMIND_API_KEY}" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
"${NEXUSMIND_BASE_URL}/v1/memory?limit=15" 2>/dev/null || true)"
|
|
67
|
+
|
|
68
|
+
if [[ -n "$MEMORIES_RESPONSE" ]]; then
|
|
69
|
+
RECENT_MEMORIES="$(echo "$MEMORIES_RESPONSE" | python3 -c "
|
|
70
|
+
import sys, json
|
|
71
|
+
try:
|
|
72
|
+
data = json.load(sys.stdin)
|
|
73
|
+
items = data if isinstance(data, list) else data.get('memories', data.get('items', data.get('data', [])))
|
|
74
|
+
lines = []
|
|
75
|
+
for m in items[:10]:
|
|
76
|
+
project = m.get('project', '') or m.get('metadata', {}).get('project', '')
|
|
77
|
+
tool = m.get('tool', '') or m.get('metadata', {}).get('tool', '')
|
|
78
|
+
content = m.get('content', m.get('text', ''))
|
|
79
|
+
label = '/'.join(filter(None, [project, tool]))
|
|
80
|
+
snippet = content[:120].replace('\n', ' ')
|
|
81
|
+
lines.append(f'- [{label}] {snippet}')
|
|
82
|
+
print('\n'.join(lines))
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
" 2>/dev/null || true)"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Output additionalContext
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
cat <<PROTOCOL
|
|
92
|
+
## NexusMind — ACTIVE PROTOCOL
|
|
93
|
+
|
|
94
|
+
You have NexusMind memory tools available. This protocol is MANDATORY and ALWAYS ACTIVE for this project.
|
|
95
|
+
|
|
96
|
+
**Project detected**: ${PROJECT}
|
|
97
|
+
|
|
98
|
+
### CORE TOOLS
|
|
99
|
+
store_memory — save decisions, bugs, discoveries, conventions PROACTIVELY (do not wait to be asked)
|
|
100
|
+
search_memory — find past decisions or context
|
|
101
|
+
list_memories — browse recent memories
|
|
102
|
+
|
|
103
|
+
### PROACTIVE SAVE RULE
|
|
104
|
+
Call store_memory IMMEDIATELY after ANY decision, bug fix, discovery, or convention — not just when asked.
|
|
105
|
+
Always pass tool="claude-code" and project="${PROJECT}".
|
|
106
|
+
|
|
107
|
+
### WHEN TO SEARCH
|
|
108
|
+
- User's first message references a feature or problem → search_memory with keywords
|
|
109
|
+
- Starting work on something that might have been done before → search_memory
|
|
110
|
+
- User asks to recall anything → search_memory
|
|
111
|
+
|
|
112
|
+
### SESSION CLOSE (MANDATORY)
|
|
113
|
+
Before saying "done", call store_memory with:
|
|
114
|
+
- What was accomplished
|
|
115
|
+
- Key decisions and why
|
|
116
|
+
- Files changed
|
|
117
|
+
- Next steps
|
|
118
|
+
|
|
119
|
+
This is NOT optional. If you skip this, the next session starts blind.
|
|
120
|
+
PROTOCOL
|
|
121
|
+
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Append recent memories if available
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
if [[ -n "$RECENT_MEMORIES" ]]; then
|
|
126
|
+
cat <<MEMORIES
|
|
127
|
+
|
|
128
|
+
### Recent Team Memories (last 10)
|
|
129
|
+
${RECENT_MEMORIES}
|
|
130
|
+
MEMORIES
|
|
131
|
+
fi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# subagent-stop.sh — NexusMind Claude Code plugin: SubagentStop hook (async)
|
|
3
|
+
# Passively captures subagent output as a memory entry for team context.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
# shellcheck source=./_helpers.sh
|
|
8
|
+
source "${SCRIPT_DIR}/_helpers.sh"
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# Guard: nothing to do without an API key
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Parse stdin JSON
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
INPUT="$(cat)"
|
|
21
|
+
|
|
22
|
+
subagent_output="$(echo "$INPUT" | python3 -c "
|
|
23
|
+
import sys, json
|
|
24
|
+
try:
|
|
25
|
+
d = json.load(sys.stdin)
|
|
26
|
+
print(d.get('stdout', ''))
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
29
|
+
" 2>/dev/null || true)"
|
|
30
|
+
|
|
31
|
+
# Skip if output is empty or very short (not worth storing)
|
|
32
|
+
if [[ -z "$subagent_output" || "${#subagent_output}" -lt 50 ]]; then
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Config
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
NEXUSMIND_BASE_URL="${NEXUSMIND_BASE_URL:-https://nexusmind-backend.fly.dev}"
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Detect project
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
PROJECT="$(detect_project)"
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Fire-and-forget: POST to /v1/memory/store
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
PAYLOAD="$(python3 -c "
|
|
50
|
+
import json, sys
|
|
51
|
+
content = sys.argv[1]
|
|
52
|
+
project = sys.argv[2]
|
|
53
|
+
# Truncate to avoid oversized payloads
|
|
54
|
+
if len(content) > 2000:
|
|
55
|
+
content = content[:2000] + '... [truncated]'
|
|
56
|
+
print(json.dumps({
|
|
57
|
+
'content': content,
|
|
58
|
+
'tool': 'claude-code-subagent',
|
|
59
|
+
'project': project,
|
|
60
|
+
'metadata': {
|
|
61
|
+
'source': 'subagent-stop-hook',
|
|
62
|
+
'passive_capture': True
|
|
63
|
+
}
|
|
64
|
+
}))
|
|
65
|
+
" "$subagent_output" "$PROJECT" 2>/dev/null || true)"
|
|
66
|
+
|
|
67
|
+
if [[ -n "$PAYLOAD" ]]; then
|
|
68
|
+
curl -sf --max-time 10 \
|
|
69
|
+
-X POST \
|
|
70
|
+
-H "Authorization: Bearer ${NEXUSMIND_API_KEY}" \
|
|
71
|
+
-H "Content-Type: application/json" \
|
|
72
|
+
-d "$PAYLOAD" \
|
|
73
|
+
"${NEXUSMIND_BASE_URL}/v1/memory/store" &>/dev/null || true
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
exit 0
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# user-prompt-submit.sh — NexusMind Claude Code plugin: UserPromptSubmit hook
|
|
3
|
+
# Outputs a systemMessage to reinforce memory tool usage on first prompt and periodically.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# ---------------------------------------------------------------------------
|
|
7
|
+
# Parse stdin JSON
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
INPUT="$(cat)"
|
|
10
|
+
|
|
11
|
+
session_id="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('session_id','unknown'))" 2>/dev/null || echo "unknown")"
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Guard: nothing to do without an API key
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
if [[ -z "${NEXUSMIND_API_KEY:-}" ]]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# State file for this session
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
STATE_FILE="/tmp/nexusmind-session-${session_id}"
|
|
24
|
+
NOW="$(date +%s)"
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# First call for this session → output initial system message
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
if [[ ! -f "$STATE_FILE" ]]; then
|
|
30
|
+
# Create state file with: session_start_time last_reminder_time prompt_count
|
|
31
|
+
echo "${NOW} ${NOW} 1" > "$STATE_FILE"
|
|
32
|
+
cat <<'JSON'
|
|
33
|
+
{"systemMessage": "NexusMind memory tools are available. Use store_memory to save decisions, bugs, and discoveries proactively. Use search_memory to look up past context."}
|
|
34
|
+
JSON
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Read existing state
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
read -r SESSION_START LAST_REMINDER PROMPT_COUNT < "$STATE_FILE" 2>/dev/null || {
|
|
42
|
+
SESSION_START="$NOW"
|
|
43
|
+
LAST_REMINDER="$NOW"
|
|
44
|
+
PROMPT_COUNT=0
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
PROMPT_COUNT=$(( PROMPT_COUNT + 1 ))
|
|
48
|
+
|
|
49
|
+
SESSION_AGE=$(( NOW - SESSION_START )) # seconds since session start
|
|
50
|
+
TIME_SINCE_REMINDER=$(( NOW - LAST_REMINDER )) # seconds since last reminder
|
|
51
|
+
|
|
52
|
+
# Update state
|
|
53
|
+
echo "${SESSION_START} ${LAST_REMINDER} ${PROMPT_COUNT}" > "$STATE_FILE"
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Periodic reminder: 15+ min since last reminder AND session is 5+ min old
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
FIFTEEN_MIN=900
|
|
59
|
+
FIVE_MIN=300
|
|
60
|
+
|
|
61
|
+
if (( TIME_SINCE_REMINDER >= FIFTEEN_MIN && SESSION_AGE >= FIVE_MIN )); then
|
|
62
|
+
# Update last reminder timestamp
|
|
63
|
+
echo "${SESSION_START} ${NOW} ${PROMPT_COUNT}" > "$STATE_FILE"
|
|
64
|
+
cat <<'JSON'
|
|
65
|
+
{"systemMessage": "MEMORY REMINDER: Save recent decisions and discoveries to NexusMind with store_memory."}
|
|
66
|
+
JSON
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# Nothing to output this time
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
exit 0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nexusmind-memory
|
|
3
|
+
description: NexusMind persistent team memory protocol — always active
|
|
4
|
+
triggers: always
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# NexusMind Memory — Always-Active Protocol
|
|
8
|
+
|
|
9
|
+
You have NexusMind memory tools available. This protocol is MANDATORY and ALWAYS ACTIVE for this project.
|
|
10
|
+
|
|
11
|
+
## Core Tools
|
|
12
|
+
|
|
13
|
+
| Tool | Purpose |
|
|
14
|
+
|------|---------|
|
|
15
|
+
| `store_memory` | Save decisions, bugs, discoveries, conventions — PROACTIVELY, without being asked |
|
|
16
|
+
| `search_memory` | Find past decisions or context before starting any relevant work |
|
|
17
|
+
| `list_memories` | Browse recent team memories |
|
|
18
|
+
|
|
19
|
+
## Proactive Save Rule
|
|
20
|
+
|
|
21
|
+
Call `store_memory` IMMEDIATELY after ANY of the following — do NOT wait to be asked:
|
|
22
|
+
|
|
23
|
+
- Architecture or design decision made
|
|
24
|
+
- Bug fixed (include root cause and what broke)
|
|
25
|
+
- Convention documented or established
|
|
26
|
+
- Tool or library choice made with reasoning
|
|
27
|
+
- Non-obvious discovery, gotcha, or edge case found
|
|
28
|
+
- Pattern established (naming, structure, approach)
|
|
29
|
+
- Feature implemented with a non-obvious approach
|
|
30
|
+
- Any configuration or environment change
|
|
31
|
+
|
|
32
|
+
Always pass `tool="claude-code"` and set `project` to the detected project name.
|
|
33
|
+
|
|
34
|
+
**Self-check after EVERY task**: "Did I make a decision, fix a bug, learn something non-obvious, or establish a convention? If yes, call store_memory NOW."
|
|
35
|
+
|
|
36
|
+
## When to Search
|
|
37
|
+
|
|
38
|
+
- User's first message references a feature or problem → call `search_memory` with keywords from their message
|
|
39
|
+
- Starting work on something that might have been done before → call `search_memory`
|
|
40
|
+
- User asks to recall anything → call `search_memory`
|
|
41
|
+
- User mentions a topic you have no context on → call `search_memory`
|
|
42
|
+
|
|
43
|
+
## Session Close (MANDATORY)
|
|
44
|
+
|
|
45
|
+
Before saying "done" (or any equivalent in the user's language), call `store_memory` with a session summary:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
What was accomplished this session
|
|
49
|
+
Key decisions made and why
|
|
50
|
+
Files changed (with paths)
|
|
51
|
+
Next steps for the following session
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This is NOT optional. Skipping this means the next session starts blind — no team context, no continuity.
|
|
55
|
+
|
|
56
|
+
## After Compaction
|
|
57
|
+
|
|
58
|
+
If you see a compaction message or "FIRST ACTION REQUIRED":
|
|
59
|
+
|
|
60
|
+
1. IMMEDIATELY call `store_memory` with a summary of what was being worked on before compaction
|
|
61
|
+
2. Call `search_memory` to recover prior context
|
|
62
|
+
3. Only THEN continue working
|
|
63
|
+
|
|
64
|
+
Do not skip step 1. Without it, everything done before compaction is lost.
|