关于 finalize
finalize 是用于处理遗留 asciinema 录制文件的技能。它可发现录制进程与未处理文件、停止进程、检查文件完整性、使用 zstd 压缩,并可将文件推送到 orphan 分支。
主要功能
- 01查找活动进程和未处理录制
- 02正常或强制停止录制进程
- 03检查录制文件完整性
- 04使用 zstd 压缩 .cast 文件
- 05可选推送文件并删除本地原件
适用场景
- 01处理意外中断后遗留的录制
- 02压缩选定录制以便存储
- 03将完成的录制推送到 orphan 分支
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ terrylica/ cc-skills --skill finalize安装后在 Claude Code 中输入/finalize确认已加载。
附带文件references/evolution-log.md
详细介绍
/asciinema-tools:finalize
完成孤立的 asciinema 录制:优雅地停止运行中的进程、压缩,并推送到孤立分支。
自我进化技能:此技能会在使用过程中持续改进。如果指令有误、参数发生变化,或需要采用变通方法,请立即修复此文件,不要拖延。只针对真实且可复现的问题进行更新。
参数
| 参数 | 描述 |
|---|---|
file | 要完成处理的特定 .cast 文件 |
--all | 完成处理所有尚未处理的 .cast 文件 |
--force | 优雅停止失败时使用 SIGKILL |
--no-push | 跳过推送到孤立分支(仅限本地) |
--keep-local | 压缩后保留本地 .cast 文件 |
工作流
- 发现:查找正在运行的 asciinema 进程和未处理的 .cast 文件
- 选择:使用 AskUserQuestion 询问要处理哪些文件
- 停止:优雅地停止运行中的进程(SIGTERM → SIGINT → SIGKILL)
- 验证:停止后检查文件完整性
- 压缩:使用 zstd 压缩 .cast 文件
- 推送:推送到孤立分支(如果已配置)
- 清理:移除本地 .cast 文件(可选)
执行
第 1 阶段:发现
/usr/bin/env bash << 'DISCOVER_EOF'
echo "=== Running asciinema processes ==="
PROCS=$(ps aux | grep -E "asciinema rec" | grep -v grep)
if [[ -n "$PROCS" ]]; then
echo "$PROCS" | while read -r line; do
PID=$(echo "$line" | awk '{print $2}')
CAST_FILE=$(echo "$line" | grep -oE '[^ ]+\.cast' | head -1)
if [[ -n "$CAST_FILE" ]]; then
SIZE=$(ls -lh "$CAST_FILE" 2>/dev/null | awk '{print $5}' || echo "?")
echo "PID $PID: $CAST_FILE ($SIZE)"
else
echo "PID $PID: (no file detected)"
fi
done
else
echo "No running asciinema processes"
fi
echo ""
echo "=== Unhandled .cast files ==="
find ~/eon -name "*.cast" -size +1M -mtime -30 2>/dev/null | while read -r f; do
SIZE=$(ls -lh "$f" | awk '{print $5}')
echo "$f ($SIZE)"
done
DISCOVER_EOF
第 2 阶段:选择
AskUserQuestion:
question: "Which recordings should be finalized?"
header: "Select"
multiSelect: true
options:
- label: "All running processes"
description: "Stop all asciinema rec processes and finalize their files"
- label: "All unhandled files"
description: "Finalize all .cast files found in ~/eon"
- label: "Specific file"
description: "Enter path to specific .cast file"
第 3 阶段:停止运行中的进程
将 PID 列表传递给此代码块;如果用户调用 /asciinema-tools:finalize 时指定了
--force,还要传入 --force。此代码块会从自身参数中解析出 --force——
不同的 bash heredoc 属于不同的进程,因此 $FORCE 必须在执行相关操作的同一代码块中解析(单独的解析阶段无法将其值传递到其他代码块)。
/usr/bin/env bash << 'STOP_EOF'
# Arguments: PID list, plus optional --force (documented in argument-hint).
# Split flags from PIDs so the documented --force flag actually takes effect.
FORCE=false
PIDS=""
for arg in "$@"; do
case "$arg" in
--force) FORCE=true ;;
*) PIDS="$PIDS $arg" ;;
esac
done
for PID in $PIDS; do
echo "Stopping PID $PID..."
# Try SIGTERM first (graceful)
kill -TERM "$PID" 2>/dev/null
sleep 2
if kill -0 "$PID" 2>/dev/null; then
echo " SIGTERM ignored, trying SIGINT..."
kill -INT "$PID" 2>/dev/null
sleep 2
fi
if kill -0 "$PID" 2>/dev/null; then
echo " Process still running. Use --force for SIGKILL"
# Only SIGKILL with --force flag
if [[ "$FORCE" == "true" ]]; then
echo " Sending SIGKILL (file may be truncated)..."
kill -9 "$PID" 2>/dev/null
sleep 1
fi
fi
if ! kill -0 "$PID" 2>/dev/null; then
echo " ✓ Process stopped"
else
echo " ✗ Process still running"
fi
done
STOP_EOF
第 4 阶段:文件完整性检查
/usr/bin/env bash << 'CHECK_EOF'
CAST_FILE="$1"
echo "Checking file integrity: $CAST_FILE"
# Check if file exists
if [[ ! -f "$CAST_FILE" ]]; then
echo " ✗ File not found"
exit 1
fi
# Check file size
SIZE=$(stat -f%z "$CAST_FILE" 2>/dev/null || stat -c%s "$CAST_FILE")
echo " Size: $(numfmt --to=iec-i "$SIZE" 2>/dev/null || echo "$SIZE bytes")"
# Check last line (NDJSON should have complete JSON arrays)
LAST_LINE=$(tail -c 500 "$CAST_FILE" | tail -1)
if [[ "$LAST_LINE" == *"]"* ]]; then
echo " ✓ File appears complete (ends with JSON array)"
else
echo " ⚠ File may be truncated (incomplete JSON)"
echo " Note: asciinema 2.0+ streams to disk, so most data is preserved"
fi
# Test with asciinema cat (quick validation)
if timeout 5 asciinema cat "$CAST_FILE" > /dev/null 2>&1; then
echo " ✓ File is playable"
else
echo " ⚠ File may have issues (but often still usable)"
fi
CHECK_EOF
第 5 阶段:压缩
/usr/bin/env bash << 'COMPRESS_EOF'
CAST_FILE="$1"
ZSTD_LEVEL="${2:-6}"
echo "Compressing: $CAST_FILE"
OUTPUT="${CAST_FILE}.zst"
if zstd -"$ZSTD_LEVEL" -f "$CAST_FILE" -o "$OUTPUT"; then
ORIG_SIZE=$(stat -f%z "$CAST_FILE" 2>/dev/null || stat -c%s "$CAST_FILE")
COMP_SIZE=$(stat -f%z "$OUTPUT" 2>/dev/null || stat -c%s "$OUTPUT")
RATIO=$(echo "scale=1; $ORIG_SIZE / $COMP_SIZE" | bc 2>/dev/null || echo "?")
echo " ✓ Compressed: $(basename "$OUTPUT")"
echo " Compression ratio: ${RATIO}:1"
else
echo " ✗ Compression failed"
exit 1
fi
COMPRESS_EOF
第 6 阶段:推送到孤立分支
/usr/bin/env bash << 'PUSH_EOF'
COMPRESSED_FILE="$1"
RECORDINGS_DIR="$HOME/asciinema_recordings"
# Find the local recordings clone
REPO_DIR=$(find "$RECORDINGS_DIR" -maxdepth 1 -type d -name "*" | head -1)
if [[ -z "$REPO_DIR" ]] || [[ ! -d "$REPO_DIR/.git" ]]; then
echo " ⚠ No orphan branch clone found at $RECORDINGS_DIR"
echo " Run /asciinema-tools:bootstrap to set up orphan branch"
exit 1
fi
echo "Pushing to orphan branch..."
# Copy compressed file
BASENAME=$(basename "$COMPRESSED_FILE")
DEST="$REPO_DIR/recordings/$BASENAME"
mkdir -p "$(dirname "$DEST")"
cp "$COMPRESSED_FILE" "$DEST"
# Commit and push
cd "$REPO_DIR"
git add -A
git commit -m "finalize: $BASENAME" 2>/dev/null || true
# Push with token (prefer env var to avoid process spawning)
GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-$(gh auth token 2>/dev/null || echo "")}}"
if [[ -n "$GH_TOKEN" ]]; then
REMOTE_URL=$(git remote get-url origin)
# Convert to token-authenticated URL
TOKEN_URL=$(echo "$REMOTE_URL" | sed "s|https://github.com|https://[email protected]|")
if git push "$TOKEN_URL" HEAD 2>/dev/null; then
echo " ✓ Pushed to orphan branch"
else
echo " ✗ Push failed (check credentials)"
fi
else
echo " ⚠ No GitHub token, skipping push"
fi
PUSH_EOF
第 7 阶段:清理确认
AskUserQuestion:
question: "Delete local .cast file after successful compression/push?"
header: "Cleanup"
options:
- label: "Yes, delete local .cast"
description: "Remove original .cast file (compressed version preserved)"
- label: "No, keep local"
description: "Keep both .cast and .cast.zst files"
用法示例
# Interactive mode - discover and select
/asciinema-tools:finalize
# Finalize specific file
/asciinema-tools:finalize ~/eon/project/tmp/session.cast
# Finalize all with force stop
/asciinema-tools:finalize --all --force
# Local only (no push)
/asciinema-tools:finalize session.cast --no-push
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 进程无法停止 | asciinema 进程挂起 | 使用 --force 标志执行 SIGKILL |
| 文件可能被截断 | 强制停止导致文件写入中断 | 大部分数据仍会保留,尝试播放该文件 |
| 未找到 zstd | 未安装 zstd | brew install zstd |
| 推送失败 | 没有 GitHub 令牌 | 设置 GH_TOKEN 或运行 gh auth login |
| 没有孤立分支 | 克隆未配置 | 先运行 /asciinema-tools:bootstrap |
| 未找到文件 | 路径错误或文件已移动 | 使用 /daemon-status 检查 |
相关命令
/asciinema-tools:daemon-status- 查看状态并查找未处理的文件/asciinema-tools:convert- 将 .cast 转换为 .txt 以供分析/asciinema-tools:summarize- 使用 AI 分析录制内容
执行后反思
此技能执行完成后,请在结束任务前进行反思:
- 定位自身。 — 编辑前找到此 SKILL.md 的规范路径。
- 哪里失败了? — 修复导致问题的指令。
- 哪些地方比预期更顺利? — 将其纳入推荐实践。
- 哪些内容发生了偏差? — 修复与实际情况不再相符的脚本、引用或依赖项。
- 记录下来。 — 在演进日志中记录触发原因、修复内容和证据。
不要拖延。下次调用时,会继承你留下的一切。
评论
支持 Markdown