mcpField
F
finalize

finalize

github.com/terrylica创意与媒体190 次安装1 天前更新
代码仓库

关于 finalize

finalize 是用于处理遗留 asciinema 录制文件的技能。它可发现录制进程与未处理文件、停止进程、检查文件完整性、使用 zstd 压缩,并可将文件推送到 orphan 分支。

主要功能

  1. 01查找活动进程和未处理录制
  2. 02正常或强制停止录制进程
  3. 03检查录制文件完整性
  4. 04使用 zstd 压缩 .cast 文件
  5. 05可选推送文件并删除本地原件

适用场景

  1. 01处理意外中断后遗留的录制
  2. 02压缩选定录制以便存储
  3. 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 文件

工作流

  1. 发现:查找正在运行的 asciinema 进程和未处理的 .cast 文件
  2. 选择:使用 AskUserQuestion 询问要处理哪些文件
  3. 停止:优雅地停止运行中的进程(SIGTERM → SIGINT → SIGKILL)
  4. 验证:停止后检查文件完整性
  5. 压缩:使用 zstd 压缩 .cast 文件
  6. 推送:推送到孤立分支(如果已配置)
  7. 清理:移除本地 .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未安装 zstdbrew 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 分析录制内容

执行后反思

此技能执行完成后,请在结束任务前进行反思:

  1. 定位自身。 — 编辑前找到此 SKILL.md 的规范路径。
  2. 哪里失败了? — 修复导致问题的指令。
  3. 哪些地方比预期更顺利? — 将其纳入推荐实践。
  4. 哪些内容发生了偏差? — 修复与实际情况不再相符的脚本、引用或依赖项。
  5. 记录下来。 — 在演进日志中记录触发原因、修复内容和证据。

不要拖延。下次调用时,会继承你留下的一切。

评论

支持 Markdown