asciinema-streaming-backup Complete system for streaming asciinema recordings to GitHub with automatic brotli archival. Uses idle-detection for intelligent chunking, zstd for concatenatable streaming compression, and GitHub Actions for final brotli recompression. 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. When to Use This Skill Use this skill when: Setting up real-time backup of asciinema recordings to GitHub Configuring idl…

chunk1\\nchunk2' ]]; then\n log_pass \"zstd concatenation PASSED\"\nelse\n log_fail \"zstd concatenation FAILED\"\nfi\n\n# ─────────────────────────────────────────────────────────────────────\n# Phase 3: Repository Validation\n# ─────────────────────────────────────────────────────────────────────\necho \"\"\necho \" Phase 3: Repository Validation\"\necho \" ─────────────────────────────\"\n\nlog_run \"Checking gh auth\"\nif gh auth status &>/dev/null; then\n ACCOUNT=$(gh api user --jq '.login' 2>/dev/null || echo \"unknown\")\n log_pass \"authenticated as $ACCOUNT\"\nelse\n log_fail \"gh not authenticated\"\nfi\n\nlog_run \"Checking orphan branch on remote\"\nif git ls-remote --heads \"$REPO_URL\" \"$BRANCH_NAME\" 2>/dev/null | grep -q \"$BRANCH_NAME\"; then\n log_pass \"$BRANCH_NAME exists on remote\"\nelse\n log_fail \"$BRANCH_NAME NOT found on remote\"\nfi\n\nlog_run \"Checking local clone\"\nif [[ -d \"$REPO_DIR\" ]]; then\n log_pass \"local directory exists: $REPO_DIR\"\nelse\n log_fail \"local directory NOT found: $REPO_DIR\"\nfi\n\nlog_run \"Checking workflow file\"\nif [[ -f \"$REPO_DIR/.github/workflows/recompress.yml\" ]]; then\n log_pass \"recompress.yml present\"\nelse\n log_fail \"recompress.yml MISSING\"\nfi\n\n# ─────────────────────────────────────────────────────────────────────\n# Phase 4: GitHub Actions Test\n# ─────────────────────────────────────────────────────────────────────\necho \"\"\necho \" Phase 4: GitHub Actions Test\"\necho \" ─────────────────────────────\"\n\n# Extract owner/repo from URL for gh commands\nOWNER_REPO=\"\"\nif [[ \"$REPO_URL\" =~ github\\.com[:/]([^/]+)/([^/.]+) ]]; then\n OWNER_REPO=\"${BASH_REMATCH[1]}/${BASH_REMATCH[2]}\"\nfi\n\nif [[ -z \"$OWNER_REPO\" ]]; then\n log_fail \"Could not parse owner/repo from URL: $REPO_URL\"\nelse\n log_run \"Triggering workflow_dispatch\"\n if gh workflow run recompress -R \"$OWNER_REPO\" --ref \"$BRANCH_NAME\" 2>/dev/null; then\n log_pass \"workflow triggered\"\n sleep 5\n\n log_run \"Fetching run status\"\n RUN_ID=$(gh run list -R \"$OWNER_REPO\" -w recompress --limit 1 --json databaseId -q '.[0].databaseId' 2>/dev/null || true)\n\n if [[ -n \"$RUN_ID\" ]]; then\n STATUS=$(gh run view \"$RUN_ID\" -R \"$OWNER_REPO\" --json status -q '.status' 2>/dev/null || echo \"unknown\")\n echo \" ⏳ Run #$RUN_ID: $STATUS\"\n\n # Wait for completion (max 60s)\n COMPLETED=false\n for _ in {1..12}; do\n STATUS_FULL=$(gh run view \"$RUN_ID\" -R \"$OWNER_REPO\" --json status,conclusion -q '.status + \" \" + .conclusion' 2>/dev/null || true)\n if [[ \"$STATUS_FULL\" == \"completed \"* ]]; then\n CONCLUSION=\"${STATUS_FULL#completed }\"\n if [[ \"$CONCLUSION\" == \"success\" ]]; then\n log_pass \"workflow completed successfully\"\n else\n log_fail \"workflow completed with: $CONCLUSION\"\n fi\n COMPLETED=true\n break\n fi\n sleep 5\n done\n\n if [[ \"$COMPLETED\" == \"false\" ]]; then\n echo \" ⏳ Run still in progress after 60s (check manually)\"\n log_pass \"workflow triggered (completion pending)\"\n fi\n else\n log_fail \"could not fetch run ID\"\n fi\n else\n log_fail \"workflow trigger failed (workflow_dispatch may not be enabled)\"\n fi\nfi\n\n# ─────────────────────────────────────────────────────────────────────\n# Summary\n# ─────────────────────────────────────────────────────────────────────\necho \"\"\necho \"║ ║\"\necho \"╠═════════════════════════════════════════════════════════════════╣\"\necho \"║ AUTONOMOUS TESTS: $PASSED passed, $FAILED failed\"\necho \"╚═════════════════════════════════════════════════════════════════╝\"\n\n[[ $FAILED -eq 0 ]] && exit 0 || exit 1\nPREFLIGHT_EOF\n```\n\n---\n\n## User-Required Tests\n\nThese tests require user action in a terminal. Use AskUserQuestion to guide the user.\n\n### Recording Validation\n\n**AskUserQuestion**:\n\n```yaml\nquestion: \"Ready to test recording? This requires you to start asciinema in another terminal.\"\nheader: \"Recording Test\"\noptions:\n - label: \"Guide me through it (Recommended)\"\n description: \"I'll show step-by-step instructions\"\n - label: \"Skip this test\"\n description: \"I trust the setup works\"\n - label: \"I've already verified recording works\"\n description: \"Mark as passed\"\n```\n\n**If user selects \"Guide me through it\"**, display:\n\n```\n╔════════════════════════════════════════════════════════════════╗\n║ USER ACTION REQUIRED: Recording Test ║\n╠════════════════════════════════════════════════════════════════╣\n║ ║\n║ In a NEW terminal, run: ║\n║ ┌────────────────────────────────────────────────────────┐ ║\n║ │ asciinema rec ~/asciinema_recordings/test_session.cast │ ║\n║ └────────────────────────────────────────────────────────┘ ║\n║ ║\n║ Then: ║\n║ 1. Type a few commands (ls, echo \"hello\", etc.) ║\n║ 2. Exit with Ctrl+D or type 'exit' ║\n║ 3. Come back here when done ║\n║ ║\n╚════════════════════════════════════════════════════════════════╝\n```\n\n**After user confirms**, Claude validates autonomously:\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF_2'\n# Claude runs after user confirms\nCAST_FILE=\"$HOME/asciinema_recordings/test_session.cast\"\n\nif [[ -f \"$CAST_FILE\" ]]; then\n echo \" ✓ test_session.cast exists\"\n\n # Check JSON header\n if head -1 \"$CAST_FILE\" | jq -e '.version' &>/dev/null; then\n echo \" ✓ Valid JSON header\"\n else\n echo \" ✗ Invalid JSON header\"\n fi\n\n # Check line count (at least header + some events)\n LINE_COUNT=$(wc -l \u003c \"$CAST_FILE\")\n if [[ $LINE_COUNT -gt 1 ]]; then\n echo \" ✓ $LINE_COUNT events recorded\"\n else\n echo \" ✗ No events recorded\"\n fi\nelse\n echo \" ✗ test_session.cast NOT found\"\nfi\nPREFLIGHT_EOF_2\n```\n\n### Live Chunker Test (Optional)\n\n**AskUserQuestion**:\n\n```yaml\nquestion: \"Ready to test live chunking? This requires running recording + chunker simultaneously.\"\nheader: \"Chunker Test (Optional)\"\noptions:\n - label: \"Guide me through it\"\n description: \"Full end-to-end test with two terminals\"\n - label: \"Skip - I trust the setup\"\n description: \"Chunker test is optional\"\n```\n\n**If user selects \"Guide me through it\"**, display:\n\n```\n╔════════════════════════════════════════════════════════════════╗\n║ USER ACTION REQUIRED: Live Chunker Test ║\n╠════════════════════════════════════════════════════════════════╣\n║ ║\n║ TERMINAL 1 (Recording): ║\n║ ┌────────────────────────────────────────────────────────┐ ║\n║ │ asciinema rec ~/asciinema_recordings/chunker_test.cast │ ║\n║ └────────────────────────────────────────────────────────┘ ║\n║ ║\n║ TERMINAL 2 (Chunker): ║\n║ ┌────────────────────────────────────────────────────────┐ ║\n║ │ ~/asciinema_recordings/\u003crepo>/idle-chunker.sh \\ │ ║\n║ │ ~/asciinema_recordings/chunker_test.cast │ ║\n║ └────────────────────────────────────────────────────────┘ ║\n║ ║\n║ In Terminal 1: ║\n║ 1. Type some commands ║\n║ 2. Wait 30+ seconds (idle threshold) ║\n║ 3. Type more commands ║\n║ 4. Exit with Ctrl+D ║\n║ ║\n║ Watch Terminal 2 for chunk creation messages. ║\n║ ║\n╚════════════════════════════════════════════════════════════════╝\n```\n\n**After user confirms**, Claude validates:\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF_3'\n# Check if chunks were created\nREPO_DIR=\"$HOME/asciinema_recordings/\u003crepo>\"\nCHUNKS=$(find \"$REPO_DIR/chunks\" -name \"*.zst\" 2>/dev/null | wc -l)\n\nif [[ $CHUNKS -gt 0 ]]; then\n echo \" ✓ $CHUNKS chunk(s) created\"\n\n # Check if git tracked\n cd \"$REPO_DIR\"\n if git status --porcelain chunks/ 2>/dev/null | grep -q .; then\n echo \" ✓ Chunks staged for commit\"\n fi\nelse\n echo \" ✗ No chunks found in $REPO_DIR/chunks/\"\nfi\nPREFLIGHT_EOF_3\n```\n\n---\n\n## Troubleshooting\n\nCommon failures and resolutions:\n\n| Failure | Cause | Resolution |\n| ----------------------------------- | -------------------- | ------------------------------------------------------------- |\n| `asciinema MISSING` | Not installed | `brew install asciinema` (macOS) or `pipx install asciinema` |\n| `zstd MISSING` | Not installed | `brew install zstd` (macOS) or `apt install zstd` (Linux) |\n| `brotli MISSING` | Not installed | `brew install brotli` (macOS) or `apt install brotli` (Linux) |\n| `gh not authenticated` | No GitHub login | Run `gh auth login` and follow prompts |\n| `gh-recordings NOT found on remote` | Branch not pushed | Run orphan branch setup from Phase 4 of skill |\n| `local directory NOT found` | Clone failed | Check repo URL and permissions, re-run clone |\n| `recompress.yml MISSING` | Workflow not created | Re-run orphan branch setup to create workflow |\n| `workflow trigger failed` | No workflow_dispatch | Add `workflow_dispatch:` trigger to workflow |\n| `zstd concatenation FAILED` | zstd version issue | Update zstd: `brew upgrade zstd` |\n| `brotli round-trip FAILED` | brotli corrupted | Reinstall: `brew reinstall brotli` |\n\n---\n\n## Execution Instructions\n\n**For Claude Code**: After running the setup phases, execute autonomous validation:\n\n1. Save the script to a temp file or run inline via Bash tool\n2. Execute with: `bash \u003cscript> \"$REPO_DIR\" \"$REPO_URL\" \"$BRANCH_NAME\"`\n3. Display formatted output to user\n4. If any test fails, show relevant troubleshooting row\n5. After autonomous tests, prompt for user-required tests via AskUserQuestion\n6. Report final summary: \"X/Y autonomous tests passed, user tests: \u003cstatus>\"\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18189,"content_sha256":"bf6f65358e0466787b0e9073d213ec24d3f1aeae2a585a2e5774e09c4a7fbd84"},{"filename":"references/configuration-reference.md","content":"**Skill**: [asciinema-streaming-backup](../SKILL.md)\n\n# Configuration Reference\n\nAll AskUserQuestion sequences for core and advanced configuration, plus URL normalization and task templates.\n\n---\n\n## Phase 2: Core Configuration\n\n### 2.1 Repository URL\n\n**If current repo detected** (from Phase 1.5):\n\n```yaml\nAskUserQuestion:\n question: \"Which repository should store the recordings?\"\n header: \"Repository\"\n options:\n - label: \"${CURRENT_REPO_OWNER}/${CURRENT_REPO_NAME} (Recommended)\"\n description: \"Current repo detected from ${DETECTED_FROM}\"\n - label: \"Create dedicated repo: ${GITHUB_ACCOUNT}/asciinema-recordings\"\n description: \"Separate repository for all recordings\"\n - label: \"Enter different repository\"\n description: \"Specify another repository (user/repo format)\"\n```\n\n**If no current repo detected**:\n\n```yaml\nAskUserQuestion:\n question: \"Enter the GitHub repository URL for storing recordings:\"\n header: \"Repository URL\"\n options:\n - label: \"Create dedicated repo: ${GITHUB_ACCOUNT}/asciinema-recordings\"\n description: \"Separate repository for all recordings (Recommended)\"\n - label: \"Enter repository manually\"\n description: \"SSH ([email protected]:user/repo.git), HTTPS, or shorthand (user/repo)\"\n```\n\n### URL Normalization\n\nHandles multiple formats:\n\n```bash\n/usr/bin/env bash \u003c\u003c 'NORMALIZE_URL_EOF'\n# Normalize to SSH format for consistent handling\nnormalize_repo_url() {\n local url=\"$1\"\n\n # Shorthand: user/repo -> [email protected]:user/repo.git\n if [[ \"$url\" =~ ^[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+$ ]]; then\n echo \"[email protected]:${url}.git\"\n # HTTPS: https://github.com/user/repo -> [email protected]:user/repo.git\n elif [[ \"$url\" =~ ^https://github\\.com/([^/]+)/([^/]+)/?$ ]]; then\n echo \"[email protected]:${BASH_REMATCH[1]}/${BASH_REMATCH[2]%.git}.git\"\n # Already SSH format\n else\n echo \"$url\"\n fi\n}\n\nURL=\"${1:?Usage: provide URL to normalize}\"\nnormalize_repo_url \"$URL\"\nNORMALIZE_URL_EOF\n```\n\n### Confirmation for Free-Form Input\n\nIf user selected \"Enter different/manually\":\n\n```yaml\nAskUserQuestion:\n question: \"You entered '${USER_INPUT}'. Normalized to: ${NORMALIZED_URL}. Is this correct?\"\n header: \"Confirm Repository\"\n options:\n - label: \"Yes, use ${NORMALIZED_URL}\"\n description: \"Proceed with this repository\"\n - label: \"No, let me re-enter\"\n description: \"Go back to repository selection\"\n```\n\n### 2.2 Recording Directory\n\n```yaml\nAskUserQuestion:\n question: \"Where should recordings be stored locally?\"\n header: \"Recording Directory\"\n options:\n - label: \"~/asciinema_recordings/${RESOLVED_REPO_NAME} (Recommended)\"\n description: \"Example: ~/asciinema_recordings/alpha-forge\"\n - label: \"Custom path\"\n description: \"Enter a different directory path\"\n```\n\n**Note**: `${RESOLVED_REPO_NAME}` is the actual repo name from Phase 1.5 or Phase 2.1, not a variable placeholder. Display the concrete path to user.\n\n### 2.3 Branch Name\n\n```yaml\nAskUserQuestion:\n question: \"What should the orphan branch be named?\"\n header: \"Branch Name\"\n options:\n - label: \"asciinema-recordings (Recommended)\"\n description: \"Matches ~/asciinema_recordings/ parent directory pattern\"\n - label: \"gh-recordings\"\n description: \"GitHub-prefixed alternative (gh = GitHub storage)\"\n - label: \"recordings\"\n description: \"Minimal name\"\n - label: \"Custom\"\n description: \"Enter a custom branch name\"\n```\n\n**Naming Convention**: The default `asciinema-recordings` matches the parent directory `~/asciinema_recordings/` for consistency.\n\n---\n\n## Phase 3: Advanced Configuration\n\n### Configuration Parameters\n\n| Parameter | Default | Options |\n| -------------- | ------- | ------------------------------------------- |\n| Idle threshold | 30s | 15s, 30s (Recommended), 60s, Custom (5-300) |\n| zstd level | 3 | 1 (fast), 3 (Recommended), 6, Custom (1-22) |\n| Brotli level | 9 | 6, 9 (Recommended), 11, Custom (1-11) |\n| Auto-push | Yes | Yes (Recommended), No |\n| Poll interval | 5s | 2s, 5s (Recommended), 10s |\n\n### 3.1 Idle Threshold\n\n```yaml\nAskUserQuestion:\n question: \"How long should the chunker wait before creating a chunk?\"\n header: \"Idle Threshold\"\n options:\n - label: \"15 seconds\"\n description: \"More frequent chunks, smaller files\"\n - label: \"30 seconds (Recommended)\"\n description: \"Balanced chunk size and frequency\"\n - label: \"60 seconds\"\n description: \"Larger chunks, less frequent uploads\"\n - label: \"Custom (5-300 seconds)\"\n description: \"Enter a custom threshold\"\n```\n\n### 3.2 zstd Compression Level\n\n```yaml\nAskUserQuestion:\n question: \"What zstd compression level for streaming chunks?\"\n header: \"zstd Level\"\n options:\n - label: \"1 (Fast)\"\n description: \"Fastest compression, larger files\"\n - label: \"3 (Recommended)\"\n description: \"Good balance of speed and compression\"\n - label: \"6 (Better compression)\"\n description: \"Slower but smaller chunks\"\n - label: \"Custom (1-22)\"\n description: \"Enter a custom level\"\n```\n\n### 3.3 Brotli Compression Level\n\n```yaml\nAskUserQuestion:\n question: \"What brotli compression level for final archives?\"\n header: \"Brotli Level\"\n options:\n - label: \"6\"\n description: \"Faster archival, slightly larger files\"\n - label: \"9 (Recommended)\"\n description: \"Great compression with reasonable speed\"\n - label: \"11 (Maximum)\"\n description: \"Best compression, slowest (may timeout on large files)\"\n - label: \"Custom (1-11)\"\n description: \"Enter a custom level\"\n```\n\n### 3.4 Auto-Push\n\n```yaml\nAskUserQuestion:\n question: \"Should chunks be automatically pushed to GitHub?\"\n header: \"Auto-Push\"\n options:\n - label: \"Yes (Recommended)\"\n description: \"Push immediately after each chunk\"\n - label: \"No\"\n description: \"Manual push when ready\"\n```\n\n### 3.5 Poll Interval\n\n```yaml\nAskUserQuestion:\n question: \"How often should the chunker check for idle state?\"\n header: \"Poll Interval\"\n options:\n - label: \"2 seconds\"\n description: \"More responsive, slightly higher CPU\"\n - label: \"5 seconds (Recommended)\"\n description: \"Good balance\"\n - label: \"10 seconds\"\n description: \"Lower resource usage\"\n```\n\n---\n\n## TodoWrite Task Templates\n\n### Template: Full Setup\n\n```\n1. [Preflight] Validate all tools installed (asciinema, zstd, brotli, git, gh)\n2. [Preflight] AskUserQuestion: offer installation for missing tools\n3. [Account] Detect GitHub accounts from 5 sources\n4. [Account] AskUserQuestion: select GitHub account\n5. [Config] AskUserQuestion: repository URL\n6. [Config] AskUserQuestion: recording directory\n7. [Config] AskUserQuestion: branch name\n8. [Advanced] AskUserQuestion: idle threshold\n9. [Advanced] AskUserQuestion: zstd level\n10. [Advanced] AskUserQuestion: brotli level\n11. [Advanced] AskUserQuestion: auto-push\n12. [Advanced] AskUserQuestion: poll interval\n13. [Branch] Check if orphan branch exists on remote\n14. [Branch] AskUserQuestion: handle existing branch\n15. [Branch] Create orphan branch if needed\n16. [Branch] Create GitHub Actions workflow with embedded parameters\n17. [Local] Clone orphan branch to ~/asciinema_recordings/\n18. [Local] Generate idle-chunker.sh with embedded parameters\n19. [Validate] Run autonomous validation (8 tests)\n20. [Validate] AskUserQuestion: recording test (user action)\n21. [Validate] AskUserQuestion: chunker live test (user action)\n22. [Guide] Display configuration summary and usage instructions\n```\n\n### Template: Recording Session\n\n```\n1. [Context] Detect workspace from $PWD\n2. [Context] Generate datetime for filename\n3. [Context] Ensure tmp/ directory exists\n4. [Command] Generate asciinema rec command\n5. [Command] Generate idle-chunker command\n6. [Guide] Display two-terminal workflow instructions\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7893,"content_sha256":"f20662cf7c2b9ba10b74617a161882a123d12714e7ee8f0742559f47ead51d52"},{"filename":"references/evolution-log.md","content":"# Evolution Log\n\n> **Convention**: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.\n\n---\n\n## 2026-02-26: Initial Evolution Log\n\n**Status**: Skill is in use and maintained. Track improvements here.\n\n### Purpose\n\nThis evolution log tracks updates to the `asciinema-streaming-backup` skill. Each entry should note:\n\n- What changed (content, structure, tooling)\n- Why it changed (bug fix, feature request, best practice)\n- Files affected\n\n### How to Use\n\n1. When updating SKILL.md or references, add an entry here with the date\n2. Keep entries reverse-chronological (newest first)\n3. Link to ADRs or GitHub issues when relevant\n4. Reference specific line changes when helpful\n\n---\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":712,"content_sha256":"b209c4f00257bf664b150cd48001f01572f7960226ab9563c760fc772e4efcf5"},{"filename":"references/github-workflow.md","content":"**Skill**: [asciinema-streaming-backup](../SKILL.md)\n\n# GitHub Actions Workflow\n\nComplete GitHub Actions workflow for recompressing zstd chunks to brotli archives.\n\n## recompress.yml\n\n```yaml\n# .github/workflows/recompress.yml\n# Lives in the gh-recordings orphan branch\n\nname: Recompress to Brotli\n\non:\n push:\n branches: [gh-recordings]\n paths: [\"chunks/**/*.zst\"]\n workflow_dispatch:\n inputs:\n force:\n description: \"Force recompress even if no new chunks\"\n required: false\n default: \"false\"\n\njobs:\n recompress:\n runs-on: ubuntu-latest\n permissions:\n contents: write\n\n steps:\n - name: Checkout orphan branch\n uses: actions/checkout@v4\n with:\n ref: gh-recordings\n fetch-depth: 1\n\n - name: Install compression tools\n run: |\n sudo apt-get update\n sudo apt-get install -y zstd brotli\n\n - name: Check for chunks\n id: check\n run: |\n if compgen -G \"chunks/*.zst\" > /dev/null 2>&1; then\n echo \"has_chunks=true\" >> $GITHUB_OUTPUT\n echo \"chunk_count=$(ls -1 chunks/*.zst 2>/dev/null | wc -l)\" >> $GITHUB_OUTPUT\n else\n echo \"has_chunks=false\" >> $GITHUB_OUTPUT\n echo \"chunk_count=0\" >> $GITHUB_OUTPUT\n fi\n\n - name: Display chunk info\n if: steps.check.outputs.has_chunks == 'true'\n run: |\n echo \"=== Chunks to process ===\"\n ls -lh chunks/*.zst\n echo \"\"\n echo \"Total chunks: ${{ steps.check.outputs.chunk_count }}\"\n\n - name: Concatenate and recompress\n if: steps.check.outputs.has_chunks == 'true'\n run: |\n mkdir -p archives\n\n # Generate archive name with timestamp\n ARCHIVE_NAME=\"session_$(date +%Y%m%d_%H%M%S).cast.br\"\n\n echo \"Processing ${{ steps.check.outputs.chunk_count }} chunks...\"\n\n # Concatenate all zstd chunks in order, decompress, recompress to brotli\n # Sort by filename to ensure correct order\n ls -1 chunks/*.zst | sort | xargs cat | zstd -d | brotli -9 -o \"archives/$ARCHIVE_NAME\"\n\n # Get sizes for logging\n CHUNKS_SIZE=$(du -sh chunks/*.zst | tail -1 | cut -f1)\n ARCHIVE_SIZE=$(ls -lh \"archives/$ARCHIVE_NAME\" | awk '{print $5}')\n\n echo \"\"\n echo \"=== Compression Results ===\"\n echo \"Input chunks: ${{ steps.check.outputs.chunk_count }} files\"\n echo \"Output archive: archives/$ARCHIVE_NAME\"\n echo \"Archive size: $ARCHIVE_SIZE\"\n\n # Cleanup chunks after successful archival\n rm -f chunks/*.zst\n echo \"Cleaned up processed chunks\"\n\n # Export for commit message\n echo \"ARCHIVE_NAME=$ARCHIVE_NAME\" >> $GITHUB_ENV\n echo \"ARCHIVE_SIZE=$ARCHIVE_SIZE\" >> $GITHUB_ENV\n\n - name: Verify archive integrity\n if: steps.check.outputs.has_chunks == 'true'\n run: |\n echo \"Verifying archive...\"\n brotli -d -c \"archives/${{ env.ARCHIVE_NAME }}\" | head -5\n echo \"...\"\n echo \"Archive verified successfully\"\n\n - name: Commit archive\n if: steps.check.outputs.has_chunks == 'true'\n uses: stefanzweifel/git-auto-commit-action@v5\n with:\n commit_message: |\n chore: archive recording to brotli\n\n Archive: ${{ env.ARCHIVE_NAME }}\n Size: ${{ env.ARCHIVE_SIZE }}\n Chunks processed: ${{ steps.check.outputs.chunk_count }}\n file_pattern: \"archives/*.br chunks/\"\n branch: gh-recordings\n\n - name: No chunks to process\n if: steps.check.outputs.has_chunks != 'true'\n run: echo \"No chunks found to process\"\n```\n\n## How It Works\n\n### Trigger Conditions\n\n1. **Push to gh-recordings**: Only triggers when `.zst` files are added/modified in `chunks/`\n2. **Manual dispatch**: Can be triggered manually with optional force flag\n\n### Processing Pipeline\n\n```\nchunks/*.zst → sort by name → cat → zstd -d → brotli -9 → archives/*.br\n```\n\n1. **Sort chunks**: Ensures correct order (chunk_001, chunk_002, etc.)\n2. **Concatenate**: Uses zstd's frame concatenation feature\n3. **Decompress**: Single pass through zstd decoder\n4. **Recompress**: Brotli -9 for ~300x total compression\n5. **Cleanup**: Removes processed chunks\n\n### Output\n\n- Archive name: `session_YYYYMMDD_HHMMSS.cast.br`\n- Location: `archives/` directory\n- Commit message includes size and chunk count\n\n## Customization\n\n### Change Brotli Level\n\nFor faster compression (less ratio):\n\n```yaml\nbrotli -6 -o \"archives/$ARCHIVE_NAME\"\n```\n\nFor maximum compression (slower):\n\n```yaml\nbrotli -11 -o \"archives/$ARCHIVE_NAME\" # May fail on very large files\n```\n\n### Keep Chunks (No Cleanup)\n\nRemove the cleanup line:\n\n```yaml\n# rm -f chunks/*.zst # Comment out to keep chunks\n```\n\n### Add Slack Notification\n\n```yaml\n- name: Notify Slack\n if: steps.check.outputs.has_chunks == 'true'\n uses: slackapi/slack-github-action@v1\n with:\n payload: |\n {\n \"text\": \"Recording archived: ${{ env.ARCHIVE_NAME }} (${{ env.ARCHIVE_SIZE }})\"\n }\n env:\n SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}\n```\n\n## Permissions Required\n\nThe workflow requires `contents: write` permission to:\n\n1. Read chunks from the repository\n2. Write archives to the repository\n3. Delete processed chunks\n4. Push commits back to gh-recordings\n\nThis is specified in the job:\n\n```yaml\npermissions:\n contents: write\n```\n\n## Troubleshooting\n\n### \"Permission denied\" on push\n\nEnsure the workflow has write permissions:\n\n1. Go to repo Settings → Actions → General\n2. Under \"Workflow permissions\", select \"Read and write permissions\"\n\n### \"No chunks found\" but chunks exist\n\nCheck the path pattern:\n\n```yaml\npaths: [\"chunks/**/*.zst\"] # Must match your chunk location\n```\n\n### Archive is corrupted\n\nVerify chunks are sequential (no gaps or overlaps):\n\n```bash\n/usr/bin/env bash \u003c\u003c 'GITHUB_WORKFLOW_SCRIPT_EOF'\nfor f in chunks/*.zst; do\n echo \"=== $f ===\"\n zstd -d -c \"$f\" | head -1\ndone\nGITHUB_WORKFLOW_SCRIPT_EOF\n```\n\n### Workflow not triggering\n\nCheck the branch filter:\n\n```yaml\nbranches: [gh-recordings] # Must match your orphan branch name\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6211,"content_sha256":"fc7823937d82a1b25dbb436f1c3bc46f2f19280d567ac601d6f34b5551aae9b1"},{"filename":"references/idle-chunker.md","content":"**Skill**: [asciinema-streaming-backup](../SKILL.md)\n\n# Idle Chunker Script (DEPRECATED)\n\n> **DEPRECATED**: This inline chunker has been superseded by the launchd daemon architecture.\n> See [ADR 2025-12-26](/docs/adr/2025-12-26-asciinema-daemon-architecture.md) for rationale.\n>\n> **Use instead**: `/asciinema-tools:daemon-setup` to configure the background daemon.\n\n---\n\n**Historical Reference**: The following documents the v9.3 inline chunker approach for reference only.\n\n## idle-chunker.sh (LEGACY)\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF'\n#!/usr/bin/env bash\n# idle-chunker.sh - Creates zstd chunks during recording idle periods\n#\n# Usage: idle-chunker.sh \u003ccast_file> \u003crecordings_dir> [idle_threshold]\n#\n# Arguments:\n# cast_file - Path to the active .cast recording file\n# recordings_dir - Path to the orphan branch clone (e.g., ~/asciinema_recordings/repo-name)\n# idle_threshold - Seconds of inactivity before chunking (default: 30)\n#\n# Environment:\n# CHUNK_PREFIX - Prefix for chunk filenames (default: chunk)\n# PUSH_ENABLED - Set to \"false\" to disable auto-push (default: true)\n# VERBOSE - Set to \"true\" for debug output (default: false)\n\nset -euo pipefail\n\n# Arguments\nCAST_FILE=\"${1:?Usage: idle-chunker.sh \u003ccast_file> \u003crecordings_dir> [idle_threshold]}\"\nRECORDINGS_DIR=\"${2:?Usage: idle-chunker.sh \u003ccast_file> \u003crecordings_dir> [idle_threshold]}\"\nIDLE_THRESHOLD=\"${3:-30}\"\n\n# Configuration\nCHUNK_PREFIX=\"${CHUNK_PREFIX:-chunk}\"\nPUSH_ENABLED=\"${PUSH_ENABLED:-true}\"\nVERBOSE=\"${VERBOSE:-false}\"\nZSTD_LEVEL=\"${ZSTD_LEVEL:-3}\"\nPOLL_INTERVAL=\"${POLL_INTERVAL:-5}\"\n\n# State\nlast_chunk_pos=0\nchunk_count=0\n\nlog() {\n echo \"[$(date +%H:%M:%S)] $*\"\n}\n\ndebug() {\n [[ \"$VERBOSE\" == \"true\" ]] && log \"DEBUG: $*\"\n}\n\n# Validate inputs\nif [[ ! -d \"$RECORDINGS_DIR\" ]]; then\n log \"ERROR: Recordings directory not found: $RECORDINGS_DIR\"\n exit 1\nfi\n\nif [[ ! -d \"$RECORDINGS_DIR/chunks\" ]]; then\n log \"Creating chunks directory...\"\n mkdir -p \"$RECORDINGS_DIR/chunks\"\nfi\n\ncd \"$RECORDINGS_DIR\"\n\nlog \"Idle chunker started\"\nlog \" Monitoring: $CAST_FILE\"\nlog \" Chunks to: $RECORDINGS_DIR/chunks/\"\nlog \" Idle threshold: ${IDLE_THRESHOLD}s\"\nlog \" Auto-push: $PUSH_ENABLED\"\nlog \"\"\nlog \"Waiting for recording to start...\"\n\n# Wait for file to exist\nwhile [[ ! -f \"$CAST_FILE\" ]]; do\n sleep 2\ndone\n\nlog \"Recording detected, monitoring for idle periods...\"\n\n# Get file modification time (cross-platform)\nget_mtime() {\n local file=\"$1\"\n if [[ \"$(uname)\" == \"Darwin\" ]]; then\n stat -f%m \"$file\" 2>/dev/null || echo 0\n else\n stat -c%Y \"$file\" 2>/dev/null || echo 0\n fi\n}\n\n# Get file size (cross-platform)\nget_size() {\n local file=\"$1\"\n if [[ \"$(uname)\" == \"Darwin\" ]]; then\n stat -f%z \"$file\" 2>/dev/null || echo 0\n else\n stat -c%s \"$file\" 2>/dev/null || echo 0\n fi\n}\n\n# Main loop\nwhile true; do\n # Check if file still exists (recording might have ended)\n if [[ ! -f \"$CAST_FILE\" ]]; then\n log \"Recording file removed, creating final chunk...\"\n break\n fi\n\n # Check idle time\n file_mtime=$(get_mtime \"$CAST_FILE\")\n now=$(date +%s)\n idle_seconds=$((now - file_mtime))\n\n debug \"Idle: ${idle_seconds}s, Threshold: ${IDLE_THRESHOLD}s\"\n\n if (( idle_seconds >= IDLE_THRESHOLD )); then\n current_size=$(get_size \"$CAST_FILE\")\n\n if (( current_size > last_chunk_pos )); then\n chunk_count=$((chunk_count + 1))\n chunk_name=\"${CHUNK_PREFIX}_$(date +%Y%m%d_%H%M%S)_${chunk_count}.cast\"\n new_bytes=$((current_size - last_chunk_pos))\n\n log \"Idle detected (${idle_seconds}s) - creating chunk...\"\n\n # Extract only new bytes since last chunk (no overlap!)\n tail -c +\"$((last_chunk_pos + 1))\" \"$CAST_FILE\" > \"chunks/$chunk_name\"\n\n # Compress with zstd\n zstd -${ZSTD_LEVEL} --rm \"chunks/$chunk_name\"\n\n log \"Created: chunks/${chunk_name}.zst (${new_bytes} bytes, chunk #${chunk_count})\"\n\n # Push to GitHub\n if [[ \"$PUSH_ENABLED\" == \"true\" ]]; then\n if git add chunks/ && git commit -m \"chunk #${chunk_count}: $(date +%H:%M)\" 2>/dev/null; then\n if git push 2>/dev/null; then\n log \"Pushed to GitHub\"\n else\n log \"WARNING: Push failed (will retry next chunk)\"\n fi\n fi\n fi\n\n # Update position tracker\n last_chunk_pos=$current_size\n\n # Reset idle detection (wait for new content)\n sleep $POLL_INTERVAL\n fi\n fi\n\n sleep $POLL_INTERVAL\ndone\n\n# Final chunk if there's remaining data\nif [[ -f \"$CAST_FILE\" ]]; then\n current_size=$(get_size \"$CAST_FILE\")\n if (( current_size > last_chunk_pos )); then\n chunk_count=$((chunk_count + 1))\n chunk_name=\"${CHUNK_PREFIX}_$(date +%Y%m%d_%H%M%S)_final.cast\"\n\n tail -c +\"$((last_chunk_pos + 1))\" \"$CAST_FILE\" > \"chunks/$chunk_name\"\n zstd -${ZSTD_LEVEL} --rm \"chunks/$chunk_name\"\n\n log \"Created final chunk: chunks/${chunk_name}.zst\"\n\n if [[ \"$PUSH_ENABLED\" == \"true\" ]]; then\n git add chunks/ && git commit -m \"chunk #${chunk_count}: final\" && git push\n log \"Pushed final chunk to GitHub\"\n fi\n fi\nfi\n\nlog \"Idle chunker finished (${chunk_count} chunks created)\"\nPREFLIGHT_EOF\n```\n\n## Usage Examples\n\n### Basic Usage\n\n```bash\n# Start recording in terminal 1\nasciinema rec ~/project/tmp/session.cast\n\n# Start chunker in terminal 2\n~/asciinema_recordings/my-repo/idle-chunker.sh ~/project/tmp/session.cast ~/asciinema_recordings/my-repo\n```\n\n### With Custom Threshold\n\n```bash\n# Chunk after 15 seconds of idle (more frequent)\nidle-chunker.sh session.cast ~/asciinema_recordings/repo 15\n\n# Chunk after 60 seconds of idle (less frequent)\nidle-chunker.sh session.cast ~/asciinema_recordings/repo 60\n```\n\n### Debug Mode\n\n```bash\nVERBOSE=true idle-chunker.sh session.cast ~/asciinema_recordings/repo\n```\n\n### Disable Auto-Push (Manual Control)\n\n```bash\nPUSH_ENABLED=false idle-chunker.sh session.cast ~/asciinema_recordings/repo\n\n# Push manually when ready\ncd ~/asciinema_recordings/repo && git push\n```\n\n## How It Works\n\n1. **File Monitoring**: Watches the .cast file's modification time\n2. **Idle Detection**: When file hasn't been modified for `IDLE_THRESHOLD` seconds\n3. **Chunk Extraction**: Uses `tail -c +N` to extract only new bytes (no overlap)\n4. **Compression**: zstd -3 provides ~10x compression with speed\n5. **Git Push**: Commits and pushes to orphan branch\n6. **Position Tracking**: Remembers last chunk position to avoid duplication\n\n## Key Design: No Overlap\n\nThe script tracks `last_chunk_pos` to ensure chunks are **sequential, not overlapping**:\n\n```\nFile: [AAAAAABBBBBBCCCCCC]\n ^ ^ ^\nChunk 1: [AAAAAA] (bytes 0-5)\nChunk 2: [BBBBBB] (bytes 6-11)\nChunk 3: [CCCCCC] (bytes 12-17)\n```\n\nThis allows zstd concatenation to work correctly:\n\n```bash\ncat chunk_1.zst chunk_2.zst chunk_3.zst > combined.zst\nzstd -d combined.zst # Produces original file\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6865,"content_sha256":"f959de29938de926538fef87b70e350e8979dc28178926f790ead3a3e5667931"},{"filename":"references/setup-scripts.md","content":"**Skill**: [asciinema-streaming-backup](../SKILL.md)\n\n# Setup Scripts\n\n## Table of Contents\n\n- [preflight-check.sh](#preflight-checksh)\n- [setup-orphan-branch.sh](#setup-orphan-branchsh)\n- [validate-system.sh](#validate-systemsh)\n- [test-workflow.sh](#test-workflowsh)\n\nComplete setup and validation scripts for the asciinema streaming backup system.\n\n## preflight-check.sh\n\nValidates all required tools are installed.\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF'\n#!/usr/bin/env bash\n# preflight-check.sh - Validates all requirements with self-correction\n#\n# Usage: preflight-check.sh [--fix]\n# --fix Attempt to install missing tools via Homebrew\n\nset -euo pipefail\n\nFIX_MODE=\"${1:-}\"\nMISSING=()\nWARNINGS=()\n\nlog() { echo \"[preflight] $*\"; }\nwarn() { WARNINGS+=(\"$*\"); }\nfail() { MISSING+=(\"$*\"); }\n\n# Check each required tool\ncheck_tool() {\n local tool=\"$1\"\n local install_cmd=\"${2:-brew install $tool}\"\n\n if command -v \"$tool\" &>/dev/null; then\n log \"$tool: OK ($(command -v \"$tool\"))\"\n else\n fail \"$tool\"\n log \"$tool: MISSING\"\n log \" Install: $install_cmd\"\n fi\n}\n\nlog \"=== Checking required tools ===\"\ncheck_tool \"asciinema\" \"brew install asciinema\"\ncheck_tool \"zstd\" \"brew install zstd\"\ncheck_tool \"brotli\" \"brew install brotli\"\ncheck_tool \"git\" \"xcode-select --install\"\ncheck_tool \"gh\" \"brew install gh\"\n\nlog \"\"\nlog \"=== Checking optional tools ===\"\nif command -v fswatch &>/dev/null; then\n log \"fswatch: OK (enables real-time monitoring)\"\nelse\n log \"fswatch: NOT INSTALLED (optional)\"\n log \" Install: brew install fswatch\"\nfi\n\n# Check asciinema version\nif command -v asciinema &>/dev/null; then\n log \"\"\n log \"=== Checking versions ===\"\n ASCIINEMA_VERSION=$(asciinema --version 2>&1 | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n if [[ -n \"$ASCIINEMA_VERSION\" ]]; then\n MAJOR=\"${ASCIINEMA_VERSION%%.*}\"\n if (( MAJOR >= 3 )); then\n log \"asciinema: v$ASCIINEMA_VERSION (Rust version, recommended)\"\n else\n warn \"asciinema: v$ASCIINEMA_VERSION (Python version, upgrade recommended)\"\n log \" Upgrade: brew upgrade asciinema\"\n fi\n fi\nfi\n\n# Check gh authentication\nif command -v gh &>/dev/null; then\n log \"\"\n log \"=== Checking GitHub CLI auth ===\"\n if gh auth status &>/dev/null; then\n log \"gh: Authenticated\"\n else\n warn \"gh: Not authenticated\"\n log \" Run: gh auth login\"\n fi\nfi\n\n# Summary\nlog \"\"\nlog \"=== Summary ===\"\n\nif [[ ${#MISSING[@]} -gt 0 ]]; then\n log \"Missing tools: ${MISSING[*]}\"\n\n if [[ \"$FIX_MODE\" == \"--fix\" ]]; then\n log \"\"\n log \"Attempting to install missing tools...\"\n brew install \"${MISSING[@]}\"\n log \"Installation complete. Re-run preflight to verify.\"\n else\n log \"\"\n log \"To install all missing tools:\"\n log \" brew install ${MISSING[*]}\"\n log \"\"\n log \"Or run: $0 --fix\"\n exit 1\n fi\nelse\n log \"All required tools installed\"\nfi\n\nif [[ ${#WARNINGS[@]} -gt 0 ]]; then\n log \"\"\n log \"Warnings:\"\n for w in \"${WARNINGS[@]}\"; do\n log \" - $w\"\n done\nfi\nPREFLIGHT_EOF\n```\n\n## setup-orphan-branch.sh\n\nCreates the orphan branch with GitHub Actions workflow.\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF_2'\n#!/usr/bin/env bash\n# setup-orphan-branch.sh - Creates gh-recordings orphan branch\n#\n# Usage: setup-orphan-branch.sh \u003crepo_url>\n# repo_url SSH or HTTPS URL (e.g., [email protected]:user/repo.git)\n#\n# Creates:\n# - Orphan branch 'gh-recordings' with separate history\n# - GitHub Actions workflow for brotli recompression\n# - Local clone at ~/asciinema_recordings/\u003crepo-name>/\n\nset -euo pipefail\n\nREPO_URL=\"${1:?Usage: setup-orphan-branch.sh \u003crepo_url>}\"\nBRANCH=\"gh-recordings\"\nBROTLI_LEVEL=\"${BROTLI_LEVEL:-9}\"\n\n# Extract repo name from URL\nREPO_NAME=$(basename \"$REPO_URL\" .git)\nLOCAL_DIR=\"$HOME/asciinema_recordings/$REPO_NAME\"\n\nlog() { echo \"[setup] $*\"; }\n\n# Detect GitHub account from gh auth\ndetect_github_account() {\n log \"Detecting GitHub accounts...\"\n ACCOUNTS=$(gh auth status 2>&1 | grep -oE 'Logged in to github.com account [^ ]+' | awk '{print $NF}' || true)\n\n if [[ -z \"$ACCOUNTS\" ]]; then\n log \"ERROR: No GitHub accounts found. Run 'gh auth login' first.\"\n exit 1\n fi\n\n ACTIVE_ACCOUNT=$(gh auth status 2>&1 | grep -A1 'github.com' | grep 'Active account: true' -B1 | head -1 | awk '{print $NF}' || echo \"$ACCOUNTS\" | head -1)\n log \"Active GitHub account: $ACTIVE_ACCOUNT\"\n\n # Check if correct account for this repo\n REPO_OWNER=$(echo \"$REPO_URL\" | sed -E 's|.*github.com[:/]([^/]+)/.*|\\1|')\n if [[ \"$ACTIVE_ACCOUNT\" != \"$REPO_OWNER\" ]]; then\n log \"Switching to account: $REPO_OWNER\"\n if ! gh auth switch --user \"$REPO_OWNER\" 2>/dev/null; then\n log \"WARNING: Could not switch to $REPO_OWNER, using $ACTIVE_ACCOUNT\"\n fi\n fi\n\n SELECTED_ACCOUNT=\"${REPO_OWNER:-$ACTIVE_ACCOUNT}\"\n}\n\n# Get SSH key for selected account\nget_ssh_key() {\n local account=\"$1\"\n local key_path=\"$HOME/.ssh/id_ed25519_${account}\"\n\n if [[ -f \"$key_path\" ]]; then\n echo \"$key_path\"\n elif [[ -f \"$HOME/.ssh/id_ed25519\" ]]; then\n echo \"$HOME/.ssh/id_ed25519\"\n else\n echo \"\"\n fi\n}\n\ndetect_github_account\nSSH_KEY=$(get_ssh_key \"$SELECTED_ACCOUNT\")\nif [[ -n \"$SSH_KEY\" ]]; then\n export GIT_SSH_COMMAND=\"ssh -i $SSH_KEY\"\n log \"Using SSH key: $SSH_KEY\"\nfi\n\nlog \"Repository: $REPO_URL\"\nlog \"Branch: $BRANCH\"\nlog \"Local directory: $LOCAL_DIR\"\nlog \"\"\n\n# Check if branch already exists\nif git ls-remote --heads \"$REPO_URL\" \"$BRANCH\" 2>/dev/null | grep -q \"$BRANCH\"; then\n log \"Orphan branch '$BRANCH' already exists on remote\"\n\n if [[ -d \"$LOCAL_DIR\" ]]; then\n log \"Local clone already exists: $LOCAL_DIR\"\n log \"Pulling latest...\"\n git -C \"$LOCAL_DIR\" pull\n else\n log \"Cloning to: $LOCAL_DIR\"\n mkdir -p \"$(dirname \"$LOCAL_DIR\")\"\n git clone --single-branch --branch \"$BRANCH\" --depth 1 \"$REPO_URL\" \"$LOCAL_DIR\"\n fi\n\n log \"Setup complete\"\n exit 0\nfi\n\nlog \"Creating orphan branch...\"\n\n# Create temporary clone for setup\nTEMP_DIR=$(mktemp -d)\ntrap \"rm -rf $TEMP_DIR\" EXIT\n\ngit clone --depth 1 \"$REPO_URL\" \"$TEMP_DIR\"\ncd \"$TEMP_DIR\"\n\n# Create orphan branch\ngit checkout --orphan \"$BRANCH\"\ngit rm -rf .\n\n# Setup directory structure\nmkdir -p .github/workflows chunks archives\n\n# Create GitHub Actions workflow (brotli level embedded at creation time)\ncat > .github/workflows/recompress.yml \u003c\u003c WORKFLOW_EOF\nname: Recompress to Brotli\n\non:\n push:\n branches: [gh-recordings]\n paths: ['chunks/**/*.zst']\n workflow_dispatch:\n\njobs:\n recompress:\n runs-on: ubuntu-latest\n permissions:\n contents: write\n\n steps:\n - uses: actions/checkout@v4\n\n - name: Install tools\n run: sudo apt-get update && sudo apt-get install -y zstd brotli\n\n - name: Recompress chunks\n run: |\n if compgen -G \"chunks/*.zst\" > /dev/null; then\n mkdir -p archives\n ARCHIVE=\"session_\\$(date +%Y%m%d_%H%M%S).cast.br\"\n ls -1 chunks/*.zst | sort | xargs cat | zstd -d | brotli -${BROTLI_LEVEL} -o \"archives/\\$ARCHIVE\"\n rm -f chunks/*.zst\n echo \"ARCHIVE=\\$ARCHIVE\" >> \\$GITHUB_ENV\n fi\n\n - name: Commit\n if: env.ARCHIVE != ''\n uses: stefanzweifel/git-auto-commit-action@v5\n with:\n commit_message: \"chore: archive to brotli (\\${{ env.ARCHIVE }})\"\n file_pattern: 'archives/*.br chunks/'\nWORKFLOW_EOF\n\n# Create placeholder files\ncat > chunks/README.md \u003c\u003c 'EOF'\n# Chunks\n\nStreaming zstd-compressed recording chunks.\nAuto-deleted after archival to brotli.\nEOF\n\ncat > archives/README.md \u003c\u003c 'EOF'\n# Archives\n\nFinal brotli-compressed recordings.\n~300x compression ratio.\nEOF\n\n# Create main README\ncat > README.md \u003c\u003c 'EOF'\n# Recording Storage\n\nOrphan branch for asciinema recording backups.\nCompletely isolated from main codebase history.\n\n## Structure\n\n- `chunks/` - Streaming zstd chunks (temporary)\n- `archives/` - Brotli archives (permanent)\n\n## Workflow\n\n1. Local idle-chunker creates zstd chunks\n2. Chunks pushed to this branch\n3. GitHub Action recompresses to brotli\n4. Chunks deleted, archives retained\n\n## Isolation\n\nThis is an orphan branch with no shared history.\nGit refuses to merge with main: \"refusing to merge unrelated histories\"\nEOF\n\n# Initial commit\ngit add .\ngit commit -m \"init: recording storage (orphan branch)\"\n\n# Push\nlog \"Pushing orphan branch to remote...\"\ngit push -u origin \"$BRANCH\"\n\n# Clone to local recordings directory\ncd -\nmkdir -p \"$(dirname \"$LOCAL_DIR\")\"\ngit clone --single-branch --branch \"$BRANCH\" --depth 1 \"$REPO_URL\" \"$LOCAL_DIR\"\n\n# Copy idle-chunker script\ncat > \"$LOCAL_DIR/idle-chunker.sh\" \u003c\u003c 'CHUNKER_EOF'\n#!/usr/bin/env bash\n# idle-chunker.sh - See references/idle-chunker.md for full version\nCAST_FILE=\"${1:?Usage: idle-chunker.sh \u003ccast_file>}\"\nIDLE_THRESHOLD=\"${2:-30}\"\ncd \"$(dirname \"$0\")\"\nlast_pos=0\necho \"Monitoring: $CAST_FILE (idle threshold: ${IDLE_THRESHOLD}s)\"\nwhile [[ -f \"$CAST_FILE\" ]] || sleep 2; do\n [[ -f \"$CAST_FILE\" ]] || continue\n mtime=$(stat -f%m \"$CAST_FILE\" 2>/dev/null || stat -c%Y \"$CAST_FILE\")\n idle=$(($(date +%s) - mtime))\n size=$(stat -f%z \"$CAST_FILE\" 2>/dev/null || stat -c%s \"$CAST_FILE\")\n if (( idle >= IDLE_THRESHOLD && size > last_pos )); then\n chunk=\"chunks/chunk_$(date +%Y%m%d_%H%M%S).cast\"\n tail -c +$((last_pos + 1)) \"$CAST_FILE\" > \"$chunk\"\n zstd -3 --rm \"$chunk\"\n git add chunks/ && git commit -m \"chunk $(date +%H:%M)\" && git push\n last_pos=$size\n echo \"[$(date +%H:%M:%S)] Created: ${chunk}.zst\"\n fi\n sleep 5\ndone\nCHUNKER_EOF\nchmod +x \"$LOCAL_DIR/idle-chunker.sh\"\n\nlog \"\"\nlog \"=== Setup Complete ===\"\nlog \"Local directory: $LOCAL_DIR\"\nlog \"Idle chunker: $LOCAL_DIR/idle-chunker.sh\"\nlog \"\"\nlog \"To start recording:\"\nlog \" 1. asciinema rec /path/to/session.cast\"\nlog \" 2. $LOCAL_DIR/idle-chunker.sh /path/to/session.cast\"\nPREFLIGHT_EOF_2\n```\n\n## validate-system.sh\n\nComplete system validation with self-correction.\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF_3'\n#!/usr/bin/env bash\n# validate-system.sh - Full system validation\n#\n# Usage: validate-system.sh \u003crepo_url> [--fix]\n\nset -euo pipefail\n\nREPO_URL=\"${1:?Usage: validate-system.sh \u003crepo_url> [--fix]}\"\nFIX_MODE=\"${2:-}\"\nREPO_NAME=$(basename \"$REPO_URL\" .git)\nLOCAL_DIR=\"$HOME/asciinema_recordings/$REPO_NAME\"\n\nERRORS=()\nFIXES=()\n\nlog() { echo \"[validate] $*\"; }\nerror() { ERRORS+=(\"$*\"); log \"ERROR: $*\"; }\nfix() { FIXES+=(\"$*\"); }\n\nlog \"=== Validating Streaming Backup System ===\"\nlog \"Repository: $REPO_URL\"\nlog \"Local: $LOCAL_DIR\"\nlog \"\"\n\n# 1. Check tools\nlog \"--- Tools ---\"\nfor tool in asciinema zstd brotli git gh; do\n if command -v \"$tool\" &>/dev/null; then\n log \"$tool: OK\"\n else\n error \"$tool: MISSING\"\n fix \"brew install $tool\"\n fi\ndone\n\n# 2. Check orphan branch exists\nlog \"\"\nlog \"--- Remote Branch ---\"\nif git ls-remote --heads \"$REPO_URL\" gh-recordings 2>/dev/null | grep -q gh-recordings; then\n log \"gh-recordings: EXISTS\"\nelse\n error \"gh-recordings: NOT FOUND\"\n fix \"./setup-orphan-branch.sh $REPO_URL\"\nfi\n\n# 3. Check local clone\nlog \"\"\nlog \"--- Local Clone ---\"\nif [[ -d \"$LOCAL_DIR\" ]]; then\n log \"Directory: EXISTS\"\n\n # Check it's correct branch\n BRANCH=$(git -C \"$LOCAL_DIR\" branch --show-current 2>/dev/null || echo \"\")\n if [[ \"$BRANCH\" == \"gh-recordings\" ]]; then\n log \"Branch: OK (gh-recordings)\"\n else\n error \"Branch: WRONG ($BRANCH)\"\n fix \"cd $LOCAL_DIR && git checkout gh-recordings\"\n fi\n\n # Check workflow exists\n if [[ -f \"$LOCAL_DIR/.github/workflows/recompress.yml\" ]]; then\n log \"Workflow: EXISTS\"\n else\n error \"Workflow: MISSING\"\n fix \"Regenerate workflow\"\n fi\n\n # Check directories\n [[ -d \"$LOCAL_DIR/chunks\" ]] && log \"chunks/: EXISTS\" || error \"chunks/: MISSING\"\n [[ -d \"$LOCAL_DIR/archives\" ]] && log \"archives/: EXISTS\" || error \"archives/: MISSING\"\n\n # Check idle-chunker\n if [[ -x \"$LOCAL_DIR/idle-chunker.sh\" ]]; then\n log \"idle-chunker.sh: EXISTS\"\n else\n error \"idle-chunker.sh: MISSING\"\n fi\nelse\n error \"Local directory: NOT FOUND\"\n fix \"git clone --single-branch --branch gh-recordings --depth 1 $REPO_URL $LOCAL_DIR\"\nfi\n\n# 4. Test compression\nlog \"\"\nlog \"--- Compression Test ---\"\nTEST_DATA=\"test-$(date +%s)\"\nif echo \"$TEST_DATA\" | zstd -3 | zstd -d | grep -q \"$TEST_DATA\"; then\n log \"zstd round-trip: OK\"\nelse\n error \"zstd round-trip: FAILED\"\nfi\n\nif echo \"$TEST_DATA\" | brotli | brotli -d | grep -q \"$TEST_DATA\"; then\n log \"brotli round-trip: OK\"\nelse\n error \"brotli round-trip: FAILED\"\nfi\n\n# 5. Test zstd concatenation\nlog \"\"\nlog \"--- Concatenation Test ---\"\nTMP=$(mktemp -d)\necho \"chunk1\" | zstd -3 > \"$TMP/a.zst\"\necho \"chunk2\" | zstd -3 > \"$TMP/b.zst\"\ncat \"$TMP/a.zst\" \"$TMP/b.zst\" > \"$TMP/combined.zst\"\nRESULT=$(zstd -d -c \"$TMP/combined.zst\")\nrm -rf \"$TMP\"\n\nif [[ \"$RESULT\" ==

asciinema-streaming-backup Complete system for streaming asciinema recordings to GitHub with automatic brotli archival. Uses idle-detection for intelligent chunking, zstd for concatenatable streaming compression, and GitHub Actions for final brotli recompression. 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. When to Use This Skill Use this skill when: Setting up real-time backup of asciinema recordings to GitHub Configuring idl…

chunk1\\nchunk2' ]]; then\n log \"zstd concatenation: OK\"\nelse\n error \"zstd concatenation: FAILED\"\nfi\n\n# Summary\nlog \"\"\nlog \"=== Summary ===\"\n\nif [[ ${#ERRORS[@]} -eq 0 ]]; then\n log \"All checks passed\"\n exit 0\nfi\n\nlog \"Errors found: ${#ERRORS[@]}\"\nfor e in \"${ERRORS[@]}\"; do\n log \" - $e\"\ndone\n\nif [[ ${#FIXES[@]} -gt 0 ]]; then\n log \"\"\n log \"Suggested fixes:\"\n for f in \"${FIXES[@]}\"; do\n log \" $f\"\n done\nfi\n\nexit 1\nPREFLIGHT_EOF_3\n```\n\n## test-workflow.sh\n\nTest the complete workflow end-to-end.\n\n```bash\n/usr/bin/env bash \u003c\u003c 'VALIDATE_EOF'\n#!/usr/bin/env bash\n# test-workflow.sh - End-to-end workflow test\n#\n# Usage: test-workflow.sh \u003clocal_recordings_dir>\n#\n# Creates a test recording, generates chunks, and verifies round-trip\n\nset -euo pipefail\n\nLOCAL_DIR=\"${1:?Usage: test-workflow.sh \u003clocal_recordings_dir>}\"\n\nlog() { echo \"[test] $*\"; }\n\nlog \"=== Testing Streaming Backup Workflow ===\"\nlog \"Directory: $LOCAL_DIR\"\n\n# Create test .cast file\nTEST_CAST=$(mktemp).cast\nlog \"\"\nlog \"Creating test recording: $TEST_CAST\"\n\ncat > \"$TEST_CAST\" \u003c\u003c 'CAST_EOF'\n{\"version\": 2, \"width\": 80, \"height\": 24, \"timestamp\": 1234567890}\n[0.1, \"o\", \"$ echo hello\\r\\n\"]\n[0.2, \"o\", \"hello\\r\\n\"]\n[0.3, \"o\", \"$ echo world\\r\\n\"]\n[0.4, \"o\", \"world\\r\\n\"]\nCAST_EOF\n\nlog \"Test recording created ($(wc -l \u003c \"$TEST_CAST\") lines)\"\n\n# Simulate chunking\nlog \"\"\nlog \"Creating test chunks...\"\ncd \"$LOCAL_DIR\"\nmkdir -p chunks\n\n# Chunk 1: header + first command\nhead -3 \"$TEST_CAST\" > chunks/test_001.cast\nzstd -3 --rm chunks/test_001.cast\nlog \"Created: chunks/test_001.cast.zst\"\n\n# Chunk 2: remaining lines\ntail -n +4 \"$TEST_CAST\" > chunks/test_002.cast\nzstd -3 --rm chunks/test_002.cast\nlog \"Created: chunks/test_002.cast.zst\"\n\n# Test concatenation\nlog \"\"\nlog \"Testing concatenation...\"\ncat chunks/test_*.zst > /tmp/test_combined.zst\nzstd -d /tmp/test_combined.zst -o /tmp/test_combined.cast\n\n# Verify content\nif diff -q \"$TEST_CAST\" /tmp/test_combined.cast &>/dev/null; then\n log \"Concatenation: PASSED (content matches)\"\nelse\n log \"Concatenation: FAILED (content differs)\"\n diff \"$TEST_CAST\" /tmp/test_combined.cast\n exit 1\nfi\n\n# Test brotli recompression\nlog \"\"\nlog \"Testing brotli recompression...\"\nbrotli -9 /tmp/test_combined.cast -o /tmp/test_archive.cast.br\nbrotli -d /tmp/test_archive.cast.br -o /tmp/test_final.cast\n\nif diff -q \"$TEST_CAST\" /tmp/test_final.cast &>/dev/null; then\n log \"Brotli round-trip: PASSED\"\nelse\n log \"Brotli round-trip: FAILED\"\n exit 1\nfi\n\n# Size comparison\nORIG_SIZE=$(wc -c \u003c \"$TEST_CAST\")\nZSTD_SIZE=$(cat chunks/test_*.zst | wc -c)\nBR_SIZE=$(wc -c \u003c /tmp/test_archive.cast.br)\n\nlog \"\"\nlog \"=== Size Comparison ===\"\nlog \"Original: $ORIG_SIZE bytes\"\nlog \"zstd chunks: $ZSTD_SIZE bytes ($(echo \"scale=1; $ORIG_SIZE / $ZSTD_SIZE\" | bc)x)\"\nlog \"brotli: $BR_SIZE bytes ($(echo \"scale=1; $ORIG_SIZE / $BR_SIZE\" | bc)x)\"\n\n# Cleanup test files\nrm -f \"$TEST_CAST\" /tmp/test_*.cast /tmp/test_*.zst /tmp/test_*.br\nrm -f chunks/test_*.zst\n\nlog \"\"\nlog \"=== All Tests Passed ===\"\nVALIDATE_EOF\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15648,"content_sha256":"e916b984afa03d112ae662d4de4786e5bc8934bf36ad5b000ec9a5a25c55b2f9"},{"filename":"references/troubleshooting.md","content":"**Skill**: [asciinema-streaming-backup](../SKILL.md)\n\n# Troubleshooting Guide\n\nCommon issues and fixes for the streaming backup system.\n\n---\n\n## \"Cannot push to orphan branch\"\n\n**Cause**: Authentication or permissions issue.\n\n**Fix**:\n\n```bash\n# Check gh auth status\ngh auth status\n\n# Re-authenticate if needed\ngh auth login\n```\n\n## \"Chunks not being created\"\n\n**Cause**: Idle threshold not reached, or file not growing.\n\n**Fix**:\n\n- Verify recording is active: `tail -f $CAST_FILE`\n- Lower threshold: `IDLE_THRESHOLD=15`\n- Check file permissions\n\n## \"GitHub Action not triggering\"\n\n**Cause**: Workflow file missing or wrong branch filter.\n\n**Fix**:\n\n```bash\n# Verify workflow exists\ncat ~/asciinema_recordings/REPO/.github/workflows/recompress.yml\n\n# Check branch filter includes gh-recordings\ngrep -A2 \"branches:\" ~/asciinema_recordings/REPO/.github/workflows/recompress.yml\n```\n\n## \"Brotli archive empty or corrupted\"\n\n**Cause**: zstd chunks not concatenating properly (overlapping data).\n\n**Fix**: Ensure idle-chunker uses `last_chunk_pos` to avoid overlap:\n\n```bash\n/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF_2'\n# Check for overlaps - each chunk should be sequential\nfor f in chunks/*.zst; do\n zstd -d \"$f\" -c | head -1\ndone\nPREFLIGHT_EOF_2\n```\n\n## Validation Failure Quick Reference\n\n| Failure | Cause | Resolution |\n| ----------------------------------- | -------------------- | ------------------------------------------------------------- |\n| `asciinema MISSING` | Not installed | `brew install asciinema` (macOS) or `pipx install asciinema` |\n| `zstd MISSING` | Not installed | `brew install zstd` (macOS) or `apt install zstd` (Linux) |\n| `brotli MISSING` | Not installed | `brew install brotli` (macOS) or `apt install brotli` (Linux) |\n| `gh not authenticated` | No GitHub login | Run `gh auth login` and follow prompts |\n| `gh-recordings NOT found on remote` | Branch not pushed | Run orphan branch setup from Phase 4 of skill |\n| `local directory NOT found` | Clone failed | Check repo URL and permissions, re-run clone |\n| `recompress.yml MISSING` | Workflow not created | Re-run orphan branch setup to create workflow |\n| `workflow trigger failed` | No workflow_dispatch | Add `workflow_dispatch:` trigger to workflow |\n| `zstd concatenation FAILED` | zstd version issue | Update zstd: `brew upgrade zstd` |\n| `brotli round-trip FAILED` | brotli corrupted | Reinstall: `brew reinstall brotli` |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2808,"content_sha256":"74f088a84944c4ee34053e618b4504a347c87d35150ebee868985a272dfb3599"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"asciinema-streaming-backup","type":"text"}]},{"type":"paragraph","content":[{"text":"Complete system for streaming asciinema recordings to GitHub with automatic brotli archival. Uses idle-detection for intelligent chunking, zstd for concatenatable streaming compression, and GitHub Actions for final brotli recompression.","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Self-Evolving Skill","type":"text","marks":[{"type":"strong"}]},{"text":": 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.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up real-time backup of asciinema recordings to GitHub","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configuring idle-detection chunking for recordings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Creating orphan branch infrastructure for recording storage","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integrating GitHub Actions for brotli recompression","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Platform","type":"text","marks":[{"type":"strong"}]},{"text":": macOS, Linux ","type":"text"},{"text":"Isolation","type":"text","marks":[{"type":"strong"}]},{"text":": Uses Git orphan branch (separate history, cannot pollute main)","type":"text"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Architecture Overview","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"┌─────────────────┐ zstd chunks ┌─────────────────┐ Actions ┌─────────────────┐\n│ asciinema rec │ ──────────────────▶ │ GitHub Orphan │ ───────────────▶ │ brotli archive │\n│ + idle-chunker │ (concatenatable) │ gh-recordings │ │ (300x compress)│\n└─────────────────┘ └─────────────────┘ └─────────────────┘\n │ │\n │ Idle ≥30s triggers chunk │ Separate history\n ▼ │ Cannot PR to main\n ~/asciinema_recordings/ ▼\n └── repo-name/ .github/workflows/\n └── chunks/*.zst └── recompress.yml","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Requirements","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Component","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Required","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Installation","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Version","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"asciinema CLI","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brew install asciinema","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3.0+ (Rust)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"zstd","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brew install zstd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Any","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brotli","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brew install brotli","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Any","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"git","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pre-installed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2.20+","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"gh CLI","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brew install gh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Any","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fswatch","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optional","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brew install fswatch","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"For real-time","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow Phases","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 0: Preflight Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"Verify all tools installed, offer self-correction if missing. Run the preflight check script, then AskUserQuestion to offer installation for missing tools.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Setup Scripts","type":"text","marks":[{"type":"link","attrs":{"href":"./references/setup-scripts.md","title":null}}]},{"text":" for the complete ","type":"text"},{"text":"preflight-check.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" script.","type":"text"}]},{"type":"paragraph","content":[{"text":"Self-Correction","type":"text","marks":[{"type":"strong"}]},{"text":": If tools are missing, generate installation command and offer to run it.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: GitHub Account Detection","type":"text"}]},{"type":"paragraph","content":[{"text":"Detect available GitHub accounts from 5 sources (SSH config, SSH keys, gh CLI, mise env, git config) and let user choose which to use.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Account & Repository Detection","type":"text","marks":[{"type":"link","attrs":{"href":"./references/account-detection.md","title":null}}]},{"text":" for the detection script, scoring logic, and AskUserQuestion flow.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1.5: Current Repository Detection","type":"text"}]},{"type":"paragraph","content":[{"text":"Detect current git repository context (","type":"text"},{"text":"CURRENT_REPO_OWNER","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"CURRENT_REPO_NAME","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"DETECTED_FROM","type":"text","marks":[{"type":"code_inline"}]},{"text":") to provide intelligent defaults for Phase 2.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Account & Repository Detection","type":"text","marks":[{"type":"link","attrs":{"href":"./references/account-detection.md#phase-15-current-repository-detection","title":null}}]},{"text":" for the detection script.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Core Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"Gather essential configuration: repository URL (with auto-detection from Phase 1.5), recording directory, and branch name.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Configuration Reference","type":"text","marks":[{"type":"link","attrs":{"href":"./references/configuration-reference.md#phase-2-core-configuration","title":null}}]},{"text":" for all AskUserQuestion sequences and URL normalization logic.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Advanced Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"Allow customization of compression and behavior parameters:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Parameter","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Range","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Idle threshold","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"30s","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5-300s","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"zstd level","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1-22","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Brotli level","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"9","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1-11","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto-push","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes/No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Poll interval","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5s","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2s, 5s, 10s","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Configuration Reference","type":"text","marks":[{"type":"link","attrs":{"href":"./references/configuration-reference.md#phase-3-advanced-configuration","title":null}}]},{"text":" for all AskUserQuestion sequences.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Orphan Branch Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"Create or configure the orphan branch with GitHub Actions workflow. Checks for existing branch first and offers clone/reset/verify options.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key actions:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check if branch exists on remote via ","type":"text"},{"text":"git ls-remote","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If exists: AskUserQuestion for clone/reset/verify","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If new: Create orphan branch, add workflow + directory structure, push","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Setup Scripts","type":"text","marks":[{"type":"link","attrs":{"href":"./references/setup-scripts.md#setup-orphan-branchsh","title":null}}]},{"text":" for the complete ","type":"text"},{"text":"setup-orphan-branch.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" script. See ","type":"text"},{"text":"GitHub Workflow","type":"text","marks":[{"type":"link","attrs":{"href":"./references/github-workflow.md","title":null}}]},{"text":" for the full ","type":"text"},{"text":"recompress.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":" Actions workflow.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 5: Local Environment Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"Configure local directory and generate customized ","type":"text"},{"text":"idle-chunker.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" with user parameters embedded from Phase 3.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key actions:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clone orphan branch to ","type":"text"},{"text":"~/asciinema_recordings/\u003crepo>/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate ","type":"text"},{"text":"idle-chunker.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" with embedded configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Display configuration summary and usage instructions","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Setup Scripts","type":"text","marks":[{"type":"link","attrs":{"href":"./references/setup-scripts.md","title":null}}]},{"text":" for local setup scripts. See ","type":"text"},{"text":"Idle Chunker","type":"text","marks":[{"type":"link","attrs":{"href":"./references/idle-chunker.md","title":null}}]},{"text":" for the complete chunker implementation.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 6: Autonomous Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"Claude executes 8 tests autonomously, displaying formatted results. Only 2 tests require user action (recording test, chunker live test).","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test Category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Count","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Autonomous?","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tool preflight","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Compression round-trip","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Repository validation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GitHub Actions trigger","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recording test","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No (USER)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Chunker live test","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No (USER)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Autonomous Validation","type":"text","marks":[{"type":"link","attrs":{"href":"./references/autonomous-validation.md","title":null}}]},{"text":" for the complete validation script, user-required test flows, and troubleshooting table.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"First-Time Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/usr/bin/env bash \u003c\u003c 'PREFLIGHT_EOF'\n# 1. Check requirements\nfor tool in asciinema zstd brotli git gh; do\n command -v \"$tool\" &>/dev/null && echo \"$tool: OK\" || echo \"$tool: MISSING\"\ndone\n\n# 2. Create orphan branch (replace with your repo)\nREPO=\"[email protected]:YOUR/REPO.git\"\n./setup-orphan-branch.sh \"$REPO\"\n\n# 3. Validate setup\n./validate-setup.sh \"$HOME/asciinema_recordings/REPO\"\nPREFLIGHT_EOF","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Recording Session","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/usr/bin/env bash \u003c\u003c 'SKILL_SCRIPT_EOF'\n# Terminal 1: Start recording\nWORKSPACE=$(basename \"$PWD\")\nasciinema rec $PWD/tmp/${WORKSPACE}_$(date +%Y-%m-%d_%H-%M).cast\n\n# Terminal 2: Start idle-chunker\n~/asciinema_recordings/REPO/idle-chunker.sh $PWD/tmp/${WORKSPACE}_*.cast\nSKILL_SCRIPT_EOF","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key Design Decisions","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Decision","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rationale","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"zstd for streaming","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Supports frame concatenation (brotli doesn't)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"brotli for archival","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Best compression ratio (~300x for .cast files)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Orphan branch","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Complete isolation, can't pollute main history","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Idle-based chunking","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Semantic breakpoints, not mid-output splits","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Shallow clone","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Minimal disk usage, can't accidentally access main","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"30s idle threshold","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Balances chunk frequency vs semantic completeness","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"Troubleshooting Guide","type":"text","marks":[{"type":"link","attrs":{"href":"./references/troubleshooting.md","title":null}}]},{"text":" for common issues and fixes.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Post-Change Checklist","type":"text"}]},{"type":"paragraph","content":[{"text":"After modifying this skill:","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Documentation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Account & Repository Detection","type":"text","marks":[{"type":"link","attrs":{"href":"./references/account-detection.md","title":null}}]},{"text":" - GitHub account detection + repo context","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configuration Reference","type":"text","marks":[{"type":"link","attrs":{"href":"./references/configuration-reference.md","title":null}}]},{"text":" - AskUserQuestion sequences + task templates","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Idle Chunker Script","type":"text","marks":[{"type":"link","attrs":{"href":"./references/idle-chunker.md","title":null}}]},{"text":" - Complete chunker implementation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub Workflow","type":"text","marks":[{"type":"link","attrs":{"href":"./references/github-workflow.md","title":null}}]},{"text":" - Full Actions workflow","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setup Scripts","type":"text","marks":[{"type":"link","attrs":{"href":"./references/setup-scripts.md","title":null}}]},{"text":" - All setup and validation scripts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Autonomous Validation","type":"text","marks":[{"type":"link","attrs":{"href":"./references/autonomous-validation.md","title":null}}]},{"text":" - Validation script and user-required tests","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Troubleshooting Guide","type":"text","marks":[{"type":"link","attrs":{"href":"./references/troubleshooting.md","title":null}}]},{"text":" - Common issues and fixes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"asciinema 3.0 Docs","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.asciinema.org/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"zstd Frame Format","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/facebook/zstd","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Git Orphan Branches","type":"text","marks":[{"type":"link","attrs":{"href":"https://graphite.dev/guides/git-orphan-branches","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Post-Execution Reflection","type":"text"}]},{"type":"paragraph","content":[{"text":"After this skill completes, reflect before closing the task:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Locate yourself.","type":"text","marks":[{"type":"strong"}]},{"text":" — Find this SKILL.md's canonical path before editing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What failed?","type":"text","marks":[{"type":"strong"}]},{"text":" — Fix the instruction that caused it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What worked better than expected?","type":"text","marks":[{"type":"strong"}]},{"text":" — Promote to recommended practice.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What drifted?","type":"text","marks":[{"type":"strong"}]},{"text":" — Fix any script, reference, or dependency that no longer matches reality.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Log it.","type":"text","marks":[{"type":"strong"}]},{"text":" — Evolution-log entry with trigger, fix, and evidence.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Do NOT defer. The next invocation inherits whatever you leave behind.","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"asciinema-streaming-backup","author":"@skillopedia","source":{"stars":49,"repo_name":"cc-skills","origin_url":"https://github.com/terrylica/cc-skills/blob/HEAD/plugins/asciinema-tools/skills/asciinema-streaming-backup/SKILL.md","repo_owner":"terrylica","body_sha256":"f0bcb03ab0d6d092917bac0344b8a8a4a4211315e652e6bda34e59ec244fc522","cluster_key":"b8081ca8e50e7e291e7b4bf164ea4645aa68f07c64a160e269ecc1360913fcae","clean_bundle":{"format":"clean-skill-bundle-v1","source":"terrylica/cc-skills/plugins/asciinema-tools/skills/asciinema-streaming-backup/SKILL.md","attachments":[{"id":"53eedd4b-ee41-5d4b-9eb9-bf462ec33c63","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/53eedd4b-ee41-5d4b-9eb9-bf462ec33c63/attachment.md","path":"references/account-detection.md","size":5727,"sha256":"da337cc7a6db48bc09bfa503902c5b5661340d1b621f264db0b44f50e0d7e617","contentType":"text/markdown; charset=utf-8"},{"id":"8ef3660e-b440-521e-a4ad-300971cc62b2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8ef3660e-b440-521e-a4ad-300971cc62b2/attachment.md","path":"references/autonomous-validation.md","size":18189,"sha256":"bf6f65358e0466787b0e9073d213ec24d3f1aeae2a585a2e5774e09c4a7fbd84","contentType":"text/markdown; charset=utf-8"},{"id":"ba8bf944-d8b4-55ad-b2f4-18d8ea741a74","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ba8bf944-d8b4-55ad-b2f4-18d8ea741a74/attachment.md","path":"references/configuration-reference.md","size":7893,"sha256":"f20662cf7c2b9ba10b74617a161882a123d12714e7ee8f0742559f47ead51d52","contentType":"text/markdown; charset=utf-8"},{"id":"a6195319-b29e-51b6-8b71-7805871c7d81","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a6195319-b29e-51b6-8b71-7805871c7d81/attachment.md","path":"references/evolution-log.md","size":712,"sha256":"b209c4f00257bf664b150cd48001f01572f7960226ab9563c760fc772e4efcf5","contentType":"text/markdown; charset=utf-8"},{"id":"f5bac536-99c2-5e48-9d4c-c6167b8caee5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f5bac536-99c2-5e48-9d4c-c6167b8caee5/attachment.md","path":"references/github-workflow.md","size":6211,"sha256":"fc7823937d82a1b25dbb436f1c3bc46f2f19280d567ac601d6f34b5551aae9b1","contentType":"text/markdown; charset=utf-8"},{"id":"32a95e21-f412-582c-a265-6e878ccc2fc2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/32a95e21-f412-582c-a265-6e878ccc2fc2/attachment.md","path":"references/idle-chunker.md","size":6865,"sha256":"f959de29938de926538fef87b70e350e8979dc28178926f790ead3a3e5667931","contentType":"text/markdown; charset=utf-8"},{"id":"cd750e4a-6702-5631-9e65-b07626b5a6e8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cd750e4a-6702-5631-9e65-b07626b5a6e8/attachment.md","path":"references/setup-scripts.md","size":15648,"sha256":"e916b984afa03d112ae662d4de4786e5bc8934bf36ad5b000ec9a5a25c55b2f9","contentType":"text/markdown; charset=utf-8"},{"id":"99de9543-9bdc-512c-8c29-66644e80a91d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/99de9543-9bdc-512c-8c29-66644e80a91d/attachment.md","path":"references/troubleshooting.md","size":2808,"sha256":"74f088a84944c4ee34053e618b4504a347c87d35150ebee868985a272dfb3599","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"d59e47ca786262a85b676f51d2786c3ea9634b9bccff043ac472ac5ee20ea29b","attachment_count":8,"text_attachments":8,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"plugins/asciinema-tools/skills/asciinema-streaming-backup/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"integrations-apis","import_tag":"clean-skills-v1","description":"Real-time asciinema backup to GitHub orphan branch. TRIGGERS - streaming backup, asciinema backup, session backup, recording backup.","allowed-tools":"Read, Bash, Glob, Write, Edit, AskUserQuestion"}},"renderedAt":1782981443894}

asciinema-streaming-backup Complete system for streaming asciinema recordings to GitHub with automatic brotli archival. Uses idle-detection for intelligent chunking, zstd for concatenatable streaming compression, and GitHub Actions for final brotli recompression. 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. When to Use This Skill Use this skill when: Setting up real-time backup of asciinema recordings to GitHub Configuring idl…