
Auto Repo Setup
- 240 installs
- 1.3k repo stars
- Updated August 4, 2026
- daymade/claude-code-skills
Diagnoses and fixes repo environment setup and completes safe git operations for non-technical users.
About
Automates codebase environment setup, dependency diagnosis, and safe git operations by reading ONBOARDING.md and validating the project runs. A developer or non-technical user uses it when a cloned repo won't start or standard setup is needed.
- Reads ONBOARDING.md/README as project map, then diagnoses gaps
- Fixes dependencies and completes safe git operations
Auto Repo Setup by the numbers
- 240 all-time installs (skills.sh)
- Ranked #541 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/daymade/claude-code-skills --skill auto-repo-setupAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 240 |
|---|---|
| repo stars | ★ 1.3k |
| Last updated | August 4, 2026 |
| Repository | daymade/claude-code-skills ↗ |
What it does
Diagnoses and fixes repo environment setup and completes safe git operations for non-technical users.
Files
Auto Repo Setup — 代码库自助配置与故障修复
概述
本 skill 让 Claude Code 成为非技术用户的"环境医生":用户把仓库 clone 下来或打开项目后说"跑不起来",Claude 自动按标准流程诊断、修复、验证,无需用户理解底层技术细节。
同时,本 skill 也规范了技术用户搭建可移交仓库的标准动作(ONBOARDING.md、SessionStart hook、PII 安全)。
目标用户:
- 主要:非技术人员(剪辑师、商务、运营)——他们不知道什么是 uv、ffmpeg、whisper.cpp
- 次要:技术用户——标准化仓库 setup 流程,降低下游维护成本
---
核心工作流
Step 0: 读取项目地图
进入任何仓库后,第一件事是读取以下文件(按优先级):
1. ONBOARDING.md — 项目专属 setup 指南(如果存在) 2. README.md — _fallback_ 3. CLAUDE.md — 项目级规则(如果存在) 4. .claude/settings.json — 检查是否有 SessionStart hook
如果 ONBOARDING.md 不存在:
- 询问用户是否需要创建(基于仓库结构自动生成草稿)
- 不要在没有指南的情况下盲目猜测 setup 步骤
Step 1: 环境审计(按 ONBOARDING.md 的验证步骤)
逐条执行 ONBOARDING.md 中的 "Step X: 验证..." 或类似章节。每执行一条必须验证输出,不要假设成功。
常见检查项(根据项目类型取舍):
| 检查项 | 命令示例 | 失败处理 |
|---|---|---|
| git 状态 | git status / git remote -v | 提示用户配置 git identity |
| 系统依赖 | ffmpeg -version / which uv | 按 ONBOARDING.md 安装 |
| Python 环境 | uv --version / python --version | 用 uv 创建 venv |
| 项目依赖 | uv sync / uv pip install -e . | 读取 pyproject.toml |
| 模型/二进制 | ls models/ / whisper.cpp/whisper-cli -h | 按文档下载/编译 |
| 环境变量 | cat .env 检查 key 是否存在 | 指导用户填入或生成 |
注意:
- 使用
uv管理 Python,禁止用系统自带 Python - 所有 Python 执行必须在虚拟环境或 uv 中
- 检查命令的退出码和 stderr,不要只看 stdout
Step 2: 修复迭代
调试先根因后 workaround(铁律): 1. 收集证据(读日志/堆栈/配置,不猜) 2. 沿调用链定位 root cause 3. 针对根因修复 4. (可选)标注「临时」workaround 并说明为何不够
禁止:
- 看到报错就直接重装/重启
- 用
rm -rf清理(必须分析文件用途、用户确认、创建备份) - 静默绕过错误(
|| true、空的 except 块)
Step 3: 运行验证(自我验证闭环)
修复后必须验证:
- 运行 ONBOARDING.md 中的 smoke test 或测试命令
- 如果项目有 pytest,跑
uv run pytest(最小集合) - 验证失败 → 回 Step 2,不要告诉用户"应该可以了"
Step 4: 交付状态汇报
用简洁的非技术语言告诉用户:
- ✅ 已修复什么
- ⚠️ 还需要用户手动做什么(如填入个人 API key)
- 📋 接下来该运行什么命令(从 ONBOARDING.md 复制)
---
安全与合规铁律
仓库可见性检查(Push Safety)
任何 `git push` 之前,必须验证仓库真实可见性:
gh repo view <owner>/<repo> --json visibility,isPrivate,stargazerCount,forkCount- public + 多 stars/forks → 默认走 PR 流程(push feature branch +
gh pr create) - public + 0 stars/forks 且用户明确授权 → 可 push main,但仍需 audit 内容
- private/internal → push main 需用户确认,风险降一档
- 禁止凭 URL 反推可见性,禁止在汇报里写"私人 repo"除非 API 确认
isPrivate: true
PII Guard 与 Secret 管理
public repo(多层扫描): 1. Layer 1 — gitleaks 标准 secret + 私有域名/IP 2. Layer 2 — 路径扫描(禁止本地生成路径) 3. Layer 3 — bash grep 兜底(中文内容、已知身份) 4. Layer 4 — AI 语义通读(前三层结构漏的无 keyword 语义私有信息)
private repo:
.env可直接提交(项目隔离的 API key)- 但仍需清理个人绝对路径(
/Users/<name>/)
Git Hook Bypass 禁令:
- ❌ Claude 禁止主动使用
--no-verify/--no-gpg-sign - ✅ 唯一例外:用户本人在当前 session 里显式输入
--no-verify - Hook 失败 → 修底层问题,不是绕过
NO FALLBACK 原则
当系统无法确定一个值(从外部系统获取的关键字段),必须 fail-fast:
# ❌ 禁止
apiKey: process.env.KIMI_API_KEY || 'sk-kimi-...'
# ✅ 正确
import os
api_key = os.environ["KIMI_API_KEY"] # KeyError if missing- 占位符(
"your-key-here")只能在.env.example里,永不进真实代码 - 写完 LLM/API 客户端初始化后自查:
.env没加载会发生什么?能看见明文吗?
---
标准模式
ONBOARDING.md 模式
可移交仓库必须包含 ONBOARDING.md,结构:
# 项目名 Setup 指南
## Step 1: 验证系统依赖
- [ ] git 已安装
- [ ] ffmpeg 已安装(`ffmpeg -version`)
- [ ] uv 已安装(`uv --version`)
## Step 2: 初始化 Python 环境uv sync
## Step 3: 验证安装uv run pytest tests/test_smoke.py -v
## Step 4: 配置环境变量
复制 `.env` 中的占位符为真实值(private repo 可直接编辑提交)
## Step 5: 运行项目
[具体命令]要求:
- 所有命令可直接复制执行(无个人路径、无假设)
- 使用相对路径或占位符(
<REPO_ROOT>) - 包含"验证"步骤,不只是"安装"步骤
SessionStart Hook 模式
让 Claude Code 打开仓库时自动检查环境:
`.claude/settings.json`:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": ".claude/hooks/session-start-check.sh"
}
]
}
]
}
}`.claude/hooks/session-start-check.sh`:
#!/usr/bin/env bash
CACHE_DIR="$HOME/.claude/cache/env-check"
mkdir -p "$CACHE_DIR"
REPO_HASH=$(cd "$(dirname "$0")/../.." && pwd | sha256sum | cut -d' ' -f1)
CACHE_FILE="$CACHE_DIR/$REPO_HASH"
if [ -f "$CACHE_FILE" ] && [ "$(find "$CACHE_FILE" -mtime -1 2>/dev/null)" ]; then
exit 0
fi
touch "$CACHE_FILE"
echo "【环境自检】你刚刚进入 [项目名] 仓库。请在执行任何任务前,先阅读 ONBOARDING.md 并按 Step 1-3 验证环境。"一键初始化脚本:
Skill 自带 scripts/init_session_start_hook.py,可为任意项目自动生成配置:
# 基础用法(自动推断项目名,默认读取 ONBOARDING.md)
python scripts/init_session_start_hook.py --repo /path/to/project
# 完整用法
python scripts/init_session_start_hook.py \
--repo /path/to/project \
--guide ONBOARDING.md \
--update-gitignore脚本行为: 1. 创建 .claude/settings.json(SessionStart hook 配置) 2. 创建 .claude/hooks/session-start-check.sh(24h 缓存 + 自检提示) 3. --update-gitignore 时追加规则,允许 .claude/settings.json 和 hooks/ 入 git 4. 自动从 git remote 或目录名推断项目名 5. 已有配置时默认跳过(--force-overwrite 覆盖)
设计原则:
- hook 只负责戳agent 检查(输出提示),不负责复杂脚本检查
- 24h TTL 缓存降频(用 repo path sha256 作为 cache key)
- 项目级配置,与全局 settings deep merge
Counter-Review Workflow
当需要创建新文件、修改核心配置、添加外部依赖、修改 CI/CD、变更安全策略时,启动多 agent 审查:
1. 并行启动 4 个 lens(各一个 subagent):
- security-lens:PII/secret 泄露、注入风险、权限过度
- devops-lens:部署影响、依赖冲突、路径硬编码
- code-quality-lens:可读性、异常处理、测试覆盖
- doc-consistency-lens:文档与代码同步、ONBOARDING.md 更新
2. Judge agent 过滤:
- 对每条 finding 用"概率 × 成本 × 现实场景"三维过滤
- 真实 + 低成本 → 立刻修
- 真实 + 高成本 → 告诉用户权衡
- 虚构 / 过度担忧 → 明说"这是过度防御,拒绝"
3. 给用户分类汇报:✅ 真问题 / ⚠️ 部分对 / ❌ 虚构 / 🚫 反而有害
---
Git 操作规范
提交代码(非技术用户场景)
用户说"帮我提交"或"保存一下"时:
1. git status 看改动 2. git diff 确认改动内容(向用户解释改了什么) 3. git add(选择性,不要无脑 git add .) 4. git commit -m "..."
- 信息用中文,描述改了什么、为什么改
- 结尾加
Co-Authored-By: Claude <noreply@anthropic.com>
5. git push 前走 Push Safety 验证
处理冲突
用户说"冲突了"时:
1. git status 定位冲突文件 2. 读取冲突文件的 <<<<<<< / ======= / >>>>>>> 区块 3. 不要自动选择某一侧——向用户解释两边的差异,让用户决定(或按业务逻辑判断) 4. 修复后 git add + git commit
历史净化(敏感信息泄露后)
如果仓库历史中存在敏感信息(个人路径、secret、内部域名):
1. 评估影响范围:哪些 commit 含敏感信息?是否已 push 到 remote? 2. Orphan branch + force push(如果历史可以全部丢弃):
git checkout --orphan new-history
git add -A
git commit -m "Initial commit: sanitized history"
git push --force origin new-history:main3. BFG Repo-Cleaner(如果需保留部分历史):用于替换文件中的敏感字符串 4. 通知用户:force push 会打断其他协作者,需协调
---
项目隔离规范
API Key 隔离
每个项目使用独立的 API key,禁止复用个人/生产 key:
- 在 provider 后台为每个项目创建独立 key
.env中只放项目专属 key- key 命名体现用途(如
video-rough-cut-dev) - 定期轮转(泄露后可单独 revoke)
路径清理
仓库中禁止出现:
- 个人绝对路径(
/Users/<name>/、/home/<name>/) - 内部域名/IP(
<private-domain>.dev、<private-domain>.pro等) - 中文真实人名/项目名(用占位符替代)
清理方法:
- 用占位符替换(
<REPO_ROOT>、<USER_HOME>、<YOUR_NAME>) - 用相对路径替代绝对路径
- 用
.env或配置文件存储环境相关值
---
常见故障排查手册
"uv 命令找不到"
- 检查
~/.local/bin是否在 PATH - 重新安装:
curl -LsSf https://astral.sh/uv/install.sh | sh
"ffmpeg 命令找不到"
- macOS:
brew install ffmpeg - 或按项目文档安装
ffmpeg-full
"whisper.cpp 编译失败"
- 检查 Xcode Command Line Tools:
xcode-select --install - 检查 Metal 支持(Apple Silicon)
"pytest 大量失败"
- 先跑最小 smoke test,不要一次性跑全量
- 检查
.env是否配置了必要的 API key - 检查测试是否依赖本地文件系统路径(应使用临时目录)
"git push 被拒绝"
- 检查远程仓库权限
- 检查是否启用了 branch protection
- 走 Push Safety 流程确认仓库可见性
---
Next Step: 代码审查与交付
完成环境配置和基础修复后,建议的自然下一步:
Options: A) 运行 Counter-Review — 如果用户准备做较大改动,启动多 agent 安全审查(Recommended) B) 生成操作文档 — 为用户生成简洁的操作指南(下一步该点什么/运行什么) C) No thanks — 当前状态已足够,用户可以直接开始使用
---
资源目录
references/
git_safety.md— Git 操作安全细则(Push Safety、Hook Bypass、历史净化)pii_guard.md— PII Guard 规则摘要与应急处理onboarding_template.md— ONBOARDING.md 标准模板
scripts/
check_env.py— 环境检查脚本(ffmpeg、uv、python、git 状态)sanitize_history.sh— 历史净化辅助脚本(检查敏感信息、生成 orphan branch)
Security scan passed
Scanned at: 2026-05-31T20:17:11.595969
Tool: gitleaks + pattern-based validation
Content hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Git 操作安全细则
Push Safety — 推送前必须验证仓库可见性
任何 `git push`(特别推到 main/master)之前,必须用 `gh` CLI 验证目标仓库的真实可见性。
gh repo view <owner>/<repo> --json visibility,isPrivate,stargazerCount,forkCount决策矩阵:
| 可见性 | Stars/Forks | 操作 |
|---|---|---|
| public | >0 | 默认走 PR 流程(push feature branch + gh pr create) |
| public | 0 + 用户明确授权 | 可 push main,但仍需 audit 内容 |
| private/internal | 任意 | push main 需用户确认,风险降一档 |
禁止:
- 凭 URL 形态反推 private/public
- 凭用户名/目录路径推断
- 在汇报里写"私人 repo"除非 API 确认
isPrivate: true - 凭历史汇报或 CLAUDE.md 描述推断
Git Hook Bypass 禁令
Claude 禁止主动使用 `--no-verify` / `--no-gpg-sign` / `-c commit.gpgsign=false` 等绕过 git hook 的参数。
- ❌ Hook 失败 → 找根因修好 → 不要"绕过试试看"
- ❌ 过去 session / 文档里的历史授权 → 不作数
- ❌ 用户没明说,但我觉得"应该跳" → 不行,停下来问
- ✅ 用户本人在当前 session 里显式输入
--no-verify→ 照办(只这一次)
Why:pre-commit hook 是拦住 secret/PII/大文件的最后一道系统性防线。AI 自作主张绕过 = 防线退化为"看 AI 心情"。
历史净化(敏感信息泄露后)
评估影响
1. 哪些 commit 含敏感信息? 2. 是否已 push 到 remote? 3. 是否有其他协作者?
方法选择
| 场景 | 方法 | 说明 |
|---|---|---|
| 历史可以全部丢弃 | Orphan branch + force push | 最干净,但打断所有协作者 |
| 需保留部分历史 | BFG Repo-Cleaner | 替换文件中的敏感字符串 |
| 仅单个文件 | git filter-branch / git filter-repo | 移除特定文件从历史 |
Orphan branch 流程
# 1. 创建无历史的新分支
git checkout --orphan new-history
# 2. 添加当前工作区内容
git add -A
# 3. 提交(注意:此时不要含敏感信息)
git commit -m "Initial commit: sanitized history"
# 4. 强制推送到 main(会覆盖远程历史)
git push --force origin new-history:main
# 5. 删除旧分支引用(本地)
git branch -D main
git checkout -b main origin/main⚠️ 警告:
- Force push 会永久删除远程历史,其他协作者需要重新 clone
- 必须先通知用户并获得确认
- 如果 secret 已泄露到公开网络,force push 不够——还需 revoke 并轮转 key
提交规范
Commit message
- 用中文描述改了什么、为什么改
- 技术细节可附在正文
- 结尾加
Co-Authored-By: Claude <noreply@anthropic.com>
选择性添加
- 不要无脑
git add . git status后选择性git add <file>- 确保 stage 的内容都是意图中的改动
ONBOARDING.md 标准模板
模板(复制到新项目后修改)
# <项目名称> Setup 指南
> 本指南面向非技术用户。遇到任何问题,直接问 Claude Code:"跑不起来了"、"环境怎么配"。
## Step 1: 验证系统依赖
在终端运行以下命令,**每行都要运行并确认输出**:
1.1 git 状态检查
git status
期望:显示 "On branch main",无未提交改动
1.2 ffmpeg 检查
ffmpeg -version | head -1
期望:显示版本号(如 "ffmpeg version 7.0")
1.3 uv 检查
uv --version
期望:显示版本号(如 "uv 0.5.x")
**任一失败 → 按下方"故障排除"修复,不要跳过。**
## Step 2: 初始化 Python 环境
2.1 进入项目目录(如果还没进)
cd <REPO_ROOT>
2.2 同步依赖(根据 pyproject.toml 安装)
uv sync
2.3 验证安装
uv run python -c "import <main_package>; print('OK')"
## Step 3: 配置环境变量
3.1 查看当前 .env
cat .env
- 如果值是占位符(如 `YOUR_KEY_HERE`),替换为真实值
- private repo:直接编辑 `.env` 然后 `git add .env && git commit -m "配置环境变量"`
- public repo:**不要提交 .env**,问 Claude Code 如何处理
## Step 4: 运行验证测试
4.1 运行 smoke test
uv run pytest tests/test_smoke.py -v
或运行项目自带验证脚本
uv run python scripts/verify_setup.py
**全部通过 → 环境就绪。**
## Step 5: 日常使用
| 任务 | 命令 |
|------|------|
| 运行项目 | `uv run python main.py` |
| 运行测试 | `uv run pytest` |
| 更新依赖 | `uv sync` |
| 提交代码 | 问 Claude Code "帮我提交" |
## 故障排除
### "命令找不到"(ffmpeg / uv / git)
- macOS: `brew install ffmpeg` / `curl -LsSf https://astral.sh/uv/install.sh | sh`
- 重新打开终端,让 PATH 生效
### "uv sync 失败"
- 检查网络连接
- 检查 `pyproject.toml` 是否存在
- 问 Claude Code
### "pytest 失败"
- 检查 `.env` 是否配置正确
- 先跑 `tests/test_smoke.py`(最小测试),不要一次性跑全量
- 问 Claude Code
### "git push 被拒"
- 问 Claude Code "push 失败了"
- 不要强行用 `--force`设计原则
1. 所有命令可直接复制执行 — 无个人路径、无假设 2. 每步有验证 — 不只是"安装",而是"安装后检查" 3. 相对路径或占位符 — <REPO_ROOT>、<YOUR_NAME> 4. 故障排除独立成节 — 常见问题自助,复杂问题找 Claude 5. 面向非技术用户 — 解释"期望输出是什么"、"失败了怎么办" 6. 与 Claude Code 配合 — 明确说"问 Claude Code"的场景
PII Guard 规则摘要与应急处理
三层扫描架构(public repo)
Layer 1 — gitleaks
标准 secret + 私有基础设施域名/IP。
覆盖规则:
- LLM provider key:
sk-kimi-(Moonshot)、sk-or-v1-(OpenRouter)、sk-ant-(api|admin)(Anthropic)、sk-(proj|svcacct|admin)-(OpenAI) - Generic
sk-兜底(allowlist 了占位符如sk-test-/sk-example/sk-your-) - PII:macOS 绝对路径
/Users/<user>/、中国手机号、个人邮箱 - 私有基础设施:内部域名(
<private-domain>.dev、<private-domain>.pro等)+ 已知生产 IP - 内置:AWS、GitHub PAT、Stripe 等
⚠️ 注意:gitleaks 有熵过滤——低熵占位符不会拦,只有高熵真实格式才拦。测试时必须用真实格式。
Layer 2 — 路径扫描
禁止本地生成路径(coverage、node_modules 等)。
Layer 3 — bash grep 兜底
同步 gitleaks 域名/IP 规则 + 已知身份(如中文人名)。gitleaks 不覆盖中文内容,Layer 3 补充拦截。
Layer 4 — AI 语义通读
1-3 全是关键词/正则/grep,只命中"有人列进规则的词"。对无 keyword 的语义私有结构性盲(中文人名/项目名、真实转录口语片段、随手举的真实例子)——hook 必漏。
push public repo 前除 hook 自动扫,必须自己 AI 通读全文做语义判断:"这名词/例子/片段,像通用占位/公开实体,还是从真实项目/人/转录拿的?"
"grep/gitleaks 无命中" ≠ 干净。
private repo 规则
.env可直接提交(项目隔离的 API key)- 但仍需清理个人绝对路径(
/Users/<name>/) - 仍需清理内部域名/IP
- 仍需清理中文真实人名/项目名
命中后怎么办
| 处理方式 | 是否允许 |
|---|---|
| 改规则(调 gitleaks.toml)/ 加 allowlist | ✅ |
--no-verify 绕过 | ❌(除非用户本人当场打) |
仓库追加 .pii-patterns 文件定义仓库特有模式 | ✅ |
| 直接 push 不管 | ❌ |
应急处理(secret 已 push)
1. 立即 revoke key — 在 provider 后台 disable key 2. 生成新 key — 用新 key 替换 .env 3. 历史净化 — 按 git_safety.md 的 Orphan branch 或 BFG 流程清理 4. 通知受影响方 — 如果 key 有访问日志,评估影响范围
#!/usr/bin/env python3
"""环境检查脚本 — 验证代码库运行所需的基础设施。
用法:
python scripts/check_env.py [--fix]
返回码:
0 — 全部通过
1 — 有缺失,但 --fix 未指定
2 — 修复尝试后仍有失败
"""
from __future__ import annotations
import argparse
import shutil
import subprocess
import sys
from dataclasses import dataclass, field
from typing import List
@dataclass
class CheckResult:
name: str
passed: bool
message: str = ""
fix_cmd: str = ""
def run_cmd(cmd: list[str]) -> tuple[int, str, str]:
try:
r = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return r.returncode, r.stdout, r.stderr
except FileNotFoundError:
return 127, "", f"command not found: {cmd[0]}"
except Exception as e:
return 1, "", str(e)
def check_git() -> CheckResult:
code, out, err = run_cmd(["git", "--version"])
if code != 0:
return CheckResult("git", False, err or "git not found", "brew install git")
return CheckResult("git", True, out.strip().split("\n")[0])
def check_ffmpeg() -> CheckResult:
code, out, err = run_cmd(["ffmpeg", "-version"])
if code != 0:
return CheckResult(
"ffmpeg", False, err or "ffmpeg not found", "brew install ffmpeg"
)
first = out.strip().split("\n")[0]
return CheckResult("ffmpeg", True, first)
def check_uv() -> CheckResult:
code, out, err = run_cmd(["uv", "--version"])
if code != 0:
return CheckResult(
"uv",
False,
err or "uv not found",
"curl -LsSf https://astral.sh/uv/install.sh | sh",
)
return CheckResult("uv", True, out.strip())
def check_python_via_uv() -> CheckResult:
code, out, err = run_cmd(["uv", "run", "python", "--version"])
if code != 0:
return CheckResult(
"python (via uv)",
False,
err or "python not available via uv",
"uv python install",
)
return CheckResult("python (via uv)", True, out.strip())
def check_pyproject_deps() -> CheckResult:
code, out, err = run_cmd(["uv", "sync", "--locked"])
if code != 0:
return CheckResult(
"dependencies (uv sync)",
False,
(err or out)[:200],
"uv sync",
)
return CheckResult("dependencies (uv sync)", True, "lockfile satisfied")
def check_dot_env() -> CheckResult:
import os
if not os.path.exists(".env"):
return CheckResult(
".env file",
False,
".env not found",
"cp .env.example .env && edit with real values",
)
with open(".env") as f:
content = f.read()
placeholders = ["YOUR_KEY_HERE", "REPLACE_ME", "placeholder", "example"]
found = [p for p in placeholders if p.lower() in content.lower()]
if found:
return CheckResult(
".env file",
False,
f"still contains placeholders: {found}",
"edit .env with real values",
)
return CheckResult(".env file", True, "configured")
def main() -> int:
parser = argparse.ArgumentParser(description="Check repo environment")
parser.add_argument("--fix", action="store_true", help="Attempt to auto-fix issues")
args = parser.parse_args()
checks: List[CheckResult] = []
# Ordered: system deps → python env → project deps → config
checks.append(check_git())
checks.append(check_ffmpeg())
checks.append(check_uv())
checks.append(check_python_via_uv())
checks.append(check_pyproject_deps())
checks.append(check_dot_env())
passed = [c for c in checks if c.passed]
failed = [c for c in checks if not c.passed]
print("=" * 50)
print("Environment Check Report")
print("=" * 50)
for c in passed:
print(f" ✅ {c.name}: {c.message}")
for c in failed:
print(f" ❌ {c.name}: {c.message}")
if c.fix_cmd:
print(f" Fix: {c.fix_cmd}")
print("=" * 50)
print(f"Result: {len(passed)}/{len(checks)} passed")
if not failed:
print("🎉 All checks passed! You're ready to go.")
return 0
if args.fix:
print("\n--fix specified, attempting repairs...")
# In practice, auto-fix is limited — we print suggestions
for c in failed:
if c.fix_cmd:
print(f" Run: {c.fix_cmd}")
print("Please re-run after fixing.")
return 2
print("\nRun with --fix to see repair commands, or ask Claude Code for help.")
return 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""一键初始化项目的 SessionStart hook。
用法:
python init_session_start_hook.py --repo /path/to/project [--guide ONBOARDING.md] [--update-gitignore]
功能:
1. 创建 .claude/settings.json(SessionStart hook 配置)
2. 创建 .claude/hooks/session-start-check.sh(24h 缓存 + 环境自检提示)
3. 可选更新 .gitignore(允许 .claude/settings.json 和 hooks/ 入 git)
要求:
- 目标目录必须是 git 仓库(或 --force 跳过检查)
- 不会覆盖已有配置(除非 --force-overwrite)
"""
from __future__ import annotations
import argparse
import json
import os
import stat
import sys
from pathlib import Path
SETTINGS_JSON = """\
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": ".claude/hooks/session-start-check.sh"
}
]
}
]
}
}
"""
HOOK_TEMPLATE = """#!/usr/bin/env bash
# SessionStart hook for {project_name}
# 24h cache + simplified nudge — agent reads {guide_file} for actual commands.
CACHE_DIR="$HOME/.claude/cache/env-check"
mkdir -p "$CACHE_DIR"
# Use repo absolute path hash as cache key
REPO_HASH=$(cd "$(dirname "$0")/../.." && pwd | sha256sum | cut -d' ' -f1)
CACHE_FILE="$CACHE_DIR/$REPO_HASH"
# Silent if checked within 24h
if [ -f "$CACHE_FILE" ] && [ "$(find "$CACHE_FILE" -mtime -1 2>/dev/null)" ]; then
exit 0
fi
# Create cache + output concise nudge
touch "$CACHE_FILE"
echo "【环境自检】你刚刚进入 {project_name} 仓库。请在执行任何任务前,先阅读 {guide_file} 并按 Step 1-3 验证环境。任一失败则按 {guide_file} 修复。"
"""
GITIGNORE_RULES = """
# Allow project-level Claude Code settings + hooks to be shared
!.claude/settings.json
!.claude/hooks/
.claude/settings.local.json
.claude/cache/
.claude/debug/
"""
def detect_project_name(repo_path: Path) -> str:
"""从目录名或 git remote 推断项目名称。"""
name = repo_path.name
git_config = repo_path / ".git" / "config"
if git_config.exists():
try:
text = git_config.read_text(encoding="utf-8", errors="replace")
for line in text.splitlines():
if "url =" in line:
url = line.split("=", 1)[1].strip()
# Extract repo name from git@host:owner/repo.git or https://host/owner/repo.git
if "/" in url:
part = url.rsplit("/", 1)[1]
if part.endswith(".git"):
part = part[:-4]
if part:
return part
except Exception:
pass
return name
def init_hook(repo_path: Path, guide_file: str, update_gitignore: bool, force_overwrite: bool, force_non_git: bool) -> int:
if not repo_path.exists():
print(f"❌ 目录不存在: {repo_path}", file=sys.stderr)
return 1
if not (repo_path / ".git").exists() and not force_non_git:
print(f"❌ {repo_path} 不是 git 仓库。如需继续,加 --force-non-git", file=sys.stderr)
return 1
project_name = detect_project_name(repo_path)
claude_dir = repo_path / ".claude"
hooks_dir = claude_dir / "hooks"
settings_file = claude_dir / "settings.json"
hook_file = hooks_dir / "session-start-check.sh"
gitignore_file = repo_path / ".gitignore"
# Create directories
hooks_dir.mkdir(parents=True, exist_ok=True)
# Write settings.json
if settings_file.exists() and not force_overwrite:
print(f"⚠️ 已存在,跳过: {settings_file}")
else:
settings_file.write_text(SETTINGS_JSON, encoding="utf-8")
print(f"✅ 创建: {settings_file}")
# Write hook script
if hook_file.exists() and not force_overwrite:
print(f"⚠️ 已存在,跳过: {hook_file}")
else:
hook_content = HOOK_TEMPLATE.format(project_name=project_name, guide_file=guide_file)
hook_file.write_text(hook_content, encoding="utf-8")
# Make executable
hook_file.chmod(hook_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
print(f"✅ 创建: {hook_file}")
# Update .gitignore
if update_gitignore:
if gitignore_file.exists():
existing = gitignore_file.read_text(encoding="utf-8", errors="replace")
# Check if rules already present
if "!.claude/settings.json" in existing:
print(f"ℹ️ .gitignore 已包含 Claude 规则,跳过")
else:
with open(gitignore_file, "a", encoding="utf-8") as f:
f.write(GITIGNORE_RULES)
print(f"✅ 更新: {gitignore_file}")
else:
gitignore_file.write_text(GITIGNORE_RULES.lstrip("\n"), encoding="utf-8")
print(f"✅ 创建: {gitignore_file}")
print("\n📋 总结:")
print(f" 项目: {project_name}")
print(f" 路径: {repo_path}")
print(f" 指南: {guide_file}")
print(f" Hook: {hook_file}")
if update_gitignore:
print(f" Gitignore: 已更新")
print("\n下次 Claude Code 进入此仓库时,SessionStart hook 会自动触发。")
return 0
def main() -> int:
parser = argparse.ArgumentParser(description="Initialize SessionStart hook for a project")
parser.add_argument("--repo", required=True, help="Target repository path")
parser.add_argument("--guide", default="ONBOARDING.md", help="Guide file name to reference in hook (default: ONBOARDING.md)")
parser.add_argument("--update-gitignore", action="store_true", help="Update .gitignore to allow .claude/ files")
parser.add_argument("--force-overwrite", action="store_true", help="Overwrite existing files")
parser.add_argument("--force-non-git", action="store_true", help="Allow running on non-git directory")
args = parser.parse_args()
return init_hook(
repo_path=Path(args.repo).resolve(),
guide_file=args.guide,
update_gitignore=args.update_gitignore,
force_overwrite=args.force_overwrite,
force_non_git=args.force_non_git,
)
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env bash
# sanitize_history.sh — 检查并清理 git 历史中的敏感信息
# 用法: ./sanitize_history.sh [--check-only] [--path <repo-root>]
#
# 注意:此脚本只辅助检查,最终修复(orphan branch / BFG)需要人工确认后执行。
set -euo pipefail
REPO_ROOT="$(pwd)"
CHECK_ONLY=false
while [[ $# -gt 0 ]]; do
case "$1" in
--check-only) CHECK_ONLY=true; shift ;;
--path) REPO_ROOT="$2"; shift 2 ;;
*) echo "Unknown arg: $1"; exit 1 ;;
esac
done
cd "$REPO_ROOT"
echo "========================================="
echo "Sanitization Check — $REPO_ROOT"
echo "========================================="
# 1. 检查常见敏感模式(全 git 历史)
echo ""
echo "[1/4] Scanning git history for common secrets..."
PATTERNS=(
'sk-[a-zA-Z0-9_-]{20,}' # API keys
'sk-or-v1-[a-zA-Z0-9_-]+' # OpenRouter
'sk-ant-[a-zA-Z0-9_-]+' # Anthropic
'sk-proj-[a-zA-Z0-9_-]+' # OpenAI project
'AK[0-9A-Za-z]{16,}' # Aliyun AK
'ghp_[a-zA-Z0-9]{36}' # GitHub PAT
'[A-Za-z0-9/+=]{40}' # Generic long base64
)
FOUND_ISSUES=0
for pat in "${PATTERNS[@]}"; do
matches=$(git log --all -p -G "$pat" -- | head -20 || true)
if [[ -n "$matches" ]]; then
echo " ⚠️ Pattern matched: $pat"
echo "$matches" | head -5
FOUND_ISSUES=$((FOUND_ISSUES + 1))
fi
done
if [[ $FOUND_ISSUES -eq 0 ]]; then
echo " ✅ No common secret patterns found in history."
fi
# 2. 检查个人绝对路径
echo ""
echo "[2/4] Scanning for personal absolute paths..."
PATH_PATTERNS=(
'/Users/[a-zA-Z0-9_-]+/'
'/home/[a-zA-Z0-9_-]+/'
)
PATH_ISSUES=0
for pat in "${PATH_PATTERNS[@]}"; do
matches=$(git log --all -p -G "$pat" -- | grep -oE "$pat" | sort -u | head -10 || true)
if [[ -n "$matches" ]]; then
echo " ⚠️ Personal paths found:"
echo "$matches"
PATH_ISSUES=$((PATH_ISSUES + 1))
fi
done
if [[ $PATH_ISSUES -eq 0 ]]; then
echo " ✅ No personal absolute paths found."
fi
# 3. 检查私有域名
echo ""
echo "[3/4] Scanning for private infrastructure domains..."
# 扩展此列表以匹配你的私有域名
PRIVATE_DOMAINS=(
'<private-domain>\.dev'
'<private-domain>\.pro'
'<your-domain>\.ai'
)
DOMAIN_ISSUES=0
for dom in "${PRIVATE_DOMAINS[@]}"; do
matches=$(git log --all -p -G "$dom" -- | head -10 || true)
if [[ -n "$matches" ]]; then
echo " ⚠️ Private domain found: $dom"
DOMAIN_ISSUES=$((DOMAIN_ISSUES + 1))
fi
done
if [[ $DOMAIN_ISSUES -eq 0 ]]; then
echo " ✅ No private domains found."
fi
# 4. 当前工作区检查
echo ""
echo "[4/4] Checking current working tree..."
if git rev-parse --git-dir > /dev/null 2>&1; then
# 检查未提交的文件中是否有敏感信息
UNCOMMITTED=$(git diff --cached --name-only || true)
if [[ -n "$UNCOMMITTED" ]]; then
echo " ℹ️ Staged files:"
echo "$UNCOMMITTED" | sed 's/^/ /'
fi
else
echo " ⚠️ Not a git repository."
fi
echo ""
echo "========================================="
echo "Summary: $((FOUND_ISSUES + PATH_ISSUES + DOMAIN_ISSUES)) potential issues found"
echo "========================================="
if [[ "$CHECK_ONLY" == true ]]; then
echo "--check-only specified. No changes made."
exit 0
fi
# 如果发现问题,提供修复建议
if [[ $((FOUND_ISSUES + PATH_ISSUES + DOMAIN_ISSUES)) -gt 0 ]]; then
echo ""
echo "建议修复流程:"
echo "1. 评估影响:哪些 commit 含敏感信息?是否已 push 到 remote?"
echo "2. 在 provider 后台 revoke 已泄露的 key"
echo "3. 生成新 key 替换 .env"
echo "4. 清理历史(选一):"
echo " A) Orphan branch(历史可全丢):git checkout --orphan new-history"
echo " B) BFG Repo-Cleaner(保留历史):https://rtyley.github.io/bfg-repo-cleaner/"
echo "5. 通知其他协作者重新 clone"
exit 1
fi
echo "✅ History looks clean."
exit 0