cc-brain 0.2.0 → 0.2.1
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/hooks/hooks.json +27 -27
- package/package.json +1 -1
- package/scripts/install.js +137 -136
- package/scripts/uninstall.js +2 -1
package/hooks/hooks.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"SessionStart": [
|
|
3
|
-
{
|
|
4
|
-
"matcher": "startup|resume|compact",
|
|
5
|
-
"hooks": [
|
|
6
|
-
{
|
|
7
|
-
"type": "command",
|
|
8
|
-
"command": "npx cc-brain load",
|
|
9
|
-
"timeout": 10,
|
|
10
|
-
"statusMessage": "Loading brain..."
|
|
11
|
-
}
|
|
12
|
-
]
|
|
13
|
-
}
|
|
14
|
-
],
|
|
15
|
-
"PreCompact": [
|
|
16
|
-
{
|
|
17
|
-
"hooks": [
|
|
18
|
-
{
|
|
19
|
-
"type": "agent",
|
|
20
|
-
"prompt": "Context is being compacted. Save important information using the structured saver.\n\nANALYZE this session for:\n- New user insights (T1 - rare)\n- New preferences (T1 - rare)\n- Project decisions + rationale (T2)\n- Current focus/state (T2)\n- Session summary (T3 - if significant)\n\nBUILD JSON payload:\n```json\n{\n \"t2\": {\n \"what\": \"Project description\",\n \"focus\": [\"current tasks\"],\n \"decisions\": {\"decision\": \"rationale\"}\n },\n \"t3\": \"Session summary if significant work done.\"\n}\n```\n\nSAVE using:\n```bash\nnpx cc-brain save --json '<payload>'\n```\n\nRules:\n- Only include tiers with new info\n- T2 updates are common, T1 updates are rare\n- Decisions need rationale\n- Keep it concise",
|
|
21
|
-
"timeout": 120,
|
|
22
|
-
"statusMessage": "Saving to brain..."
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"SessionStart": [
|
|
3
|
+
{
|
|
4
|
+
"matcher": "startup|resume|compact",
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "npx cc-brain load",
|
|
9
|
+
"timeout": 10,
|
|
10
|
+
"statusMessage": "Loading brain..."
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PreCompact": [
|
|
16
|
+
{
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "agent",
|
|
20
|
+
"prompt": "Context is being compacted. Save important information using the structured saver.\n\nANALYZE this session for:\n- New user insights (T1 - rare)\n- New preferences (T1 - rare)\n- Project decisions + rationale (T2)\n- Current focus/state (T2)\n- Session summary (T3 - if significant)\n\nBUILD JSON payload:\n```json\n{\n \"t2\": {\n \"what\": \"Project description\",\n \"focus\": [\"current tasks\"],\n \"decisions\": {\"decision\": \"rationale\"}\n },\n \"t3\": \"Session summary if significant work done.\"\n}\n```\n\nSAVE using:\n```bash\nnpx cc-brain save --json '<payload>'\n```\n\nRules:\n- Only include tiers with new info\n- T2 updates are common, T1 updates are rare\n- Decisions need rationale\n- Keep it concise",
|
|
21
|
+
"timeout": 120,
|
|
22
|
+
"statusMessage": "Saving to brain..."
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
package/package.json
CHANGED
package/scripts/install.js
CHANGED
|
@@ -1,136 +1,137 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Install cc-brain
|
|
5
|
-
* Sets up brain directory and hooks
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, realpathSync } from 'fs';
|
|
9
|
-
import { join, dirname } from 'path';
|
|
10
|
-
import { homedir } from 'os';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const HOME = homedir();
|
|
14
|
-
const CLAUDE_DIR = join(HOME, '.claude');
|
|
15
|
-
const BRAIN_DIR = join(CLAUDE_DIR, 'brain');
|
|
16
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
-
const PROJECT_ROOT = join(__dirname, '..');
|
|
18
|
-
|
|
19
|
-
console.log('Installing cc-brain...\n');
|
|
20
|
-
|
|
21
|
-
// Create brain directory structure
|
|
22
|
-
const dirs = [
|
|
23
|
-
BRAIN_DIR,
|
|
24
|
-
join(BRAIN_DIR, 'projects')
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
for (const dir of dirs) {
|
|
28
|
-
if (!existsSync(dir)) {
|
|
29
|
-
mkdirSync(dir, { recursive: true });
|
|
30
|
-
console.log(`Created: ${dir}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Clean up legacy files from previous versions
|
|
35
|
-
const legacyFiles = [
|
|
36
|
-
join(BRAIN_DIR, 'load-brain.sh'),
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
for (const file of legacyFiles) {
|
|
40
|
-
if (existsSync(file)) {
|
|
41
|
-
unlinkSync(file);
|
|
42
|
-
console.log(`Removed: ${file} (legacy)`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Copy template brain files if they don't exist
|
|
47
|
-
const templates = ['user.md', 'preferences.md'];
|
|
48
|
-
for (const file of templates) {
|
|
49
|
-
const dest = join(BRAIN_DIR, file);
|
|
50
|
-
if (!existsSync(dest)) {
|
|
51
|
-
copyFileSync(join(PROJECT_ROOT, 'brain', file), dest);
|
|
52
|
-
console.log(`Created: ${dest}`);
|
|
53
|
-
} else {
|
|
54
|
-
console.log(`Exists: ${dest} (skipped)`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Add hooks to settings.json
|
|
59
|
-
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
60
|
-
let settings = {};
|
|
61
|
-
|
|
62
|
-
if (existsSync(settingsPath)) {
|
|
63
|
-
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Read our hooks config
|
|
67
|
-
const hooks = JSON.parse(readFileSync(join(PROJECT_ROOT, 'hooks', 'hooks.json'), 'utf-8'));
|
|
68
|
-
|
|
69
|
-
// Resolve absolute paths — avoids PATH issues on macOS with nvm/fnm/volta
|
|
70
|
-
// Hook subprocesses don't source shell profiles, so npx/node may not be on PATH
|
|
71
|
-
let nodePath;
|
|
72
|
-
try {
|
|
73
|
-
nodePath = realpathSync(process.execPath);
|
|
74
|
-
} catch {
|
|
75
|
-
nodePath = process.execPath;
|
|
76
|
-
}
|
|
77
|
-
const loaderScript = join(PROJECT_ROOT, 'src', 'loader.js');
|
|
78
|
-
hooks.SessionStart[0].hooks[0].command = `"${nodePath}" "${loaderScript}"`;
|
|
79
|
-
|
|
80
|
-
// Merge hooks — preserve user's other hooks, replace/append ours
|
|
81
|
-
function isCcBrainHook(entry) {
|
|
82
|
-
if (!entry || !entry.hooks) return false;
|
|
83
|
-
return entry.hooks.some(h =>
|
|
84
|
-
(h.command && (h.command.includes('cc-brain') || h.command.includes('loader.js'))) ||
|
|
85
|
-
(h.prompt && h.prompt.includes('structured saver'))
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
settings.hooks
|
|
98
|
-
settings.hooks.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
join(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
${BRAIN_DIR}/
|
|
136
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Install cc-brain
|
|
5
|
+
* Sets up brain directory and hooks
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, realpathSync } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const HOME = homedir();
|
|
14
|
+
const CLAUDE_DIR = join(HOME, '.claude');
|
|
15
|
+
const BRAIN_DIR = join(CLAUDE_DIR, 'brain');
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const PROJECT_ROOT = join(__dirname, '..');
|
|
18
|
+
|
|
19
|
+
console.log('Installing cc-brain...\n');
|
|
20
|
+
|
|
21
|
+
// Create brain directory structure
|
|
22
|
+
const dirs = [
|
|
23
|
+
BRAIN_DIR,
|
|
24
|
+
join(BRAIN_DIR, 'projects')
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for (const dir of dirs) {
|
|
28
|
+
if (!existsSync(dir)) {
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
console.log(`Created: ${dir}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Clean up legacy files from previous versions
|
|
35
|
+
const legacyFiles = [
|
|
36
|
+
join(BRAIN_DIR, 'load-brain.sh'),
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
for (const file of legacyFiles) {
|
|
40
|
+
if (existsSync(file)) {
|
|
41
|
+
unlinkSync(file);
|
|
42
|
+
console.log(`Removed: ${file} (legacy)`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Copy template brain files if they don't exist
|
|
47
|
+
const templates = ['user.md', 'preferences.md'];
|
|
48
|
+
for (const file of templates) {
|
|
49
|
+
const dest = join(BRAIN_DIR, file);
|
|
50
|
+
if (!existsSync(dest)) {
|
|
51
|
+
copyFileSync(join(PROJECT_ROOT, 'brain', file), dest);
|
|
52
|
+
console.log(`Created: ${dest}`);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`Exists: ${dest} (skipped)`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Add hooks to settings.json
|
|
59
|
+
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
60
|
+
let settings = {};
|
|
61
|
+
|
|
62
|
+
if (existsSync(settingsPath)) {
|
|
63
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Read our hooks config
|
|
67
|
+
const hooks = JSON.parse(readFileSync(join(PROJECT_ROOT, 'hooks', 'hooks.json'), 'utf-8'));
|
|
68
|
+
|
|
69
|
+
// Resolve absolute paths — avoids PATH issues on macOS with nvm/fnm/volta
|
|
70
|
+
// Hook subprocesses don't source shell profiles, so npx/node may not be on PATH
|
|
71
|
+
let nodePath;
|
|
72
|
+
try {
|
|
73
|
+
nodePath = realpathSync(process.execPath);
|
|
74
|
+
} catch {
|
|
75
|
+
nodePath = process.execPath;
|
|
76
|
+
}
|
|
77
|
+
const loaderScript = join(PROJECT_ROOT, 'src', 'loader.js');
|
|
78
|
+
hooks.SessionStart[0].hooks[0].command = `"${nodePath}" "${loaderScript}"`;
|
|
79
|
+
|
|
80
|
+
// Merge hooks — preserve user's other hooks, replace/append ours
|
|
81
|
+
function isCcBrainHook(entry) {
|
|
82
|
+
if (!entry || !entry.hooks) return false;
|
|
83
|
+
return entry.hooks.some(h =>
|
|
84
|
+
(h.command && (h.command.includes('cc-brain') || h.command.includes('loader.js'))) ||
|
|
85
|
+
(h.prompt && h.prompt.includes('structured saver')) ||
|
|
86
|
+
(h.statusMessage && h.statusMessage.includes('brain'))
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function mergeHookArray(existing, ours) {
|
|
91
|
+
if (!existing) return ours;
|
|
92
|
+
// Filter out old cc-brain hooks, then append ours
|
|
93
|
+
const filtered = existing.filter(entry => !isCcBrainHook(entry));
|
|
94
|
+
return [...filtered, ...ours];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
settings.hooks = settings.hooks || {};
|
|
98
|
+
settings.hooks.SessionStart = mergeHookArray(settings.hooks.SessionStart, hooks.SessionStart);
|
|
99
|
+
settings.hooks.PreCompact = mergeHookArray(settings.hooks.PreCompact, hooks.PreCompact);
|
|
100
|
+
|
|
101
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
102
|
+
console.log(`\nUpdated: ${settingsPath}`);
|
|
103
|
+
|
|
104
|
+
// --- Install Skills to ~/.claude/skills/ ---
|
|
105
|
+
const SKILLS_DIR = join(CLAUDE_DIR, 'skills');
|
|
106
|
+
const skillNames = ['save', 'recall', 'brain'];
|
|
107
|
+
|
|
108
|
+
for (const name of skillNames) {
|
|
109
|
+
const skillDir = join(SKILLS_DIR, name);
|
|
110
|
+
mkdirSync(skillDir, { recursive: true });
|
|
111
|
+
copyFileSync(
|
|
112
|
+
join(PROJECT_ROOT, 'skills', `${name}.md`),
|
|
113
|
+
join(skillDir, 'SKILL.md')
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`\nInstalled skills to: ${SKILLS_DIR}`);
|
|
118
|
+
console.log(' /save, /recall, /brain');
|
|
119
|
+
|
|
120
|
+
console.log(`
|
|
121
|
+
cc-brain installed!
|
|
122
|
+
|
|
123
|
+
Brain location: ${BRAIN_DIR}
|
|
124
|
+
|
|
125
|
+
Memory tiers:
|
|
126
|
+
T1 (always): user.md, preferences.md
|
|
127
|
+
T2 (project): projects/{name}/context.md
|
|
128
|
+
T3 (archive): projects/{name}/archive/
|
|
129
|
+
|
|
130
|
+
Hooks installed:
|
|
131
|
+
SessionStart → loads T1 + T2 into context
|
|
132
|
+
PreCompact → saves important bits before compaction
|
|
133
|
+
|
|
134
|
+
Edit your brain:
|
|
135
|
+
${BRAIN_DIR}/user.md
|
|
136
|
+
${BRAIN_DIR}/preferences.md
|
|
137
|
+
`);
|
package/scripts/uninstall.js
CHANGED
|
@@ -24,7 +24,8 @@ function isCcBrainHook(entry) {
|
|
|
24
24
|
if (!entry || !entry.hooks) return false;
|
|
25
25
|
return entry.hooks.some(h =>
|
|
26
26
|
(h.command && (h.command.includes('cc-brain') || h.command.includes('loader.js'))) ||
|
|
27
|
-
(h.prompt && h.prompt.includes('structured saver'))
|
|
27
|
+
(h.prompt && h.prompt.includes('structured saver')) ||
|
|
28
|
+
(h.statusMessage && h.statusMessage.includes('brain'))
|
|
28
29
|
);
|
|
29
30
|
}
|
|
30
31
|
|