GitHub Issue 驱动的自动解决脚本,基于 Claude Code CLI。
CC 自主阅读代码、理解上下文、修改文件、运行测试、自审查后提 PR。人类只验收最终结果。
灵感来源于 @duwei 的自动化工作流理念。
GitHub Issues
│
▼
┌──────────┐ ┌───────────┐ ┌───────────┐ ┌────────┐ ┌──────┐
│ 拉取 │───▶│ 建独立分支 │───▶│ CC 解决 │───▶│ CC 验收 │───▶│ 提PR │
│ Open │ │ ccloop/ │ │ 读码+修改 │ │ 审查diff│ │ 等合并│
│ Issues │ │ issue-{n} │ │ 运行测试 │ │ 通过/否 │ │ │
└──────────┘ └───────────┘ └─────┬─────┘ └───┬────┘ └──────┘
│ │
失败/重试 未通过→重试
│
切回main,下一个issue
| 阶段 | 职责 | 说明 |
|---|---|---|
| 0. 建分支 | 从 main 创建 ccloop/issue-{n} |
每个 issue 独立分支,互不影响 |
| 1. 解决 | CC 在分支上阅读代码 → 分析 → 修改 → 测试 | 精准编辑引导,锚定上下文再修改 |
| 2. 验收 | CC 审查 diff → 5 项检查清单 → 通过或不通过 | 不通过则反馈原因,重试 |
| 3. 提交等合并 | 验收通过 → commit → push → 创建 PR → 等人工审批 | PR 等待合并,不自动 merge |
Issue 类型:
- research — CC 输出调研报告,直接评论到 issue
- 要求 WebSearch 搜索(至少 5 次)、结构化对比表格、趋势预测、3000 字以上
- 输出到
RESEARCH.md文件
- feature/fix — CC 修改代码,通过自审查后提 PR
已处理(标记 ccloop-done)的 issue,如果用户后续在 issue 下添加新评论,ccloop 会:
- 检测到新评论(基于上次处理时间戳)
- 自动移除
ccloop-done标签 - 重新执行完整处理流程
- Python 3.10+
- Claude Code CLI 已安装并认证
- GitHub Token(需 repo 权限)
git clone https://github.com/yuzebin/ccloop.git
cd ccloop
pip install -r requirements.txt只需 1 个环境变量 + 命令行传入仓库名:
export GITHUB_TOKEN="ghp_xxxx"
python ccloop.py owner/repo可选环境变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
MAX_RETRIES |
3 |
每个 issue 重试次数 |
WORK_DIR |
ccloop_repos/ |
本地工作目录(集中管理) |
CLAUDE_MODEL |
CC默认 | 指定模型 ID |
HTTP_PROXY |
空 | HTTP 代理(如 http://127.0.0.1:1084) |
MAX_BODY_CHARS |
3000 |
Issue 正文截断上限 |
MAX_PROMPT_CHARS |
50000 |
Prompt 总长度熔断阈值 |
SANITIZE_ENV |
true |
清洗 CC 子进程的敏感环境变量 |
CC_ALLOWED_TOOLS |
Read,Edit,Write,Bash,Glob,Grep,WebSearch,WebFetch |
CC 工具白名单 |
CC_DISALLOWED_TOOLS |
Agent,NotebookEdit |
CC 工具黑名单 |
USE_SANDBOX |
false |
启用 macOS sandbox-exec 沙箱 |
SANDBOX_PROFILE |
sandbox/ccloop_seatbelt.sb |
Seatbelt profile 路径 |
可选命令行参数:
| 参数 | 说明 |
|---|---|
--dry-run |
CC 正常调用,不推送/建PR/评论 |
--label bug |
只处理特定 label |
--work-dir /path |
工作目录(clone + 运行 CC) |
--clean |
清理工作目录后退出(不执行其他操作) |
GitHub Token 需要以下权限:
| 权限 | 用途 |
|---|---|
| Contents: Read & Write | clone 仓库、push 分支、创建 PR |
| Issues: Read & Write | 读取 issue、评论、添加/移除 label |
Classic PAT(ghp_ 前缀)勾选 repo scope 即可。Fine-grained PAT 需要在 Repository access 中添加目标仓库。
# 监控单个仓库
python ccloop.py yourname/yourrepo
# dry-run 预览
python ccloop.py yourname/yourrepo --dry-run
# 只处理 bug label
python ccloop.py yourname/yourrepo --label bug
# 指定工作目录
python ccloop.py yourname/yourrepo --work-dir /data/repos多仓库并行监控(每个 repo 独立锁,互不干扰):
# crontab -e
*/30 * * * * cd /path/to/ccloop && python ccloop.py team/project-a >> /var/log/ccloop_a.log 2>&1
*/30 * * * * cd /path/to/ccloop && python ccloop.py team/project-b >> /var/log/ccloop_b.log 2>&1
*/15 * * * * cd /path/to/ccloop && python ccloop.py team/hotfix-repo >> /var/log/ccloop_hotfix.log 2>&1--dry-run 模式用于调试和安全预览:
| 操作 | dry-run 行为 |
|---|---|
| CC 调用 | 正常执行(可测试 prompt 质量) |
| git push | 跳过,打印 [DRY-RUN] 日志 |
| 创建 PR | 跳过,返回假 PR 编号 |
| issue 评论 | 跳过,打印到日志 |
| 标记已处理 | 跳过 |
| 统计记录 | 正常记录 |
ccloop/
├── ccloop.py # CLI 入口(argparse + --dry-run)
├── ccloop_lib/ # 核心包
│ ├── config.py # 环境变量 + 安全配置 + 日志初始化
│ ├── stats.py # 统计持久化(JSON)
│ ├── lock.py # fcntl 文件锁
│ ├── claude.py # CC CLI 交互 + 环境清洗 + 沙箱 + JSON 容错解析
│ ├── prompts.py # 3 个 prompt 构建器 + 截断 + 文件树读取
│ ├── git_ops.py # Git 操作(支持 dry-run)
│ ├── github_ops.py # GitHub 操作(懒初始化 + dry-run)
│ └── solver.py # CCLoop 核心编排
├── sandbox/ # macOS Seatbelt 沙箱配置
│ └── ccloop_seatbelt.sb
├── tests/ # 184 个测试, 100% 覆盖率
│ ├── test_solver_flow.py # 核心流程 mock 集成
│ ├── test_solver.py # Solver 单元
│ ├── test_git_ops.py # Git 操作
│ ├── test_github_ops.py # GitHub live+dry-run
│ ├── test_claude.py # CC CLI + 安全 + 沙箱
│ ├── test_prompts.py # Prompt + 截断 + JSON 解析
│ ├── test_prompts_extra.py # get_repo_tree
│ ├── test_edge_cases.py # 边界情况
│ ├── test_config.py # 配置推导
│ ├── test_stats.py # 统计模块
│ ├── test_lock.py # 文件锁
│ ├── test_cli.py # CLI 入口
│ └── conftest.py # 共享 fixtures
├── Dockerfile # Docker 容器化
├── docker-compose.yml # Docker Compose 编排
├── .env.example # 环境变量模板
├── requirements.txt # 运行依赖
├── requirements-dev.txt # 开发依赖(pytest)
├── README.md
└── .gitignore
ccloop.py (CLI)
└── solver.CCLoop
├── stats.Stats ← 统计
├── claude.call_claude ← CC CLI
├── prompts.* ← Prompt 构建
├── git_ops.GitOps ← Git 操作
└── github_ops.GitHubOps ← GitHub API
四层纵深防御,从应用层到系统层逐级加强:
| 层级 | 措施 | 默认 | 说明 |
|---|---|---|---|
| L1 | 工具白/黑名单 | 开启 | --allowedTools 限制 CC 可用工具,--disallowedTools 阻止 Agent 等危险工具 |
| L2 | 环境变量清洗 | 开启 | CC 子进程剥离 GITHUB_TOKEN、AWS_*、SSH_AUTH_SOCK 等 12 个敏感 key |
| L2 | Prompt 长度熔断 | 开启 | Issue 正文截断(3000 字)+ 总 prompt 限制(50000 字)防止超限循环 |
| L3 | macOS 沙箱 | 关闭 | USE_SANDBOX=true 启用 seatbelt profile,限制文件系统和网络 |
| L4 | Docker 容器 | 关闭 | cap_drop: ALL、read_only、资源限制,完整隔离 |
CC 以 --dangerously-skip-permissions 运行(非交互模式必须),但 --allowedTools 限制其只能使用白名单工具。Prompt 长度熔断防止超长 issue body 导致无限重试。
USE_SANDBOX=true python ccloop.py owner/repo沙箱 profile(sandbox/ccloop_seatbelt.sb)限制 CC 子进程只能:
- 读写工作目录
- 执行 claude 和 git
- 出站连接 github.com
cp .env.example .env # 编辑填入 GITHUB_TOKEN
docker compose up -d
docker compose logs -f ccloop容器安全配置:cap_drop: ALL、no-new-privileges、mem_limit: 2g、cpus: 2。
| 措施 | 说明 |
|---|---|
| CC 无 git 权限 | prompt 约束不执行 git 命令,脚本控制所有 git 操作 |
| token 安全 | 使用 x-access-token 格式 + GIT_TERMINAL_PROMPT=0 |
| 分支隔离 | 每次尝试独立分支 ccloop/issue-{n}-v{attempt} |
| 原子重置 | 重试前 reset --hard + clean -fd,保证干净状态 |
| 文件锁 | fcntl.flock 内核级互斥,防并发 |
| 懒初始化 | GitHub API 首次访问才连接,构造时不发网络请求 |
运行日志按 issue 归档,统计持久化到 stats.json:
.ccloop/logs/
├── ccloop.log # 主日志
├── stats.json # 累计统计
└── issue_42/
├── prompt_143052.md # 发送给 CC 的 prompt
├── cc_output_143052.md # CC 完整输出 + 耗时
├── prompt_143158.md # 自审查 prompt
└── cc_output_143158.md # 自审查输出
统计指标:处理数、解决数、成功率、重试率、CC 调用次数、CC 耗时,按 issue 类型分类。
- 每个 issue 在独立分支
ccloop/issue-{n}上开发 - CC 解决 + 自审查验收通过后,自动 commit 并创建 PR
- PR 描述包含:issue 链接、修改文件列表、方案说明、耗时统计
- PR 等待人工审批合并(不自动 merge)
- 如验收失败或重试耗尽,issue 评论包含完整错误记录 + AI 根因分析
- 灵感来源于 @duwei 的自动化工作流理念
- 编辑格式优化借鉴 oh-my-pi 的 hashline 思路——锚定上下文再精确修改
- Prompt 工程参考 Claude Code 的工具使用最佳实践
MIT