finalize
SKILL.md
/asciinema-tools:finalize
Finalize orphaned asciinema recordings: stop running processes gracefully, compress, and push to the orphan branch.
Arguments
| Argument | Description |
|---|---|
file |
Specific .cast file to finalize |
--all |
Finalize all unhandled .cast files |
--force |
Use SIGKILL if graceful stop fails |
--no-push |
Skip pushing to orphan branch (local only) |
--keep-local |
Keep local .cast after compression |
Workflow
- Discovery: Find running asciinema processes and unhandled .cast files
- Selection: AskUserQuestion for which files to process
- Stop: Gracefully stop running processes (SIGTERM → SIGINT → SIGKILL)
- Verify: Check file integrity after stop
- Compress: zstd compress .cast files
- Push: Push to orphan branch (if configured)
- Cleanup: Remove local .cast (optional)
Execution
Phase 1: Discovery
/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
Phase 2: Selection
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"
Phase 3: Stop Running Processes
/usr/bin/env bash << 'STOP_EOF'
# Arguments: PID list
PIDS="$@"
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
Phase 4: File Integrity Check
/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
Phase 5: Compress
/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
Phase 6: Push to Orphan Branch
/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://$GH_TOKEN@github.com|")
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
Phase 7: Cleanup Confirmation
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"
Example Usage
# 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
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Process won't stop | Hung asciinema process | Use --force flag for SIGKILL |
| File may be truncated | Forced stop interrupted file | Most data preserved, try playing it |
| zstd not found | zstd not installed | brew install zstd |
| Push failed | No GitHub token | Set GH_TOKEN or run gh auth login |
| No orphan branch | Clone not configured | Run /asciinema-tools:bootstrap first |
| File not found | Wrong path or already moved | Check with /daemon-status |
Related Commands
/asciinema-tools:daemon-status- View status and find unhandled files/asciinema-tools:convert- Convert .cast to .txt for analysis/asciinema-tools:summarize- AI-powered analysis of recordings
Weekly Installs
21
Repository
terrylica/cc-skillsGitHub Stars
19
First Seen
Feb 27, 2026
Security Audits
Installed on
opencode21
gemini-cli21
github-copilot21
codex21
kimi-cli21
amp21