Ticket-to-PR development workflow skills for Claude Code, with pluggable issue trackers (Jira Cloud, Azure DevOps, GitHub Issues, or a backlog in the repo).
Ten skills, one CLI, 634 tests, no third-party dependencies.
Scripts distil, they do not relay. A Jira issue payload is mostly noise —
renderedFields, avatarUrls, self links, changelog. The CLI returns a
normalised, capped summary plus a map of what else exists, so an agent spends
tokens on the ticket, not on the API's shape. A typical triage is ~1.4 KB.
Policy lives in code, not in prompts. Skills pick a subcommand from a closed
set. There is no --jql, no --wiql, no --fields, no --url — a free-text
flag is an invitation to invent a field name that does not exist. Depth limits,
output caps, field normalisation, quality gates and the citation audit are all
enforced by the CLI and covered by tests, so every session behaves identically.
Git is read-only until you say otherwise. Every command prints what it
would run and lets the user run it — a wrong computation is then a wasted paste,
not a wrong branch. --execute runs it instead, through an allowlist that
admits five subcommands and refuses every rewrite of history: no reset, no
rebase, no --amend, and no force-push in any spelling. Two switches turn it
off standing.
/plugin marketplace add matheusPavaneli/workbench
/plugin install workbench@workbench
wb is python "${CLAUDE_PLUGIN_ROOT}/lib/wb.py". Requires Python 3.9+.
wb init # proposes a config: provider from the remote, preset and flow detected
wb init --write # writes it
wb doctor # checks the whole chainIt proposes and you dispose: nothing is written without --write, an existing
config is never replaced without --force, and it never writes a credential.
Detection that cannot be trusted says so, the same way the preset does.
Nine of the ten skills never touch a tracker, so needing one to reach them was a cost with nothing behind it. Commit four lines and a clone works with no per-machine setup at all:
{ "provider": "local", "preset": "solo-saas" }in .workflow/config.json. Then wb task new "the thing to do" --type bug
writes a task under .workflow/tasks/, and everything downstream is unchanged.
One context per account, once per machine:
wb ctx add personal --provider jira \
--base-url https://you.atlassian.net --project SAAS \
--pat-env JIRA_TOKEN_ME --email you@example.com --preset solo-saas
export JIRA_TOKEN_ME=... # the token stays in the environment
wb ctx use personal
wb doctorGitHub needs less: wb ctx add oss --provider github reads owner/repo from
the checkout's remote and borrows the token gh already holds.
wb doctor checks the whole chain in one pass — Python, git, the resolved
context, the credential, the tracker, the commit identity, the branching flow,
the test runner, and whether .workflow/ is about to be committed — and prints
the exact fix for anything wrong.
| Skill | Reads | Writes |
|---|---|---|
triage-task |
the tracker | triage.json |
plan-change |
triage.json + the code |
sdd.json, audit.json |
implement-change |
sdd.json |
the diff, evidence.md |
review-diff |
the diff | findings |
write-commit |
the diff + sdd.json |
commit.txt |
draft-pr |
sdd.json + evidence.json |
pr.md |
address-review |
PR comments | review-response.md |
frame-product |
an idea | frame.md |
trace-incident |
a symptom | incident.md, sdd.json |
write-handover |
sdd.json + triage.json |
handover.md |
Each runs alone; each names in its description what it consumes and produces, so
they chain without being told to. Artifacts live in .workflow/<KEY>/, which is
how a later skill reads an earlier one's output without it passing through the
conversation twice.
Three entry points: a ticket (triage-task), an idea (frame-product), a
symptom (trace-incident).
Chaining through files only pays if a new session can read the thread back.
$ wb status ABC-123
ABC-123 Coupon applied after the charge [bug, jira]
triage ok jira In Progress
plan ok 3 file(s), 4 step(s), 2 verify
audit ok 6 citation(s) verified
scope part 2 of 3 planned file(s) changed
verify --
handover -- required for bug work
next: wb impl verify ABC-123
wb next is the shorter question — what do I run rather than where does
this stand — and works out which ticket without being told, from the branch
name, then from what was touched last:
$ wb next
ABC-123 (branch) audit BLOCKED 1 of 4 citation(s) unverified
next: fix the plan, then: wb sdd audit ABC-123
wb status with no key lists everything in flight. --stats has two halves:
a snapshot of where work is stuck now, and a history from a local command log
of where this repo keeps losing time — a stage that always passes on the second
attempt is invisible in the snapshot and the most expensive thing in the log.
wb task clean <KEY> removes one ticket's artifacts once the work is done with.
It lists and removes nothing; --force is a second command, because a plan and
its evidence can be produced again and a frame or a handover was written once,
by hand:
$ wb task clean ABC-123
/repo/.workflow/ABC-123
triage.json
sdd.json
frame.md hand-written, not regenerable
3 file(s), nothing removed
remove them: wb task clean ABC-123 --force
The key resolves through the same validation as every other artifact path, so the context binding, the local backlog and the event log sitting beside the ticket directories are not names the command avoids — they are names it cannot produce.
Two selectors clean a checkout rather than a ticket. --merged takes everything
that reached a commit or a PR and has no branch left on the remote;
--older-than 30d takes everything untouched since then. Both halves of
--merged matter — "no branch names this key" on its own also describes work
that was never branched at all, which is in flight rather than finished. Either
way the listing says which stage each ticket stopped at, so a selector that
caught live work is visible before --force rather than after:
$ wb task clean --older-than 30d
/repo/.workflow/ABC-7 still at scope (BLOCKED)
sdd.json
triage.json
1 ticket(s), 2 file(s), nothing removed
remove them: wb task clean --older-than 30d --force
wb surface prints every group, action and flag, read off the live parser
rather than a list kept by hand. wb surface <group> narrows it, --json is
the form a session reads before composing an unfamiliar call:
$ wb surface pr
wb pr context branch, commits, plan summary, verification verdict
key [positional; required]
--base <value>
--target <value>
wb pr check reject filler, empty sections and placeholders in a draft
--file <value> [required]
--shape <value> [one of: trivial, small, large]
This is the schema MCP publishes for every tool in every session. Here it costs nothing until something asks: no skill names it, and a flag that was never in the parser cannot appear in it.
Ceremony has a cost that is not measured in minutes: a process too heavy for a one-line fix gets routed around, and then it only ever sees the changes nobody minded doing carefully. So the short route is named and computed rather than improvised:
$ wb route ABC-123 --files src/util.py
ABC-123 light route (1 file(s), no critical zone)
1. triage triage-task wb task get ABC-123
2. plan plan-change wb sdd audit ABC-123
3. implement implement-change wb impl check ABC-123
4. verify implement-change wb impl verify ABC-123
5. commit write-commit wb commit check --file <path> --key ABC-123
waived by the light tier: steps, product
the floor is not waived: citations, the file list, verify and rollback still apply
Touch a critical zone, exceed two files, or pick up a bug ticket and the same command returns the full eight steps, with the reason. It is a router, not an eleventh skill: ten descriptions are already this plugin's always-on cost, and one more to say "do less" would be the joke telling itself.
A seven-section plan for a one-line change costs more than the change, and a
gate that does not pay for itself is one people route around. sdd audit
computes a tier from the plan's own file list: at most two files, no
critical zone and no bug/support ticket waives steps and product. Citations,
the file list, verify and rollback are required at every tier, and the tier
is computed rather than declared, so a plan cannot ask for a lower bar.
write-handover exists because a support ticket has an audience that is not
engineering. A QA lead has to validate the fix without reading the diff, and the
person who raised it has to understand what happened without knowing the
codebase exists. It is required on bug, support and incident tickets, and the
audit fails without it.
Skill descriptions sit in the system prompt of every session, used or not. The ten here total ~2.0 KB, about 500 tokens always on, and a test asserts that ceiling so it cannot creep. Everything else is paid only on use: a SKILL.md when it triggers, a reference file only if that skill reads one.
Provider quirks, depth policy and the preset table are not in that path.
They are enforced in code and emitted as resolved output — wb repo profile
prints the six gates that apply rather than a table of five presets for the
model to pick a row from.
Where work starts and how it reaches production is configuration, not an assumption. One source branch holds the truth; zero or more validation branches carry the same commits for testing.
wb flow set --source main --validation homolog --branch-pattern "feature/{key}-{slug}"
wb flow start ABC-123 --title "Checkout fails on expired coupons"
wb flow carry ABC-123 --to homolog # the commits to cherry-pick, oldest firstOrder is the point of carry: a series applied newest-first conflicts on every
commit after the first.
Every base is a remote-tracking ref, so there is no pull step — start fetches
and branches from origin/<base>, and carry fetches before it measures the
range, because measuring "what the source lacks" against stale refs carries
commits that were already merged.
The commands above print. Add --execute to run them:
wb flow start ABC-123 --title "..." --execute
wb flow carry ABC-123 --to homolog --execute
wb git commit ABC-123 --execute # the message from wb commit check, the author from the context
wb git push --execute # first publish onlyThe printed and the executed forms are the same objects, so --execute cannot
run something other than what it showed. A refusal or a failure stops the series
and hands back the rest, starting at the step that failed. Preconditions are
checked before each step: a clean tree to switch branch, a working branch to
commit, and no upstream to push — recovering from a bad push onto a published
branch means a force-push, so this tool cannot reach the situation.
Turn it off standing with WB_NO_EXECUTE=1 or "execute": false in
.workflow/config.json. See flow.md and
execution.md.
The preset sets the bar a plan must clear. It never lowers the floor: a unit test
for changed logic, a regression test with every bug fix, no silently swallowed
errors, no secrets in code, and a stated rollback path apply to every preset —
prototype included.
| Preset | For |
|---|---|
prototype |
no users yet; reversibility over everything |
solo-saas |
one operator, paying users — managed over self-hosted, money paths tested like critical infrastructure, support cost is a design criterion |
startup |
small team, users in production |
scaleup |
migrations, feature flags, staged rollout |
enterprise |
backwards compatibility, runbooks, cross-team blast radius |
Detected from repo evidence (wb repo profile), overridable with --set.
Detection is allowed to be wrong. It is not allowed to be wrong silently:
where the evidence supports more than one bar — an unreadable contributor
count, CI on a one-person repo, or a monorepo — the preset comes back marked
LOW confidence with the alternatives named, and wb status keeps saying so
until somebody settles it with --confirm or --set.
A monorepo has no single answer, so it can give a different bar to each part:
{ "preset": "startup",
"preset_paths": { "packages/billing/**": "enterprise",
"apps/playground/**": "prototype" } }wb repo gates <paths> resolves the rules for the files a change actually
touches, and wb sdd gates prints them for the repo as a whole (or for a preset
named with --preset, to see what a different bar would demand). A change spanning two presets is held to the higher one — the
alternative is a plan that meets neither — and the audit fails a plan that
declares a preset below what its own files demand.
Rigour is not uniform inside a repo either: billing, auth, user data, migrations
and secrets raise the bar locally whatever the preset says.
A plan states claims about the codebase. Each carries a file:line and the text
of that line, and wb sdd audit reopens every one to check the text is really
there.
FAIL 1/2 citation(s) unverified
missing_file src/validator.py:10 no such file; the path in the citation does not exist
fix the plan, not the check. Do not implement from a failed audit.
It is a script, deliberately, and not a second pass by the model: a model
auditing its own work confirms its own errors. implement-change refuses to run
on a plan whose audit did not pass.
wb init [--write] propose (or write) this repo's config
wb doctor everything that has to be true, in one pass
wb route [KEY] the steps this change actually needs
wb next [KEY] the single command to run now
wb status [KEY] | --stats where work stands, and what to run next
wb ctx show | list | add | use | test | record
wb task list | get | new | done
wb repo profile [--confirm] | zones | gates <paths>
wb sdd audit [--rebaseline] | get | render | handover | gates
wb flow show | start | carry | set start, carry take --execute
wb impl check | verify
wb review context | gates
wb commit convention | check
wb pr context | check
wb git ctx | diff | commit | push commit, push take --execute
Exit codes: 2 usage, 3 config, 4 auth, 5 provider, 6 not found, 7 audit failed.
- configuration.md — contexts, matching rules, keychain, exit codes
- providers.md — internal schema, tracker quirks, adding a provider
- depth-policy.md — depth, expansion handles, output caps
- execution.md — scope guard, verification boundary, declared environment, git façade and execution
- CHANGELOG.md — what changed, and the deprecation policy
- status.md — the pipeline, the command history, rigour tiers, settled gates
- flow.md — source and validation branches, cherry-pick carrying, branch naming
These are for maintainers. Agents do not read them: the behaviour they describe is enforced in code, and loading them into a session would pay twice for the same guarantee.
PYTHONPATH="lib;tests" python -m unittest discover -s tests -q # Windows
PYTHONPATH="lib:tests" python -m unittest discover -s tests -q # macOS, LinuxCI runs the suite on Linux and Windows against Python 3.9 and 3.12
(.github/workflows/tests.yml).
Fixtures under tests/fixtures/ follow the vendors' published contracts, which
were checked against the documentation — endpoints, parameter names, response
shapes and link-direction semantics are verified. What they cannot cover is your
instance: custom fields, custom link types, and workflow state names. Replace
them with anonymised payloads from your own tenant to close that gap. See
providers.md.
MIT