claude-compass 0.0.1 → 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.
- package/README.md +116 -0
- package/bin/claude-compass.js +48 -0
- package/package.json +25 -1
- package/src/init.js +161 -0
- package/src/reset.js +54 -0
- package/src/status.js +90 -0
- package/src/update.js +34 -0
- package/templates/agents/claude-md-updater.md +60 -0
- package/templates/hooks/post-tool-use.js +116 -0
- package/templates/hooks/session-end.js +49 -0
- package/claude-compass-spec.md +0 -217
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# claude-compass
|
|
2
|
+
|
|
3
|
+
Automatic `CLAUDE.md` lifecycle management for [Claude Code](https://claude.ai/code).
|
|
4
|
+
|
|
5
|
+
Installs hooks and an agent into your project so that `CLAUDE.md` stays accurate as your codebase evolves — silently, without interrupting your workflow.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
You write code in Claude Code
|
|
11
|
+
↓
|
|
12
|
+
PostToolUse hook fires after significant file writes
|
|
13
|
+
↓
|
|
14
|
+
One-line note appended to .claude/pending-updates.md
|
|
15
|
+
(pure JS, zero LLM tokens)
|
|
16
|
+
↓
|
|
17
|
+
Session ends → SessionEnd hook prints a summary if 3+ changes logged
|
|
18
|
+
↓
|
|
19
|
+
You run: claude-compass update (or type "update CLAUDE.md" in chat)
|
|
20
|
+
↓
|
|
21
|
+
claude-md-updater agent reads pending-updates.md + CLAUDE.md
|
|
22
|
+
Makes surgical updates, clears the log
|
|
23
|
+
Prints: "CLAUDE.md updated — 2 entries added, 1 updated, 9 skipped"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- [Claude Code](https://claude.ai/code) CLI
|
|
29
|
+
- Node.js 18+
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g claude-compass
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Setup
|
|
38
|
+
|
|
39
|
+
Run once per project, from the project root:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
claude-compass init
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This will:
|
|
46
|
+
- Create `.claude/pending-updates.md` and `.claude/context/`
|
|
47
|
+
- Copy hooks into `.claude/hooks/`
|
|
48
|
+
- Register hooks in `~/.claude/settings.json`
|
|
49
|
+
- Install the `claude-md-updater` agent into `.claude/agents/`
|
|
50
|
+
- Create a `CLAUDE.md` stub if one doesn't exist (backs up any existing one first)
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
### `claude-compass init`
|
|
55
|
+
First-time setup. Safe to re-run — won't overwrite an existing `CLAUDE.md` without backing it up first.
|
|
56
|
+
|
|
57
|
+
### `claude-compass status`
|
|
58
|
+
Shows the current state of your project context.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
✦ claude-compass status
|
|
62
|
+
|
|
63
|
+
Project: my-project
|
|
64
|
+
CLAUDE.md last updated 2 days ago (Apr 1)
|
|
65
|
+
Pending updates: 4 changes waiting
|
|
66
|
+
Last session: logged changes (today 3:45 PM)
|
|
67
|
+
Agent: claude-md-updater ✓ installed
|
|
68
|
+
Hooks: post-tool-use ✓ session-end ✓
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `claude-compass update`
|
|
72
|
+
Tells you how to trigger the `claude-md-updater` agent from inside Claude Code. The actual update is done by the agent (requires a Claude Code session).
|
|
73
|
+
|
|
74
|
+
### `claude-compass reset`
|
|
75
|
+
Clears `pending-updates.md` without updating `CLAUDE.md`. Asks for confirmation. Use `-y` to skip.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
claude-compass reset -y
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## File structure after init
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
your-project/
|
|
85
|
+
├── CLAUDE.md ← stable project context
|
|
86
|
+
└── .claude/
|
|
87
|
+
├── pending-updates.md ← append-only change log (auto-managed)
|
|
88
|
+
├── agents/
|
|
89
|
+
│ └── claude-md-updater.md ← the updater agent
|
|
90
|
+
├── hooks/
|
|
91
|
+
│ ├── post-tool-use.js ← logs significant file writes
|
|
92
|
+
│ └── session-end.js ← prints session summary
|
|
93
|
+
└── context/ ← optional reference files
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## What gets logged
|
|
97
|
+
|
|
98
|
+
The `post-tool-use` hook fires after every `Write`, `Edit`, or `MultiEdit` tool call in Claude Code. It ignores:
|
|
99
|
+
|
|
100
|
+
- `node_modules/`, `.git/`, `dist/`, `build/`, `.next/`
|
|
101
|
+
- Lockfiles (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, etc.)
|
|
102
|
+
|
|
103
|
+
Everything else gets a one-line entry in `pending-updates.md`.
|
|
104
|
+
|
|
105
|
+
## Updating CLAUDE.md
|
|
106
|
+
|
|
107
|
+
When you're ready to sync, either:
|
|
108
|
+
|
|
109
|
+
1. Type `update CLAUDE.md` in the Claude Code chat, or
|
|
110
|
+
2. Run `claude-compass update` in the terminal — it will remind you of the exact phrase to use
|
|
111
|
+
|
|
112
|
+
The `claude-md-updater` agent reads the pending log, decides what's significant enough to document, makes surgical edits to `CLAUDE.md`, and clears the log. It costs roughly 2,000–4,000 tokens per run.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const [,, command, ...args] = process.argv;
|
|
5
|
+
|
|
6
|
+
const commands = {
|
|
7
|
+
init: () => require('../src/init.js').run(args),
|
|
8
|
+
status: () => require('../src/status.js').run(args),
|
|
9
|
+
update: () => require('../src/update.js').run(args),
|
|
10
|
+
reset: () => require('../src/reset.js').run(args),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (!command || command === '--help' || command === '-h') {
|
|
14
|
+
printHelp();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (command === '--version' || command === '-v') {
|
|
19
|
+
const pkg = require('../package.json');
|
|
20
|
+
console.log(pkg.version);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!commands[command]) {
|
|
25
|
+
console.error(`claude-compass: unknown command '${command}'`);
|
|
26
|
+
console.error(`Run 'claude-compass --help' for usage.`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
commands[command]().catch(err => {
|
|
31
|
+
console.error(`claude-compass: ${err.message}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function printHelp() {
|
|
36
|
+
console.log(`claude-compass — CLAUDE.md lifecycle management for Claude Code
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
claude-compass init First-time setup. Creates structure, installs hooks and agent.
|
|
40
|
+
claude-compass status Show current state of pending updates and hooks.
|
|
41
|
+
claude-compass update Trigger the claude-md-updater agent to sync CLAUDE.md.
|
|
42
|
+
claude-compass reset Clear pending-updates.md without updating CLAUDE.md.
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
-h, --help Show this help message
|
|
46
|
+
-v, --version Show version number
|
|
47
|
+
`);
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +1,25 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-compass",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Automatic CLAUDE.md lifecycle management for Claude Code",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claude-compass": "./bin/claude-compass.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"claude",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"anthropic",
|
|
12
|
+
"ai",
|
|
13
|
+
"developer-tools"
|
|
14
|
+
],
|
|
15
|
+
"author": "codemode001",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18.0.0"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"templates"
|
|
24
|
+
]
|
|
25
|
+
}
|
package/src/init.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
const PKG_DIR = path.join(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
async function run() {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
13
|
+
const contextDir = path.join(claudeDir, 'context');
|
|
14
|
+
const pendingPath = path.join(claudeDir, 'pending-updates.md');
|
|
15
|
+
const agentsDir = path.join(claudeDir, 'agents');
|
|
16
|
+
const hooksDir = path.join(claudeDir, 'hooks');
|
|
17
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
18
|
+
|
|
19
|
+
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
20
|
+
|
|
21
|
+
// Warn if not a git repo
|
|
22
|
+
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
23
|
+
console.warn(' Warning: current directory does not appear to be a git repository.');
|
|
24
|
+
console.warn(' claude-compass works best inside a git repo, but continuing anyway.\n');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Backup existing CLAUDE.md
|
|
28
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
29
|
+
const backupPath = claudeMdPath + '.backup';
|
|
30
|
+
fs.copyFileSync(claudeMdPath, backupPath);
|
|
31
|
+
console.log(` Backed up existing CLAUDE.md → CLAUDE.md.backup`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Create .claude directories
|
|
35
|
+
fs.mkdirSync(contextDir, { recursive: true });
|
|
36
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
37
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
// Create pending-updates.md if missing
|
|
40
|
+
if (!fs.existsSync(pendingPath)) {
|
|
41
|
+
fs.writeFileSync(pendingPath, '', 'utf8');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Install project-level hooks (copies of templates)
|
|
45
|
+
const hookSrc = path.join(PKG_DIR, 'templates', 'hooks');
|
|
46
|
+
installHook(path.join(hookSrc, 'post-tool-use.js'), path.join(hooksDir, 'post-tool-use.js'));
|
|
47
|
+
installHook(path.join(hookSrc, 'session-end.js'), path.join(hooksDir, 'session-end.js'));
|
|
48
|
+
|
|
49
|
+
// Register hooks in ~/.claude/settings.json
|
|
50
|
+
registerHooksInSettings(settingsPath, cwd);
|
|
51
|
+
|
|
52
|
+
// Install agent
|
|
53
|
+
const agentSrc = path.join(PKG_DIR, 'templates', 'agents', 'claude-md-updater.md');
|
|
54
|
+
const agentDest = path.join(agentsDir, 'claude-md-updater.md');
|
|
55
|
+
fs.copyFileSync(agentSrc, agentDest);
|
|
56
|
+
|
|
57
|
+
// Create CLAUDE.md if missing
|
|
58
|
+
if (!fs.existsSync(claudeMdPath)) {
|
|
59
|
+
const stub = buildClaudeMdStub(cwd);
|
|
60
|
+
fs.writeFileSync(claudeMdPath, stub, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Print confirmation
|
|
64
|
+
console.log(`\n✦ claude-compass initialized\n`);
|
|
65
|
+
console.log(` CLAUDE.md ✓ ready`);
|
|
66
|
+
console.log(` pending-updates ✓ ready`);
|
|
67
|
+
console.log(` hooks ✓ post-tool-use, session-end registered`);
|
|
68
|
+
console.log(` agent ✓ claude-md-updater installed`);
|
|
69
|
+
console.log(`\n Changes will be logged silently during sessions.`);
|
|
70
|
+
console.log(` Run: claude-compass status to check anytime.\n`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function installHook(src, dest) {
|
|
74
|
+
fs.copyFileSync(src, dest);
|
|
75
|
+
fs.chmodSync(dest, 0o755);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function registerHooksInSettings(settingsPath, projectCwd) {
|
|
79
|
+
// Ensure ~/.claude directory exists
|
|
80
|
+
const claudeGlobal = path.join(os.homedir(), '.claude');
|
|
81
|
+
fs.mkdirSync(claudeGlobal, { recursive: true });
|
|
82
|
+
|
|
83
|
+
let settings = {};
|
|
84
|
+
if (fs.existsSync(settingsPath)) {
|
|
85
|
+
try {
|
|
86
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
87
|
+
} catch {
|
|
88
|
+
settings = {};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!settings.hooks) settings.hooks = {};
|
|
93
|
+
|
|
94
|
+
const postToolUsePath = path.join(projectCwd, '.claude', 'hooks', 'post-tool-use.js');
|
|
95
|
+
const sessionEndPath = path.join(projectCwd, '.claude', 'hooks', 'session-end.js');
|
|
96
|
+
|
|
97
|
+
// PostToolUse hook — correct Claude Code format: { matcher, hooks: [{ type, command }] }
|
|
98
|
+
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
99
|
+
const postHookCmd = `node "${postToolUsePath}"`;
|
|
100
|
+
const hasPost = settings.hooks.PostToolUse.some(entry =>
|
|
101
|
+
Array.isArray(entry.hooks) && entry.hooks.some(h => h.command === postHookCmd)
|
|
102
|
+
);
|
|
103
|
+
if (!hasPost) {
|
|
104
|
+
settings.hooks.PostToolUse.push({
|
|
105
|
+
matcher: 'Write|Edit|MultiEdit',
|
|
106
|
+
hooks: [{ type: 'command', command: postHookCmd }],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Stop hook (session end)
|
|
111
|
+
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
112
|
+
const stopHookCmd = `node "${sessionEndPath}"`;
|
|
113
|
+
const hasStop = settings.hooks.Stop.some(entry =>
|
|
114
|
+
Array.isArray(entry.hooks) && entry.hooks.some(h => h.command === stopHookCmd)
|
|
115
|
+
);
|
|
116
|
+
if (!hasStop) {
|
|
117
|
+
settings.hooks.Stop.push({
|
|
118
|
+
matcher: '',
|
|
119
|
+
hooks: [{ type: 'command', command: stopHookCmd }],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildClaudeMdStub(cwd) {
|
|
127
|
+
const projectName = path.basename(cwd);
|
|
128
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
129
|
+
return `# ${projectName}
|
|
130
|
+
|
|
131
|
+
## Purpose
|
|
132
|
+
|
|
133
|
+
<!-- Describe what this project does in 1-2 sentences -->
|
|
134
|
+
|
|
135
|
+
## Tech Stack
|
|
136
|
+
|
|
137
|
+
<!-- List main languages, frameworks, and key libraries -->
|
|
138
|
+
|
|
139
|
+
## Architecture
|
|
140
|
+
|
|
141
|
+
<!-- Key architectural decisions and patterns -->
|
|
142
|
+
|
|
143
|
+
## Coding Conventions
|
|
144
|
+
|
|
145
|
+
<!-- Naming conventions, patterns, things to know -->
|
|
146
|
+
|
|
147
|
+
## Folder Structure
|
|
148
|
+
|
|
149
|
+
<!-- Brief overview of important directories -->
|
|
150
|
+
|
|
151
|
+
## Critical Gotchas
|
|
152
|
+
|
|
153
|
+
<!-- Things that are easy to get wrong or that burned you before -->
|
|
154
|
+
|
|
155
|
+
## Last Updated
|
|
156
|
+
|
|
157
|
+
${date}
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { run };
|
package/src/reset.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
|
|
7
|
+
async function run(args) {
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
const pendingPath = path.join(cwd, '.claude', 'pending-updates.md');
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(pendingPath)) {
|
|
12
|
+
console.error(`claude-compass: .claude/pending-updates.md not found.`);
|
|
13
|
+
console.error(`Run 'claude-compass init' first.`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const content = fs.readFileSync(pendingPath, 'utf8').trim();
|
|
18
|
+
const lines = content ? content.split('\n').filter(l => l.trim()) : [];
|
|
19
|
+
|
|
20
|
+
if (lines.length === 0) {
|
|
21
|
+
console.log(`✦ claude-compass: pending-updates.md is already empty. Nothing to reset.`);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --yes / -y skips confirmation
|
|
26
|
+
const skipConfirm = args.includes('--yes') || args.includes('-y');
|
|
27
|
+
|
|
28
|
+
if (!skipConfirm) {
|
|
29
|
+
console.log(`\n This will discard ${lines.length} pending change${lines.length === 1 ? '' : 's'} without updating CLAUDE.md.\n`);
|
|
30
|
+
const confirmed = await confirm(` Are you sure? [y/N] `);
|
|
31
|
+
if (!confirmed) {
|
|
32
|
+
console.log(` Aborted. Nothing was changed.\n`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fs.writeFileSync(pendingPath, '', 'utf8');
|
|
38
|
+
console.log(`\n✦ claude-compass: pending-updates.md cleared (${lines.length} entr${lines.length === 1 ? 'y' : 'ies'} discarded).\n`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function confirm(prompt) {
|
|
42
|
+
return new Promise(resolve => {
|
|
43
|
+
const rl = readline.createInterface({
|
|
44
|
+
input: process.stdin,
|
|
45
|
+
output: process.stdout,
|
|
46
|
+
});
|
|
47
|
+
rl.question(prompt, answer => {
|
|
48
|
+
rl.close();
|
|
49
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { run };
|
package/src/status.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
async function run() {
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
const projectName = path.basename(cwd);
|
|
10
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
11
|
+
const pendingPath = path.join(claudeDir, 'pending-updates.md');
|
|
12
|
+
const agentPath = path.join(claudeDir, 'agents', 'claude-md-updater.md');
|
|
13
|
+
const postHookPath = path.join(claudeDir, 'hooks', 'post-tool-use.js');
|
|
14
|
+
const sessionHookPath = path.join(claudeDir, 'hooks', 'session-end.js');
|
|
15
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
16
|
+
|
|
17
|
+
console.log(`\n✦ claude-compass status\n`);
|
|
18
|
+
console.log(` Project: ${projectName}`);
|
|
19
|
+
|
|
20
|
+
// CLAUDE.md age
|
|
21
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
22
|
+
const stat = fs.statSync(claudeMdPath);
|
|
23
|
+
const age = formatAge(stat.mtime);
|
|
24
|
+
console.log(` CLAUDE.md last updated ${age}`);
|
|
25
|
+
} else {
|
|
26
|
+
console.log(` CLAUDE.md ✗ not found — run claude-compass init`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Pending updates
|
|
30
|
+
if (fs.existsSync(pendingPath)) {
|
|
31
|
+
const content = fs.readFileSync(pendingPath, 'utf8').trim();
|
|
32
|
+
const lines = content ? content.split('\n').filter(l => l.trim()) : [];
|
|
33
|
+
if (lines.length === 0) {
|
|
34
|
+
console.log(` Pending updates: none`);
|
|
35
|
+
} else {
|
|
36
|
+
console.log(` Pending updates: ${lines.length} change${lines.length === 1 ? '' : 's'} waiting`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Last session: find last timestamp
|
|
40
|
+
if (lines.length > 0) {
|
|
41
|
+
const lastLine = lines[lines.length - 1];
|
|
42
|
+
const match = lastLine.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2})\]/);
|
|
43
|
+
if (match) {
|
|
44
|
+
const ts = new Date(match[1]);
|
|
45
|
+
const age = formatAge(ts);
|
|
46
|
+
console.log(` Last session: logged changes (${age})`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
console.log(` Pending updates: ✗ not found — run claude-compass init`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Agent
|
|
54
|
+
const agentInstalled = fs.existsSync(agentPath);
|
|
55
|
+
console.log(` Agent: claude-md-updater ${agentInstalled ? '✓ installed' : '✗ not found'}`);
|
|
56
|
+
|
|
57
|
+
// Hooks
|
|
58
|
+
const postOk = fs.existsSync(postHookPath);
|
|
59
|
+
const sessionOk = fs.existsSync(sessionHookPath);
|
|
60
|
+
const hooksStr = [
|
|
61
|
+
`post-tool-use ${postOk ? '✓' : '✗'}`,
|
|
62
|
+
`session-end ${sessionOk ? '✓' : '✗'}`,
|
|
63
|
+
].join(' ');
|
|
64
|
+
console.log(` Hooks: ${hooksStr}`);
|
|
65
|
+
|
|
66
|
+
if (!postOk || !sessionOk || !agentInstalled) {
|
|
67
|
+
console.log(`\n Some components are missing. Run: claude-compass init`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log('');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function formatAge(date) {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const diff = now - date.getTime();
|
|
76
|
+
const minutes = Math.floor(diff / 60000);
|
|
77
|
+
const hours = Math.floor(diff / 3600000);
|
|
78
|
+
const days = Math.floor(diff / 86400000);
|
|
79
|
+
|
|
80
|
+
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
81
|
+
const dateStr = date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
82
|
+
|
|
83
|
+
if (minutes < 1) return 'just now';
|
|
84
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? '' : 's'} ago (today ${timeStr})`;
|
|
85
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? '' : 's'} ago (today ${timeStr})`;
|
|
86
|
+
if (days === 1) return `1 day ago (${dateStr})`;
|
|
87
|
+
return `${days} days ago (${dateStr})`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { run };
|
package/src/update.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
async function run() {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const pendingPath = path.join(cwd, '.claude', 'pending-updates.md');
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(pendingPath)) {
|
|
11
|
+
console.error(`claude-compass: .claude/pending-updates.md not found.`);
|
|
12
|
+
console.error(`Run 'claude-compass init' first.`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const content = fs.readFileSync(pendingPath, 'utf8').trim();
|
|
17
|
+
const lines = content ? content.split('\n').filter(l => l.trim()) : [];
|
|
18
|
+
|
|
19
|
+
if (lines.length === 0) {
|
|
20
|
+
console.log(`✦ claude-compass: nothing to update — pending-updates.md is empty`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// When invoked as a CLI command from inside Claude Code, Claude Code itself
|
|
25
|
+
// will see this output and can invoke the agent. The update command just
|
|
26
|
+
// signals intent — the actual LLM work is done by the claude-md-updater agent.
|
|
27
|
+
console.log(`\n✦ claude-compass: ${lines.length} pending change${lines.length === 1 ? '' : 's'} found`);
|
|
28
|
+
console.log(`\n To sync CLAUDE.md, type the following in your Claude Code chat:`);
|
|
29
|
+
console.log(`\n update CLAUDE.md\n`);
|
|
30
|
+
console.log(` The claude-md-updater agent will read pending-updates.md and make`);
|
|
31
|
+
console.log(` surgical updates to CLAUDE.md, then clear the pending log.\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { run };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-md-updater
|
|
3
|
+
description: Use this agent when the user runs `claude-compass update`, explicitly asks to update CLAUDE.md, asks to sync project context, or says "update claude compass" / "sync claude compass". Also triggers when pending-updates.md has 10 or more entries. Does NOT trigger during normal coding work, file edits, debugging sessions, or any task that was not an explicit request to update CLAUDE.md.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the claude-md-updater agent. Your only job is to make surgical, accurate updates to CLAUDE.md based on what actually changed during recent coding sessions.
|
|
7
|
+
|
|
8
|
+
## Your inputs
|
|
9
|
+
|
|
10
|
+
1. `.claude/pending-updates.md` — an append-only log of file writes that happened since the last update
|
|
11
|
+
2. `CLAUDE.md` — the project context file you will update
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
**Step 1 — Read both files in full.**
|
|
16
|
+
|
|
17
|
+
Read `.claude/pending-updates.md` and `CLAUDE.md`. If pending-updates.md is empty or missing, print:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
✦ claude-compass: nothing to update — pending-updates.md is empty
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then stop.
|
|
24
|
+
|
|
25
|
+
**Step 2 — Evaluate each pending entry.**
|
|
26
|
+
|
|
27
|
+
For each line in pending-updates.md, decide whether it represents a change significant enough to update CLAUDE.md. Apply these criteria strictly:
|
|
28
|
+
|
|
29
|
+
- New file in a key directory (new route, new service, new migration, new component) → **yes, update**
|
|
30
|
+
- Change to an existing file that's already documented in CLAUDE.md AND the change alters the documented behavior → **maybe, update only if the documented fact is now wrong**
|
|
31
|
+
- Lockfile, build output, config tweak, minor edit to an already-documented file → **no, skip**
|
|
32
|
+
- Changes to test files → **only if they reveal a new testing convention not yet documented**
|
|
33
|
+
|
|
34
|
+
When in doubt, skip. CLAUDE.md should contain stable facts, not a changelog.
|
|
35
|
+
|
|
36
|
+
**Step 3 — Make surgical edits to CLAUDE.md.**
|
|
37
|
+
|
|
38
|
+
- Add new lines where appropriate — under the relevant section heading
|
|
39
|
+
- Update existing lines only if the documented fact is now incorrect
|
|
40
|
+
- Never rewrite entire sections wholesale
|
|
41
|
+
- Never delete existing content unless it is demonstrably wrong
|
|
42
|
+
- Keep CLAUDE.md concise — it should be scannable in under 2 minutes
|
|
43
|
+
- Update the `## Last Updated` line at the bottom with today's date
|
|
44
|
+
|
|
45
|
+
**Step 4 — Clear pending-updates.md.**
|
|
46
|
+
|
|
47
|
+
Write an empty string to `.claude/pending-updates.md` (overwrite with empty content).
|
|
48
|
+
|
|
49
|
+
**Step 5 — Print a one-line summary.**
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
✦ CLAUDE.md updated — X entries added, Y updated, Z skipped
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Hard constraints
|
|
56
|
+
|
|
57
|
+
- Do not invent facts. Only document what is directly evidenced by the pending entries or already in CLAUDE.md.
|
|
58
|
+
- Do not add speculative architecture, planned features, or inferred conventions unless you have direct evidence.
|
|
59
|
+
- Do not add verbose explanations — CLAUDE.md entries should be 1-2 lines each.
|
|
60
|
+
- This agent must not trigger during normal coding. It only runs when explicitly invoked.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// PostToolUse hook for claude-compass
|
|
5
|
+
// Fires after every Claude Code tool use.
|
|
6
|
+
// Appends one line to .claude/pending-updates.md for significant file writes.
|
|
7
|
+
// Zero LLM calls. Pure JavaScript.
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
function main() {
|
|
13
|
+
let input = '';
|
|
14
|
+
|
|
15
|
+
process.stdin.setEncoding('utf8');
|
|
16
|
+
process.stdin.on('data', chunk => { input += chunk; });
|
|
17
|
+
process.stdin.on('end', () => {
|
|
18
|
+
try {
|
|
19
|
+
run(input.trim());
|
|
20
|
+
} catch (err) {
|
|
21
|
+
// Hooks must never crash Claude Code — exit silently
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(rawInput) {
|
|
28
|
+
let event;
|
|
29
|
+
try {
|
|
30
|
+
event = JSON.parse(rawInput);
|
|
31
|
+
} catch {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const toolName = event?.tool_name || event?.tool || '';
|
|
36
|
+
const toolInput = event?.tool_input || event?.input || {};
|
|
37
|
+
|
|
38
|
+
// Only act on file-write tools
|
|
39
|
+
const writeTools = ['Write', 'Edit', 'MultiEdit'];
|
|
40
|
+
if (!writeTools.includes(toolName)) {
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get the file path written
|
|
45
|
+
const filePath = toolInput?.file_path || toolInput?.path || '';
|
|
46
|
+
if (!filePath) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Ignore noisy paths
|
|
51
|
+
const ignoredSegments = [
|
|
52
|
+
'node_modules/',
|
|
53
|
+
'.git/',
|
|
54
|
+
'/dist/',
|
|
55
|
+
'/build/',
|
|
56
|
+
'/.next/',
|
|
57
|
+
'/out/',
|
|
58
|
+
'/coverage/',
|
|
59
|
+
'/__pycache__/',
|
|
60
|
+
'/vendor/',
|
|
61
|
+
];
|
|
62
|
+
const normalised = filePath.replace(/\\/g, '/');
|
|
63
|
+
if (ignoredSegments.some(seg => normalised.includes(seg))) {
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Ignore lockfiles
|
|
68
|
+
const lockfiles = [
|
|
69
|
+
'package-lock.json',
|
|
70
|
+
'yarn.lock',
|
|
71
|
+
'pnpm-lock.yaml',
|
|
72
|
+
'bun.lockb',
|
|
73
|
+
'Gemfile.lock',
|
|
74
|
+
'poetry.lock',
|
|
75
|
+
'Pipfile.lock',
|
|
76
|
+
'composer.lock',
|
|
77
|
+
'go.sum',
|
|
78
|
+
];
|
|
79
|
+
const basename = path.basename(filePath);
|
|
80
|
+
if (lockfiles.includes(basename)) {
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Build a brief description from context
|
|
85
|
+
const description = buildDescription(toolName, toolInput, filePath);
|
|
86
|
+
|
|
87
|
+
// Resolve pending-updates.md relative to cwd (project root)
|
|
88
|
+
const pendingPath = path.join(process.cwd(), '.claude', 'pending-updates.md');
|
|
89
|
+
|
|
90
|
+
// If the file doesn't exist yet the project isn't initialised — skip silently
|
|
91
|
+
if (!fs.existsSync(pendingPath)) {
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const timestamp = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
|
96
|
+
const line = `[${timestamp}] FILE_WRITE: ${filePath} — ${description}\n`;
|
|
97
|
+
|
|
98
|
+
fs.appendFileSync(pendingPath, line, 'utf8');
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function buildDescription(toolName, toolInput, filePath) {
|
|
103
|
+
if (toolName === 'Write') {
|
|
104
|
+
return `file written`;
|
|
105
|
+
}
|
|
106
|
+
if (toolName === 'Edit') {
|
|
107
|
+
return `file edited`;
|
|
108
|
+
}
|
|
109
|
+
if (toolName === 'MultiEdit') {
|
|
110
|
+
const count = (toolInput?.edits || []).length;
|
|
111
|
+
return count > 1 ? `${count} edits applied` : `file edited`;
|
|
112
|
+
}
|
|
113
|
+
return `modified`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// SessionEnd hook for claude-compass
|
|
5
|
+
// Fires when a Claude Code session ends.
|
|
6
|
+
// Prints a summary if 3+ changes were logged during the session.
|
|
7
|
+
// Zero LLM calls. Pure JavaScript.
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
function main() {
|
|
13
|
+
try {
|
|
14
|
+
run();
|
|
15
|
+
} catch {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function run() {
|
|
21
|
+
const pendingPath = path.join(process.cwd(), '.claude', 'pending-updates.md');
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(pendingPath)) {
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const content = fs.readFileSync(pendingPath, 'utf8').trim();
|
|
28
|
+
if (!content) {
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const lines = content.split('\n').filter(l => l.trim().length > 0);
|
|
33
|
+
const count = lines.length;
|
|
34
|
+
|
|
35
|
+
if (count < 3) {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (count >= 10) {
|
|
40
|
+
console.log(`\n✦ claude-compass: ${count} changes logged — CLAUDE.md may be out of date`);
|
|
41
|
+
console.log(` run: claude-compass update\n`);
|
|
42
|
+
} else {
|
|
43
|
+
console.log(`\n✦ claude-compass: ${count} changes logged — run claude-compass update to sync CLAUDE.md\n`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main();
|
package/claude-compass-spec.md
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
claude-memory — Full Product Spec
|
|
2
|
-
What It Is
|
|
3
|
-
claude-memory is an npm package that extends Claude Code with automatic CLAUDE.md lifecycle management. It installs hooks and agents into Claude Code's existing infrastructure so that project context stays accurate over time — silently, without interrupting the developer's workflow.
|
|
4
|
-
It works exclusively with Claude Code. No separate runtime, no background processes, no cloud dependency.
|
|
5
|
-
|
|
6
|
-
How It Works — The Full Flow
|
|
7
|
-
Developer works in Claude Code
|
|
8
|
-
↓
|
|
9
|
-
PostToolUse hook fires after significant file writes
|
|
10
|
-
↓
|
|
11
|
-
Hook appends one-line note to .claude/pending-updates.md
|
|
12
|
-
(pure bash, zero LLM tokens)
|
|
13
|
-
↓
|
|
14
|
-
Session ends
|
|
15
|
-
↓
|
|
16
|
-
SessionEnd hook prints summary:
|
|
17
|
-
"✦ claude-memory: 3 changes logged"
|
|
18
|
-
↓
|
|
19
|
-
Developer finishes their day or a feature
|
|
20
|
-
↓
|
|
21
|
-
claude-md-updater agent runs (manually or on threshold)
|
|
22
|
-
↓
|
|
23
|
-
Reads pending-updates.md + CLAUDE.md
|
|
24
|
-
Makes surgical updates to CLAUDE.md
|
|
25
|
-
Clears pending-updates.md
|
|
26
|
-
Prints: "CLAUDE.md updated — 2 conventions added, 1 gotcha recorded"
|
|
27
|
-
|
|
28
|
-
npm Package Structure
|
|
29
|
-
claude-memory/
|
|
30
|
-
├── package.json
|
|
31
|
-
├── bin/
|
|
32
|
-
│ └── claude-memory.js ← CLI entry point
|
|
33
|
-
├── src/
|
|
34
|
-
│ ├── install.js ← installs hooks + agents into ~/.claude
|
|
35
|
-
│ ├── status.js ← reads and displays current state
|
|
36
|
-
│ ├── update.js ← manually triggers claude-md-updater
|
|
37
|
-
│ └── init.js ← creates CLAUDE.md + folder structure
|
|
38
|
-
├── templates/
|
|
39
|
-
│ ├── hooks/
|
|
40
|
-
│ │ ├── post-tool-use.js ← the hook that logs changes
|
|
41
|
-
│ │ └── session-end.js ← the hook that prints summary
|
|
42
|
-
│ └── agents/
|
|
43
|
-
│ └── claude-md-updater.md ← the updater agent
|
|
44
|
-
└── README.md
|
|
45
|
-
|
|
46
|
-
Installation Experience
|
|
47
|
-
User runs:
|
|
48
|
-
bashnpm install -g claude-memory
|
|
49
|
-
claude-memory init
|
|
50
|
-
claude-memory init does the following in order:
|
|
51
|
-
|
|
52
|
-
Detects if the current directory is a git repo — if not, warns but continues
|
|
53
|
-
Checks if CLAUDE.md already exists — if yes, backs it up to CLAUDE.md.backup before touching anything
|
|
54
|
-
Creates .claude/context/ directory
|
|
55
|
-
Creates .claude/pending-updates.md with empty state
|
|
56
|
-
Installs hooks into ~/.claude/hooks/ (global) or .claude/hooks/ (project)
|
|
57
|
-
Installs claude-md-updater agent into .claude/agents/
|
|
58
|
-
If no CLAUDE.md exists, runs codebase-explorer agent to generate one
|
|
59
|
-
Prints:
|
|
60
|
-
|
|
61
|
-
✦ claude-memory initialized
|
|
62
|
-
|
|
63
|
-
CLAUDE.md ✓ ready
|
|
64
|
-
pending-updates ✓ ready
|
|
65
|
-
hooks ✓ post-tool-use, session-end registered
|
|
66
|
-
agent ✓ claude-md-updater installed
|
|
67
|
-
|
|
68
|
-
Changes will be logged silently during sessions.
|
|
69
|
-
Run: claude-memory status to check anytime.
|
|
70
|
-
|
|
71
|
-
File Architecture
|
|
72
|
-
CLAUDE.md — stable core
|
|
73
|
-
Only contains things that rarely change:
|
|
74
|
-
|
|
75
|
-
Project purpose (1-2 sentences)
|
|
76
|
-
Tech stack
|
|
77
|
-
Key architectural decisions
|
|
78
|
-
Coding conventions
|
|
79
|
-
Folder structure overview
|
|
80
|
-
Critical gotchas
|
|
81
|
-
A ## Last Updated line at the bottom with date
|
|
82
|
-
|
|
83
|
-
.claude/pending-updates.md
|
|
84
|
-
Append-only log during sessions. Format:
|
|
85
|
-
[2026-04-03 14:23] FILE_WRITE: server/routes/auth.js — new OAuth route added
|
|
86
|
-
[2026-04-03 14:31] FILE_WRITE: server/db/migrations/004_add_sessions.sql — new sessions table
|
|
87
|
-
[2026-04-03 15:12] AGENT_NOTE: discovered that session tokens must be rotated on each request
|
|
88
|
-
Gets cleared after each successful CLAUDE.md update.
|
|
89
|
-
.claude/context/ — optional reference files
|
|
90
|
-
Only created if needed. Never auto-loaded — agents read them explicitly when relevant:
|
|
91
|
-
.claude/context/
|
|
92
|
-
├── decisions.md ← architectural decisions and why
|
|
93
|
-
├── known-issues.md ← current bugs and gotchas
|
|
94
|
-
└── recent-changes.md ← what changed in last 2 weeks
|
|
95
|
-
|
|
96
|
-
Hook Design
|
|
97
|
-
PostToolUse Hook — post-tool-use.js
|
|
98
|
-
Fires after every tool use in Claude Code.
|
|
99
|
-
Logic:
|
|
100
|
-
|
|
101
|
-
If tool is NOT a file write (Write, Edit, MultiEdit) → exit immediately, do nothing
|
|
102
|
-
If file written is in node_modules/, .git/, dist/, build/ → exit, do nothing
|
|
103
|
-
If file written is a lockfile (package-lock.json, yarn.lock) → exit, do nothing
|
|
104
|
-
Otherwise → append one line to .claude/pending-updates.md:
|
|
105
|
-
|
|
106
|
-
[timestamp] FILE_WRITE: path/to/file — {brief description from tool context}
|
|
107
|
-
This is pure JavaScript/bash. Zero LLM calls. Costs nothing.
|
|
108
|
-
SessionEnd Hook — session-end.js
|
|
109
|
-
Fires when Claude Code session ends.
|
|
110
|
-
Logic:
|
|
111
|
-
|
|
112
|
-
Read .claude/pending-updates.md
|
|
113
|
-
Count lines
|
|
114
|
-
If 0 lines → print nothing
|
|
115
|
-
If 1-2 lines → print nothing (too minor to surface)
|
|
116
|
-
If 3+ lines → print:
|
|
117
|
-
|
|
118
|
-
✦ claude-memory: 4 changes logged — run claude-memory update to sync CLAUDE.md
|
|
119
|
-
|
|
120
|
-
If 10+ lines → print with urgency:
|
|
121
|
-
|
|
122
|
-
✦ claude-memory: 12 changes logged — CLAUDE.md may be out of date
|
|
123
|
-
run: claude-memory update
|
|
124
|
-
|
|
125
|
-
The claude-md-updater Agent
|
|
126
|
-
This is the only part that uses LLM tokens.
|
|
127
|
-
Trigger: Either manually via claude-memory update CLI command, or when developer explicitly asks in Claude Code: "update CLAUDE.md" or "sync claude memory."
|
|
128
|
-
Agent description for Claude Code:
|
|
129
|
-
Use this agent when the user runs claude-memory update, asks to update
|
|
130
|
-
CLAUDE.md, asks to sync project context, or says "update claude memory".
|
|
131
|
-
Also triggers when pending-updates.md has 10 or more entries. Does NOT
|
|
132
|
-
trigger during normal coding work.
|
|
133
|
-
What the agent does:
|
|
134
|
-
|
|
135
|
-
Reads .claude/pending-updates.md in full
|
|
136
|
-
Reads current CLAUDE.md in full
|
|
137
|
-
For each entry in pending-updates, decides: does this represent a change significant enough to update CLAUDE.md? Criteria:
|
|
138
|
-
|
|
139
|
-
New file in a key directory (new route, new service, new migration) → yes
|
|
140
|
-
Change to existing file that's already documented → maybe, only if it changes the documented behavior
|
|
141
|
-
Lockfile, config tweak, minor edit → no
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Makes only surgical edits to CLAUDE.md — adds lines, updates existing lines, never rewrites sections wholesale
|
|
145
|
-
Updates the ## Last Updated line
|
|
146
|
-
Clears .claude/pending-updates.md (writes empty file)
|
|
147
|
-
Prints a one-line summary:
|
|
148
|
-
|
|
149
|
-
✦ CLAUDE.md updated — 2 entries added, 1 updated, 9 skipped
|
|
150
|
-
Token cost estimate: ~2,000-4,000 tokens per run. At typical API rates, less than $0.01 per update. Run it once a day — negligible.
|
|
151
|
-
|
|
152
|
-
CLI Commands
|
|
153
|
-
claude-memory init
|
|
154
|
-
First-time setup. Run once per project.
|
|
155
|
-
|
|
156
|
-
Creates folder structure
|
|
157
|
-
Installs hooks and agent
|
|
158
|
-
Generates CLAUDE.md if missing
|
|
159
|
-
Safe to re-run — won't overwrite existing CLAUDE.md without confirmation
|
|
160
|
-
|
|
161
|
-
claude-memory status
|
|
162
|
-
Shows current state. Safe to run anytime.
|
|
163
|
-
✦ claude-memory status
|
|
164
|
-
|
|
165
|
-
Project: my-project
|
|
166
|
-
CLAUDE.md last updated 2 days ago (Apr 1)
|
|
167
|
-
Pending updates: 4 changes waiting
|
|
168
|
-
Last session: logged 2 changes (today 3:45pm)
|
|
169
|
-
Agent: claude-md-updater ✓ installed
|
|
170
|
-
Hooks: post-tool-use ✓ session-end ✓
|
|
171
|
-
claude-memory update
|
|
172
|
-
Manually triggers the claude-md-updater agent inside Claude Code.
|
|
173
|
-
|
|
174
|
-
Only works when run from inside a Claude Code session
|
|
175
|
-
If run outside Claude Code, prints:
|
|
176
|
-
|
|
177
|
-
✦ claude-memory: open Claude Code and run this command from inside a session,
|
|
178
|
-
or type "update CLAUDE.md" in your Claude Code chat.
|
|
179
|
-
claude-memory reset
|
|
180
|
-
Clears pending-updates.md without updating CLAUDE.md.
|
|
181
|
-
|
|
182
|
-
Asks for confirmation first
|
|
183
|
-
Useful if pending changes are all irrelevant
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
package.json
|
|
187
|
-
json{
|
|
188
|
-
"name": "claude-memory",
|
|
189
|
-
"version": "0.1.0",
|
|
190
|
-
"description": "Automatic CLAUDE.md lifecycle management for Claude Code",
|
|
191
|
-
"bin": {
|
|
192
|
-
"claude-memory": "./bin/claude-memory.js"
|
|
193
|
-
},
|
|
194
|
-
"keywords": [
|
|
195
|
-
"claude",
|
|
196
|
-
"claude-code",
|
|
197
|
-
"anthropic",
|
|
198
|
-
"ai",
|
|
199
|
-
"developer-tools"
|
|
200
|
-
],
|
|
201
|
-
"engines": {
|
|
202
|
-
"node": ">=18.0.0"
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
What To Build First — Prioritized Order
|
|
207
|
-
Tell Claude Code to build in this order so you have something working at each step:
|
|
208
|
-
|
|
209
|
-
npm package skeleton — package.json, bin/claude-memory.js, basic CLI routing
|
|
210
|
-
claude-memory init — folder creation, file scaffolding, confirmation output
|
|
211
|
-
claude-memory status — reads state and displays it
|
|
212
|
-
PostToolUse hook — the change logger
|
|
213
|
-
SessionEnd hook — the session summary
|
|
214
|
-
claude-md-updater agent — the CLAUDE.md syncer
|
|
215
|
-
claude-memory update — triggers the agent
|
|
216
|
-
claude-memory reset — clears pending updates
|
|
217
|
-
Polish — error handling, edge cases, first-run experience
|