BMAD module that mirrors sprint tracking to GitLab Issues or GitHub Issues. Supports both cloud and self-hosted instances via their respective CLIs (glab / gh).
Uses native BMad TOML customization for workflow integrations. Installs through both BMad routes: the classic installer (npx bmad-method install --custom-source) and the Skills CLI (npx skills add).
- BMAD Method module (BMM) 6.12.0+ installed in your project
glabCLI (GitLab) orghCLI (GitHub) installed and authenticated- Repository with Issues enabled
uv(mandatory from BMM 6.12.0+)
The module plugs into BMM via two TOML hooks per target workflow:
activation_steps_append— runs before the BMM workflow: sets up the worktree, resolves variables (prd_key,story_key, branch patterns), captures context.on_complete— runs after the BMM workflow: commits, pushes, creates/updates issues, manages MRs, posts comments.
The TOML files in assets/custom/ are pure pointers — they reference workflow YAML files in assets/workflows/ (deployed to _bmad/_config/custom/workflows/) that carry the actual logic. No business logic lives in TOML.
For the full architecture — branch/MR direction table, platform differences, step-authoring rules, caller-negotiation channels, bmad-loop integration design — see CLAUDE.md.
The module participates in your CI pipeline via two layers:
common/wait-for-green-ci.yaml— the on_complete hook blocks until the MR/PR pipeline is green, for up to 30 minutes. The initial check goes throughcommon/check-mr-ci.yaml→common/get-mr-pipeline.yaml; the rounds that follow inline the same lookup, andcommon/get-failed-jobs.yamlis read only on the failure path. The wait is aLOOPof eighteen rounds, each a single command of at most four polls 25 s apart (~100 s, inside the tool's 120 s default): the interpreter runs commands through a tool that times out, so one 30-minute command would be killed before it could report anything andci-status.jsonwould never be written. Two answers end the wait green without a pipeline:no_ci— the branch defines no pipeline, or three consecutive rounds saw an empty run list although it does (the counter resets on any round that sees one) — andno_mr, the flow with no remote MR at all, where there is no gate to enforce. Every lookup is pinned to the commit the hook pushed (git rev-parse HEAD, filtered on as--commiton GitHub and as the pipeline'sshaon GitLab): both platforms list the previous commit's run for the first seconds after a push, so an unpinned gate could report green over a tree CI never built. "No run for this commit yet" is an empty listing, which the rules above already treat as "still coming".ci-status.sh— the bmad-loop[verify]command (deployed to.bmad-loop/ci-status.shby the setup skill). Reads the latestci-status.jsonwritten by the unified workflow; exits 0 (green) or 1 (red, with diagnostic). The intelligent work (polling, log parsing, distinguishing flaky from real) is done by the on_complete hook —ci-status.shis a fast, deterministic file-read.ci-status.json— transient gate output at the worktree root: one story's verdict, written bycommon/write-ci-status.yamland read byci-status.sh. The setup skill gitignores it (step 4) because bmad-loop commits the story worktree as a single commit, and untracked the file rode that commit into the integration branch, leaving every story's stale verdict there.
In the manual flow (/bmad-build), the hook blocks the workflow on CI. In the bmad-loop flow, a red CI triggers an automatic repair session (re-invoke bmad-build-auto with the diagnostic) up to max_dev_attempts before deferring the story.
BMad has two install routes. They never coexist in one project — pick the one your project already uses.
| Route | BMad versions | What this module ships for it |
|---|---|---|
Classic installer — npx bmad-method install |
released BMAD (6.12.x) | skills/module.yaml, skills/module-help.csv, .claude-plugin/marketplace.json |
Skills CLI — npx skills add + bmad setup |
BMAD main only (6.13.0-next, not yet released) |
skills/*/module-manifest.toml |
- Install or update BMad in your project as usual (
npx bmad-method install, BMM ≥ 6.12.0). - Add this module as a custom source. Interactive: run the installer again and answer yes to "install custom or community modules", then paste the repo URL or local path. Non-interactive:
npx bmad-method install --directory . --modules bmm \
--custom-source https://github.com/jrevillard/bmad-issue-tracking \
--tools claude-code --yesPin a release with --custom-source https://github.com/jrevillard/bmad-issue-tracking@v3.0.0, or point at a local clone (--custom-source /path/to/bmad-issue-tracking; changes take effect on reinstall). Both <repo> (Discovery mode via marketplace.json) and <repo>/skills (Direct mode) work as the source.
The installer registers the module as bmad-issue-tracking with its version in _bmad/_config/manifest.yaml, copies both skills to .claude/skills/ (the module directory _bmad/bmad-issue-tracking/ holds only its config and help catalog), and adds their rows to _bmad/_config/bmad-help.csv.
npx skills add bmad-code-org/BMAD-METHOD # BMad core, then run `bmad setup` in your coding tool
npx skills add jrevillard/bmad-issue-tracking # this moduleThe Skills CLI reads each skills/<name>/module-manifest.toml; both declare module = "bmad-issue-tracking".
Pinning to a release: the skills CLI treats
@<ref>after the package name as a skill name filter, not a git ref —npx skills add jrevillard/bmad-issue-tracking@v3.0.0looks for a skill namedv3.0.0. For release-tag installs, the GitHub URL form is the only reliable syntax (see Development install).
/bmad-issue-tracking-setup
This deploys TOML overrides to _bmad/custom/, shared tasks to _bmad/_config/custom/, and configures:
issue_tracking.platform(gitlab or github)issue_tracking.enabled(true)issue_tracking.branch_patterns(default:feat/{prd_key}/prd,feat/{prd_key}/{story_key})issue_tracking.host/issue_tracking.project(issue tracker; same keys for GitLab and GitHub) andissue_tracking.git_platform(from the git remote;git_host/git_projectonly when tracker and remote differ)
prd_key is captured automatically when running /bmad-prd (via activation_steps_append). No manual configuration needed.
For contributors testing branches or local edits before a release is tagged. Neither route deploys the TOML overrides, workflow YAMLs or the bmad-loop CI gate into your project's _bmad/; you must still run /bmad-issue-tracking-setup afterwards.
npx bmad-method install --directory . --modules bmm --custom-source /absolute/path/to/bmad-issue-tracking --tools claude-code --yesLocal sources are read from disk on every reinstall, no commit needed. A Git URL accepts /tree/<branch> or @<tag> to pick a ref.
npx skills add https://github.com/jrevillard/bmad-issue-tracking/tree/skills-as-modules # a branch
npx skills add https://github.com/jrevillard/bmad-issue-tracking/tree/v3.0.0 # a release tagThe CLI clones that ref (not the default branch), so each <skill>/module-manifest.toml is read from it. This is the form the pinning note above refers to.
npx skills add /absolute/path/to/bmad-issue-trackingThe CLI reports per skill whether it copied or symlinked into the agent directory (.claude/skills/<skill>/, or a canonical .agents/skills/<skill>/). After a copy install, re-run the command after each edit; a symlinked install reads the working tree directly.
npx skills updatewon't roll either form forward to a tagged release — you'll need to remove the dev install (npx skills remove) and reinstall via the production command.- The
bmad setupdoctor'sstate: "blocked"for thebmad-issue-trackingmodule is expected until you publish a tag matching the manifest'sversion. The install itself is healthy — only the release comparability check fails.
This repo ships only this module's two skills (bmad-issue-tracking-setup,
bmad-issue-tracking-sync). BMM core is NOT colocated here.
To test the install flow end-to-end, use a throwaway consumer project — never reinstall BMM into this repo:
One fresh directory per route — the two routes must not share a project.
Classic route (released BMAD); both skills end up registered as module bmad-issue-tracking:
mkdir /tmp/consumer-classic && cd /tmp/consumer-classic && git init
npx bmad-method install --directory . --modules bmm --custom-source /path/to/bmad-issue-tracking --tools claude-code --yesSkills CLI route (BMAD main); the picker lists exactly 2 skills:
mkdir /tmp/consumer-skills && cd /tmp/consumer-skills && git init
npx skills add bmad-code-org/BMAD-METHOD
npx skills add /path/to/bmad-issue-trackinguv run --with pytest --with pyyaml pytest runs the suite; tests/test_packaging.py keeps the metadata of both routes in agreement.
Registered as slash commands in your IDE.
| Skill | Command | Purpose |
|---|---|---|
| Sync Issues | /bmad-issue-tracking-sync |
Sync sprint-status.yaml to issues, mark draft PR ready |
| Setup | /bmad-issue-tracking-setup |
One-time integration setup |
Deployed to _bmad/custom/. Survive BMM updates automatically.
| Override file | Target workflow | Hook | Behavior |
|---|---|---|---|
bmad-create-prd.toml |
create-prd |
activation_steps_append, on_complete |
Captures prd_key at activation, creates PRD issue + PRD branch + draft PR/MR on completion. Superseded by bmad-prd.toml |
bmad-prd.toml |
bmad-prd |
activation_steps_append, on_complete |
Unified PRD override: detects create/update/validate intent, replaces bmad-create-prd.toml and bmad-edit-prd.toml |
bmad-create-architecture.toml |
create-architecture |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, commits and pushes on completion |
bmad-ux.toml |
bmad-ux |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, commits and pushes on completion. Replaces bmad-create-ux-design.toml (skill retired in BMM 6.8.0) |
bmad-create-epics-and-stories.toml |
create-epics-and-stories |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, commits and pushes on completion |
bmad-create-story.toml |
create-story |
activation_steps_append, on_complete |
Sets up story worktree at activation, creates issue + MR on completion (shim — deprecated upstream, bmad-build is the official path) |
bmad-dev-story.toml |
dev-story |
activation_steps_append, on_complete |
Switches to story worktree at activation, posts summary, updates status (shim — deprecated upstream, bmad-build is the official path) |
bmad-code-review.toml |
code-review |
activation_steps_append, on_complete |
Switches to story worktree at activation, posts review, updates status |
bmad-build.toml |
bmad-build |
on_complete |
Manual flow: routes to the interactive dispatcher, which reads the spec status and runs the matching phase of post-dev-complete (push, wait for CI, update issue, comment); offers the optional MR merge on review-finish |
bmad-build-auto.toml |
bmad-build-auto |
on_complete |
bmad-loop flow (unattended): routes to the non-interactive dispatcher; same phases plus ci-status.json for the bmad-loop [verify] gate; never prompts and never stages orchestrator-owned files |
bmad-sprint-planning.toml |
sprint-planning |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, triggers full issue sync |
bmad-sprint-status.toml |
sprint-status |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, triggers full issue sync (consolidated into bmad-sprint-planning in BMM 6.12.0, retained as shim alias) |
bmad-edit-prd.toml |
edit-prd |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, updates PRD issue description. Superseded by bmad-prd.toml |
bmad-correct-course.toml |
correct-course |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, updates issue descriptions for modified stories/epics/PRD |
bmad-retrospective.toml |
retrospective |
activation_steps_append, on_complete |
Switches to PRD worktree at activation, creates issue with retrospective content |
Note: All overrides require BMM 6.12.0+ (uniform customize.toml support across all BMM workflows; targets the 6.12.0 skill set).
Copied to _bmad/_config/custom/ — referenced by TOML on_complete hooks.
bmad-workflow-lang.md— the workflow language specification the TOML hooks referenceworkflows/issue-sync/—prepare.yaml(platform, labels, board, PRD issue) andsync.yaml(sync issues, mark MR ready, summary)
/bmad-issue-tracking-sync
Creates/updates issues for all sprint entries, manages labels, reconciles statuses, marks draft PR ready when all epics are done.
A project has one PRD and one initiative key, set once. BMM 6.12.0 writes a single
{planning_artifacts}/prd.md, and the module derives everything from it: one PRD issue
(PRD: <prd-key>), one PRD branch (branch_patterns.prd with the key substituted) and
one PRD worktree. bmad-prd reads the intent off that file — a prd.md that already
carries a prd_key is an update, whatever the user asked for — so asking to create a
second PRD with a new key continues as an update to the existing one and says so. The
key lives in the PRD frontmatter; to work on a different initiative, use a different
repository.
Issues created by the module follow a fixed naming convention:
| Type | Title |
|---|---|
| PRD | PRD: <prd-key> |
| Story | Story 1.4: Login Form |
| Epic | Epic 1: Authentication |
| Retrospective | Retrospective: Epic 1 |
Epic titles are derived from the planning artifacts created by BMM workflows (epics.md). Story titles come from the spec, through common/story-title.yaml: the frontmatter title:, else the first # H1, else the story key's slug segments (1-4-login-form → Login Form). Whichever tier answers, a leading Story N.M: is stripped — the prefix is added once, by the caller.
When branch_patterns is configured in the setup — one PRD branch per repository, story
branches beneath it:
| Event | Action |
|---|---|
| PRD created | PRD worktree created in activation + draft PR/MR (PRD → default branch) |
| Story created | Story worktree created in activation (from PRD) + issue + MR (story → PRD) |
| Story developed | Story worktree entered, changes committed |
| Story reviewed | Issue status updated, worktree exited (only if MR merged) |
| All epics done | Draft PR/MR marked as ready for review |
The module is compatible with bmad-loop (deterministic orchestrator that drives bmad-build-auto per story in isolated worktrees). bmad-loop is the single writer of sprint-status.yaml; the module mirrors it to issues. Zero user interaction during the run.
Prerequisites: bmad-loop ≥ 0.9.0, BMM ≥ 6.12.0, sprint-status.yaml from bmad-sprint-planning.
Flow:
bmad-loop run— each story is implemented/reviewed/verified in its own worktree and merged back locally. At the end of everybmad-build-autosession, the skill executes itson_completehook (frombmad-build-auto.toml), which runscommon/post-build-dispatch-auto.yaml→common/post-build-dispatch.yaml→common/post-dev-complete.yaml. This unified workflow handles the full lifecycle for the story:- dev-finish phase (spec status
in-review/in-progress): pushes the code, ensures the issue + trace MR exist (common/ensure-issue.yaml/common/ensure-mr.yaml), waits for CI (common/wait-for-green-ci.yaml), writesci-status.json(common/write-ci-status.yaml), and updates the issue status. The MR is ensured before the gate: with no MR the gate has nothing to read and writes green. - review-finish phase (spec status
done): commits review modifications, pushes, posts the review findings comment, mirrors the story to its issue (status label, result comment, MR link), then ensures the trace MR, waits for CI and writesci-status.json. It ensures the MR for the same reason dev-finish does, and it is the phase where that matters most:bmad-build-autofinalises dev and review in one session, so under bmad-loop review-finish is the only phase a story ever reaches — without the MR the gate read nothing and wrote green. ci-status.sh([verify]command): readsci-status.jsonwritten by the unified workflow. A red CI fails the verify command (with rich diagnostic), and bmad-loop runs a feedback-driven repair session (re-invokingbmad-build-autowith the diagnostic as feedback) — the story is auto-fixed and re-verified, up tomax_dev_attempts, before the merge-back. A missingci-status.jsonalso fails (fixable) — the on_complete hook did not write it. Only a budget-exhausted CI defers the story (bmad-loop resolveto recover). Nothing else fails it:passed,no_ci(no pipeline defined, or three consecutive empty poll rounds) andno_mr(no remote MR to gate on) all write green. No bmad-loop plugins are needed — theon_completehook drives everything.
- dev-finish phase (spec status
/bmad-issue-tracking-sync— unattended safety net: mirrors the updatedsprint-status.yamlto issues (labels, statuses, closedone), no worktree required, no prompts.git push origin main— the local merge-back is never pushed by bmad-loop.
Status mapping (bmad-loop values → module labels):
| bmad-loop sprint-status | Issue |
|---|---|
backlog |
status::backlog |
ready-for-dev |
status::ready-for-dev |
in-progress |
status::in-progress |
review |
status::review |
awaiting-operator |
status::awaiting-operator (issue stays open — external action pending, confirm with bmad-loop confirm) |
done |
status::done + issue closed |
Execution trace: the unified workflow's ensure-mr.yaml ensures a trace MR/PR exists per story (left open) — a CI vehicle and the story's execution trace. After the local merge-back is pushed to the target branch, GitLab auto-marks it merged, keeping the story's diff and pipeline as a durable record. On GitHub there is no auto-detection of an out-of-band merge, so the trace PR would stay open — the close-trace-mr bmad-loop plugin (setup step 5) closes it at post_merge, looking it up with gh api repos/<owner>/<repo>/pulls -X GET -f head=<owner>:<branch> -f state=open and closing only PRs whose head.ref is the branch that was merged. Without the plugin, close it with gh pr close <number> when the story is done.
If you're upgrading from a version that used ci-wait.sh:
- Re-run
/bmad-issue-tracking-setup— it will deployci-status.shand updatepolicy.toml - Delete the old
ci-wait.sh:rm .bmad-loop/ci-wait.sh - If you previously installed the
story-track-dev/story-track-reviewbmad-loop plugins (now removed — superseded by thebmad-build-auto.tomlon_completehook): delete them withrm -rf .bmad-loop/plugins/story-track-dev .bmad-loop/plugins/story-track-reviewand remove them from[plugins] enabledin.bmad-loop/policy.toml.
The architecture is simpler: at the end of every bmad-build-auto session, the skill's on_complete hook (from bmad-build-auto.toml) runs common/post-build-dispatch-auto.yaml → common/post-build-dispatch.yaml → common/post-dev-complete.yaml (dev-finish / review-finish), which pushes code + ensures issue/MR + waits CI + writes ci-status.json + tracks issue. ci-status.sh (verify command) reads the latest ci-status.json. No polling or API calls in the shell script — the workflow does the polling via common/wait-for-green-ci.yaml.
Limits (by design): no MR discussion threads (the MR is a CI vehicle + trace, not a review conversation); mark-mr-ready is not used in this flow.
| Aspect | GitLab | GitHub |
|---|---|---|
| CLI | glab |
gh |
| Labels | status::done (double colon) |
status:done (single colon) |
| Description file | -F "description=@file" |
--body-file "file" |
| State changes | Single glab api call with state_event |
Separate gh issue close / gh issue reopen |
| Label updates | -f "labels=..." (replaces all) |
--add-label / --remove-label (targeted) |
| Boards | Created automatically | Skipped in v1 |
| Enterprise | -R on subcommands, --hostname on glab api only |
-R on subcommands, --hostname on gh api only |
- Skills — classic route: re-run
npx bmad-method install(custom sources are refreshed with the rest). Skills CLI route:npx skills update, then run thebmadskill →bmad doctor. On either route re-run/bmad-issue-tracking-setupto refresh the deployed TOML/YAML assets in your_bmad/custom/and_bmad/_config/custom/workflows/trees. - TOML overrides — no action needed (survive BMM updates unless we rename a workflow).
- Shared tasks — no action needed
Set issue_tracking.enabled: false in _bmad/custom/issue-tracking.yaml.
bmad doctor reports state: "blocked" for issue-tracking. Expected until a tag matching the manifest's version is published. The install itself is healthy — only the release comparability check fails. (Dev installs always show this.)
/bmad-issue-tracking-setup says "platform mismatch". Your git remote (origin) and issue tracker are on different platforms (e.g. code on GitLab, issues on GitHub). The setup skill detects the mismatch and asks for the issue tracker host and project explicitly. The git_platform is set from the remote; platform is set from your answer. Issue ops use platform; MR/PR ops use git_platform. See CLAUDE.md § Platform differences.
Stories appear in the wrong issue. Parallel PRDs collide on story keys (1-3-login-form in two PRDs). common/find-issue.yaml is scoped by prd_key — pass it explicitly from the workflow (prd_key is captured during PRD activation and re-derived from prd.md in unattended flows).
/bmad-issue-tracking-sync prompts for prd_key. You're running it without a PRD worktree. Use common/find-prd-key.yaml (auto-resolves from prd.md at the repo root, fails closed) or pass prd_key via the workflow variable scope. The bmad-loop flow runs unattended — see CLAUDE.md § bmad-loop flow.
ci-status.json missing on disk. The on_complete hook didn't run — typically because the build session was interrupted before reaching the hook. Re-run the build to regenerate. The ci-status.sh verify treats missing ci-status.json as fixable (rc=1), so bmad-loop retries via a repair session rather than escalating.
prd_key is empty in _bmad/custom/issue-tracking.yaml. Normal for new installs — it's captured automatically the first time /bmad-create-prd (or bmad-prd with create intent) runs via activation_steps_append. Not a bug.
The issue_tracking block in _bmad/custom/issue-tracking.yaml controls the integration:
issue_tracking:
enabled: true
platform: gitlab # or github
host: gitlab.com # always configured by setup
project: group/project # always configured by setup
branch_patterns:
prd: "feat/{prd_key}/prd"
story: "feat/{prd_key}/{story_key}"platform— required.gitlaborgithub. Determines which CLI to use (glab/gh).host— required. The issue tracker host (e.g.gitlab.com,github.com, or a self-hosted instance).project— required. The project path (e.g.my-org/my-repo).branch_patterns.prd— required. Pattern for the PRD branch. Must contain{prd_key}.branch_patterns.story— required. Pattern for story branches. Must contain{prd_key}and{story_key}.
Cross-platform scenario: If your code is on GitLab but you want to track issues on GitHub (or vice versa), the setup skill detects the mismatch and asks for the issue tracker host and project explicitly. The git_platform (from the remote) drives MR/PR ops; the platform (from your answer) drives issue ops.
Released under the MIT License.