Finalize orphaned recordings - stop processes, compress, push to orphan branch. TRIGGERS - finalize recording, stop asciinema, orphaned recording, cleanup recording, push recording.
Finalizes orphaned asciinema recordings by stopping processes, compressing files, and pushing to an orphan branch.
/plugin marketplace add terrylica/cc-skills/plugin install asciinema-tools@cc-skills[file|--all] [--force] [--no-push] [--keep-local]Finalize orphaned asciinema recordings: stop running processes gracefully, compress, and push to the orphan branch.
| 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 |
/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
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"
/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
/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
/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
/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
GH_TOKEN=$(gh auth token 2>/dev/null)
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
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-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