环境预检(Step 0)

GitCode 相关 skill 在执行任何业务步骤之前,必须先完成环境预检。预检失败时应立即停止并通过 AskUserQuestion 询问用户,而不是带着不完整的环境继续往下走(否则常见症状:API 401、克隆卡死、行号偏移等问题会延迟暴露,浪费排查成本)。

预检遵循一个原则:能本地解决的不打扰用户,缺了用户才知道的关键输入立刻问


预检项

1. Token(必检)

按以下顺序探测:

  1. 用户当前消息中是否直接提供了 token(关键词:token=...access_token=...、形如 gitcode_pat_* 或 32+ 位字母数字串)
  2. 环境变量 GITCODE_TOKENecho "${GITCODE_TOKEN:+set}"(只判断是否存在,不打印明文)
  3. 都没有 → 通过 AskUserQuestion 询问用户

询问示例:

问题: 未检测到 GitCode Token,需要你提供以继续。
选项:
  - 我现在提供 token(在下一条消息中输入)
  - 已配置环境变量,让我重试一次
  - 取消本次操作

拿到 token 后只在当前会话临时使用,不写入任何文件、不写入日志明文。日志中只记录"已获取 token(来源:env / user / arg)"。

2. Git 与基础工具(必检)

command -v git >/dev/null 2>&1 || echo "MISSING: git"
command -v curl >/dev/null 2>&1 || echo "MISSING: curl"
command -v python3 >/dev/null 2>&1 || echo "MISSING: python3"

任一缺失 → AskUserQuestion 告知用户需要安装,并询问是否继续。

3. 临时目录(必检)

确认 /tmp 可写:

test -w /tmp || echo "MISSING: /tmp not writable"

不可写 → AskUserQuestion 询问替代路径(如 ~/tmp)。

4. 输出目录(仅 pr-to-design-doc / 需要落盘的 skill)

确认目标输出目录(如 ./docs/、用户指定路径)的父目录存在且可写。不存在则 mkdir -p 创建,无权限则 AskUserQuestion 询问替代路径。

5. Git 提交用户信息(仅创建 commit 的 skill:gitcode-issue-handler)

会调用 git commit 的 skill 必须在 Step 0 阶段确认 user.nameuser.email 已配置,否则 git commit 会在 Step 5 才报错(Author identity unknown),白白浪费一轮 clone+改代码+跑测试的工作量——越早暴露越好。

NAME=$(git config --global user.name 2>/dev/null)
EMAIL=$(git config --global user.email 2>/dev/null)
if [ -n "$NAME" ] && [ -n "$EMAIL" ]; then
  GIT_AUTHOR_SOURCE="global"
fi
  • 上述两项都已配置 → 标 GIT_AUTHOR_SOURCE=global;后续克隆出的工作目录会自动继承 global 配置,无需git config 写到 local
  • 任一缺失 → AskUserQuestion 询问用户

询问示例(仅 global 缺失时触发):

问题: 未检测到 git 提交用户信息(global user.name / user.email),请提供:
选项:
  - 用我下面提供的 name 和 email(在下一条消息中给出)
  - 用 fork_owner 拼出一个占位(不推荐,作者归属会失真)
  - 取消本次操作

拿到用户提供的值后只在工作目录上写禁止改 ~/.gitconfig 全局

# 仅当 Step 0 走"用户提供"分支时执行;走 global 分支跳过
git -C "$WORK_DIR" config user.name  "$NAME"
git -C "$WORK_DIR" config user.email "$EMAIL"
GIT_AUTHOR_SOURCE="user"

理由:用户的全局 git 配置可能服务于多个项目和身份,skill 不应擅自覆盖;用户主动提供的值是一次性使用,只配 local 不污染全局。

预检报告里展示读到的 Name <email>,方便用户一眼确认是否是本次想用的身份(commit author 本就是公开字段,预检阶段透明展示反而避免事后才发现写错了人)。


预检报告格式

预检完成后,向用户输出一段简短的可视化报告(不要 AskUserQuestion,除非有项目失败):

环境预检
  ✅ Token: 已获取(来源:env GITCODE_TOKEN)
  ✅ Git: git version 2.39.2
  ✅ Curl: 可用
  ✅ Python: python3 3.10.12
  ✅ /tmp: 可写
  ✅ Git author: pingchuantang <pingchuantang@gitcode.com>(来源:global,仅 gitcode-issue-handler 等创建 commit 的 skill 才检)
预检通过,开始执行 ...

任一项失败时,必须先通过 AskUserQuestion 解决该项,不要带着失败项继续。


何时复用预检

  • 同一会话内首次执行 GitCode skill 时必检
  • 后续 skill 调用如果是同一 token、同一会话,可跳过 token 询问,但仍打印一行"沿用上次会话 token"以便用户知情
  • 如果中途出现 401 / 403,立即重新走预检的"询问用户提供新 token"分支,而不是反复重试

反模式

  • ❌ 跳过预检直接 curl ... ?access_token=$GITCODE_TOKEN,让 401 自然暴露
  • ❌ 把多个预检失败项打包到一条 AskUserQuestion 里(违反"一次只问一个"的原则)
  • ❌ 在日志中打印 token 明文
  • ❌ 自动写入 token 到 ~/.bashrc 或任何持久化文件
  • ❌ 创建 commit 类 skill 跳过 git author 预检,等到 git commit 才报 Author identity unknown——clone+改代码+跑测试的工作量已经废了
  • ❌ 修改全局 ~/.gitconfig 的 user.name / user.email——会污染用户的其他项目身份;只在工作目录用 git -C <work_dir> config 设置
  • 从 fork_url / 用户名等地方"脑补"一个身份塞进 commit(典型反例:git -c user.name='pingchuantang' -c user.email='pingchuantang@gitcode.com' commit ...)。即便看起来能跑通,最终 PR 上的 commit author 是错的——可能挂错人头、与用户实际身份不符。身份只能来自两个来源:global 配置 或 用户在 AskUserQuestion 里提供,没有第三条路。
  • ❌ 用 git commit -c user.name=... / --author="..." 这类 inline 覆盖来绕过 Step 0 预检——任何时候 git commit 命令都不应带身份相关的 flag,让它读 work_dir / global 配置即可。