Cc-skills finalize
Finalize orphaned recordings - stop processes, compress, push to orphan branch. TRIGGERS - finalize recording, stop asciinema, orphaned recording, cleanup recording, push recording.
install
source · Clone the upstream repo
git clone https://github.com/terrylica/cc-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/terrylica/cc-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/asciinema-tools/skills/finalize" ~/.claude/skills/terrylica-cc-skills-finalize && rm -rf "$T"
manifest:
plugins/asciinema-tools/skills/finalize/SKILL.mdsource content
/asciinema-tools:finalize
Finalize orphaned asciinema recordings: stop running processes gracefully, compress, and push to the orphan branch.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Arguments
| Argument | Description |
|---|---|
| Specific .cast file to finalize |
| Finalize all unhandled .cast files |
| Use SIGKILL if graceful stop fails |
| Skip pushing to orphan branch (local only) |
| 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 flag for SIGKILL |
| File may be truncated | Forced stop interrupted file | Most data preserved, try playing it |
| zstd not found | zstd not installed | |
| Push failed | No GitHub token | Set GH_TOKEN or run |
| No orphan branch | Clone not configured | Run first |
| File not found | Wrong path or already moved | Check with |
Related Commands
- View status and find unhandled files/asciinema-tools:daemon-status
- Convert .cast to .txt for analysis/asciinema-tools:convert
- AI-powered analysis of recordings/asciinema-tools:summarize
Post-Execution Reflection
After this skill completes, reflect before closing the task:
- Locate yourself. — Find this SKILL.md's canonical path before editing.
- What failed? — Fix the instruction that caused it.
- What worked better than expected? — Promote to recommended practice.
- What drifted? — Fix any script, reference, or dependency that no longer matches reality.
- Log it. — Evolution-log entry with trigger, fix, and evidence.
Do NOT defer. The next invocation inherits whatever you leave behind.