@vibe-x/agent-better-checkpoint 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.
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # check_uncommitted.sh — Stop Hook: 检查未提交的变更
4
+ #
5
+ # 在 AI 对话结束时触发,检查工作区是否存在未提交的变更。
6
+ # 如果存在,输出提醒信息让 AI Agent 执行 fallback checkpoint commit。
7
+ #
8
+ # 支持平台:
9
+ # - Cursor: stop hook (stdin JSON 含 workspace_roots)
10
+ # - Claude Code: Stop hook (stdin JSON 含 hook_event_name)
11
+ #
12
+ # 输出协议:
13
+ # - 无问题: {} (空 JSON)
14
+ # - 有问题 (Cursor): {"followup_message": "..."}
15
+ # - 有问题 (Claude Code): {"decision": "block", "reason": "..."}
16
+ #
17
+ # JSON 解析使用 grep+sed,不依赖 jq。
18
+
19
+ set -euo pipefail
20
+
21
+ # ============================================================
22
+ # 辅助函数:简易 JSON 字段提取(不依赖 jq)
23
+ # ============================================================
24
+
25
+ # 输出允许通过的 JSON
26
+ output_allow() {
27
+ echo '{}'
28
+ exit 0
29
+ }
30
+
31
+ # 从 JSON 中提取布尔字段值
32
+ # Usage: json_bool "$json" "field_name" → 输出 "true" 或 "false"
33
+ json_bool() {
34
+ local json="$1" field="$2"
35
+ if echo "$json" | grep -qE "\"${field}\"[[:space:]]*:[[:space:]]*true"; then
36
+ echo "true"
37
+ else
38
+ echo "false"
39
+ fi
40
+ }
41
+
42
+ # 从 JSON 中提取字符串字段值
43
+ # Usage: json_string "$json" "field_name" → 输出字符串值(不含引号)
44
+ json_string() {
45
+ local json="$1" field="$2"
46
+ echo "$json" | grep -oE "\"${field}\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" \
47
+ | sed -E "s/\"${field}\"[[:space:]]*:[[:space:]]*\"([^\"]*)\"/\1/" \
48
+ | head -1
49
+ }
50
+
51
+ # 从 JSON 的 workspace_roots / workspaceRoots 数组中提取第一个路径
52
+ # Usage: json_workspace_root "$json" → 输出路径
53
+ json_workspace_root() {
54
+ local json="$1"
55
+ local result=""
56
+
57
+ # 尝试 workspace_roots 和 workspaceRoots
58
+ for field in "workspace_roots" "workspaceRoots"; do
59
+ result=$(echo "$json" \
60
+ | grep -oE "\"${field}\"[[:space:]]*:[[:space:]]*\[[^]]*\]" \
61
+ | grep -oE '"[^"]*"' \
62
+ | tail -n +2 \
63
+ | head -1 \
64
+ | tr -d '"' || true)
65
+ if [[ -n "$result" ]]; then
66
+ echo "$result"
67
+ return
68
+ fi
69
+ done
70
+ }
71
+
72
+ # ============================================================
73
+ # 平台检测
74
+ # ============================================================
75
+
76
+ detect_platform() {
77
+ local json="$1"
78
+ if [[ -z "$json" ]]; then
79
+ echo "unknown"
80
+ return
81
+ fi
82
+ # Claude Code: 有 hook_event_name 或 tool_name
83
+ if echo "$json" | grep -qE '"hook_event_name"|"tool_name"'; then
84
+ echo "claude_code"
85
+ else
86
+ echo "cursor"
87
+ fi
88
+ }
89
+
90
+ # ============================================================
91
+ # Workspace Root 检测
92
+ # ============================================================
93
+
94
+ get_workspace_root() {
95
+ local json="$1"
96
+
97
+ # 优先从 stdin JSON 获取
98
+ if [[ -n "$json" ]]; then
99
+ local ws
100
+ ws=$(json_workspace_root "$json")
101
+ if [[ -n "$ws" ]]; then
102
+ echo "$ws"
103
+ return
104
+ fi
105
+ fi
106
+
107
+ # 回退到环境变量
108
+ for env_var in CURSOR_PROJECT_DIR CLAUDE_PROJECT_DIR WORKSPACE_ROOT PROJECT_ROOT; do
109
+ local val="${!env_var:-}"
110
+ if [[ -n "$val" ]]; then
111
+ echo "$val"
112
+ return
113
+ fi
114
+ done
115
+
116
+ # 最终回退到 PWD / cwd
117
+ echo "${PWD:-$(pwd)}"
118
+ }
119
+
120
+ # ============================================================
121
+ # Git 操作
122
+ # ============================================================
123
+
124
+ is_git_repo() {
125
+ git -C "$1" rev-parse --is-inside-work-tree &>/dev/null
126
+ }
127
+
128
+ has_uncommitted_changes() {
129
+ local status
130
+ status=$(git -C "$1" status --porcelain 2>/dev/null)
131
+ [[ -n "$status" ]]
132
+ }
133
+
134
+ get_change_summary() {
135
+ local workspace="$1"
136
+ local max_lines="${2:-20}"
137
+ local output
138
+ output=$(git -C "$workspace" status --short 2>/dev/null || true)
139
+
140
+ if [[ -z "$output" ]]; then
141
+ return
142
+ fi
143
+
144
+ local total
145
+ total=$(echo "$output" | wc -l)
146
+
147
+ if [[ "$total" -gt "$max_lines" ]]; then
148
+ echo "$output" | head -n "$max_lines"
149
+ echo " ... and $((total - max_lines)) more files"
150
+ else
151
+ echo "$output"
152
+ fi
153
+ }
154
+
155
+ # ============================================================
156
+ # 输出提醒
157
+ # ============================================================
158
+
159
+ # 转义字符串用于 JSON 值(处理引号、反斜杠、换行等)
160
+ json_escape() {
161
+ local str="$1"
162
+ str="${str//\\/\\\\}" # 反斜杠
163
+ str="${str//\"/\\\"}" # 双引号
164
+ str="${str//$'\n'/\\n}" # 换行
165
+ str="${str//$'\r'/\\r}" # 回车
166
+ str="${str//$'\t'/\\t}" # Tab
167
+ echo "$str"
168
+ }
169
+
170
+ output_block() {
171
+ local message="$1"
172
+ local platform="$2"
173
+ local escaped
174
+ escaped=$(json_escape "$message")
175
+
176
+ if [[ "$platform" == "cursor" ]]; then
177
+ echo "{\"followup_message\":\"${escaped}\"}"
178
+ elif [[ "$platform" == "claude_code" ]]; then
179
+ echo "{\"decision\":\"block\",\"reason\":\"${escaped}\"}"
180
+ else
181
+ echo "{\"message\":\"${escaped}\"}"
182
+ fi
183
+ exit 0
184
+ }
185
+
186
+ # ============================================================
187
+ # 主逻辑
188
+ # ============================================================
189
+
190
+ main() {
191
+ # 从 stdin 读取 JSON(非阻塞:如果无输入则为空)
192
+ local input=""
193
+ if [[ ! -t 0 ]]; then
194
+ input=$(cat)
195
+ fi
196
+
197
+ # Claude Code 的 stop_hook_active 防止无限循环
198
+ if [[ -n "$input" ]]; then
199
+ local stop_active
200
+ stop_active=$(json_bool "$input" "stop_hook_active")
201
+ if [[ "$stop_active" == "true" ]]; then
202
+ output_allow
203
+ fi
204
+ fi
205
+
206
+ # 检测平台
207
+ local platform
208
+ platform=$(detect_platform "$input")
209
+
210
+ # 获取 workspace root
211
+ local workspace
212
+ workspace=$(get_workspace_root "$input")
213
+
214
+ # 检查是否为 git 仓库
215
+ if ! is_git_repo "$workspace"; then
216
+ output_allow
217
+ fi
218
+
219
+ # 检查是否有未提交变更
220
+ if ! has_uncommitted_changes "$workspace"; then
221
+ output_allow
222
+ fi
223
+
224
+ # 获取变更摘要
225
+ local changes
226
+ changes=$(get_change_summary "$workspace")
227
+ local changes_indented
228
+ changes_indented=$(echo "$changes" | sed 's/^/ /')
229
+
230
+ # 构建提醒消息
231
+ local reminder
232
+ reminder="## ⚠️ Uncommitted Changes Detected
233
+
234
+ There are uncommitted changes in the workspace. Please create a checkpoint commit before ending the conversation.
235
+
236
+ **Changed files:**
237
+ \`\`\`
238
+ ${changes_indented}
239
+ \`\`\`
240
+
241
+ **Action Required**: Run the checkpoint script to commit these changes:
242
+
243
+ **macOS/Linux:**
244
+ \`\`\`bash
245
+ ~/.agent-better-checkpoint/scripts/checkpoint.sh \"checkpoint(<scope>): <description>\" \"<user-prompt>\" --type fallback
246
+ \`\`\`
247
+
248
+ **Windows (PowerShell):**
249
+ \`\`\`powershell
250
+ powershell -File \"\$env:USERPROFILE/.agent-better-checkpoint/scripts/checkpoint.ps1\" \"checkpoint(<scope>): <description>\" \"<user-prompt>\" -Type fallback
251
+ \`\`\`"
252
+
253
+ output_block "$reminder" "$platform"
254
+ }
255
+
256
+ main
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # checkpoint.sh — 创建语义化的 Git checkpoint commit
4
+ #
5
+ # AI 生成描述性内容(subject + body),本脚本负责:
6
+ # 1. 截断 user-prompt(≤60字符,头尾截断)
7
+ # 2. 通过 git interpret-trailers 追加元信息
8
+ # 3. 执行 git add -A && git commit
9
+ #
10
+ # Usage:
11
+ # checkpoint.sh <message> [user-prompt] [--type auto|fallback]
12
+
13
+ set -euo pipefail
14
+
15
+ # ============================================================
16
+ # 参数解析
17
+ # ============================================================
18
+ MESSAGE="${1:-}"
19
+ USER_PROMPT="${2:-}"
20
+ CHECKPOINT_TYPE="auto"
21
+
22
+ shift 2 2>/dev/null || true
23
+ while [[ $# -gt 0 ]]; do
24
+ case "$1" in
25
+ --type)
26
+ CHECKPOINT_TYPE="${2:-auto}"
27
+ shift 2
28
+ ;;
29
+ *)
30
+ shift
31
+ ;;
32
+ esac
33
+ done
34
+
35
+ if [[ -z "$MESSAGE" ]]; then
36
+ echo "Error: commit message is required" >&2
37
+ echo "Usage: checkpoint.sh <message> [user-prompt] [--type auto|fallback]" >&2
38
+ exit 1
39
+ fi
40
+
41
+ # ============================================================
42
+ # 平台检测
43
+ # ============================================================
44
+ detect_platform() {
45
+ if [[ -n "${CLAUDE_CODE:-}" ]] || command -v claude &>/dev/null; then
46
+ echo "claude-code"
47
+ elif [[ -n "${CURSOR_VERSION:-}" ]] || [[ -n "${CURSOR_TRACE_ID:-}" ]]; then
48
+ echo "cursor"
49
+ else
50
+ echo "unknown"
51
+ fi
52
+ }
53
+
54
+ AGENT_PLATFORM=$(detect_platform)
55
+
56
+ # ============================================================
57
+ # User-Prompt 截断(≤60 字符,头尾保留 + 中间省略号)
58
+ # ============================================================
59
+ truncate_prompt() {
60
+ local prompt="$1"
61
+ local max_len=60
62
+ local len=${#prompt}
63
+
64
+ if [[ $len -le $max_len ]]; then
65
+ echo "$prompt"
66
+ return
67
+ fi
68
+
69
+ local head_len=$(( (max_len - 3) / 2 ))
70
+ local tail_len=$(( max_len - 3 - head_len ))
71
+ local head="${prompt:0:$head_len}"
72
+ local tail="${prompt:$((len - tail_len)):$tail_len}"
73
+ echo "${head}...${tail}"
74
+ }
75
+
76
+ TRUNCATED_PROMPT=""
77
+ if [[ -n "$USER_PROMPT" ]]; then
78
+ TRUNCATED_PROMPT=$(truncate_prompt "$USER_PROMPT")
79
+ fi
80
+
81
+ # ============================================================
82
+ # 检查是否有变更
83
+ # ============================================================
84
+ has_changes() {
85
+ # staged 变更
86
+ if ! git diff --cached --quiet 2>/dev/null; then
87
+ return 0
88
+ fi
89
+ # unstaged 变更
90
+ if ! git diff --quiet 2>/dev/null; then
91
+ return 0
92
+ fi
93
+ # untracked 文件
94
+ if [[ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]]; then
95
+ return 0
96
+ fi
97
+ return 1
98
+ }
99
+
100
+ if ! has_changes; then
101
+ echo "No changes to commit."
102
+ exit 0
103
+ fi
104
+
105
+ # ============================================================
106
+ # git add -A
107
+ # ============================================================
108
+ git add -A
109
+
110
+ # ============================================================
111
+ # 构建 trailer 并提交
112
+ # ============================================================
113
+ TRAILER_ARGS=(
114
+ --trailer "Agent: ${AGENT_PLATFORM}"
115
+ --trailer "Checkpoint-Type: ${CHECKPOINT_TYPE}"
116
+ )
117
+
118
+ if [[ -n "$TRUNCATED_PROMPT" ]]; then
119
+ TRAILER_ARGS+=(--trailer "User-Prompt: ${TRUNCATED_PROMPT}")
120
+ fi
121
+
122
+ echo "$MESSAGE" | git interpret-trailers "${TRAILER_ARGS[@]}" | git commit -F -
123
+
124
+ echo "Checkpoint committed successfully."
@@ -0,0 +1,209 @@
1
+ <#
2
+ .SYNOPSIS
3
+ check_uncommitted.ps1 — Stop Hook: 检查未提交的变更 (Windows PowerShell)
4
+
5
+ .DESCRIPTION
6
+ 在 AI 对话结束时触发,检查工作区是否存在未提交的变更。
7
+ 如果存在,输出提醒信息让 AI Agent 执行 fallback checkpoint commit。
8
+
9
+ 支持平台:
10
+ - Cursor: stop hook (stdin JSON 含 workspace_roots)
11
+ - Claude Code: Stop hook (stdin JSON 含 hook_event_name)
12
+
13
+ 输出协议:
14
+ - 无问题: {} (空 JSON)
15
+ - 有问题 (Cursor): {"followup_message": "..."}
16
+ - 有问题 (Claude Code): {"decision": "block", "reason": "..."}
17
+ #>
18
+
19
+ $ErrorActionPreference = "Stop"
20
+
21
+ # ============================================================
22
+ # 辅助函数
23
+ # ============================================================
24
+
25
+ function Output-Allow {
26
+ Write-Output '{}'
27
+ exit 0
28
+ }
29
+
30
+ function Output-Block {
31
+ param(
32
+ [string]$Message,
33
+ [string]$Platform
34
+ )
35
+
36
+ # 转义 JSON 字符串
37
+ $Escaped = $Message -replace '\\', '\\\\' `
38
+ -replace '"', '\"' `
39
+ -replace "`r`n", '\n' `
40
+ -replace "`n", '\n' `
41
+ -replace "`r", '\r' `
42
+ -replace "`t", '\t'
43
+
44
+ switch ($Platform) {
45
+ "cursor" {
46
+ Write-Output "{`"followup_message`":`"$Escaped`"}"
47
+ }
48
+ "claude_code" {
49
+ Write-Output "{`"decision`":`"block`",`"reason`":`"$Escaped`"}"
50
+ }
51
+ default {
52
+ Write-Output "{`"message`":`"$Escaped`"}"
53
+ }
54
+ }
55
+ exit 0
56
+ }
57
+
58
+ # ============================================================
59
+ # 平台检测
60
+ # ============================================================
61
+
62
+ function Detect-Platform {
63
+ param($InputData)
64
+
65
+ if (-not $InputData) {
66
+ return "unknown"
67
+ }
68
+ if ($InputData.PSObject.Properties["hook_event_name"] -or
69
+ $InputData.PSObject.Properties["tool_name"]) {
70
+ return "claude_code"
71
+ }
72
+ return "cursor"
73
+ }
74
+
75
+ # ============================================================
76
+ # Workspace Root 检测
77
+ # ============================================================
78
+
79
+ function Get-WorkspaceRoot {
80
+ param($InputData)
81
+
82
+ # 优先从 stdin JSON 获取
83
+ if ($InputData) {
84
+ foreach ($field in @("workspace_roots", "workspaceRoots")) {
85
+ $roots = $null
86
+ try { $roots = $InputData.$field } catch {}
87
+ if ($roots -and $roots.Count -gt 0) {
88
+ return $roots[0]
89
+ }
90
+ }
91
+ }
92
+
93
+ # 回退到环境变量
94
+ foreach ($envVar in @("CURSOR_PROJECT_DIR", "CLAUDE_PROJECT_DIR", "WORKSPACE_ROOT", "PROJECT_ROOT")) {
95
+ $val = [Environment]::GetEnvironmentVariable($envVar)
96
+ if ($val) { return $val }
97
+ }
98
+
99
+ # 最终回退到当前目录
100
+ return (Get-Location).Path
101
+ }
102
+
103
+ # ============================================================
104
+ # Git 操作
105
+ # ============================================================
106
+
107
+ function Test-GitRepo {
108
+ param([string]$Path)
109
+ try {
110
+ $null = git -C $Path rev-parse --is-inside-work-tree 2>$null
111
+ return $LASTEXITCODE -eq 0
112
+ }
113
+ catch {
114
+ return $false
115
+ }
116
+ }
117
+
118
+ function Test-UncommittedChanges {
119
+ param([string]$Path)
120
+ $status = git -C $Path status --porcelain 2>$null
121
+ return [bool]$status
122
+ }
123
+
124
+ function Get-ChangeSummary {
125
+ param(
126
+ [string]$Path,
127
+ [int]$MaxLines = 20
128
+ )
129
+
130
+ $output = git -C $Path status --short 2>$null
131
+ if (-not $output) { return "" }
132
+
133
+ $lines = $output -split "`n"
134
+ if ($lines.Count -gt $MaxLines) {
135
+ $shown = $lines[0..($MaxLines - 1)]
136
+ $shown += " ... and $($lines.Count - $MaxLines) more files"
137
+ return ($shown -join "`n")
138
+ }
139
+ return ($lines -join "`n")
140
+ }
141
+
142
+ # ============================================================
143
+ # 主逻辑
144
+ # ============================================================
145
+
146
+ # 从 stdin 读取 JSON
147
+ $InputData = $null
148
+ try {
149
+ $rawInput = [Console]::In.ReadToEnd()
150
+ if ($rawInput.Trim()) {
151
+ $InputData = $rawInput | ConvertFrom-Json
152
+ }
153
+ }
154
+ catch {
155
+ # 忽略 JSON 解析错误
156
+ }
157
+
158
+ # Claude Code 的 stop_hook_active 防止无限循环
159
+ if ($InputData -and $InputData.PSObject.Properties["stop_hook_active"]) {
160
+ if ($InputData.stop_hook_active -eq $true) {
161
+ Output-Allow
162
+ }
163
+ }
164
+
165
+ # 检测平台
166
+ $Platform = Detect-Platform -InputData $InputData
167
+
168
+ # 获取 workspace root
169
+ $Workspace = Get-WorkspaceRoot -InputData $InputData
170
+
171
+ # 检查是否为 git 仓库
172
+ if (-not (Test-GitRepo -Path $Workspace)) {
173
+ Output-Allow
174
+ }
175
+
176
+ # 检查是否有未提交变更
177
+ if (-not (Test-UncommittedChanges -Path $Workspace)) {
178
+ Output-Allow
179
+ }
180
+
181
+ # 获取变更摘要
182
+ $Changes = Get-ChangeSummary -Path $Workspace
183
+ $ChangesIndented = ($Changes -split "`n" | ForEach-Object { " $_" }) -join "`n"
184
+
185
+ # 构建提醒消息
186
+ $Reminder = @"
187
+ ## ⚠️ Uncommitted Changes Detected
188
+
189
+ There are uncommitted changes in the workspace. Please create a checkpoint commit before ending the conversation.
190
+
191
+ **Changed files:**
192
+ ``````
193
+ $ChangesIndented
194
+ ``````
195
+
196
+ **Action Required**: Run the checkpoint script to commit these changes:
197
+
198
+ **macOS/Linux:**
199
+ ``````bash
200
+ ~/.agent-better-checkpoint/scripts/checkpoint.sh "checkpoint(<scope>): <description>" "<user-prompt>" --type fallback
201
+ ``````
202
+
203
+ **Windows (PowerShell):**
204
+ ``````powershell
205
+ powershell -File "`$env:USERPROFILE/.agent-better-checkpoint/scripts/checkpoint.ps1" "checkpoint(<scope>): <description>" "<user-prompt>" -Type fallback
206
+ ``````
207
+ "@
208
+
209
+ Output-Block -Message $Reminder -Platform $Platform
@@ -0,0 +1,123 @@
1
+ <#
2
+ .SYNOPSIS
3
+ checkpoint.ps1 — 创建语义化的 Git checkpoint commit (Windows PowerShell)
4
+
5
+ .DESCRIPTION
6
+ AI 生成描述性内容(subject + body),本脚本负责:
7
+ 1. 截断 user-prompt(≤60字符,头尾截断)
8
+ 2. 通过 git interpret-trailers 追加元信息
9
+ 3. 执行 git add -A && git commit
10
+
11
+ .PARAMETER Message
12
+ Full commit message (subject + blank line + body). Required.
13
+
14
+ .PARAMETER UserPrompt
15
+ The user's original prompt/request. Optional.
16
+
17
+ .PARAMETER Type
18
+ Checkpoint type: "auto" (default) or "fallback".
19
+
20
+ .EXAMPLE
21
+ .\checkpoint.ps1 "checkpoint(auth): add JWT refresh" "implement token refresh"
22
+ #>
23
+
24
+ param(
25
+ [Parameter(Mandatory = $true, Position = 0)]
26
+ [string]$Message,
27
+
28
+ [Parameter(Position = 1)]
29
+ [string]$UserPrompt = "",
30
+
31
+ [string]$Type = "auto"
32
+ )
33
+
34
+ $ErrorActionPreference = "Stop"
35
+
36
+ # ============================================================
37
+ # 平台检测
38
+ # ============================================================
39
+
40
+ function Detect-Platform {
41
+ if ($env:CLAUDE_CODE -or (Get-Command claude -ErrorAction SilentlyContinue)) {
42
+ return "claude-code"
43
+ }
44
+ if ($env:CURSOR_VERSION -or $env:CURSOR_TRACE_ID) {
45
+ return "cursor"
46
+ }
47
+ return "unknown"
48
+ }
49
+
50
+ $AgentPlatform = Detect-Platform
51
+
52
+ # ============================================================
53
+ # User-Prompt 截断(≤60 字符,头尾保留 + 中间省略号)
54
+ # ============================================================
55
+
56
+ function Truncate-Prompt {
57
+ param([string]$Prompt)
58
+
59
+ $MaxLen = 60
60
+ if ($Prompt.Length -le $MaxLen) {
61
+ return $Prompt
62
+ }
63
+
64
+ $HeadLen = [math]::Floor(($MaxLen - 3) / 2)
65
+ $TailLen = $MaxLen - 3 - $HeadLen
66
+ $Head = $Prompt.Substring(0, $HeadLen)
67
+ $Tail = $Prompt.Substring($Prompt.Length - $TailLen, $TailLen)
68
+ return "${Head}...${Tail}"
69
+ }
70
+
71
+ $TruncatedPrompt = ""
72
+ if ($UserPrompt) {
73
+ $TruncatedPrompt = Truncate-Prompt -Prompt $UserPrompt
74
+ }
75
+
76
+ # ============================================================
77
+ # 检查是否有变更
78
+ # ============================================================
79
+
80
+ function Test-HasChanges {
81
+ # staged 变更
82
+ $diffCached = git diff --cached --quiet 2>$null
83
+ if ($LASTEXITCODE -ne 0) { return $true }
84
+
85
+ # unstaged 变更
86
+ $diffWorking = git diff --quiet 2>$null
87
+ if ($LASTEXITCODE -ne 0) { return $true }
88
+
89
+ # untracked 文件
90
+ $untracked = git ls-files --others --exclude-standard 2>$null
91
+ if ($untracked) { return $true }
92
+
93
+ return $false
94
+ }
95
+
96
+ if (-not (Test-HasChanges)) {
97
+ Write-Host "No changes to commit."
98
+ exit 0
99
+ }
100
+
101
+ # ============================================================
102
+ # git add -A
103
+ # ============================================================
104
+
105
+ git add -A
106
+
107
+ # ============================================================
108
+ # 构建 trailer 并提交
109
+ # ============================================================
110
+
111
+ $TrailerArgs = @(
112
+ "--trailer", "Agent: $AgentPlatform",
113
+ "--trailer", "Checkpoint-Type: $Type"
114
+ )
115
+
116
+ if ($TruncatedPrompt) {
117
+ $TrailerArgs += @("--trailer", "User-Prompt: $TruncatedPrompt")
118
+ }
119
+
120
+ # 通过管道传递消息 → git interpret-trailers → git commit
121
+ $Message | git interpret-trailers @TrailerArgs | git commit -F -
122
+
123
+ Write-Host "Checkpoint committed successfully."