mcpField
B
bootstrap

bootstrap

github.com/terrylicaAgent 工作流292 次安装1 天前更新
代码仓库

关于 bootstrap

用于生成在 Claude Code 外启动 asciinema 录制会话的引导脚本。它会检查环境与 GitHub 仓库设置,并依赖 launchd 守护进程处理分块。

主要功能

  1. 01检查 asciinema、zstd 和 git
  2. 02检测 Git 仓库及分支状态
  3. 03选择或创建录制仓库与孤儿分支
  4. 04生成引导脚本
  5. 05提供启动与故障排查说明

适用场景

  1. 01为 Claude Code 会话配置终端录制
  2. 02将录制保存到指定 GitHub 仓库
  3. 03检查录制所需的守护进程和工具

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/terrylica/cc-skills --skill bootstrap

安装后在 Claude Code 中输入/bootstrap确认已加载。

附带文件references/evolution-log.md

详细介绍

/asciinema-tools:bootstrap

生成一个在 Claude Code 外部运行、用于启动录制会话的引导脚本。

重要:分块由 launchd 守护进程处理。如果尚未运行 /asciinema-tools:daemon-setup,请先运行。

自我演进的技能:此技能会在使用过程中不断改进。如果说明有误、参数发生偏移,或需要采用变通方法,请立即修复此文件,不要拖延。仅针对真实且可复现的问题进行更新。

架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                        DAEMON-BASED RECORDING WORKFLOW                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  1. ONE-TIME SETUP (if not done):                                           │
│     /asciinema-tools:daemon-setup                                           │
│     → Configures launchd daemon with Keychain credentials                   │
│                                                                             │
│  2. GENERATE BOOTSTRAP (in Claude Code):                                    │
│     /asciinema-tools:bootstrap                                              │
│     → Generates tmp/bootstrap-claude-session.sh                             │
│                                                                             │
│  3. EXIT CLAUDE and RUN BOOTSTRAP:                                          │
│     $ ./tmp/bootstrap-claude-session.sh    ← NOT source!                    │
│     → Writes config for daemon                                              │
│     → Starts asciinema recording                                            │
│                                                                             │
│  4. WORK IN RECORDING:                                                      │
│     $ claude                                                                │
│     → Daemon automatically pushes chunks to GitHub                          │
│                                                                             │
│  5. EXIT (two times):                                                       │
│     Ctrl+D (exit Claude) → exit (end recording)                             │
│     → Daemon pushes final chunk                                             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

参数

参数说明
-r, --repoGitHub 仓库(例如:owner/repo)
-b, --branch孤儿分支名称(默认:asciinema-recordings)
--setup-orphan强制创建孤儿分支
-y, --yes跳过确认提示

执行

阶段 0:预检
/usr/bin/env bash << 'PREFLIGHT_EOF'
MISSING=()
for tool in asciinema zstd git; do
  command -v "$tool" &>/dev/null || MISSING+=("$tool")
done

if [[ ${#MISSING[@]} -gt 0 ]]; then
  echo "MISSING: ${MISSING[*]}"
  exit 1
fi

echo "PREFLIGHT: OK"
asciinema --version | head -1

# Check daemon status
if launchctl list 2>/dev/null | grep -q "asciinema-chunker"; then
  echo "DAEMON: RUNNING"
else
  echo "DAEMON: NOT_RUNNING"
fi
PREFLIGHT_EOF

如果 DAEMON:NOT_RUNNING,请使用 AskUserQuestion:

Question: "The chunker daemon is not running. Chunks won't be pushed to GitHub without it."
Header: "Daemon"
Options:
  - label: "Run daemon setup (Recommended)"
    description: "Switch to /asciinema-tools:daemon-setup to configure the daemon"
  - label: "Continue anyway"
    description: "Generate bootstrap script without daemon (local recording only)"
阶段 1:检测仓库上下文

必须:在 AskUserQuestion 之前运行,以自动填充选项。

/usr/bin/env bash << 'DETECT_CONTEXT_EOF'
IN_GIT_REPO="false"
CURRENT_REPO_URL=""
CURRENT_REPO_OWNER=""
CURRENT_REPO_NAME=""
ORPHAN_BRANCH_EXISTS="false"
LOCAL_CLONE_EXISTS="false"
ORPHAN_BRANCH="asciinema-recordings"

if git rev-parse --git-dir &>/dev/null 2>&1; then
  IN_GIT_REPO="true"

  if git remote get-url origin &>/dev/null 2>&1; then
    CURRENT_REPO_URL=$(git remote get-url origin)
  elif [[ -n "$(git remote)" ]]; then
    REMOTE=$(git remote | head -1)
    CURRENT_REPO_URL=$(git remote get-url "$REMOTE")
  fi

  if [[ -n "$CURRENT_REPO_URL" ]]; then
    if [[ "$CURRENT_REPO_URL" =~ github\.com[:/]([^/]+)/([^/.]+) ]]; then
      CURRENT_REPO_OWNER="${BASH_REMATCH[1]}"
      CURRENT_REPO_NAME="${BASH_REMATCH[2]%.git}"
    fi

    if git ls-remote --heads "$CURRENT_REPO_URL" "$ORPHAN_BRANCH" 2>/dev/null | grep -q "$ORPHAN_BRANCH"; then
      ORPHAN_BRANCH_EXISTS="true"
    fi

    LOCAL_CLONE_PATH="$HOME/asciinema_recordings/$CURRENT_REPO_NAME"
    if [[ -d "$LOCAL_CLONE_PATH/.git" ]]; then
      LOCAL_CLONE_EXISTS="true"
    fi
  fi
fi

echo "IN_GIT_REPO=$IN_GIT_REPO"
echo "CURRENT_REPO_URL=$CURRENT_REPO_URL"
echo "CURRENT_REPO_OWNER=$CURRENT_REPO_OWNER"
echo "CURRENT_REPO_NAME=$CURRENT_REPO_NAME"
echo "ORPHAN_BRANCH_EXISTS=$ORPHAN_BRANCH_EXISTS"
echo "LOCAL_CLONE_EXISTS=$LOCAL_CLONE_EXISTS"
DETECT_CONTEXT_EOF
阶段 2:选择仓库(必须使用 AskUserQuestion)

根据检测结果:

如果 IN_GIT_REPO=true,ORPHAN_BRANCH_EXISTS=true:

Question: "Orphan branch found in {repo}. Use it?"
Header: "Destination"
Options:
  - label: "Use existing (Recommended)"
    description: "Branch 'asciinema-recordings' already configured in {repo}"
  - label: "Use different repository"
    description: "Store recordings in a different repo"

如果 IN_GIT_REPO=true,ORPHAN_BRANCH_EXISTS=false:

Question: "No orphan branch in {repo}. Create one?"
Header: "Setup"
Options:
  - label: "Create orphan branch (Recommended)"
    description: "Initialize with GitHub Actions workflow for brotli"
  - label: "Use different repository"
    description: "Store recordings elsewhere"

如果 IN_GIT_REPO=false:

Question: "Not in a git repo. Where to store recordings?"
Header: "Destination"
Options:
  - label: "Dedicated recordings repo"
    description: "Use {owner}/asciinema-recordings"
  - label: "Enter repository"
    description: "Specify owner/repo manually"
阶段 3:创建孤儿分支(如有需要)

先清除 SSH 缓存,再创建孤儿分支:

/usr/bin/env bash << 'CREATE_ORPHAN_EOF'
REPO_URL="${1:?}"
BRANCH="${2:-asciinema-recordings}"
LOCAL_PATH="$HOME/asciinema_recordings/$(basename "$REPO_URL" .git)"

# Clear SSH caches first
rm -f ~/.ssh/control-* 2>/dev/null || true
ssh -O exit [email protected] 2>/dev/null || true

# Get GitHub token for HTTPS clone (prefer env var to avoid process spawning)
GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-$(gh auth token 2>/dev/null || echo "")}}"
if [[ -n "$GH_TOKEN" ]]; then
  # Parse owner/repo
  if [[ "$REPO_URL" =~ github\.com[:/]([^/]+)/([^/.]+) ]]; then
    OWNER_REPO="${BASH_REMATCH[1]}/${BASH_REMATCH[2]}"
    AUTH_URL="https://${GH_TOKEN}@github.com/${OWNER_REPO}.git"
    CLEAN_URL="https://github.com/${OWNER_REPO}.git"
  else
    AUTH_URL="$REPO_URL"
    CLEAN_URL="$REPO_URL"
  fi
else
  AUTH_URL="$REPO_URL"
  CLEAN_URL="$REPO_URL"
fi

# Clone and create orphan
TEMP_DIR=$(mktemp -d)
git clone "$AUTH_URL" "$TEMP_DIR/repo"
cd "$TEMP_DIR/repo"

# Create orphan branch
git checkout --orphan "$BRANCH"
git reset --hard
git commit --allow-empty -m "Initialize asciinema recordings"
git push origin "$BRANCH"

# Cleanup temp
rm -rf "$TEMP_DIR"

# Clone orphan branch locally
mkdir -p "$(dirname "$LOCAL_PATH")"
git clone --single-branch --branch "$BRANCH" --depth 1 "$AUTH_URL" "$LOCAL_PATH"

# Strip token from remote
git -C "$LOCAL_PATH" remote set-url origin "$CLEAN_URL"

mkdir -p "$LOCAL_PATH/chunks"

echo "ORPHAN_CREATED: $LOCAL_PATH"
CREATE_ORPHAN_EOF
阶段 4:生成引导脚本

生成简化版引导脚本(分块由守护进程处理):

/usr/bin/env bash << 'GENERATE_SCRIPT_EOF'
REPO_URL="${1:?}"
BRANCH="${2:-asciinema-recordings}"
LOCAL_REPO="${3:-$HOME/asciinema_recordings/$(basename "$REPO_URL" .git)}"
OUTPUT_FILE="${4:-$PWD/tmp/bootstrap-claude-session.sh}"

mkdir -p "$(dirname "$OUTPUT_FILE")"

cat > "$OUTPUT_FILE" << 'SCRIPT_EOF'
#!/usr/bin/env bash
# bootstrap-claude-session.sh - Start asciinema recording session
# Generated by /asciinema-tools:bootstrap
# Chunking handled by launchd daemon

if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
    echo "ERROR: Do not source this script. Run directly: ./${BASH_SOURCE[0]##*/}"
    return 1
fi

set -uo pipefail

SCRIPT_EOF

# Append configuration
cat >> "$OUTPUT_FILE" << SCRIPT_CONFIG
REPO_URL="$REPO_URL"
BRANCH="$BRANCH"
LOCAL_REPO="$LOCAL_REPO"
SCRIPT_CONFIG

cat >> "$OUTPUT_FILE" << 'SCRIPT_BODY'
WORKSPACE="$(basename "$PWD")"
DATETIME="$(date +%Y-%m-%d_%H-%M)"
ASCIINEMA_DIR="$HOME/.asciinema"
ACTIVE_DIR="$ASCIINEMA_DIR/active"
CAST_FILE="$ACTIVE_DIR/${WORKSPACE}_${DATETIME}.cast"
CONFIG_FILE="${CAST_FILE%.cast}.json"

echo "╔════════════════════════════════════════════════════════════════╗"
echo "║  asciinema Recording Session                                   ║"
echo "╠════════════════════════════════════════════════════════════════╣"

# Check daemon
if ! launchctl list 2>/dev/null | grep -q "asciinema-chunker"; then
    echo "║  WARNING: Daemon not running! Run /asciinema-tools:daemon-start║"
    echo "╠════════════════════════════════════════════════════════════════╣"
fi

# Clear SSH caches
rm -f ~/.ssh/control-* 2>/dev/null || true
ssh -O exit [email protected] 2>/dev/null || true

# Setup
mkdir -p "$ACTIVE_DIR" "$LOCAL_REPO/chunks"

# Write config for daemon
cat > "$CONFIG_FILE" <<EOF
{
    "repo_url": "$REPO_URL",
    "branch": "$BRANCH",
    "local_repo": "$LOCAL_REPO",
    "workspace": "$WORKSPACE",
    "started": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF

cleanup() {
    echo ""
    echo "Recording ended. Daemon will push final chunk."
    echo "Check status: /asciinema-tools:daemon-status"
}
trap cleanup EXIT

echo "║  Recording to: $CAST_FILE"
echo "║  Run 'claude' inside this session. Exit twice to end.         ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""

asciinema rec --stdin "$CAST_FILE"
SCRIPT_BODY

chmod +x "$OUTPUT_FILE"
echo "SCRIPT_GENERATED: $OUTPUT_FILE"
GENERATE_SCRIPT_EOF
阶段 5:显示说明
## Bootstrap Complete

Script generated at: `tmp/bootstrap-claude-session.sh`

### Quick Start

1. Exit Claude Code: `exit` or Ctrl+D
2. Run bootstrap: `./tmp/bootstrap-claude-session.sh` ← NOT source!
3. Inside recording, run: `claude`
4. Work normally - daemon pushes chunks to GitHub
5. Exit twice: Ctrl+D (Claude) → `exit` (recording)

### What Happens

- asciinema records to `~/.asciinema/active/{workspace}_{datetime}.cast`
- Daemon monitors for idle periods
- On idle, chunk is compressed and pushed via Keychain PAT
- Daemon sends Pushover notification on failures
- Recording is decoupled from Claude Code session

### Daemon Commands

| Command                          | Description         |
| -------------------------------- | ------------------- |
| `/asciinema-tools:daemon-status` | Check daemon health |
| `/asciinema-tools:daemon-logs`   | View logs           |
| `/asciinema-tools:daemon-start`  | Start daemon        |
| `/asciinema-tools:daemon-stop`   | Stop daemon         |

跳过逻辑

  • 如果提供了 -r 和 -b -> 跳过仓库选择
  • 如果提供了 -y -> 跳过所有确认
  • 如果提供了 --setup-orphan -> 强制创建孤儿分支

故障排除

问题原因解决方案
未找到 asciinema未安装 asciinemabrew install asciinema
未找到 zstd未安装 zstdbrew install zstd
守护进程未运行守护进程未启动运行 /asciinema-tools:daemon-start
Git 克隆失败身份验证问题或 URL 错误运行 gh auth login
孤儿分支错误分支已存在移除 --setup-orphan 标志
脚本不可执行权限问题运行 chmod +x tmp/bootstrap-*.sh
源码错误脚本被作为源码加载直接执行:./script.sh

执行后反思

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

  1. 确认位置。——在编辑前查找此 SKILL.md 的规范路径(使用 Glob 搜索此技能名称)。所有修正都应针对此文件及其同级的 references/,绝不要修改其他文档。
  2. 出了什么问题?——修复导致问题的说明。如果问题可能再次发生,将其作为反模式添加。
  3. 哪些方面超出预期?——将其提升为推荐做法,并记录原因。
  4. 哪些内容发生了偏移?——任何与实际情况不再一致的脚本、参考资料或外部依赖,都应立即修正。
  5. 记录下来。——每项变更都要在演进日志中记录触发原因、修复内容和依据。

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

评论

支持 Markdown