关于 github-sync
github-sync 是一项双向同步 BMAD 规划文件与 GitHub Projects v2 的技能。它按字段区分数据归属,并要求先展示变更报告、获得批准后再执行修改。
主要功能
- 01双向同步 BMAD 与 GitHub Projects
- 02初始化项目字段和配置
- 03将故事推送为 GitHub 问题
- 04拉取状态、负责人和任务完成情况
- 05变更需审批且同步具幂等性
适用场景
- 01为 BMAD 文件配置 GitHub Project
- 02根据规划故事创建 GitHub 问题
- 03用 GitHub 进展更新本地故事
- 04同步前审阅计划变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bmad-labs/ skills --skill github-sync安装后在 Claude Code 中输入/github-sync确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/config-schema.mdcontent-mapping.mdgh-commands.mdscripts/parse-artifacts.py详细介绍
GitHub Sync — BMAD 到 GitHub Projects v2
在以 Markdown 文件存储的 BMAD 规划产物(史诗、用户故事、冲刺计划)与 GitHub Projects v2 之间双向同步,以便直观地跟踪项目。
快速导航
ghCLI 命令、GraphQL 查询 →references/gh-commands.md- Issue 正文模板、字段映射、标签、里程碑 →
references/content-mapping.md- 配置文件格式 →
references/config-schema.md- 产物解析脚本 →
scripts/parse-artifacts.py
四条规则(绝不可违反)
-
绝不自动同步。 每项会修改数据的操作(创建 Issue、更新字段、修改文件)都必须先生成同步报告,展示给用户,并等待用户明确批准后才能执行。用户必须清楚看到将发生的所有变更。
-
字段级别的唯一事实来源。 每个字段只有一个归属方——BMAD 文件或 GitHub。同步时始终以归属方的数据为准。绝不覆盖归属方的数据。
- 归 BMAD 所有: 用户故事正文、验收标准、任务、冲刺分配、史诗分组、依赖关系
- 归 GitHub 所有: 状态、开发人员分配、任务复选框完成状态、故事点、优先级
-
幂等操作。 同一同步操作运行两次应产生相同结果。回链标识符(用户故事文件中的 H1 标题链接、规划 frontmatter 中的
gh_item_url)可防止重复创建。创建之前始终检查是否已有同步状态。 -
绝不使用脚本自动解析 Issue 正文。 生成 Issue 正文时,必须使用 Read 工具直接读取每个用户故事文件,并按照
references/content-mapping.md中的模板手动撰写正文。自动解析脚本可能以难以察觉的方式静默失败:格式假设错误时会删掉任务列表,移除代码块时会大幅删减开发说明,还会生成空的章节标题。一次错误的推送就可能同时污染全部 27 个 Issue。读取文件。编写正文。不走捷径。
流程识别
| 用户说 | 工作流 | 是否需要配置? |
|---|---|---|
| “set up github project”、“onboard”、“initialize github” | 入门设置 | 否(会创建配置) |
| “push to github”、“sync push”、“create issues” | 推送 | 是 |
| “pull from github”、“sync pull”、“update from github” | 拉取 | 是 |
| “sync status”、“what's out of sync” | 状态检查 | 是 |
如果项目根目录中不存在 .github-sync.yaml,始终先进入入门设置。
前置条件检查(每个工作流运行前执行)
运行任何工作流前,按顺序验证以下内容。遇到第一个失败项时停止。
Step 1: gh --version
→ If missing: "Install GitHub CLI: https://cli.github.com/"
Step 2: gh auth status
→ If not authenticated: "Run: gh auth login"
Step 3: gh auth status (check for "project" in scopes)
→ If missing: "Run: gh auth refresh -s project"
Step 4: (For push/pull/status only) Check .github-sync.yaml exists
→ If missing: "Run onboarding first: say 'set up github project'"
入门设置工作流
这是首次设置流程。它会创建或连接到一个 GitHub Project,并创建自定义字段、标签和按阶段划分的里程碑。
步骤 1:检测 BMAD 产物
运行解析器以发现现有内容:
python3 .agents/skills/github-sync/scripts/parse-artifacts.py --mode scan \
--stories-dir .artifacts/implementation-artifacts
同时检查以下文件是否存在:
.artifacts/planning-artifacts/epics.md.artifacts/planning-artifacts/sprint-plan.md
注意: 产物路径
.artifacts/可能因项目而异。请检查实际目录结构——有些项目使用_bmad-output/。与用户确认正确路径,并在整个流程中使用这些路径。相应更新配置中的paths.*。
报告数量:“找到 N 个用户故事文件、M 个史诗、K 个冲刺。”
步骤 2:收集 GitHub 详情
自动检测当前仓库:
gh repo view --json owner,name --jq '.owner.login + "/" + .name'
列出现有项目,并询问用户是使用其中一个还是创建新项目:
gh project list --owner {owner} --format json
展示检测到的所有者/仓库和现有项目。继续之前请征求用户确认。
步骤 3:解析史诗和冲刺数据
现在阅读 references/content-mapping.md——第 7 节(标签方案)、第 8 节(里程碑方案)和第 12 节(史诗推导)。你需要这些内容来生成准确的入门设置报告。
然后解析实际产物:
python3 .agents/skills/github-sync/scripts/parse-artifacts.py --mode epics \
--file .artifacts/planning-artifacts/epics.md
python3 .agents/skills/github-sync/scripts/parse-artifacts.py --mode sprints \
--file .artifacts/planning-artifacts/sprint-plan.md
步骤 4:生成入门设置报告
展示报告,列出所有将要创建的内容:
=== GITHUB SYNC — ONBOARDING REPORT ===
Repository: {owner}/{repo}
Project: #{N} "{title}" (existing) OR new project "{title}"
Will create/verify:
Custom fields (7):
- Story ID (TEXT)
- Epic (SINGLE_SELECT: 12 options, rainbow colors)
- Sprint (SINGLE_SELECT: Sprint 01–12, rainbow colors + sprint goal descriptions)
- Dev (SINGLE_SELECT: All, Dev 1, Dev 2, Dev 3, Dev 1+2)
- Sprint Start (DATE)
- Sprint End (DATE)
- Story Points (NUMBER)
Built-in fields to set:
- Start date (DATE, pre-existing) ← same dates as Sprint Start
- Target date (DATE, pre-existing) ← same dates as Sprint End
- Status (single-select, pre-existing)
{N} labels:
- Epic labels (one per epic, e.g., epic:1-foundation ... epic:12-testing)
- Phase labels (e.g., phase:poc, phase:hardening)
- Layer labels (optional, project-specific architecture layers)
{K} milestones (phase-based, NOT per-sprint):
- PoC (due: {sprint_6_end}) Sprints 1–6
- Production Hardening (due: {sprint_12_end}) Sprints 7–12
Note: No "Phase" custom field — the milestone covers phase membership.
Proceed with onboarding? [Y/N]
在此处停止。等待用户批准。
步骤 5:执行设置
现在阅读 references/gh-commands.md——你需要用到下面每项操作的确切命令。
如果获得批准,请按以下顺序执行:
- 创建或连接 GitHub Project(第 2 节)——如果使用现有项目,只需获取其节点 ID
- 创建自定义字段(第 3 节)——Story ID、Epic、Sprint、Dev、Sprint Start、Sprint End、Story Points
- 查询所有字段 ID(第 4 节)——包括预先存在的 Start date、Target date、Status 字段
- 为 Sprint 和 Epic 字段设置彩虹颜色及 Sprint 目标描述(第 13 节)
- Sprint 选项:循环使用 RED→ORANGE→YELLOW→GREEN→BLUE→PURPLE→PINK
- Sprint 描述 = sprint-plan.md 中的 Sprint 目标
- Epic 选项:RED→ORANGE→YELLOW→GREEN→BLUE→PURPLE→PINK→RED→ORANGE→YELLOW→GREEN→BLUE
- 创建标签(第 5 节)——共 17 个标签
- 创建基于阶段的里程碑(第 6 节)——PoC 和 Production Hardening
**重要:**调用
updateProjectV2Field设置颜色/描述后,单选选项 ID 会发生变化(该变更会替换选项列表)。调用此变更后,务必重新查询选项 ID,并在配置中使用新的 ID。
步骤 6:编写配置
现在阅读 references/config-schema.md——你需要用到确切的 YAML 格式。
在项目根目录创建 .github-sync.yaml,按照该架构填写所有 ID。
注意:field_ids 或 option_ids 中不包含 phase 字段——Phase 已替换为里程碑。
步骤 7:报告完成情况
Onboarding complete!
Project: https://github.com/orgs/{owner}/projects/{number}
Config: .github-sync.yaml
Next: Run "push stories to github" to create issues.
推送工作流(文件 → GitHub)
将 BMAD 故事文件推送到 GitHub 作为 issue,添加到项目中,设置所有字段和日期字段,并建立关联。
步骤 1:加载配置
读取 .github-sync.yaml。确认所有字段 ID 均已填写。如果有任何 ID 为空,请从 GitHub 重新查询字段 ID(这些 ID 可能已丢失)。
步骤 2:扫描故事
python3 .agents/skills/github-sync/scripts/parse-artifacts.py --mode scan \
--stories-dir .artifacts/implementation-artifacts
对于每个故事,确定使用创建还是编辑模式:
- H1 为普通标题
# Story X.X: Title(无链接)→ CREATE - H1 包含
# [Story X.X: Title](url)→ UPDATE(已同步)
步骤 3:处理部分同步(可选)
如果用户指定了筛选条件(例如“推送故事 1.1 1.2”、“推送 epic 1”、“推送 sprint 2”),请在继续之前筛选故事列表。如果没有筛选条件,则推送所有故事。
步骤 4:读取故事文件并构建 issue 内容
⚠️ 规则 4 在此处适用:使用 Read 工具读取每个文件。手动构建正文。不要使用脚本。
对于每个故事:
- 使用 Read 工具读取文件
- 阅读
references/content-mapping.md——查看第 1–4 节和第 9 节,了解确切格式 - 按照模板构建 issue 正文:
- 用户故事引用块
- 将 AC 编写为单行 Then 子句摘要(复选框)
- 完整任务列表(仅包含 Task 2+——根据内容而非位置排除仅供 Agent 使用的任务)
- 使用
#NGitHub issue 编号表示依赖关系 - 可折叠的开发说明:表格、配置片段、架构模式——不要包含代码骨架、ASCII 树、“What NOT to Do”列表或参考资料
- 从 sprint-plan.md 中查找 Sprint、Dev 和 Epic(不要从故事文件正文中查找)
步骤 5:生成同步报告
=== GITHUB SYNC — PUSH REPORT ===
Generated: {timestamp}
--- CREATES ({count} new issues) ---
[CREATE] {story_id} {short_title}
File: {story_file_path}
Epic: {epic_label} | Sprint: {sprint} | Dev: {dev} | Phase: {phase}
Labels: epic:{N}-{name}, phase:{phase}
Milestone: {PoC | Production Hardening}
--- UPDATES ({count} existing issues) ---
[UPDATE] {story_id} {short_title} (#{issue_number})
Changes: body updated
--- SKIPPED ({count} items) ---
[SKIP] {story_id} {short_title} — already synced, no local changes
--- SUMMARY ---
Creates: {N} | Updates: {N} | Skips: {N}
Proceed? [Y/N]
在此停止。等待用户批准。
步骤 6:执行推送
如果获得批准,对于每个 CREATE:
- 将问题正文写入临时文件(
/tmp/issue-{story_id}.md) gh issue create --title "..." --body-file /tmp/... --label "epic:X-name" --label "phase:poc" --milestone "{PoC|Production Hardening}"- 从输出中提取问题 URL
gh project item-add {project_number} --owner {owner} --url {issue_url} --format json- 从 JSON 输出中提取项目 ID
- 使用
gh project item-edit设置所有项目字段:- Story ID(文本)
- Epic(单选选项)
- Sprint(单选选项,例如 “Sprint 01”)
- Dev(单选选项)
- Sprint Start(来自 sprint-plan.md 的日期)
- Sprint End(来自 sprint-plan.md 的日期)
- Status(Backlog)
- Start date(与 Sprint Start 日期相同——预先存在的字段)
- Target date(与 Sprint End 日期相同——预先存在的字段)
- 将回链写入本地文件:将 H1 替换为
# [Story X.X: Title](issue_url)
对于每个 UPDATE:
- 将更新后的正文写入临时文件
gh issue edit {number} --body-file /tmp/... --milestone "{phase_milestone}"- 如果 sprint/epic 发生变化,则更新项目字段
步骤 7:设置关联关系
创建所有问题后,建立 GitHub 原生关联关系。
阅读 references/gh-commands.md 第 12 节,获取准确的 GraphQL mutation。
7a. 将 Epic 跟踪问题设为父项
为每个 Epic 创建一个跟踪问题(如果尚未创建)。正文模板见
references/content-mapping.md 第 10 节。然后为每个故事设置其 Epic 跟踪问题为父项:
mutation {
addSubIssue(input: {
issueId: "{epic_tracking_issue_node_id}"
subIssueId: "{story_issue_node_id}"
replaceParent: true
}) {
issue { number }
subIssue { number }
}
}
7b. 阻塞依赖项
从 sprint-plan.md 读取依赖关系表(每个 sprint 故事表中的 Dependencies 列)。对于每个依赖项 A → B(故事 A 依赖故事 B):
mutation {
addBlockedBy(input: {
issueId: "{story_A_node_id}"
blockingIssueId: "{story_B_node_id}"
}) {
issue { number }
blockingIssue { number }
}
}
始终从 sprint-plan.md 获取依赖项,而不是从故事文件正文获取。 sprint 计划是权威的依赖关系记录。
通过以下方式获取问题节点 ID:
gh api graphql -f query='
query {
repository(owner:"{owner}", name:"{repo}") {
issues(first:50, orderBy:{field:CREATED_AT, direction:ASC}) {
nodes { number id title }
}
}
}
'
步骤 8:报告完成情况
Push complete!
Created: {N} issues
Updated: {N} issues
Skipped: {N} items
Relationships set: {N} parent, {M} blocked-by
Config updated: last_synced = {timestamp}
拉取工作流(GitHub → 文件)
将状态、受指派人和任务完成情况从 GitHub 拉回本地 BMAD 文件。
步骤 1:加载配置并查询 GitHub
读取 .github-sync.yaml。然后阅读 references/gh-commands.md 第 9 节,获取完整的
GraphQL 项目条目查询。执行查询以获取所有项目条目。
步骤 2:将条目与本地文件匹配
对于每个具有 “Story ID” 字段值的 GitHub 条目:
- 查找匹配的本地故事文件(根据故事 ID → 文件名模式)
- 比较 GitHub 状态与本地文件状态:
- 状态:GitHub 状态与本地
Status:行 - 受指派人:GitHub 受指派人与本地开发人员映射
- 任务复选框:GitHub 问题正文中的复选框与本地文件中的复选框
- 状态:GitHub 状态与本地
步骤 3:生成同步报告
=== GITHUB SYNC — PULL REPORT ===
Generated: {timestamp}
--- FILE UPDATES ({count} items) ---
[UPDATE] {story_filename}.md
Status: ready-for-dev → done (from GitHub #{issue_number})
Assignee: (none) → @{username}
--- NO CHANGES ({count} items) ---
[OK] {story_filename}.md — in sync
--- GITHUB-ONLY ({count} items) ---
[WARN] GitHub #{N} "{title}" — no matching local file
--- SUMMARY ---
File updates: {N} | No changes: {N} | Warnings: {N}
Proceed? [Y/N]
在此处暂停。等待用户批准。
步骤 4:执行拉取
如果获得批准,对于每个文件更新:
- 读取故事文件
- 使用映射后的 BMAD 状态更新
Status:行 - 写回文件
- 更新配置中的
last_synced
状态检查(只读)
比较本地文件与 GitHub 状态,不做任何更改。无需批准。
# Scan local files
python3 .agents/skills/github-sync/scripts/parse-artifacts.py --mode scan \
--stories-dir .artifacts/implementation-artifacts
# Query GitHub items (full field values)
# Use GraphQL query from references/gh-commands.md section 9
输出比较表:
=== GITHUB SYNC — STATUS ===
| Story | Local Status | GitHub Status | Sprint | Synced? | Issue |
|-------|--------------|---------------|--------|---------|-------|
| 1.1 | ready-for-dev | Done | 01 | NO | #1 |
| 1.2 | ready-for-dev | Ready | 01 | YES | #2 |
| 2.1 | backlog | (not synced) | 02 | NO | — |
Summary: {N} in sync, {M} out of sync, {K} not yet pushed
部分同步筛选条件
| 筛选条件 | 示例 | 效果 |
|---|---|---|
| 按故事 ID | push stories 1.1 1.2 1.3 | 仅同步这些故事 |
| 按 Epic | push epic 3 | Epic 3 中的所有故事 |
| 按 sprint | push sprint 2 | 分配给 Sprint 2 的所有故事 |
| 按状态 | push unsynced | 仅同步尚未推送的故事 |
| 全部 | push 或 push all | 所有故事(默认) |
错误处理
| 错误 | 操作 |
|---|---|
gh 命令返回非零退出代码 | 显示错误输出,建议修复方法,然后停止 |
| 配置中缺少 Field ID | 通过 gh-commands.md 的第 4 节重新查询字段 ID |
| 达到速率限制(HTTP 429) | 显示警告,建议等待 60 秒,然后停止 |
| Story 文件格式异常 | 跳过,并在同步报告中发出警告 |
| 里程碑已存在 | 跳过创建(幂等) |
| 标签已存在 | 跳过创建(幂等) |
| Story 已有关联的 Issue | 切换到更新模式 |
| 单选字段选项 ID 已过期 | 每次调用 updateProjectV2Field 后重新查询——该 mutation 会替换所有选项并生成新的 ID |
addBlockedBy 负载字段错误 | 返回字段为 blockingIssue(而非 blockedByIssue) |
updateProjectV2Field 拒绝 projectId | 此 mutation 只接受 fieldId,不接受 projectId |
updateProjectV2Field 拒绝选项 id | 选项只能使用 name/color/description——不能包含 id 字段 |
参考文件索引
| 文件 | 阅读时机 |
|---|---|
references/gh-commands.md | 需要准确的 gh CLI 命令或 GraphQL 查询时 |
references/content-mapping.md | 需要构建 Issue 正文、映射字段,或检查标签/里程碑方案时 |
references/config-schema.md | 需要创建或读取 .github-sync.yaml 时 |