claude-baton 2.1.3 → 2.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/README.md +5 -4
- package/commands/memo-checkpoint.md +11 -1
- package/commands/memo-eod.md +9 -3
- package/commands/memo-resume.md +2 -0
- package/commands/memo-retro.md +84 -0
- package/dist/cli.js +68 -11
- package/dist/index.js +32 -7
- package/dist/store.d.ts +2 -0
- package/dist/store.js +25 -2
- package/dist/types.d.ts +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +8 -0
- package/package.json +1 -1
- package/prompts/auto_checkpoint.txt +3 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ You're deep in a Claude Code session — 45 minutes in, multiple files changed,
|
|
|
24
24
|
|
|
25
25
|
## ✨ The Solution
|
|
26
26
|
|
|
27
|
-
claude-baton gives Claude Code **persistent memory across sessions**.
|
|
27
|
+
claude-baton gives Claude Code **persistent memory across sessions**. One command saves what was built, what's next, and why — one command brings it back. Context compaction? A PreCompact hook auto-saves before anything is lost.
|
|
28
28
|
|
|
29
29
|
```
|
|
30
30
|
Session 1 Session 2
|
|
@@ -41,11 +41,12 @@ Session 1 Session 2
|
|
|
41
41
|
|
|
42
42
|
## 🎯 What You Get
|
|
43
43
|
|
|
44
|
-
- **🔄
|
|
44
|
+
- **🔄 One-command resume** — Start any session with `/memo-resume` to restore what was built, decisions, blockers, and next steps
|
|
45
45
|
- **💾 Auto-checkpoint** — PreCompact hook saves your session state *before* Claude compacts context. You never have to remember.
|
|
46
46
|
- **📊 Diff intelligence** — Resume shows what changed since your last session (new commits, modified files, dependency changes)
|
|
47
47
|
- **🧠 Decision memory** — Key decisions and their reasoning survive across sessions
|
|
48
48
|
- **📋 EOD summaries** — Generate end-of-day reports from all sessions with `/memo-eod`
|
|
49
|
+
- **💰 Cost transparency** — EOD shows daily LLM calls; `claude-baton status` shows all-time usage and DB size
|
|
49
50
|
- **🔒 Fully local** — All data in a local SQLite database. No cloud, no API keys, no data leaves your machine.
|
|
50
51
|
|
|
51
52
|
## 🏗️ Claude Code vs Claude Code + Baton
|
|
@@ -202,7 +203,7 @@ fix: MCP server registration via claude mcp add
|
|
|
202
203
|
|
|
203
204
|
```bash
|
|
204
205
|
claude-baton setup # 🔧 one-time setup (MCP, hooks, commands)
|
|
205
|
-
claude-baton status # 📊 checkpoint counts, db size
|
|
206
|
+
claude-baton status # 📊 checkpoint counts, LLM calls, db size
|
|
206
207
|
claude-baton projects # 📁 list tracked projects
|
|
207
208
|
claude-baton export [--project] # 📤 export as JSON
|
|
208
209
|
claude-baton import <file> # 📥 import from JSON
|
|
@@ -268,7 +269,7 @@ git clone https://github.com/bakabaka91/claude-baton.git
|
|
|
268
269
|
cd claude-baton
|
|
269
270
|
npm install
|
|
270
271
|
npm run build
|
|
271
|
-
npm test #
|
|
272
|
+
npm test # 98 tests
|
|
272
273
|
```
|
|
273
274
|
|
|
274
275
|
## 🗑️ Uninstall
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Save session state before context loss.
|
|
2
2
|
|
|
3
|
+
**Important: Run all git commands exactly as written below — do NOT add -C flags or path arguments. The working directory is already the project root.**
|
|
4
|
+
|
|
3
5
|
## Steps
|
|
4
6
|
|
|
5
7
|
1. Detect the project from the current working directory.
|
|
@@ -8,7 +10,6 @@ Save session state before context loss.
|
|
|
8
10
|
- `git branch --show-current`
|
|
9
11
|
- `git status --short`
|
|
10
12
|
- `git diff --name-only HEAD`
|
|
11
|
-
- `git log --since="2 hours ago" --format="%h %s"`
|
|
12
13
|
- `git log --oneline -10` (this becomes the git_snapshot)
|
|
13
14
|
|
|
14
15
|
3. Check if any plan documents exist (PLAN.md, docs/*.md specs, roadmaps). If one is being actively worked on, note the file path and the current phase/step (e.g. "docs/v2-plan.md Phase 2 Step 3").
|
|
@@ -20,6 +21,11 @@ Save session state before context loss.
|
|
|
20
21
|
- **decisions**: key choices made and WHY
|
|
21
22
|
- **blockers**: anything blocking progress
|
|
22
23
|
- **plan_reference**: file path and section of the active plan document (e.g. "docs/v2-plan.md Phase 2 Step 3"), or omit if no plan is active
|
|
24
|
+
- **learnings**: review the conversation for corrections, failures, and wrong assumptions. For each, write a short actionable statement. Examples:
|
|
25
|
+
- "User corrected: always run tests before committing"
|
|
26
|
+
- "Assumed API returns array but it returns object"
|
|
27
|
+
- "Build failed because forgot to update imports after rename"
|
|
28
|
+
If nothing went wrong this session, use an empty array.
|
|
23
29
|
|
|
24
30
|
5. Call the `save_checkpoint` MCP tool with all fields:
|
|
25
31
|
- what_was_built
|
|
@@ -31,6 +37,7 @@ Save session state before context loss.
|
|
|
31
37
|
- branch (from step 2)
|
|
32
38
|
- uncommitted_files (array of lines from git status --short)
|
|
33
39
|
- git_snapshot (from git log --oneline -10)
|
|
40
|
+
- learnings (array of strings from step 4)
|
|
34
41
|
|
|
35
42
|
6. Print structured confirmation:
|
|
36
43
|
|
|
@@ -41,4 +48,7 @@ Save session state before context loss.
|
|
|
41
48
|
📍 State: [current_state summary]
|
|
42
49
|
🎯 Next: [next_steps]
|
|
43
50
|
📝 Uncommitted: [count] files
|
|
51
|
+
|
|
52
|
+
📜 Commits this session:
|
|
53
|
+
[List commits made during THIS conversation from your context — or "No commits this session" if none were made]
|
|
44
54
|
```
|
package/commands/memo-eod.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
End-of-day summary combining git activity with stored checkpoints.
|
|
2
2
|
|
|
3
|
+
**Important: Run all git commands exactly as written below — do NOT add -C flags or path arguments. The working directory is already the project root.**
|
|
4
|
+
|
|
3
5
|
## Steps
|
|
4
6
|
|
|
5
7
|
1. Detect the project from the current working directory.
|
|
6
8
|
|
|
7
|
-
2. Collect git activity for today by running:
|
|
8
|
-
- `git log --since="
|
|
9
|
+
2. Collect git activity for today by running (replace YYYY-MM-DD with today's actual date as a literal string — do NOT use $() command substitution):
|
|
10
|
+
- `git log --since="YYYY-MM-DD 00:00:00" --until="YYYY-MM-DD 23:59:59" --format="%h|||%s|||%ai|||%an" --all`
|
|
9
11
|
- Parse and group commits by conventional commit prefix (feat/fix/chore/refactor/test/docs).
|
|
10
12
|
|
|
11
13
|
3. Review changed files:
|
|
12
|
-
- `git diff --name-only HEAD~10 HEAD
|
|
14
|
+
- `git diff --name-only HEAD~10 HEAD`
|
|
13
15
|
|
|
14
16
|
4. Call the `daily_summary` MCP tool (defaults to today).
|
|
15
17
|
This internally gathers checkpoints and sends them to Sonnet for synthesis, then stores the result.
|
|
@@ -37,6 +39,10 @@ End-of-day summary combining git activity with stored checkpoints.
|
|
|
37
39
|
- 📝 Commits today: [count]
|
|
38
40
|
- 📁 Files changed: [count]
|
|
39
41
|
- 🏷️ Feature areas: [list of commit prefixes]
|
|
42
|
+
|
|
43
|
+
### 💰 Baton Usage
|
|
44
|
+
- LLM calls today: [usage.llm_calls_today from daily_summary response] ([N] auto-checkpoints + 1 EOD)
|
|
45
|
+
- Database: [usage.db_size from daily_summary response]
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
7. Confirm: "✅ EOD saved for [Project Name] — [DATE]. [N] commits today."
|
package/commands/memo-resume.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Restore context from last checkpoint at session start.
|
|
2
2
|
|
|
3
|
+
**Important: Run all git commands exactly as written below — do NOT add -C flags or path arguments. The working directory is already the project root.**
|
|
4
|
+
|
|
3
5
|
## Steps
|
|
4
6
|
|
|
5
7
|
1. Detect the project from the current working directory.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Analyze recent checkpoints to surface learnings and suggest memory updates.
|
|
2
|
+
|
|
3
|
+
Run periodically (weekly or after major milestones) to close the learning loop.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
1. Detect the project from the current working directory. Construct the project slug by replacing all `/` with `-` in the full path.
|
|
8
|
+
|
|
9
|
+
2. Call the `list_checkpoints` MCP tool with `limit: 10` (no date) to fetch the 10 most recent checkpoints.
|
|
10
|
+
|
|
11
|
+
3. Load current memory state:
|
|
12
|
+
- Read all files in `~/.claude/projects/[PROJECT_SLUG]/memory/`
|
|
13
|
+
- Read `MEMORY.md` in the memory directory
|
|
14
|
+
- Read the project's `CLAUDE.md` (in the working directory root)
|
|
15
|
+
|
|
16
|
+
4. Check git history for correction patterns:
|
|
17
|
+
```bash
|
|
18
|
+
git log --oneline -30
|
|
19
|
+
```
|
|
20
|
+
Look for reverts, quick-follow fix commits (e.g., "fix: ..." immediately after a feature commit), and patterns indicating repeated mistakes.
|
|
21
|
+
|
|
22
|
+
5. Analyze the checkpoint data:
|
|
23
|
+
|
|
24
|
+
**Extract learnings:**
|
|
25
|
+
- Pull the `learnings` array from each checkpoint (may be empty or absent on older checkpoints)
|
|
26
|
+
- Also extract signals from `decisions_made`, `blockers`, and `what_was_built`
|
|
27
|
+
|
|
28
|
+
**Frequency analysis:**
|
|
29
|
+
- Count how many times each learning (or semantically similar learning) appears
|
|
30
|
+
- Single occurrence = note only
|
|
31
|
+
- 2+ occurrences = high confidence, propose as rule
|
|
32
|
+
|
|
33
|
+
**Cross-reference:**
|
|
34
|
+
- Is this learning already captured in a memory file? → skip
|
|
35
|
+
- Is this learning already a CLAUDE.md rule? → skip
|
|
36
|
+
- Is this a correction/preference? → propose as feedback memory
|
|
37
|
+
- Is this about the project? → propose as project memory
|
|
38
|
+
|
|
39
|
+
6. Present the report:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
## 🔄 Retro — [PROJECT_NAME] — [DATE]
|
|
43
|
+
|
|
44
|
+
### 🧠 Memory Health
|
|
45
|
+
|
|
46
|
+
- 📁 Total memories: [count of files in memory dir]
|
|
47
|
+
- 📏 MEMORY.md line count: [N]/200 (⚠️ warn if >150)
|
|
48
|
+
- 🪦 Stale memories: [any referencing completed/outdated work]
|
|
49
|
+
- 🔀 Duplicate/overlapping memories: [any found]
|
|
50
|
+
|
|
51
|
+
### 📚 Learnings Summary
|
|
52
|
+
|
|
53
|
+
**🔁 Recurring (2+ times) — propose as rules:**
|
|
54
|
+
|
|
55
|
+
- [learning] — appeared in [N] checkpoints
|
|
56
|
+
- ...
|
|
57
|
+
|
|
58
|
+
**📌 Single occurrence — note only:**
|
|
59
|
+
|
|
60
|
+
- [learning]
|
|
61
|
+
- ...
|
|
62
|
+
|
|
63
|
+
### 🔍 Git Correction Patterns
|
|
64
|
+
|
|
65
|
+
[Reverts, quick-follow fixes that indicate recurring mistakes, or "✅ None detected"]
|
|
66
|
+
|
|
67
|
+
### 🛠️ Proposed Changes
|
|
68
|
+
|
|
69
|
+
**📝 Memory file actions:**
|
|
70
|
+
|
|
71
|
+
- ➕ **Create:** [new memory file if a pattern warrants it, with suggested filename and content]
|
|
72
|
+
- ✏️ **Update:** [existing memory with outdated info]
|
|
73
|
+
- 🔗 **Merge:** [overlapping memories to consolidate]
|
|
74
|
+
- 🗑️ **Prune:** [stale memories to delete]
|
|
75
|
+
|
|
76
|
+
**📐 CLAUDE.md rules** (only for recurring learnings not already captured):
|
|
77
|
+
|
|
78
|
+
1. Add to section [X]: "[exact rule wording]"
|
|
79
|
+
_Reason: appeared in [N] checkpoints, e.g., [quote from learning]_
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
7. Ask: **"Which changes should I apply? (all / list numbers / none)"**
|
|
83
|
+
|
|
84
|
+
Do NOT modify CLAUDE.md, memory files, or any project files without explicit approval.
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from "path";
|
|
|
6
6
|
import os from "os";
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
8
|
import { initDatabase, getDefaultDbPath, saveDatabase, countAll, listProjects, insertCheckpoint, getLatestCheckpoint, getAllCheckpoints, getAllDailySummaries, deleteProjectData, deleteAllData, } from "./store.js";
|
|
9
|
-
import { ensureDir, normalizeProjectPath } from "./utils.js";
|
|
9
|
+
import { ensureDir, formatSize, normalizeProjectPath } from "./utils.js";
|
|
10
10
|
import { callClaudeJson } from "./llm.js";
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
function readPromptTemplate(name) {
|
|
@@ -176,7 +176,8 @@ export async function handleSetup() {
|
|
|
176
176
|
console.error(" Registered PreCompact hook");
|
|
177
177
|
}
|
|
178
178
|
// Register allowed tools for frictionless slash commands (idempotent)
|
|
179
|
-
|
|
179
|
+
// Bash patterns use legacy allowedTools (still works for Bash tools)
|
|
180
|
+
const BATON_BASH_TOOLS = [
|
|
180
181
|
"Bash(git status*)",
|
|
181
182
|
"Bash(git log*)",
|
|
182
183
|
"Bash(git diff*)",
|
|
@@ -185,7 +186,7 @@ export async function handleSetup() {
|
|
|
185
186
|
];
|
|
186
187
|
const allowedTools = (settings.allowedTools ?? []);
|
|
187
188
|
let toolsAdded = 0;
|
|
188
|
-
for (const tool of
|
|
189
|
+
for (const tool of BATON_BASH_TOOLS) {
|
|
189
190
|
if (!allowedTools.includes(tool)) {
|
|
190
191
|
allowedTools.push(tool);
|
|
191
192
|
toolsAdded++;
|
|
@@ -193,7 +194,29 @@ export async function handleSetup() {
|
|
|
193
194
|
}
|
|
194
195
|
if (toolsAdded > 0) {
|
|
195
196
|
settings.allowedTools = allowedTools;
|
|
196
|
-
|
|
197
|
+
}
|
|
198
|
+
// MCP tools use permissions.allow (required for MCP tool auto-approval)
|
|
199
|
+
const BATON_MCP_TOOLS = [
|
|
200
|
+
"mcp__claude-baton__save_checkpoint",
|
|
201
|
+
"mcp__claude-baton__get_checkpoint",
|
|
202
|
+
"mcp__claude-baton__list_checkpoints",
|
|
203
|
+
"mcp__claude-baton__daily_summary",
|
|
204
|
+
];
|
|
205
|
+
const permissions = (settings.permissions ?? {});
|
|
206
|
+
const allowList = (permissions.allow ?? []);
|
|
207
|
+
let mcpToolsAdded = 0;
|
|
208
|
+
for (const tool of BATON_MCP_TOOLS) {
|
|
209
|
+
if (!allowList.includes(tool)) {
|
|
210
|
+
allowList.push(tool);
|
|
211
|
+
mcpToolsAdded++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (mcpToolsAdded > 0) {
|
|
215
|
+
permissions.allow = allowList;
|
|
216
|
+
settings.permissions = permissions;
|
|
217
|
+
}
|
|
218
|
+
if (toolsAdded + mcpToolsAdded > 0) {
|
|
219
|
+
console.error(` Registered ${toolsAdded + mcpToolsAdded} allowed tools`);
|
|
197
220
|
}
|
|
198
221
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
199
222
|
const dbPath = getDefaultDbPath();
|
|
@@ -289,21 +312,53 @@ export async function handleUninstall(opts) {
|
|
|
289
312
|
}
|
|
290
313
|
console.error(" Removed PreCompact hook");
|
|
291
314
|
}
|
|
292
|
-
// Remove allowed tools
|
|
315
|
+
// Remove allowed tools (bash patterns)
|
|
293
316
|
if (Array.isArray(settings.allowedTools)) {
|
|
294
|
-
const
|
|
317
|
+
const BATON_BASH_PATTERNS = [
|
|
295
318
|
"Bash(git status*)",
|
|
296
319
|
"Bash(git log*)",
|
|
297
320
|
"Bash(git diff*)",
|
|
298
321
|
"Bash(git branch*)",
|
|
322
|
+
"Bash(git -C *status*)",
|
|
323
|
+
"Bash(git -C *log*)",
|
|
324
|
+
"Bash(git -C *diff*)",
|
|
325
|
+
"Bash(git -C *branch*)",
|
|
299
326
|
"Bash(node *claude-baton*)",
|
|
300
327
|
];
|
|
301
|
-
|
|
328
|
+
// Note: git -C patterns kept in uninstall to clean up from older installs
|
|
329
|
+
settings.allowedTools = settings.allowedTools.filter((t) => !BATON_BASH_PATTERNS.includes(t));
|
|
302
330
|
if (settings.allowedTools.length === 0) {
|
|
303
331
|
delete settings.allowedTools;
|
|
304
332
|
}
|
|
305
333
|
console.error(" Removed allowed tools");
|
|
306
334
|
}
|
|
335
|
+
// Remove MCP tool permissions
|
|
336
|
+
if (settings.permissions &&
|
|
337
|
+
typeof settings.permissions === "object" &&
|
|
338
|
+
Array.isArray(settings.permissions.allow)) {
|
|
339
|
+
const perms = settings.permissions;
|
|
340
|
+
const BATON_MCP_PATTERNS = [
|
|
341
|
+
"mcp__claude-baton__save_checkpoint",
|
|
342
|
+
"mcp__claude-baton__get_checkpoint",
|
|
343
|
+
"mcp__claude-baton__list_checkpoints",
|
|
344
|
+
"mcp__claude-baton__daily_summary",
|
|
345
|
+
];
|
|
346
|
+
perms.allow = perms.allow.filter((t) => !BATON_MCP_PATTERNS.includes(t));
|
|
347
|
+
if (perms.allow.length === 0) {
|
|
348
|
+
delete perms.allow;
|
|
349
|
+
}
|
|
350
|
+
if (Object.keys(perms).length === 0) {
|
|
351
|
+
delete settings.permissions;
|
|
352
|
+
}
|
|
353
|
+
console.error(" Removed MCP tool permissions");
|
|
354
|
+
}
|
|
355
|
+
// Clean up legacy MCP entries from allowedTools (from older versions)
|
|
356
|
+
if (Array.isArray(settings.allowedTools)) {
|
|
357
|
+
settings.allowedTools = settings.allowedTools.filter((t) => !t.startsWith("mcp__claude-baton__"));
|
|
358
|
+
if (settings.allowedTools.length === 0) {
|
|
359
|
+
delete settings.allowedTools;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
307
362
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
308
363
|
}
|
|
309
364
|
catch {
|
|
@@ -351,13 +406,15 @@ export async function handleStatus(opts) {
|
|
|
351
406
|
const projectPath = opts.project ?? normalizeProjectPath(process.cwd());
|
|
352
407
|
const counts = countAll(db, projectPath);
|
|
353
408
|
const dbSize = statSync(dbPath).size;
|
|
409
|
+
const llmCalls = counts.auto_checkpoints + counts.daily_summaries;
|
|
354
410
|
console.log(`Project: ${projectPath}`);
|
|
355
|
-
console.log(`Database: ${dbPath} (${(dbSize
|
|
411
|
+
console.log(`Database: ${dbPath} (${formatSize(dbSize)})`);
|
|
356
412
|
console.log();
|
|
357
413
|
console.log("Counts:");
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
414
|
+
console.log(` checkpoints: ${counts.checkpoints} (${counts.checkpoints - counts.auto_checkpoints} manual, ${counts.auto_checkpoints} auto)`);
|
|
415
|
+
console.log(` daily_summaries: ${counts.daily_summaries}`);
|
|
416
|
+
console.log();
|
|
417
|
+
console.log(`LLM calls (claude -p): ${llmCalls} (${counts.auto_checkpoints} auto-checkpoints + ${counts.daily_summaries} EOD summaries)`);
|
|
361
418
|
}
|
|
362
419
|
// --- Projects command ---
|
|
363
420
|
export async function handleProjects() {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { initDatabase, getDefaultDbPath, insertCheckpoint, getLatestCheckpoint, getCheckpoint, getCheckpointsByDate, insertDailySummary, } from "./store.js";
|
|
4
|
+
import { initDatabase, getDefaultDbPath, insertCheckpoint, getLatestCheckpoint, getCheckpoint, getCheckpointsByDate, getRecentCheckpoints, insertDailySummary, countAll, } from "./store.js";
|
|
5
5
|
import { callClaudeJson } from "./llm.js";
|
|
6
|
-
import { normalizeProjectPath } from "./utils.js";
|
|
6
|
+
import { formatSize, normalizeProjectPath } from "./utils.js";
|
|
7
7
|
import { readFileSync, statSync } from "fs";
|
|
8
8
|
import path from "path";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
@@ -76,6 +76,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
76
76
|
type: "string",
|
|
77
77
|
description: "Reference to active plan document and section, e.g. 'docs/plan.md Phase 2 Step 3'",
|
|
78
78
|
},
|
|
79
|
+
learnings: {
|
|
80
|
+
type: "array",
|
|
81
|
+
items: { type: "string" },
|
|
82
|
+
description: "Actionable learnings from the session — corrections, wrong assumptions, failed approaches",
|
|
83
|
+
},
|
|
79
84
|
source: {
|
|
80
85
|
type: "string",
|
|
81
86
|
enum: ["manual", "auto"],
|
|
@@ -108,13 +113,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
108
113
|
},
|
|
109
114
|
{
|
|
110
115
|
name: "list_checkpoints",
|
|
111
|
-
description: "List
|
|
116
|
+
description: "List checkpoints by date or most recent",
|
|
112
117
|
inputSchema: {
|
|
113
118
|
type: "object",
|
|
114
119
|
properties: {
|
|
115
120
|
date: {
|
|
116
121
|
type: "string",
|
|
117
|
-
description: "YYYY-MM-DD
|
|
122
|
+
description: "YYYY-MM-DD — returns checkpoints for this date. If omitted, returns most recent across all dates.",
|
|
123
|
+
},
|
|
124
|
+
limit: {
|
|
125
|
+
type: "number",
|
|
126
|
+
description: "Max checkpoints to return (default 10). Used when date is omitted.",
|
|
118
127
|
},
|
|
119
128
|
project: {
|
|
120
129
|
type: "string",
|
|
@@ -161,6 +170,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
161
170
|
uncommittedFiles: a?.uncommitted_files,
|
|
162
171
|
gitSnapshot: a?.git_snapshot,
|
|
163
172
|
planReference: a?.plan_reference,
|
|
173
|
+
learnings: a?.learnings,
|
|
164
174
|
source: a?.source ?? "manual",
|
|
165
175
|
}, dbPath);
|
|
166
176
|
return { content: [{ type: "text", text: `Checkpoint saved: ${id}` }] };
|
|
@@ -177,14 +187,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
177
187
|
};
|
|
178
188
|
}
|
|
179
189
|
case "list_checkpoints": {
|
|
180
|
-
const cpDate = args?.date
|
|
181
|
-
const
|
|
190
|
+
const cpDate = args?.date;
|
|
191
|
+
const limit = args?.limit ?? 10;
|
|
192
|
+
const cps = cpDate
|
|
193
|
+
? getCheckpointsByDate(db, projectPath, cpDate)
|
|
194
|
+
: getRecentCheckpoints(db, projectPath, limit);
|
|
182
195
|
const summary = cps.map((cp) => ({
|
|
183
196
|
id: cp.id,
|
|
184
197
|
created_at: cp.created_at,
|
|
185
198
|
what_was_built: cp.what_was_built,
|
|
186
199
|
branch: cp.branch,
|
|
187
200
|
current_state: cp.current_state,
|
|
201
|
+
decisions_made: cp.decisions_made,
|
|
202
|
+
learnings: cp.learnings,
|
|
188
203
|
}));
|
|
189
204
|
return {
|
|
190
205
|
content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
|
|
@@ -209,11 +224,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
209
224
|
.replace("{{ACTIVITY}}", activityParts.join("\n\n"));
|
|
210
225
|
const summaryResult = await callClaudeJson(summaryPrompt, "sonnet", 60000);
|
|
211
226
|
insertDailySummary(db, projectPath, date, summaryResult, dbPath);
|
|
227
|
+
// Attach usage stats for cost transparency
|
|
228
|
+
const autoCheckpointsToday = checkpoints.filter((cp) => cp.source === "auto").length;
|
|
229
|
+
const counts = countAll(db, projectPath);
|
|
230
|
+
const dbSizeBytes = statSync(dbPath).size;
|
|
231
|
+
const usage = {
|
|
232
|
+
llm_calls_today: autoCheckpointsToday + 1, // +1 for this EOD call
|
|
233
|
+
auto_checkpoints_today: autoCheckpointsToday,
|
|
234
|
+
total_llm_calls: counts.auto_checkpoints + counts.daily_summaries,
|
|
235
|
+
db_size: formatSize(dbSizeBytes),
|
|
236
|
+
};
|
|
212
237
|
return {
|
|
213
238
|
content: [
|
|
214
239
|
{
|
|
215
240
|
type: "text",
|
|
216
|
-
text: JSON.stringify(summaryResult, null, 2),
|
|
241
|
+
text: JSON.stringify({ ...summaryResult, usage }, null, 2),
|
|
217
242
|
},
|
|
218
243
|
],
|
|
219
244
|
};
|
package/dist/store.d.ts
CHANGED
|
@@ -12,10 +12,12 @@ export declare function insertCheckpoint(db: Database, projectPath: string, sess
|
|
|
12
12
|
gitSnapshot?: string;
|
|
13
13
|
planReference?: string;
|
|
14
14
|
source?: "manual" | "auto";
|
|
15
|
+
learnings?: string[];
|
|
15
16
|
}, dbPath?: string): string;
|
|
16
17
|
export declare function getCheckpoint(db: Database, id: string): Checkpoint | null;
|
|
17
18
|
export declare function getLatestCheckpoint(db: Database, projectPath: string): Checkpoint | null;
|
|
18
19
|
export declare function getCheckpointsByDate(db: Database, projectPath: string, date: string): Checkpoint[];
|
|
20
|
+
export declare function getRecentCheckpoints(db: Database, projectPath: string, limit?: number): Checkpoint[];
|
|
19
21
|
export declare function getAllCheckpoints(db: Database, projectPath?: string): Checkpoint[];
|
|
20
22
|
export declare function insertDailySummary(db: Database, projectPath: string, date: string, summary: Record<string, unknown>, dbPath?: string): string;
|
|
21
23
|
export declare function getDailySummary(db: Database, projectPath: string, date: string): DailySummary | null;
|
package/dist/store.js
CHANGED
|
@@ -88,12 +88,21 @@ export function initSchema(db) {
|
|
|
88
88
|
if (!msg.includes("duplicate column"))
|
|
89
89
|
throw e;
|
|
90
90
|
}
|
|
91
|
+
// Migration: add learnings column for existing databases
|
|
92
|
+
try {
|
|
93
|
+
db.exec("ALTER TABLE checkpoints ADD COLUMN learnings TEXT DEFAULT '[]'");
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
const msg = e instanceof Error ? e.message : "";
|
|
97
|
+
if (!msg.includes("duplicate column"))
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
91
100
|
}
|
|
92
101
|
// --- Checkpoints CRUD ---
|
|
93
102
|
export function insertCheckpoint(db, projectPath, sessionId, currentState, whatWasBuilt, nextSteps, opts, dbPath) {
|
|
94
103
|
const id = crypto.randomUUID();
|
|
95
|
-
db.run(`INSERT INTO checkpoints (id, project_path, session_id, branch, current_state, what_was_built, next_steps, decisions_made, blockers, uncommitted_files, git_snapshot, plan_reference, source, created_at)
|
|
96
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
104
|
+
db.run(`INSERT INTO checkpoints (id, project_path, session_id, branch, current_state, what_was_built, next_steps, decisions_made, blockers, uncommitted_files, git_snapshot, plan_reference, source, learnings, created_at)
|
|
105
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
97
106
|
id,
|
|
98
107
|
projectPath,
|
|
99
108
|
sessionId,
|
|
@@ -107,6 +116,7 @@ export function insertCheckpoint(db, projectPath, sessionId, currentState, whatW
|
|
|
107
116
|
opts?.gitSnapshot ?? null,
|
|
108
117
|
opts?.planReference ?? null,
|
|
109
118
|
opts?.source ?? "manual",
|
|
119
|
+
JSON.stringify(opts?.learnings ?? []),
|
|
110
120
|
new Date().toISOString(),
|
|
111
121
|
]);
|
|
112
122
|
if (dbPath)
|
|
@@ -137,6 +147,7 @@ function parseCheckpointRow(row) {
|
|
|
137
147
|
uncommitted_files: JSON.parse(row.uncommitted_files),
|
|
138
148
|
git_snapshot: row.git_snapshot ?? null,
|
|
139
149
|
plan_reference: row.plan_reference ?? null,
|
|
150
|
+
learnings: JSON.parse(row.learnings ?? "[]"),
|
|
140
151
|
source: row.source ?? "manual",
|
|
141
152
|
};
|
|
142
153
|
}
|
|
@@ -154,6 +165,16 @@ export function getCheckpointsByDate(db, projectPath, date) {
|
|
|
154
165
|
stmt.free();
|
|
155
166
|
return results;
|
|
156
167
|
}
|
|
168
|
+
export function getRecentCheckpoints(db, projectPath, limit = 10) {
|
|
169
|
+
const stmt = db.prepare("SELECT * FROM checkpoints WHERE project_path = ? ORDER BY created_at DESC, rowid DESC LIMIT ?");
|
|
170
|
+
stmt.bind([projectPath, limit]);
|
|
171
|
+
const results = [];
|
|
172
|
+
while (stmt.step()) {
|
|
173
|
+
results.push(parseCheckpointRow(stmt.getAsObject()));
|
|
174
|
+
}
|
|
175
|
+
stmt.free();
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
157
178
|
export function getAllCheckpoints(db, projectPath) {
|
|
158
179
|
let sql = "SELECT * FROM checkpoints";
|
|
159
180
|
const params = [];
|
|
@@ -233,11 +254,13 @@ export function countAll(db, projectPath) {
|
|
|
233
254
|
const p = [projectPath];
|
|
234
255
|
return {
|
|
235
256
|
checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE project_path = ?", p),
|
|
257
|
+
auto_checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE project_path = ? AND source = 'auto'", p),
|
|
236
258
|
daily_summaries: countTable(db, "SELECT COUNT(*) as count FROM daily_summaries WHERE project_path = ?", p),
|
|
237
259
|
};
|
|
238
260
|
}
|
|
239
261
|
return {
|
|
240
262
|
checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints", []),
|
|
263
|
+
auto_checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE source = 'auto'", []),
|
|
241
264
|
daily_summaries: countTable(db, "SELECT COUNT(*) as count FROM daily_summaries", []),
|
|
242
265
|
};
|
|
243
266
|
}
|
package/dist/types.d.ts
CHANGED
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -21,3 +21,11 @@ export function normalizeProjectPath(p) {
|
|
|
21
21
|
export function ensureDir(dirPath) {
|
|
22
22
|
mkdirSync(dirPath, { recursive: true });
|
|
23
23
|
}
|
|
24
|
+
// --- Formatting helpers ---
|
|
25
|
+
export function formatSize(bytes) {
|
|
26
|
+
if (bytes < 1024)
|
|
27
|
+
return `${bytes} B`;
|
|
28
|
+
if (bytes < 1024 * 1024)
|
|
29
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
30
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -19,6 +19,7 @@ Analyze these inputs and produce a JSON object with these fields:
|
|
|
19
19
|
- decisions_made: Key decisions made since the previous checkpoint, if any (1-2 sentences, or "None")
|
|
20
20
|
- blockers: Anything blocking progress, if any (1 sentence, or "None")
|
|
21
21
|
- plan_reference: If the session references an active plan document (e.g. PLAN.md, docs/plan.md, a spec file), include the file path and the specific section being worked on (e.g. "docs/v2-plan.md Phase 2 Step 3"). Null if no plan document is referenced.
|
|
22
|
+
- learnings: Review the transcript for corrections made by the user, failed approaches, wrong assumptions, or mistakes by Claude. For each, write a short actionable learning statement. Examples: "User corrected: always run tests before committing", "Assumed API returns array but it returns object", "Build failed because forgot to update imports after rename". If nothing went wrong, output an empty array.
|
|
22
23
|
|
|
23
24
|
Rules:
|
|
24
25
|
- Be concise — each field should be 1-3 sentences max
|
|
@@ -40,5 +41,6 @@ Example output:
|
|
|
40
41
|
"next_steps": "Wire up login form to POST /auth/login endpoint. Add token refresh middleware.",
|
|
41
42
|
"decisions_made": "Using bcrypt over argon2 for password hashing due to simpler deployment. Storing refresh tokens in httpOnly cookies.",
|
|
42
43
|
"blockers": "None",
|
|
43
|
-
"plan_reference": "docs/auth-plan.md Phase 2 Step 1"
|
|
44
|
+
"plan_reference": "docs/auth-plan.md Phase 2 Step 1",
|
|
45
|
+
"learnings": ["Assumed bcrypt was already installed but had to add it to dependencies"]
|
|
44
46
|
}
|