@vibe-x/agent-better-checkpoint 0.1.1 → 0.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 +20 -3
- package/bin/cli.mjs +23 -23
- package/package.json +1 -1
- package/platform/unix/check_uncommitted.sh +263 -52
- package/platform/unix/checkpoint.sh +13 -13
- package/platform/win/check_uncommitted.ps1 +278 -57
- package/platform/win/checkpoint.ps1 +13 -13
- package/skill/SKILL.md +23 -13
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# checkpoint.sh —
|
|
3
|
+
# checkpoint.sh — Create semantic Git checkpoint commits
|
|
4
4
|
#
|
|
5
|
-
# AI
|
|
6
|
-
# 1.
|
|
7
|
-
# 2.
|
|
8
|
-
# 3.
|
|
5
|
+
# AI provides descriptive content (subject + body). This script:
|
|
6
|
+
# 1. Truncates user-prompt (≤60 chars, head+tail)
|
|
7
|
+
# 2. Appends metadata via git interpret-trailers
|
|
8
|
+
# 3. Runs git add -A && git commit
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
11
|
# checkpoint.sh <message> [user-prompt] [--type auto|fallback]
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
set -euo pipefail
|
|
14
14
|
|
|
15
15
|
# ============================================================
|
|
16
|
-
#
|
|
16
|
+
# Argument parsing
|
|
17
17
|
# ============================================================
|
|
18
18
|
MESSAGE="${1:-}"
|
|
19
19
|
USER_PROMPT="${2:-}"
|
|
@@ -39,7 +39,7 @@ if [[ -z "$MESSAGE" ]]; then
|
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
41
|
# ============================================================
|
|
42
|
-
#
|
|
42
|
+
# Platform detection
|
|
43
43
|
# ============================================================
|
|
44
44
|
detect_platform() {
|
|
45
45
|
if [[ -n "${CLAUDE_CODE:-}" ]] || command -v claude &>/dev/null; then
|
|
@@ -54,7 +54,7 @@ detect_platform() {
|
|
|
54
54
|
AGENT_PLATFORM=$(detect_platform)
|
|
55
55
|
|
|
56
56
|
# ============================================================
|
|
57
|
-
# User-Prompt
|
|
57
|
+
# User-Prompt truncation (≤60 chars, head+tail + ellipsis)
|
|
58
58
|
# ============================================================
|
|
59
59
|
truncate_prompt() {
|
|
60
60
|
local prompt="$1"
|
|
@@ -79,18 +79,18 @@ if [[ -n "$USER_PROMPT" ]]; then
|
|
|
79
79
|
fi
|
|
80
80
|
|
|
81
81
|
# ============================================================
|
|
82
|
-
#
|
|
82
|
+
# Check for changes
|
|
83
83
|
# ============================================================
|
|
84
84
|
has_changes() {
|
|
85
|
-
#
|
|
85
|
+
# Staged changes
|
|
86
86
|
if ! git diff --cached --quiet 2>/dev/null; then
|
|
87
87
|
return 0
|
|
88
88
|
fi
|
|
89
|
-
#
|
|
89
|
+
# Unstaged changes
|
|
90
90
|
if ! git diff --quiet 2>/dev/null; then
|
|
91
91
|
return 0
|
|
92
92
|
fi
|
|
93
|
-
#
|
|
93
|
+
# Untracked files
|
|
94
94
|
if [[ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]]; then
|
|
95
95
|
return 0
|
|
96
96
|
fi
|
|
@@ -108,7 +108,7 @@ fi
|
|
|
108
108
|
git add -A
|
|
109
109
|
|
|
110
110
|
# ============================================================
|
|
111
|
-
#
|
|
111
|
+
# Build trailers and commit
|
|
112
112
|
# ============================================================
|
|
113
113
|
TRAILER_ARGS=(
|
|
114
114
|
--trailer "Agent: ${AGENT_PLATFORM}"
|
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
check_uncommitted.ps1 — Stop Hook:
|
|
3
|
+
check_uncommitted.ps1 — Stop Hook: check for uncommitted changes (Windows PowerShell)
|
|
4
4
|
|
|
5
5
|
.DESCRIPTION
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Triggered at AI conversation end. Checks workspace for uncommitted changes.
|
|
7
|
+
If found, outputs reminder for AI Agent to run fallback checkpoint commit.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- Cursor: stop hook (stdin JSON
|
|
11
|
-
- Claude Code: Stop hook (stdin JSON
|
|
9
|
+
Supported platforms:
|
|
10
|
+
- Cursor: stop hook (stdin JSON with workspace_roots)
|
|
11
|
+
- Claude Code: Stop hook (stdin JSON with hook_event_name)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
13
|
+
Output protocol:
|
|
14
|
+
- OK: {} (empty JSON)
|
|
15
|
+
- Block (Cursor): {"followup_message": "..."}
|
|
16
|
+
- Block (Claude Code): {"decision": "block", "reason": "..."}
|
|
17
|
+
|
|
18
|
+
Config: .vibe-x/agent-better-checkpoint/config.yml (project-level, optional)
|
|
17
19
|
#>
|
|
18
20
|
|
|
19
21
|
$ErrorActionPreference = "Stop"
|
|
20
22
|
|
|
23
|
+
$CONFIG_FILE_NAME = ".vibe-x/agent-better-checkpoint/config.yml"
|
|
24
|
+
|
|
21
25
|
# ============================================================
|
|
22
|
-
#
|
|
26
|
+
# Helper functions
|
|
23
27
|
# ============================================================
|
|
24
28
|
|
|
25
29
|
function Output-Allow {
|
|
30
|
+
param([string]$Info = "")
|
|
31
|
+
if ($Info) {
|
|
32
|
+
[Console]::Error.WriteLine("[checkpoint] $Info")
|
|
33
|
+
}
|
|
26
34
|
Write-Output '{}'
|
|
27
35
|
exit 0
|
|
28
36
|
}
|
|
@@ -33,7 +41,6 @@ function Output-Block {
|
|
|
33
41
|
[string]$Platform
|
|
34
42
|
)
|
|
35
43
|
|
|
36
|
-
# 转义 JSON 字符串
|
|
37
44
|
$Escaped = $Message -replace '\\', '\\\\' `
|
|
38
45
|
-replace '"', '\"' `
|
|
39
46
|
-replace "`r`n", '\n' `
|
|
@@ -56,7 +63,145 @@ function Output-Block {
|
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
# ============================================================
|
|
59
|
-
#
|
|
66
|
+
# Config parsing
|
|
67
|
+
# ============================================================
|
|
68
|
+
|
|
69
|
+
function Parse-CheckpointConfig {
|
|
70
|
+
param([string]$ConfigPath)
|
|
71
|
+
|
|
72
|
+
$config = @{
|
|
73
|
+
MinChangedLines = $null
|
|
74
|
+
MinChangedFiles = $null
|
|
75
|
+
PassivePatterns = @()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (-not (Test-Path $ConfigPath)) {
|
|
79
|
+
return $config
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$lines = Get-Content $ConfigPath -ErrorAction SilentlyContinue
|
|
83
|
+
if (-not $lines) { return $config }
|
|
84
|
+
|
|
85
|
+
$inPassiveSection = $false
|
|
86
|
+
|
|
87
|
+
foreach ($line in $lines) {
|
|
88
|
+
# Skip comments
|
|
89
|
+
if ($line -match '^\s*#') { continue }
|
|
90
|
+
if (-not $line.Trim()) { continue }
|
|
91
|
+
|
|
92
|
+
# Thresholds under trigger_if_any
|
|
93
|
+
if ($line -match '^\s+min_changed_lines:\s*(\d+)') {
|
|
94
|
+
$config.MinChangedLines = [int]$Matches[1]
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
if ($line -match '^\s+min_changed_files:\s*(\d+)') {
|
|
98
|
+
$config.MinChangedFiles = [int]$Matches[1]
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# passive_patterns section
|
|
103
|
+
if ($line -match '^passive_patterns:') {
|
|
104
|
+
$inPassiveSection = $true
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
if ($inPassiveSection) {
|
|
108
|
+
if ($line -match '^\s+-\s+(.+)') {
|
|
109
|
+
$val = $Matches[1].Trim()
|
|
110
|
+
$val = $val -replace '^["'']', '' -replace '["'']$', ''
|
|
111
|
+
$val = $val -replace '\s*#.*$', ''
|
|
112
|
+
if ($val) {
|
|
113
|
+
$config.PassivePatterns += $val
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
elseif ($line -match '^\S') {
|
|
117
|
+
$inPassiveSection = $false
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return $config
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# ============================================================
|
|
126
|
+
# Passive file matching
|
|
127
|
+
# ============================================================
|
|
128
|
+
|
|
129
|
+
function Test-PassiveFile {
|
|
130
|
+
param(
|
|
131
|
+
[string]$FilePath,
|
|
132
|
+
[string[]]$Patterns
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
foreach ($pattern in $Patterns) {
|
|
136
|
+
# dir/** → match all files under dir/
|
|
137
|
+
if ($pattern -match '^(.+)/\*\*$') {
|
|
138
|
+
$prefix = $Matches[1] + "/"
|
|
139
|
+
if ($FilePath.StartsWith($prefix)) { return $true }
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# *.ext → match suffix
|
|
144
|
+
if ($pattern -match '^\*(\..+)$') {
|
|
145
|
+
$suffix = $Matches[1]
|
|
146
|
+
if ($FilePath.EndsWith($suffix)) { return $true }
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Exact match
|
|
151
|
+
if ($FilePath -eq $pattern) { return $true }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return $false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# ============================================================
|
|
158
|
+
# Changed line count
|
|
159
|
+
# ============================================================
|
|
160
|
+
|
|
161
|
+
function Get-ChangedLineCount {
|
|
162
|
+
param(
|
|
163
|
+
[string]$Workspace,
|
|
164
|
+
[string[]]$Files
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if ($Files.Count -eq 0) { return 0 }
|
|
168
|
+
$total = 0
|
|
169
|
+
|
|
170
|
+
# Tracked files: staged + unstaged
|
|
171
|
+
$diffArgs = @("-C", $Workspace, "diff", "--numstat", "--") + $Files
|
|
172
|
+
$cachedArgs = @("-C", $Workspace, "diff", "--cached", "--numstat", "--") + $Files
|
|
173
|
+
|
|
174
|
+
foreach ($args in @($diffArgs, $cachedArgs)) {
|
|
175
|
+
$output = git @args 2>$null
|
|
176
|
+
if ($output) {
|
|
177
|
+
foreach ($line in ($output -split "`n")) {
|
|
178
|
+
$parts = $line -split "`t"
|
|
179
|
+
if ($parts.Count -ge 2) {
|
|
180
|
+
$adds = if ($parts[0] -ne "-") { [int]$parts[0] } else { 0 }
|
|
181
|
+
$dels = if ($parts[1] -ne "-") { [int]$parts[1] } else { 0 }
|
|
182
|
+
$total += $adds + $dels
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Untracked files: total lines count as changes
|
|
189
|
+
foreach ($file in $Files) {
|
|
190
|
+
$untracked = git -C $Workspace ls-files --others --exclude-standard -- $file 2>$null
|
|
191
|
+
if ($untracked) {
|
|
192
|
+
$fullPath = Join-Path $Workspace $file
|
|
193
|
+
if (Test-Path $fullPath -PathType Leaf) {
|
|
194
|
+
$lineCount = (Get-Content $fullPath -ErrorAction SilentlyContinue | Measure-Object -Line).Lines
|
|
195
|
+
$total += $lineCount
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return $total
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# ============================================================
|
|
204
|
+
# Platform detection
|
|
60
205
|
# ============================================================
|
|
61
206
|
|
|
62
207
|
function Detect-Platform {
|
|
@@ -73,13 +218,12 @@ function Detect-Platform {
|
|
|
73
218
|
}
|
|
74
219
|
|
|
75
220
|
# ============================================================
|
|
76
|
-
# Workspace
|
|
221
|
+
# Workspace root detection
|
|
77
222
|
# ============================================================
|
|
78
223
|
|
|
79
224
|
function Get-WorkspaceRoot {
|
|
80
225
|
param($InputData)
|
|
81
226
|
|
|
82
|
-
# 优先从 stdin JSON 获取
|
|
83
227
|
if ($InputData) {
|
|
84
228
|
foreach ($field in @("workspace_roots", "workspaceRoots")) {
|
|
85
229
|
$roots = $null
|
|
@@ -90,18 +234,16 @@ function Get-WorkspaceRoot {
|
|
|
90
234
|
}
|
|
91
235
|
}
|
|
92
236
|
|
|
93
|
-
# 回退到环境变量
|
|
94
237
|
foreach ($envVar in @("CURSOR_PROJECT_DIR", "CLAUDE_PROJECT_DIR", "WORKSPACE_ROOT", "PROJECT_ROOT")) {
|
|
95
238
|
$val = [Environment]::GetEnvironmentVariable($envVar)
|
|
96
239
|
if ($val) { return $val }
|
|
97
240
|
}
|
|
98
241
|
|
|
99
|
-
# 最终回退到当前目录
|
|
100
242
|
return (Get-Location).Path
|
|
101
243
|
}
|
|
102
244
|
|
|
103
245
|
# ============================================================
|
|
104
|
-
# Git
|
|
246
|
+
# Git operations
|
|
105
247
|
# ============================================================
|
|
106
248
|
|
|
107
249
|
function Test-GitRepo {
|
|
@@ -115,12 +257,6 @@ function Test-GitRepo {
|
|
|
115
257
|
}
|
|
116
258
|
}
|
|
117
259
|
|
|
118
|
-
function Test-UncommittedChanges {
|
|
119
|
-
param([string]$Path)
|
|
120
|
-
$status = git -C $Path status --porcelain 2>$null
|
|
121
|
-
return [bool]$status
|
|
122
|
-
}
|
|
123
|
-
|
|
124
260
|
function Get-ChangeSummary {
|
|
125
261
|
param(
|
|
126
262
|
[string]$Path,
|
|
@@ -140,70 +276,155 @@ function Get-ChangeSummary {
|
|
|
140
276
|
}
|
|
141
277
|
|
|
142
278
|
# ============================================================
|
|
143
|
-
#
|
|
279
|
+
# Reminder message build
|
|
280
|
+
# ============================================================
|
|
281
|
+
|
|
282
|
+
function Build-Reminder {
|
|
283
|
+
param([string]$Workspace)
|
|
284
|
+
|
|
285
|
+
$Changes = Get-ChangeSummary -Path $Workspace
|
|
286
|
+
$ChangesIndented = ($Changes -split "`n" | ForEach-Object { " $_" }) -join "`n"
|
|
287
|
+
|
|
288
|
+
# Project-local script; fallback to global
|
|
289
|
+
$checkpointSh = "~/.vibe-x/agent-better-checkpoint/scripts/checkpoint.sh"
|
|
290
|
+
$checkpointPs1 = "`$env:USERPROFILE/.vibe-x/agent-better-checkpoint/scripts/checkpoint.ps1"
|
|
291
|
+
if (Test-Path (Join-Path $Workspace ".vibe-x/agent-better-checkpoint/checkpoint.sh")) {
|
|
292
|
+
$checkpointSh = ".vibe-x/agent-better-checkpoint/checkpoint.sh"
|
|
293
|
+
}
|
|
294
|
+
if (Test-Path (Join-Path $Workspace ".vibe-x/agent-better-checkpoint/checkpoint.ps1")) {
|
|
295
|
+
$checkpointPs1 = ".\.vibe-x\agent-better-checkpoint\checkpoint.ps1"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return @"
|
|
299
|
+
## ⚠️ Uncommitted Changes Detected
|
|
300
|
+
|
|
301
|
+
There are uncommitted changes in the workspace. Please create a checkpoint commit before ending the conversation.
|
|
302
|
+
|
|
303
|
+
**Changed files:**
|
|
304
|
+
``````
|
|
305
|
+
$ChangesIndented
|
|
306
|
+
``````
|
|
307
|
+
|
|
308
|
+
**Action Required**: Run the checkpoint script to commit these changes:
|
|
309
|
+
|
|
310
|
+
**macOS/Linux:**
|
|
311
|
+
``````bash
|
|
312
|
+
$checkpointSh "checkpoint(<scope>): <description>" "<user-prompt>" --type fallback
|
|
313
|
+
``````
|
|
314
|
+
|
|
315
|
+
**Windows (PowerShell):**
|
|
316
|
+
``````powershell
|
|
317
|
+
powershell -File "$checkpointPs1" "checkpoint(<scope>): <description>" "<user-prompt>" -Type fallback
|
|
318
|
+
``````
|
|
319
|
+
"@
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
# ============================================================
|
|
323
|
+
# Main logic
|
|
144
324
|
# ============================================================
|
|
145
325
|
|
|
146
|
-
# 从 stdin 读取 JSON
|
|
147
326
|
$InputData = $null
|
|
327
|
+
$rawInput = ""
|
|
148
328
|
try {
|
|
149
329
|
$rawInput = [Console]::In.ReadToEnd()
|
|
150
330
|
if ($rawInput.Trim()) {
|
|
151
331
|
$InputData = $rawInput | ConvertFrom-Json
|
|
152
332
|
}
|
|
153
333
|
}
|
|
154
|
-
catch {
|
|
155
|
-
# 忽略 JSON 解析错误
|
|
156
|
-
}
|
|
334
|
+
catch {}
|
|
157
335
|
|
|
158
|
-
# Claude Code 的 stop_hook_active 防止无限循环
|
|
159
336
|
if ($InputData -and $InputData.PSObject.Properties["stop_hook_active"]) {
|
|
160
337
|
if ($InputData.stop_hook_active -eq $true) {
|
|
161
338
|
Output-Allow
|
|
162
339
|
}
|
|
163
340
|
}
|
|
164
341
|
|
|
165
|
-
# 检测平台
|
|
166
342
|
$Platform = Detect-Platform -InputData $InputData
|
|
167
|
-
|
|
168
|
-
# 获取 workspace root
|
|
169
343
|
$Workspace = Get-WorkspaceRoot -InputData $InputData
|
|
170
344
|
|
|
171
|
-
#
|
|
345
|
+
# Delegate to project-local script when present (committed with project)
|
|
346
|
+
$ProjectScript = Join-Path $Workspace ".vibe-x/agent-better-checkpoint/check_uncommitted.ps1"
|
|
347
|
+
if (Test-Path $ProjectScript -PathType Leaf) {
|
|
348
|
+
$rawInput | & $ProjectScript
|
|
349
|
+
exit $LASTEXITCODE
|
|
350
|
+
}
|
|
351
|
+
|
|
172
352
|
if (-not (Test-GitRepo -Path $Workspace)) {
|
|
173
353
|
Output-Allow
|
|
174
354
|
}
|
|
175
355
|
|
|
176
|
-
#
|
|
177
|
-
|
|
356
|
+
# Get all changes
|
|
357
|
+
$StatusOutput = git -C $Workspace status --porcelain 2>$null
|
|
358
|
+
if (-not $StatusOutput) {
|
|
178
359
|
Output-Allow
|
|
179
360
|
}
|
|
180
361
|
|
|
181
|
-
#
|
|
182
|
-
$
|
|
183
|
-
$
|
|
362
|
+
# Load config
|
|
363
|
+
$ConfigFile = Join-Path $Workspace $CONFIG_FILE_NAME
|
|
364
|
+
$Config = Parse-CheckpointConfig -ConfigPath $ConfigFile
|
|
184
365
|
|
|
185
|
-
#
|
|
186
|
-
$
|
|
187
|
-
|
|
366
|
+
# ---- 分离主动/被动文件 ----
|
|
367
|
+
$ActiveFiles = @()
|
|
368
|
+
$PassiveFilesList = @()
|
|
188
369
|
|
|
189
|
-
|
|
370
|
+
foreach ($line in ($StatusOutput -split "`n")) {
|
|
371
|
+
if ($line.Length -lt 4) { continue }
|
|
372
|
+
$file = $line.Substring(3)
|
|
373
|
+
# Handle rename
|
|
374
|
+
if ($file -match ' -> (.+)$') {
|
|
375
|
+
$file = $Matches[1]
|
|
376
|
+
}
|
|
377
|
+
$file = $file.Trim('"')
|
|
190
378
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
379
|
+
if ($Config.PassivePatterns.Count -gt 0 -and (Test-PassiveFile -FilePath $file -Patterns $Config.PassivePatterns)) {
|
|
380
|
+
$PassiveFilesList += $file
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
$ActiveFiles += $file
|
|
384
|
+
}
|
|
385
|
+
}
|
|
195
386
|
|
|
196
|
-
|
|
387
|
+
# ---- 无主动文件 → 仅被动变更,跳过 ----
|
|
388
|
+
if ($ActiveFiles.Count -eq 0) {
|
|
389
|
+
Output-Allow -Info "Skipped: only passive file changes ($($PassiveFilesList.Count) files). Patterns: $($Config.PassivePatterns -join ', ')"
|
|
390
|
+
}
|
|
197
391
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
392
|
+
# ---- 无阈值配置 → 有主动变更就触发 ----
|
|
393
|
+
if ($null -eq $Config.MinChangedLines -and $null -eq $Config.MinChangedFiles) {
|
|
394
|
+
$Reminder = Build-Reminder -Workspace $Workspace
|
|
395
|
+
Output-Block -Message $Reminder -Platform $Platform
|
|
396
|
+
}
|
|
202
397
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
398
|
+
# ---- 检查触发条件(OR 关系) ----
|
|
399
|
+
$Triggered = $false
|
|
400
|
+
$ActiveFileCount = $ActiveFiles.Count
|
|
401
|
+
$ActiveLineCount = 0
|
|
402
|
+
|
|
403
|
+
# Check file count first (cheaper)
|
|
404
|
+
if ($null -ne $Config.MinChangedFiles -and $ActiveFileCount -ge $Config.MinChangedFiles) {
|
|
405
|
+
$Triggered = $true
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# Then check line count
|
|
409
|
+
if (-not $Triggered -and $null -ne $Config.MinChangedLines) {
|
|
410
|
+
$ActiveLineCount = Get-ChangedLineCount -Workspace $Workspace -Files $ActiveFiles
|
|
411
|
+
if ($ActiveLineCount -ge $Config.MinChangedLines) {
|
|
412
|
+
$Triggered = $true
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (-not $Triggered) {
|
|
417
|
+
if ($ActiveLineCount -eq 0 -and $null -ne $Config.MinChangedLines) {
|
|
418
|
+
# Already computed
|
|
419
|
+
}
|
|
420
|
+
elseif ($ActiveLineCount -eq 0) {
|
|
421
|
+
$ActiveLineCount = Get-ChangedLineCount -Workspace $Workspace -Files $ActiveFiles
|
|
422
|
+
}
|
|
423
|
+
$minFilesStr = if ($null -ne $Config.MinChangedFiles) { $Config.MinChangedFiles } else { "unset" }
|
|
424
|
+
$minLinesStr = if ($null -ne $Config.MinChangedLines) { $Config.MinChangedLines } else { "unset" }
|
|
425
|
+
Output-Allow -Info "Skipped: changes below threshold ($ActiveFileCount files, $ActiveLineCount lines). Config: min_changed_files=$minFilesStr, min_changed_lines=$minLinesStr"
|
|
426
|
+
}
|
|
208
427
|
|
|
428
|
+
# ---- 达到阈值,触发提交提醒 ----
|
|
429
|
+
$Reminder = Build-Reminder -Workspace $Workspace
|
|
209
430
|
Output-Block -Message $Reminder -Platform $Platform
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
checkpoint.ps1 —
|
|
3
|
+
checkpoint.ps1 — Create semantic Git checkpoint commits (Windows PowerShell)
|
|
4
4
|
|
|
5
5
|
.DESCRIPTION
|
|
6
|
-
AI
|
|
7
|
-
1.
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
6
|
+
AI provides descriptive content (subject + body). This script:
|
|
7
|
+
1. Truncates user-prompt (≤60 chars, head+tail)
|
|
8
|
+
2. Appends metadata via git interpret-trailers
|
|
9
|
+
3. Runs git add -A && git commit
|
|
10
10
|
|
|
11
11
|
.PARAMETER Message
|
|
12
12
|
Full commit message (subject + blank line + body). Required.
|
|
@@ -34,7 +34,7 @@ param(
|
|
|
34
34
|
$ErrorActionPreference = "Stop"
|
|
35
35
|
|
|
36
36
|
# ============================================================
|
|
37
|
-
#
|
|
37
|
+
# Platform detection
|
|
38
38
|
# ============================================================
|
|
39
39
|
|
|
40
40
|
function Detect-Platform {
|
|
@@ -50,7 +50,7 @@ function Detect-Platform {
|
|
|
50
50
|
$AgentPlatform = Detect-Platform
|
|
51
51
|
|
|
52
52
|
# ============================================================
|
|
53
|
-
# User-Prompt
|
|
53
|
+
# User-Prompt truncation (≤60 chars, head+tail + ellipsis)
|
|
54
54
|
# ============================================================
|
|
55
55
|
|
|
56
56
|
function Truncate-Prompt {
|
|
@@ -74,19 +74,19 @@ if ($UserPrompt) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
# ============================================================
|
|
77
|
-
#
|
|
77
|
+
# Check for changes
|
|
78
78
|
# ============================================================
|
|
79
79
|
|
|
80
80
|
function Test-HasChanges {
|
|
81
|
-
#
|
|
81
|
+
# Staged changes
|
|
82
82
|
$diffCached = git diff --cached --quiet 2>$null
|
|
83
83
|
if ($LASTEXITCODE -ne 0) { return $true }
|
|
84
84
|
|
|
85
|
-
#
|
|
85
|
+
# Unstaged changes
|
|
86
86
|
$diffWorking = git diff --quiet 2>$null
|
|
87
87
|
if ($LASTEXITCODE -ne 0) { return $true }
|
|
88
88
|
|
|
89
|
-
#
|
|
89
|
+
# Untracked files
|
|
90
90
|
$untracked = git ls-files --others --exclude-standard 2>$null
|
|
91
91
|
if ($untracked) { return $true }
|
|
92
92
|
|
|
@@ -105,7 +105,7 @@ if (-not (Test-HasChanges)) {
|
|
|
105
105
|
git add -A
|
|
106
106
|
|
|
107
107
|
# ============================================================
|
|
108
|
-
#
|
|
108
|
+
# Build trailers and commit
|
|
109
109
|
# ============================================================
|
|
110
110
|
|
|
111
111
|
$TrailerArgs = @(
|
|
@@ -117,7 +117,7 @@ if ($TruncatedPrompt) {
|
|
|
117
117
|
$TrailerArgs += @("--trailer", "User-Prompt: $TruncatedPrompt")
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
#
|
|
120
|
+
# Pipe message → git interpret-trailers → git commit
|
|
121
121
|
$Message | git interpret-trailers @TrailerArgs | git commit -F -
|
|
122
122
|
|
|
123
123
|
Write-Host "Checkpoint committed successfully."
|
package/skill/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-better-checkpoint
|
|
|
3
3
|
description: "Automatically creates semantic Git checkpoint commits during AI coding sessions. Replaces opaque platform checkpoints with transparent, queryable Git commits using Conventional Commits format with Git Trailers. You MUST follow this skill whenever you make code changes — commit after each meaningful edit."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.2.0"
|
|
7
7
|
author: "alienzhou"
|
|
8
8
|
category: "version-control"
|
|
9
9
|
---
|
|
@@ -18,18 +18,18 @@ You are an AI coding assistant with **checkpoint awareness**. Every meaningful e
|
|
|
18
18
|
|
|
19
19
|
## 🔧 Installation Check
|
|
20
20
|
|
|
21
|
-
Before your first commit, verify the checkpoint scripts are
|
|
21
|
+
Before your first commit, verify the checkpoint scripts are available:
|
|
22
22
|
|
|
23
|
-
**
|
|
24
|
-
**
|
|
23
|
+
**Project-local (preferred, committed with project):** Check if `.vibe-x/agent-better-checkpoint/checkpoint.sh` exists in the workspace.
|
|
24
|
+
**Global fallback:** Check if `~/.vibe-x/agent-better-checkpoint/scripts/checkpoint.sh` exists (macOS/Linux) or `$env:USERPROFILE/.vibe-x/agent-better-checkpoint/scripts/checkpoint.ps1` (Windows).
|
|
25
25
|
|
|
26
|
-
If
|
|
26
|
+
If neither exists, run:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx @vibe-x/agent-better-checkpoint@0.
|
|
29
|
+
npx @vibe-x/agent-better-checkpoint@0.2.0
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This
|
|
32
|
+
This installs the global scripts and stop hook. Projects can also commit `.vibe-x/agent-better-checkpoint/` (config + scripts) for self-contained setup.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -94,18 +94,28 @@ Switch to flex-column layout with collapsible sidebar.
|
|
|
94
94
|
|
|
95
95
|
## 🛠️ How to Commit
|
|
96
96
|
|
|
97
|
-
Call the checkpoint script after composing your message. Determine the OS and use the appropriate command
|
|
97
|
+
Call the checkpoint script after composing your message. Determine the OS and use the appropriate command.
|
|
98
|
+
|
|
99
|
+
**Prefer project-local when present** (`.vibe-x/agent-better-checkpoint/` committed with project):
|
|
98
100
|
|
|
99
101
|
**macOS/Linux:**
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
|
-
|
|
104
|
+
# Project-local (if .vibe-x/agent-better-checkpoint/ exists)
|
|
105
|
+
.vibe-x/agent-better-checkpoint/checkpoint.sh "<commit-message>" "<user-prompt>"
|
|
106
|
+
|
|
107
|
+
# Or global fallback
|
|
108
|
+
~/.vibe-x/agent-better-checkpoint/scripts/checkpoint.sh "<commit-message>" "<user-prompt>"
|
|
103
109
|
```
|
|
104
110
|
|
|
105
111
|
**Windows (PowerShell):**
|
|
106
112
|
|
|
107
113
|
```powershell
|
|
108
|
-
|
|
114
|
+
# Project-local (if .vibe-x/agent-better-checkpoint/ exists)
|
|
115
|
+
powershell -File ".\.vibe-x\agent-better-checkpoint\checkpoint.ps1" "<commit-message>" "<user-prompt>"
|
|
116
|
+
|
|
117
|
+
# Or global fallback
|
|
118
|
+
powershell -File "$env:USERPROFILE/.vibe-x/agent-better-checkpoint/scripts/checkpoint.ps1" "<commit-message>" "<user-prompt>"
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
### Parameters:
|
|
@@ -119,7 +129,7 @@ powershell -File "$env:USERPROFILE/.agent-better-checkpoint/scripts/checkpoint.p
|
|
|
119
129
|
### Example (macOS/Linux):
|
|
120
130
|
|
|
121
131
|
```bash
|
|
122
|
-
~/.agent-better-checkpoint/scripts/checkpoint.sh \
|
|
132
|
+
~/.vibe-x/agent-better-checkpoint/scripts/checkpoint.sh \
|
|
123
133
|
"checkpoint(auth): add JWT token refresh logic
|
|
124
134
|
|
|
125
135
|
Implement automatic token refresh when access token expires.
|
|
@@ -130,7 +140,7 @@ Uses refresh token rotation for security." \
|
|
|
130
140
|
### Example (Windows):
|
|
131
141
|
|
|
132
142
|
```powershell
|
|
133
|
-
powershell -File "$env:USERPROFILE/.agent-better-checkpoint/scripts/checkpoint.ps1" `
|
|
143
|
+
powershell -File "$env:USERPROFILE/.vibe-x/agent-better-checkpoint/scripts/checkpoint.ps1" `
|
|
134
144
|
"checkpoint(auth): add JWT token refresh logic`n`nImplement automatic token refresh when access token expires.`nUses refresh token rotation for security." `
|
|
135
145
|
"帮我实现 token 刷新机制"
|
|
136
146
|
```
|
|
@@ -167,4 +177,4 @@ This should feel natural — commit as you go, like any good developer.
|
|
|
167
177
|
|
|
168
178
|
---
|
|
169
179
|
|
|
170
|
-
**Version**: 0.
|
|
180
|
+
**Version**: 0.2.0
|