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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-brain",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Persistent memory system for Claude Code - remembers context across sessions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- function mergeHookArray(existing, ours) {
90
- if (!existing) return ours;
91
- // Filter out old cc-brain hooks, then append ours
92
- const filtered = existing.filter(entry => !isCcBrainHook(entry));
93
- return [...filtered, ...ours];
94
- }
95
-
96
- settings.hooks = settings.hooks || {};
97
- settings.hooks.SessionStart = mergeHookArray(settings.hooks.SessionStart, hooks.SessionStart);
98
- settings.hooks.PreCompact = mergeHookArray(settings.hooks.PreCompact, hooks.PreCompact);
99
-
100
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
101
- console.log(`\nUpdated: ${settingsPath}`);
102
-
103
- // --- Install Skills to ~/.claude/skills/ ---
104
- const SKILLS_DIR = join(CLAUDE_DIR, 'skills');
105
- const skillNames = ['save', 'recall', 'brain'];
106
-
107
- for (const name of skillNames) {
108
- const skillDir = join(SKILLS_DIR, name);
109
- mkdirSync(skillDir, { recursive: true });
110
- copyFileSync(
111
- join(PROJECT_ROOT, 'skills', `${name}.md`),
112
- join(skillDir, 'SKILL.md')
113
- );
114
- }
115
-
116
- console.log(`\nInstalled skills to: ${SKILLS_DIR}`);
117
- console.log(' /save, /recall, /brain');
118
-
119
- console.log(`
120
- cc-brain installed!
121
-
122
- Brain location: ${BRAIN_DIR}
123
-
124
- Memory tiers:
125
- T1 (always): user.md, preferences.md
126
- T2 (project): projects/{name}/context.md
127
- T3 (archive): projects/{name}/archive/
128
-
129
- Hooks installed:
130
- SessionStart → loads T1 + T2 into context
131
- PreCompact saves important bits before compaction
132
-
133
- Edit your brain:
134
- ${BRAIN_DIR}/user.md
135
- ${BRAIN_DIR}/preferences.md
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
+ `);
@@ -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