@vibe-x/agent-better-checkpoint 0.1.1 → 0.3.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 CHANGED
@@ -1,4 +1,4 @@
1
- # Agent Better Checkpoint
1
+ # Agent Better Checkpoint (ABC)
2
2
 
3
3
  **One-line install, zero config.** Turns AI agent edits into transparent, queryable Git commits.
4
4
 
@@ -6,6 +6,8 @@
6
6
  npx @vibe-x/agent-better-checkpoint
7
7
  ```
8
8
 
9
+ ![Agent Better Checkpoint](./assets/agent-better-checkpoint.png)
10
+
9
11
  That's it. Your AI coding assistant (Cursor, Claude Code) will now auto-commit every meaningful edit with semantic messages and structured metadata — no more opaque checkpoints.
10
12
 
11
13
  ---
@@ -40,6 +42,19 @@ git log --format="%(trailers:key=Agent,valueonly)" # by agent
40
42
  git log --grep="User-Prompt:.*registration" # by prompt keyword
41
43
  ```
42
44
 
45
+ ### Works with Your Favorite Git Tools
46
+
47
+ Since every checkpoint is a standard Git commit, the entire Git ecosystem is at your disposal — what was once a hidden, platform-specific checkpoint becomes a first-class citizen you can browse, search, diff, and rebase.
48
+
49
+ | Tool | Type | What You Get |
50
+ |------|------|-------------|
51
+ | [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) | VS Code / Cursor Extension | Inline blame, file history, visual commit graph, interactive rebase — see *who* (you or the AI) changed *what* and *when*, right in the editor |
52
+ | [lazygit](https://github.com/jesseduffield/lazygit) | Terminal UI | Fast keyboard-driven staging, diff browsing, cherry-pick, and rebase across checkpoint commits |
53
+ | [tig](https://github.com/jonas/tig) | Terminal UI | Lightweight ncurses Git log viewer, great for quickly scanning checkpoint history |
54
+ | [GitHub / GitLab Web UI](https://github.com) | Web | Browse, compare, and share checkpoint history online after pushing |
55
+
56
+ For example, with **GitLens in Cursor** you can hover any line to see which checkpoint introduced it and the original user prompt, view all checkpoints for a file in a timeline, or search commits by `checkpoint(` prefix and `User-Prompt` trailer content.
57
+
43
58
  ---
44
59
 
45
60
  ## How It Works
@@ -81,6 +96,28 @@ npx @vibe-x/agent-better-checkpoint --platform cursor
81
96
  npx @vibe-x/agent-better-checkpoint --platform claude
82
97
  ```
83
98
 
99
+ ### Project-local Install
100
+
101
+ Install scripts into your project for self-contained setup (commit with repo):
102
+
103
+ ```bash
104
+ cd /path/to/your/project
105
+ npx @vibe-x/agent-better-checkpoint --target .
106
+ ```
107
+
108
+ Or specify a target directory:
109
+
110
+ ```bash
111
+ npx @vibe-x/agent-better-checkpoint --target /path/to/your/project
112
+ ```
113
+
114
+ Uninstall project-local only:
115
+
116
+ ```bash
117
+ npx @vibe-x/agent-better-checkpoint --uninstall --target .
118
+ npx @vibe-x/agent-better-checkpoint --uninstall --target /path/to/project
119
+ ```
120
+
84
121
  ### Via [skills.sh](https://skills.sh)
85
122
 
86
123
  ```bash
@@ -93,11 +130,13 @@ The AI agent will auto-bootstrap the runtime scripts on first use.
93
130
 
94
131
  | Location | Content |
95
132
  |----------|---------|
96
- | `~/.agent-better-checkpoint/scripts/` | Commit script (`checkpoint.sh` / `.ps1`) |
97
- | `~/.agent-better-checkpoint/hooks/stop/` | Stop hook (`check_uncommitted.sh` / `.ps1`) |
133
+ | `~/.vibe-x/agent-better-checkpoint/scripts/` | Commit script (`checkpoint.sh` / `.ps1`) |
134
+ | `~/.vibe-x/agent-better-checkpoint/hooks/stop/` | Stop hook (`check_uncommitted.sh` / `.ps1`) |
98
135
  | Platform skill directory | `SKILL.md` — AI agent instructions |
99
136
  | Platform hook config | Stop hook registration |
100
137
 
138
+ > **Project-local mode**: Projects can also commit `.vibe-x/agent-better-checkpoint/` (config + scripts) for self-contained setup. When present, the global hook delegates to the project-local scripts automatically.
139
+
101
140
  ### Uninstall
102
141
 
103
142
  ```bash
package/bin/cli.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * agent-better-checkpoint 安装器 (Node.js)
4
+ * agent-better-checkpoint installer (Node.js)
5
5
  *
6
- * 通过 npx 一键安装 checkpoint 脚本、stop hook SKILL.md 到用户环境。
7
- * 按平台(macOS/Linux vs Windows)选择性部署对应脚本。
6
+ * One-click install via npx: checkpoint scripts, stop hook, and SKILL.md to user env.
7
+ * Deploys platform-specific scripts (macOS/Linux vs Windows).
8
8
  *
9
9
  * Usage:
10
10
  * npx @vibe-x/agent-better-checkpoint
@@ -18,26 +18,27 @@ import { homedir, platform } from 'node:os';
18
18
  import { fileURLToPath } from 'node:url';
19
19
 
20
20
  // ============================================================
21
- // 路径常量
21
+ // Path constants
22
22
  // ============================================================
23
23
 
24
24
  const __filename = fileURLToPath(import.meta.url);
25
25
  const __dirname = dirname(__filename);
26
26
  const PKG_ROOT = resolve(__dirname, '..');
27
27
 
28
- const INSTALL_BASE = join(homedir(), '.agent-better-checkpoint');
28
+ const INSTALL_BASE = join(homedir(), '.vibe-x', 'agent-better-checkpoint');
29
29
  const SKILL_NAME = 'agent-better-checkpoint';
30
30
 
31
- // 包内源文件路径
31
+ // In-package source paths
32
32
  const PLATFORM_DIR = join(PKG_ROOT, 'platform');
33
33
  const SKILL_SRC = join(PKG_ROOT, 'skill', 'SKILL.md');
34
+ const CONFIG_TEMPLATE = join(PLATFORM_DIR, 'config.template.yml');
34
35
 
35
36
  // ============================================================
36
- // 参数解析
37
+ // Argument parsing
37
38
  // ============================================================
38
39
 
39
40
  function parseArgs(argv) {
40
- const args = { platform: null, uninstall: false };
41
+ const args = { platform: null, uninstall: false, target: null };
41
42
  for (let i = 2; i < argv.length; i++) {
42
43
  switch (argv[i]) {
43
44
  case '--platform':
@@ -47,6 +48,13 @@ function parseArgs(argv) {
47
48
  process.exit(1);
48
49
  }
49
50
  break;
51
+ case '--target':
52
+ args.target = argv[++i];
53
+ if (!args.target) {
54
+ console.error('Error: --target requires a path argument');
55
+ process.exit(1);
56
+ }
57
+ break;
50
58
  case '--uninstall':
51
59
  args.uninstall = true;
52
60
  break;
@@ -73,18 +81,19 @@ Usage:
73
81
 
74
82
  Options:
75
83
  --platform <cursor|claude> Target AI platform (auto-detected if omitted)
84
+ --target <path> Install to specified project directory (. for cwd)
76
85
  --uninstall Remove installed files and hook registrations
77
86
  -h, --help Show this help message
78
87
  `);
79
88
  }
80
89
 
81
90
  // ============================================================
82
- // 平台检测
91
+ // Platform detection
83
92
  // ============================================================
84
93
 
85
94
  function detectAIPlatform() {
86
95
  const home = homedir();
87
- // 优先 Claude(如果两者都存在,用户可以用 --platform 覆盖)
96
+ // Prefer Claude (if both exist, user can override with --platform)
88
97
  if (existsSync(join(home, '.claude'))) return 'claude';
89
98
  if (existsSync(join(home, '.cursor'))) return 'cursor';
90
99
  return null;
@@ -97,7 +106,7 @@ function getOSType() {
97
106
  }
98
107
 
99
108
  // ============================================================
100
- // 文件操作辅助
109
+ // File operation helpers
101
110
  // ============================================================
102
111
 
103
112
  function ensureDir(dir) {
@@ -116,7 +125,7 @@ function setExecutable(filepath) {
116
125
  const st = statSync(filepath);
117
126
  chmodSync(filepath, st.mode | 0o111);
118
127
  } catch {
119
- // Windows chmod 可能无效,忽略
128
+ // chmod may be ineffective on Windows, ignore
120
129
  }
121
130
  }
122
131
 
@@ -134,7 +143,7 @@ function writeJsonFile(filepath, data) {
134
143
  }
135
144
 
136
145
  // ============================================================
137
- // 安装逻辑
146
+ // Install logic
138
147
  // ============================================================
139
148
 
140
149
  function installScripts(osType) {
@@ -176,7 +185,7 @@ function installSkill(aiPlatform) {
176
185
  let skillDest;
177
186
 
178
187
  if (aiPlatform === 'cursor') {
179
- // 检查 skills.sh 安装路径
188
+ // Check skills.sh install path
180
189
  const skillsShPath = join(homedir(), '.cursor', 'skills', SKILL_NAME, 'SKILL.md');
181
190
  if (existsSync(skillsShPath)) {
182
191
  console.log(` Skill → already installed at ${skillsShPath} (skipped)`);
@@ -186,7 +195,7 @@ function installSkill(aiPlatform) {
186
195
  skillDir = join(homedir(), '.cursor', 'skills', SKILL_NAME);
187
196
  skillDest = join(skillDir, 'SKILL.md');
188
197
  } else if (aiPlatform === 'claude') {
189
- // Claude Code: 按标准 skills 目录安装
198
+ // Claude Code: install to standard skills directory
190
199
  const skillsRootDir = join(homedir(), '.claude', 'skills');
191
200
  skillDir = join(skillsRootDir, SKILL_NAME);
192
201
  skillDest = join(skillDir, 'SKILL.md');
@@ -208,7 +217,7 @@ function registerCursorHook(osType) {
208
217
  if (!config.hooks) config.hooks = {};
209
218
  if (!config.hooks.stop) config.hooks.stop = [];
210
219
 
211
- // 构建 hook 命令
220
+ // Build hook command
212
221
  let hookCmd;
213
222
  if (osType === 'unix') {
214
223
  hookCmd = `bash ${INSTALL_BASE}/hooks/stop/check_uncommitted.sh`;
@@ -216,12 +225,12 @@ function registerCursorHook(osType) {
216
225
  hookCmd = `powershell -File "${INSTALL_BASE}\\hooks\\stop\\check_uncommitted.ps1"`;
217
226
  }
218
227
 
219
- // 检查是否已注册
228
+ // Check if already registered
220
229
  const alreadyRegistered = config.hooks.stop.some(
221
230
  h => typeof h === 'object' && h.command && h.command.includes(SKILL_NAME.replace(/-/g, ''))
222
231
  );
223
232
 
224
- // 用更精确的检测:检查命令中是否包含 agent-better-checkpoint
233
+ // More precise check: command includes agent-better-checkpoint
225
234
  const registered = config.hooks.stop.some(
226
235
  h => typeof h === 'object' && h.command && h.command.includes('agent-better-checkpoint')
227
236
  );
@@ -234,6 +243,37 @@ function registerCursorHook(osType) {
234
243
  console.log(` Config → ${hooksPath}`);
235
244
  }
236
245
 
246
+ function installProjectLocal(targetDir, osType) {
247
+ const projectBase = join(resolve(targetDir), '.vibe-x', 'agent-better-checkpoint');
248
+ ensureDir(projectBase);
249
+
250
+ if (osType === 'unix') {
251
+ copyFileSafe(join(PLATFORM_DIR, 'unix', 'checkpoint.sh'), join(projectBase, 'checkpoint.sh'));
252
+ copyFileSafe(join(PLATFORM_DIR, 'unix', 'check_uncommitted.sh'), join(projectBase, 'check_uncommitted.sh'));
253
+ setExecutable(join(projectBase, 'checkpoint.sh'));
254
+ setExecutable(join(projectBase, 'check_uncommitted.sh'));
255
+ }
256
+ copyFileSafe(join(PLATFORM_DIR, 'win', 'checkpoint.ps1'), join(projectBase, 'checkpoint.ps1'));
257
+ copyFileSafe(join(PLATFORM_DIR, 'win', 'check_uncommitted.ps1'), join(projectBase, 'check_uncommitted.ps1'));
258
+
259
+ const configDest = join(projectBase, 'config.yml');
260
+ if (!existsSync(configDest) && existsSync(CONFIG_TEMPLATE)) {
261
+ copyFileSafe(CONFIG_TEMPLATE, configDest);
262
+ }
263
+
264
+ console.log(` Project → ${projectBase}/`);
265
+ }
266
+
267
+ function uninstallProjectLocal(targetDir) {
268
+ const projectBase = join(resolve(targetDir), '.vibe-x', 'agent-better-checkpoint');
269
+ if (existsSync(projectBase)) {
270
+ rmSync(projectBase, { recursive: true, force: true });
271
+ console.log(` Removed project-local: ${projectBase}`);
272
+ } else {
273
+ console.log(` ${projectBase} not found, nothing to remove`);
274
+ }
275
+ }
276
+
237
277
  function registerClaudeHook(osType) {
238
278
  const settingsPath = join(homedir(), '.claude', 'settings.json');
239
279
  let settings = readJsonFile(settingsPath) || {};
@@ -265,9 +305,15 @@ function registerClaudeHook(osType) {
265
305
  }
266
306
 
267
307
  // ============================================================
268
- // 卸载逻辑
308
+ // Uninstall logic
269
309
  // ============================================================
270
310
 
311
+ function hasInstallation(platform) {
312
+ const home = homedir();
313
+ const skillDir = join(home, platform === 'cursor' ? '.cursor' : '.claude', 'skills', SKILL_NAME);
314
+ return existsSync(skillDir);
315
+ }
316
+
271
317
  function uninstallScripts() {
272
318
  if (existsSync(INSTALL_BASE)) {
273
319
  rmSync(INSTALL_BASE, { recursive: true, force: true });
@@ -324,15 +370,16 @@ function unregisterClaudeHook() {
324
370
  }
325
371
 
326
372
  // ============================================================
327
- // 主入口
373
+ // Main entry
328
374
  // ============================================================
329
375
 
330
376
  function main() {
331
377
  const args = parseArgs(process.argv);
332
378
  const osType = getOSType();
333
379
  const aiPlatform = args.platform || detectAIPlatform();
380
+ const projectTargetDir = args.target ? resolve(args.target) : null;
334
381
 
335
- if (!aiPlatform) {
382
+ if (!aiPlatform && !projectTargetDir && !args.uninstall) {
336
383
  console.error(
337
384
  'Error: could not detect AI platform.\n' +
338
385
  'Please specify: npx @vibe-x/agent-better-checkpoint --platform cursor|claude'
@@ -341,38 +388,72 @@ function main() {
341
388
  }
342
389
 
343
390
  if (args.uninstall) {
344
- // 卸载流程
345
- console.log(`\n[${aiPlatform === 'cursor' ? 'Cursor' : 'Claude Code'}] Uninstalling...`);
391
+ if (projectTargetDir) {
392
+ console.log('\n[Project-local] Uninstalling...');
393
+ uninstallProjectLocal(projectTargetDir);
394
+ }
346
395
 
347
- if (aiPlatform === 'cursor') {
348
- uninstallCursorSkill();
349
- unregisterCursorHook();
350
- } else {
351
- uninstallClaudeSkill();
352
- unregisterClaudeHook();
396
+ // 指定 --platform 时只清该平台;未指定时清理所有已安装的平台
397
+ const platforms = args.platform
398
+ ? [args.platform]
399
+ : ['cursor', 'claude'].filter(p => hasInstallation(p));
400
+
401
+ if (!projectTargetDir || args.platform) {
402
+ for (const p of platforms) {
403
+ console.log(`\n[${p === 'cursor' ? 'Cursor' : 'Claude Code'}] Uninstalling...`);
404
+ if (p === 'cursor') {
405
+ uninstallCursorSkill();
406
+ unregisterCursorHook();
407
+ } else {
408
+ uninstallClaudeSkill();
409
+ unregisterClaudeHook();
410
+ }
411
+ }
412
+ if (platforms.length > 0) {
413
+ uninstallScripts();
414
+ }
353
415
  }
354
416
 
355
- uninstallScripts();
356
- console.log(`\n✅ Uninstallation complete!`);
417
+ if (platforms.length === 0 && !projectTargetDir) {
418
+ console.log('\nNo installation found for any platform.');
419
+ }
420
+ console.log('\n✅ Uninstallation complete!');
357
421
  } else {
358
- // 安装流程
359
- console.log(`\n[${aiPlatform === 'cursor' ? 'Cursor' : 'Claude Code'}] Installing... (OS: ${osType})`);
360
-
361
- installScripts(osType);
362
- installSkill(aiPlatform);
363
-
364
- if (aiPlatform === 'cursor') {
365
- registerCursorHook(osType);
422
+ if (projectTargetDir) {
423
+ if (!aiPlatform) {
424
+ console.error('Error: --target requires AI platform for global hook. Specify --platform cursor|claude');
425
+ process.exit(1);
426
+ }
427
+ console.log(`\n[${aiPlatform === 'cursor' ? 'Cursor' : 'Claude Code'}] Installing... (OS: ${osType})`);
428
+ installScripts(osType);
429
+ installSkill(aiPlatform);
430
+ if (aiPlatform === 'cursor') {
431
+ registerCursorHook(osType);
432
+ } else {
433
+ registerClaudeHook(osType);
434
+ }
435
+ installProjectLocal(projectTargetDir, osType);
366
436
  } else {
367
- registerClaudeHook(osType);
437
+ console.log(`\n[${aiPlatform === 'cursor' ? 'Cursor' : 'Claude Code'}] Installing... (OS: ${osType})`);
438
+ installScripts(osType);
439
+ installSkill(aiPlatform);
440
+ if (aiPlatform === 'cursor') {
441
+ registerCursorHook(osType);
442
+ } else {
443
+ registerClaudeHook(osType);
444
+ }
368
445
  }
369
446
 
370
- console.log(`\n✅ Installation complete!`);
371
- console.log(`\nInstalled components:`);
372
- console.log(` 📜 Checkpoint script → ~/.agent-better-checkpoint/scripts/`);
373
- console.log(` 🔒 Stop hook → ~/.agent-better-checkpoint/hooks/stop/`);
447
+ console.log('\n✅ Installation complete!');
448
+ console.log('\nInstalled components:');
449
+ console.log(` 📜 Checkpoint script → ~/.vibe-x/agent-better-checkpoint/scripts/`);
450
+ console.log(` 🔒 Stop hook → ~/.vibe-x/agent-better-checkpoint/hooks/stop/`);
374
451
  console.log(` 📖 SKILL.md → ${aiPlatform === 'cursor' ? '~/.cursor/skills/' : '~/.claude/skills/'}${SKILL_NAME}/`);
375
- console.log(`\nThe AI agent will now auto-commit with semantic messages. Happy coding! 🎉`);
452
+ if (projectTargetDir) {
453
+ const projectBase = join(resolve(projectTargetDir), '.vibe-x', 'agent-better-checkpoint');
454
+ console.log(` 📁 Project-local → ${projectBase}/`);
455
+ }
456
+ console.log('\nThe AI agent will now auto-commit with semantic messages. Happy coding! 🎉');
376
457
  }
377
458
  }
378
459
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-x/agent-better-checkpoint",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Semantic Git checkpoint commits for AI coding sessions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,16 @@
1
+ # Checkpoint commit trigger conditions
2
+ #
3
+ # Controls when stop hook (check_uncommitted) triggers commit reminder.
4
+ # Affects only stop hook detection, not checkpoint.sh commit behavior.
5
+
6
+ # Trigger reminder if ANY condition met (OR)
7
+ # Empty/commented = condition disabled
8
+ trigger_if_any:
9
+ # min_changed_files: 3 # At least N active files changed
10
+ min_changed_lines: 5 # At least N lines changed
11
+
12
+ # Passive file patterns — matched files do not trigger reminder alone
13
+ passive_patterns:
14
+ - ".discuss/**"
15
+ - ".vibe-x/**"
16
+ - ".DS_Store"