claude-baton 2.1.3 → 2.1.5

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 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**. It automatically saves what was built, what's next, and why — then restores it perfectly when you come back.
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
- - **🔄 Seamless resume** — Start any session with `/memo-resume` and pick up exactly where you left off
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 # 97 tests
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").
@@ -41,4 +42,7 @@ Save session state before context loss.
41
42
  📍 State: [current_state summary]
42
43
  🎯 Next: [next_steps]
43
44
  📝 Uncommitted: [count] files
45
+
46
+ 📜 Commits this session:
47
+ [List commits made during THIS conversation from your context — or "No commits this session" if none were made]
44
48
  ```
@@ -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="$(date '+%Y-%m-%d') 00:00:00" --until="$(date '+%Y-%m-%d') 23:59:59" --format="%h|||%s|||%ai|||%an" --all`
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 2>/dev/null | head -30`
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."
@@ -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.
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
- const BATON_TOOLS = [
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 BATON_TOOLS) {
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
- console.error(` Registered ${toolsAdded} allowed tools`);
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 BATON_PATTERNS = [
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
- settings.allowedTools = settings.allowedTools.filter((t) => !BATON_PATTERNS.includes(t));
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 / 1024).toFixed(1)} KB)`);
411
+ console.log(`Database: ${dbPath} (${formatSize(dbSize)})`);
356
412
  console.log();
357
413
  console.log("Counts:");
358
- for (const [key, value] of Object.entries(counts)) {
359
- console.log(` ${key}: ${value}`);
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, 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";
@@ -209,11 +209,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
209
209
  .replace("{{ACTIVITY}}", activityParts.join("\n\n"));
210
210
  const summaryResult = await callClaudeJson(summaryPrompt, "sonnet", 60000);
211
211
  insertDailySummary(db, projectPath, date, summaryResult, dbPath);
212
+ // Attach usage stats for cost transparency
213
+ const autoCheckpointsToday = checkpoints.filter((cp) => cp.source === "auto").length;
214
+ const counts = countAll(db, projectPath);
215
+ const dbSizeBytes = statSync(dbPath).size;
216
+ const usage = {
217
+ llm_calls_today: autoCheckpointsToday + 1, // +1 for this EOD call
218
+ auto_checkpoints_today: autoCheckpointsToday,
219
+ total_llm_calls: counts.auto_checkpoints + counts.daily_summaries,
220
+ db_size: formatSize(dbSizeBytes),
221
+ };
212
222
  return {
213
223
  content: [
214
224
  {
215
225
  type: "text",
216
- text: JSON.stringify(summaryResult, null, 2),
226
+ text: JSON.stringify({ ...summaryResult, usage }, null, 2),
217
227
  },
218
228
  ],
219
229
  };
package/dist/store.js CHANGED
@@ -233,11 +233,13 @@ export function countAll(db, projectPath) {
233
233
  const p = [projectPath];
234
234
  return {
235
235
  checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE project_path = ?", p),
236
+ auto_checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE project_path = ? AND source = 'auto'", p),
236
237
  daily_summaries: countTable(db, "SELECT COUNT(*) as count FROM daily_summaries WHERE project_path = ?", p),
237
238
  };
238
239
  }
239
240
  return {
240
241
  checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints", []),
242
+ auto_checkpoints: countTable(db, "SELECT COUNT(*) as count FROM checkpoints WHERE source = 'auto'", []),
241
243
  daily_summaries: countTable(db, "SELECT COUNT(*) as count FROM daily_summaries", []),
242
244
  };
243
245
  }
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export declare function getProjectPath(): string;
2
2
  export declare function normalizeProjectPath(p: string): string;
3
3
  export declare function ensureDir(dirPath: string): void;
4
+ export declare function formatSize(bytes: number): string;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-baton",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Session lifecycle management for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",