From ticket to merged PR, without leaving Claude Code.
A Claude Code plugin of dev-workflow commands, skills, agents and guard hooks. Each command asks one question up front, then runs to the end, in any GitHub repo.
Quick start · Workflow · Commands · Per-repo profile · Companion plugins · Reference
- One question, then done. A command collects every decision its arguments leave open — target, scope, each push, PR, merge or tracker write — in one question, then runs without stopping.
- Works in any repo. A per-repo profile settles the base branch, trunk or git-flow, Jira or no tracker, drafts and reviewers. Nothing is hardcoded.
- Spec to PR, test first.
/implementwrites a plan, has a fresh context review it, writes failing tests, builds (through subagents when superpowers is installed), and maps every acceptance criterion to the test that proves it. - Fresh eyes on every PR. A background agent that did not write the code reviews it once for code and security issues, fixes what it confirms and pushes.
- Guardrails built in. Hooks block AI signatures in commits, PRs and docs, keep secrets out of prompts and tracked files, and stop pushes from the wrong
ghaccount. - See what runs. The optional cockpit shows the implement flow, each agent, cache health and rate limits beside the transcript.
# In Claude Code
/plugin marketplace add devmaxxx/bond
/plugin install bond@devmaxxxThen, in any GitHub repo:
/bond:branch add csv export --worktree # a fresh branch in a ready worktree
/bond:implement add a CSV export to the reports page
/bond:ship-pr # review, browser-test, mark ready
/bond:merge-pr # squash-merge once green
Every command is namespaced /bond:<name>; /bond:help lists them.
flowchart LR
A["/branch or /start"] --> B["/implement"]
B --> C["PR opened"]
C --> D["/ship-pr"]
D --> E["/merge-pr"]
C -. red CI .-> F["/fix-ci"]
D -. behind base .-> G["/rebase"]
F --> D
G --> D
/implement runs six phases, shown live in the cockpit. You can pause, stop, resume or rerun any phase:
Setup → Plan → Build → Verify → Review → Ship + PR
The build is inline TDD: one red→green slice per behaviour, typecheck and single test files as it goes, the full suite once in Verify, and one commit at the end — so the repo's commit hook runs once. --subagents switches to one implementer and one reviewer per task, and --thorough adds a plan-review agent; both are slower.
/implement --spec <path> with no ticket key is spec-only: no Jira, no upfront question, no browser, and library APIs checked against what is installed before any web lookup. The spec and the repo are the whole input.
--review-plan stops for approval after the plan, --checkpoints after every phase, and --resume picks a stopped run back up.
Every command except /help opens with one question carrying each decision its
arguments leave open — target, scope, and every push, PR, merge or tracker write
it will make — and none when the arguments settle it all; after the answer it
runs to the end. The rule is Commands ask first in shared/standing-rules.md,
and tests/commands-ask-first.test.mjs fails a command that lacks the step.
| Command | What it does |
|---|---|
/branch |
Start a branch — checked out or in a ready worktree — cut from the base trunk or git-flow picks |
/start |
Check out a fresh typed branch — creating the Jira issue first where there is a tracker |
/worktree |
open a worktree for a branch, ticket or PR (env files + deps), close it, prune merged and stale ones |
| Command | What it does |
|---|---|
/implement |
Fetch (or create) a Jira ticket — or take a free-text task or a --spec file where there is no tracker — then branch, plan (reviewed in a fresh context), tests first, code, and map every acceptance criterion to its test, each phase through its superpowers skill when that plugin is installed; --review-plan / --checkpoints stop for approval, --resume continues |
/implement-batch |
Implement many tickets (Jira version/epic/JQL, GitHub milestone) in parallel worktrees, scheduled so no two touch the same files |
/investigate |
Investigate a deployed failure to a proven root cause and write the investigation doc |
| Command | What it does |
|---|---|
/open-pr |
Open a PR for the current branch on GitHub |
/ship-pr |
After implement: browser-test the PR, tick its test plan, mark ready, loop review → fix → CI |
/review-pr |
Senior-reviewer PR review as one comment: fresh base merge, vertical + horizontal, claims checked against read-only prod data |
/merge-pr |
Merge one PR (the current branch's or a named one) once green: push first, squash-merge, remove its branch and worktree |
| Command | What it does |
|---|---|
/fix-ci |
Fix a failed pipeline from its URL (Woodpecker, GitHub Actions), a PR, or --mine: diagnose, fix, push, watch the re-run |
/rebase |
Rebase (or merge, for a shared branch) onto the base, resolve conflicts, re-test, push with --force-with-lease |
/sweep-pr |
Sweep open PRs: retarget the base, rebase, restart infra CI once, squash-merge the green ones under --merge, clean up |
| Command | What it does |
|---|---|
/jira |
Create, edit, assign, comment on, or transition a Jira issue (assigned to you by default) |
/linkedin |
Write a LinkedIn post or article from the source, facts and numbers checked, with an optional cover-image prompt |
/linkedin-draft |
Put an approved post or article into LinkedIn's composer or article editor through Chrome — a draft, never published |
/disk-analyze |
Analyze disk usage: runaway logs, deleted-but-open files, caches; clean the safe ones |
/help |
List all bond plugin commands with their descriptions |
shared/project-profile.md resolves, per repo, the base branch, the branch
model (trunk, or gitflow with feature work off develop and hotfixes off
the production branch), whether there is a Jira tracker at all, whether PRs open as drafts, and who the reviewers are.
A repo can state its own answers in .bond/project.json; otherwise they are
inferred from the origin remote. Without a manifest the tracker resolves to
none, and /implement takes a free-text task, cuts a
<type>/<description> branch and opens a PR with no ## Jira section.
Every command reads that profile rather than assuming a base branch or a tracker:
- CI-flexible —
/ship-prand/fix-ciwork against GitHub Actions and Woodpecker alike; the profile's Resolve PR coordinates and PR details and CI status procedures normalise the checks to one vocabulary, so no command branches on a raw status string. - Tracker-flexible —
/implementand/starttake free text or a--specfile where there is no Jira. - Guarded, not faked —
/jiragenuinely needs Jira. It says the prerequisite is missing and stops, rather than pretending to work.
// .bond/project.json — every field optional
{
"tracker": "jira",
"jiraSite": "example.atlassian.net",
"baseBranch": "main",
"branchModel": "gitflow",
"draft": true,
"reviewers": ["octocat"]
}Optional plugins from the same marketplace. Each builds on bond.
plugins/bond-cockpit/ is a mod: a plugin of function hooks (hooks/hooks.json →
modules) that runs inside Claude Code rather than as spawned node processes,
so it needs a build with plugin hook modules. It is opt-in and depends on bond:
// ~/.claude/settings.json
{ "enabledPlugins": { "bond-cockpit@devmaxxx": true } }Everything the cockpit shows
- Status line — the
/bond:implementflow while a run reports progress:feat/x ▰▰▰▱▱▱▱▱▱▱ 3/10 · Build,⏸before the phase while it is stopped; empty with no run and once every phase is done. The cache hit rate and countdown live in the strip and the pane, not here. - Pane — docks to the right of the transcript in the fullscreen layout from
110 columns, inline above the prompt otherwise; it opens only on
/bond-cockpit. Three tabs, main first and in front (ctrl+x tab cycles them;/bond-cockpit joborsessionopens one). main keeps only what matters now: the task, then a few lines from each other tab under a heading that opens it (press it, orj/sin the pane) — under job, what runs now and agents working with a workflow's finished/started count; under session, the cache hit and countdown, the main model with its own tokens and requests, and the 5h and context gauges (other limits only once they turn yellow or red). job has the full Job card, then the Cache card. session has the Session card. The Job card: the prompt being worked on, its elapsed time (took …once done), what runs now — the tool in flight with what it works on and for how long,approve?when the permission check asked,thinking, orwaiting for you— the tool count with failures and the last tool, aworkflowgauge of the run's finished over started agents with the script's name and run id, aflowgauge and phase list while a/bond:implementrun reports progress (⏸and the reason at a checkpoint), and two lines per agent loop (subagent, teammate or workflow agent): its name and model from the spawn, calls and run time, then its latest tool and last test, typecheck or lint counts — red when any failed, with a warning when it rangit pushorgh pr create. Working ones first, quiet after two minutes without a call, gone five minutes after finishing. The main tab'sagentsrow adds the red and pushed counts. Press an agent's name (ctrl+x into the pane, arrows, Enter) and an agent tab opens: its state, run, model, its last 12 calls with their ages, and a message line that sends it a note the way SendMessage does — to check on it or change what it does next. The Cache card wears the entry's state on its border and header (● warm,◐ expiring,○ expired): the hit rate beside one bar split into read / wrote / new tokens, then a TTL meter with the countdown, and advice only once there is something to do. Session stays grey: hit rate with a sparkline of the last 20 requests coloured by hit, cost relative to no cache with the input tokens saved after the write surcharge, and prefix rebuilds (requests under 50% hit) with how long ago the last one was; then amodelssection, every model that answered this session, subagents included, with its requests and tokens and the main thread's marked; then ausagesection of aligned gauges for the 5h and weekly rate-limit windows with time to reset and for the context fill, coloured at 75% and 90%. Its badge adds the session's cost. Bars stretch to the pane's width./bond-cockpitopens it. - Strip under the prompt — the main card, always there below the hint line
without opening a pane.
/bond-cockpit collapsefolds it to one line (state, flow, cache, 5h and context, cost),hidetakes it away,expand/showbring it back; the mode is kept across sessions, and there is no close. - Band above the prompt — on every tab. While a
/bond:implementrun reports progress: its line (branch, phase bar, count and the current phase, yellow with⏸and the reason while a checkpoint waits or after a stop; gone once every phase is done) and its buttons (ctrl+x tab to focus):⏸ pause(p, stops at the next phase boundary),■ stop(x, aborts the turn now),▶ resume(r, once it stands still) and↻ rerun <phase>; the job tab's phase list has a ↻ beside every phase reached, which runs/bond:implement --resume <branch> --from "<phase>". While the strip is hidden, a show cockpit button (c) brings it back. - plugins tab — profiles and every plugin and user skill, each with what it
adds to every request (its skills' listing plus its MCP tools) and how often
it was used (Skill and plugin MCP calls, all sessions). A press toggles one in
this checkout's
.claude/settings.local.json;▶ <profile>applies a profile (full, the default with everything on; minimal, job-search, dev, frontend, design, architecture, review, work, authoring),✎edits one (toggles then change the profile, kept across sessions),resethands everything back to the user settings, andreload pluginsputs/reload-pluginsin the prompt. bond-cockpit itself never goes off./bond-cockpit profile [<name>|reset]lists or applies one, and the model calls the same through themcp__bond-cockpit__profiletool, which the routing-plugins skill uses to pick a profile per session. - settings tab — built-in features that cost context: git and PR
instructions, the Artifact tool, bundled skills and workflows, claude.ai skills,
plugins and connectors, auto memory, workflows, a skill description cap
(full, 160, 80 chars), and each MCP server (
deniedMcpServers), each with its measured tokens and a toggle written to the same local file.
No request reports its cache TTL, so cacheTtl (default auto) infers it on
every request: a subscription past a rate-limit window is in overage and drops
to 5m; otherwise the last model switch's report; otherwise 1h on a subscription
and 5m on an API key. 5m or 1h fixes it. The card badge says overage,
set or API when the TTL came from one of those.
plugins/bond-oss/ creates, audits, demos and releases npm libraries on one stack
(pnpm, tsdown, Vitest, @antfu/eslint-config, ESM-only) and publishes them through
npm trusted publishing (OIDC, no token). Its skills — oss-lib-stack,
npm-publishing, oss-release-flow, oss-demos — carry the traps the libraries
already hit; templates/ holds the files a new lib starts from. Personal names
(GitHub owner, license holder, npm user) live in ~/.config/bond/oss.json, never
in the plugin.
// ~/.claude/settings.json (or a library's .claude/settings.local.json)
{ "enabledPlugins": { "bond-oss@devmaxxx": true } }| Command | Purpose |
|---|---|
/bond-oss:new-lib |
Free-name check, scaffold from templates/ts-lib, GitHub repo, CI + release workflows, README, examples, first PR |
/bond-oss:audit |
Check a lib against the stack and its alternatives; fix license, package, CI, README and examples drift in one PR |
/bond-oss:demo |
Examples run in CI, a GitHub Pages or Expo demo, GIFs recorded by committed scripts |
/bond-oss:release |
Milestone, release PR, merge on green, tag, one watched run, npm + GitHub release verified; bootstraps trusted publishing on a first release |
plugins/bond-beauty-crm/ drives beauty-crm's own task flow (pnpm tasks), which
bond's Jira/gh commands do not know. Enable it in the beauty-crm clone only:
// beauty-crm/.claude/settings.local.json
{ "enabledPlugins": { "bond-beauty-crm@devmaxxx": true } }| Command | Purpose |
|---|---|
/bond-beauty-crm:batch |
Close several milestone tasks that share files in one PR: claim, claim --join, done each, one pr |
/bond-beauty-crm:implement |
Start a task: claim its card, ask every question before the first edit (shared/questions-first.md), build, done, pr |
- readable-code-structure — splits long functions into small named ones and replaces awkward/clever control flow (search loops, N+1 in loops, nested ternaries, flag params) with plain expressions. Triggers on clean-up/refactor/"make this readable" requests and during review. Also carries the two mechanical rules that share its trigger exactly: brace every control body (one-liners and guard clauses included), and collapse repeated passes over one collection. Bundled under
skills/readable-code-structure/. - comment-hygiene — comments the why, deletes comments that restate the code, strips ticket IDs, and handles tool directives / TODOs / dead code / license headers. Triggers on writing or reviewing comments and on clean-up/"remove comments" requests. Bundled under
skills/comment-hygiene/. - testing-behavior — writes tests that pin the caller's contract, not the current implementation; refuses change-detector tests and tautologies, and stops to ask before enshrining suspicious behaviour. Triggers when adding, editing, or reviewing tests. Bundled under
skills/testing-behavior/. - vertical-horizontal-review — enforces a two-pass code review: vertical (trace one feature through every layer) + horizontal (sweep every sibling of the kinds the change touches for drift). Project-agnostic. Triggers on "review this change/diff/branch/PR". Bundled under
skills/vertical-horizontal-review/. - code-review — two vendored review corpora behind one skill.
stacks/: per-language and per-framework review guides (TypeScript, React, Vue, Angular, Svelte, NestJS, Python, Django, FastAPI, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, Ruby, C/C++, Zig, Qt, CSS) plus cross-cutting ones (N+1, concurrency, error handling, performance, architecture), from awesome-skills/code-review-skill (MIT).security/: OWASP-derived references per vulnerability class with the rule for when a pattern is a finding (CC BY-SA 4.0). Thepr-reviewand track reviewer agents and vertical-horizontal-review run the stack checklists;security-reviewreads the security references. Not Claude Code's built-in/code-review. Both directories are unchanged apart fromSKILL.mdrenamedGUIDE.md; refresh them from upstream rather than editing them. Bundled underskills/code-review/. - sharp-edges, differential-review, variant-analysis, insecure-defaults — Trail of Bits' security review skills.
security-reviewtriages a diff with differential-review (risk per file,git log -Son removed checks, callers of risky files), probes each verifier, parser, decoder and security flag with sharp-edges (zero, empty, malformed, oversized input, each footgun reproduced), judges config fallbacks with insecure-defaults, and hunts every surviving finding's variants with variant-analysis; the track reviewer runs sharp-edges as its footgun lens. Vendored from trailofbits/skills under CC BY-SA 4.0 — each directory carries itsLICENSEand aNOTICE.mdnaming the upstream commit and what changed; the first three are unchanged, and insecure-defaults pairs upstream's reference corpus with aSKILL.mdbond wrote, because upstream ships it as a Workflow pipeline. Refresh them from upstream rather than editing them. Bundled underskills/. - pr-review-comment — the vertical-horizontal review written up as one PR comment a human merges on: a fresh merge of the base, a one-line verdict first, operational claims backed by read-only prod or staging numbers (peak rates against the code's limits, source and window named), checks for queued work (idempotency, concurrency strategy, retries, what covered the gap), write-path completeness, rollback, stale description claims, a severity per finding, and open questions. Used by
/review-pr. Bundled underskills/pr-review-comment/. - pr-template — enforces the one shared PR title + description format (Summary / Jira / Test plan) and the reviewer rules on every pull request, sourced from
shared/pr-template.md. Triggers on "open/create/draft a PR" and manualgh pr createcalls. Bundled underskills/pr-template/. - photo-jpeg-exiftool — converts a photo to JPEG, resizes it or matches its colour profile with
sips, and reads, copies or strips its metadata withexiftool, all on this machine, never through a website. It copies tags only from the source file of the same shot and never invents a camera, a date or GPS. Disabled for the model (disable-model-invocation: true): it never triggers on its own and costs no context; run it by hand with/bond:photo-jpeg-exiftool. Bundled underskills/photo-jpeg-exiftool/. - authorship-conventions — naming and attribution for every git artefact: Conventional Branch
<type>/<description>, Conventional Commits subject, prose why body, one human owner, zero AI signatures (noCo-Authored-Bynaming a tool, noClaude-Session:, no "generated with") in commits, PR bodies/comments or docs — plus the rename trap: renaming a branch after its PR is open closes the PR. UnderTRACKER=jirathe<prefix>/<KEY>branch shape bond imposes wins; everywhere else the spec wins. Backed by thecheck-commit/check-dochooks. Triggers oncheckout -b, "commit", "amend", "open a PR", "write the ADR/plan/README". Also decides which of the twoghaccounts pushes — the active one is machine-global, so another session may have moved it since. Bundled underskills/authorship-conventions/. - routing-model-and-effort — picks a (model, effort) pair per task phase: opus/medium by default, high for a change over five files, across modules or untested, fable for planning only on the hard predicates, opus for every build unless a model is named, a fresh
bond:effort-<tier>subagent whenever the pair differs from the session, and recon — search, callers, a docs lookup, one check run — spawned onhaiku. Triggers when a task will change files or needs a plan, and when a model or effort comes up. Bundled underskills/routing-model-and-effort/. - routing-plugins — keeps the
fullprofile (everything on) by default and, when the user asks to trim context or/contextshows the listing heavy, picks one narrower profile from the repo and the task (job-search, authoring, work, review, architecture, design, frontend, dev, minimal), asks once, and applies it through the cockpit's profile tool to.claude/settings.local.json; profiles and why each plugin is in or out inreferences/profiles.md. Bundled underskills/routing-plugins/. - routing-code-review — routes the
/code-reviewlevel off the diff:highby default,medium/lowwhen the diff is small, single-module, tested and risk-free, a question formax, andultraonly recommended (the user launches and pays for it);--fixfor our own diff,--comment/--poston the user's word. Triggers when a review is about to be launched. Bundled underskills/routing-code-review/. - linkedin-writing — voice and format for LinkedIn posts and articles: facts from the repo, numbers checked against the vendor's docs, attribution left to the author, plain text for posts, one cover-image prompt with its technical description, and how to fill LinkedIn's editors in Chrome without publishing. Used by
/linkedinand/linkedin-draft. Bundled underskills/linkedin-writing/.
- effort-low / effort-medium / effort-high / effort-xhigh / effort-max — one agent per reasoning effort level; the caller passes
modelat call time.effort-high,effort-xhighandeffort-maxfall back tomodel: opuswhen the caller omits it;effort-lowandeffort-mediumfall back to the session default subagent model, so a cheap tier is not silently run on the most expensive model. Used by the routing-model-and-effort skill because effort is only settable through an agent definition. Bundled underagents/. - test-runner — runs one test, typecheck or lint command on haiku at low effort and returns only the failures: the runner's own counts on line 1, then at most 40
path:line: messagelines and… N more. A command that cannot start comes back as the first 5 lines of stderr. Hand it every check whose full output would otherwise land in the main context. Bundled underagents/. - ci-diagnose — reads one failed pipeline (Woodpecker or GitHub Actions URL) on sonnet at medium effort and returns per failed step its class — flaky, infra, code, or pre-existing on the base — a
file:linecause and at most 5 log lines. Read-only;/fix-ciacts on it. Bundled underagents/. - pr-watch — watches one PR until it can merge, on opus at medium effort: polls its CI every ~5 minutes, sorts each red check as
/fix-cidoes, fixes and pushes a cause the branch's own code made in the worktree it is given, and loops on the re-run until green, until only pre-existing, infra or human-owned causes are left, or until its rounds or deadline run out. Never merges, approves, force-pushes or reruns a cause twice. The standing rule «Waiting goes to an agent» sends every wait on a PR's CI through it. Bundled underagents/. - pr-audit — audits a PR that was just opened, on opus at high effort, in a context that did not write it: one pass of the code and security review pair over the PR's head, the verified findings fixed and pushed once — no second round, no browser run, no test-plan ticking — then removes the worktree. Never merges, approves, marks ready or moves the ticket. Bundled under
agents/. - pr-status — reads one PR once on haiku at low effort, state, mergeable, the CI verdict with one root-cause line per failed check, the review decision, and every unhandled inline thread, review body and conversation comment with its id (minus the ids the caller already handled). Read-only — never replies, resolves, reruns or polls.
/ship-prreads each settled round and its final state through it. Bundled underagents/. - pr-review — reviews one open PR's diff on opus at high effort: reads each hunk with its callers, verifies every finding before reporting it, and returns at most 15 ranked findings with
file:lineand the input that breaks it. Read-only — never edits, pushes or comments. Paired with security-review throughshared/pr-review-flow.md;/ship-prruns the pair right after the PR is opened, before the browser check. Bundled underagents/. - security-review — reviews a diff, PR or path for security defects on opus at high effort: access control and IDOR, injection, auth and sessions (verifiers fail closed on malformed input), footgun APIs and insecure defaults, SSRF, XSS, secrets, races on money, data exposure, and an audit when a lockfile changed — triaged, probed and variant-hunted with the vendored Trail of Bits skills. Verifies each finding and returns at most 15 with
file:line, the attack and the fix. Read-only. Always runs paired with pr-review throughshared/pr-review-flow.md(/ship-pr,/sweep-pr,/merge-pr,/review-pr, pr-audit), alongside Claude Code's built-in/security-reviewrun headless at the PR head with no setting sources; a critical or high finding from any of the three blocks. Bundled underagents/.
Under TRACKER=jira, bond calls one MCP server by the name bond-atlassian
(the official Atlassian remote server, https://mcp.atlassian.com/v1/mcp,
OAuth). bond does not install it: add it to the project's local scope under that
name with claude mcp add, so its tools appear as mcp__bond-atlassian__*.
Repos with no Jira need no MCP server — gh covers GitHub.
Anything in this plugin that needs a browser — reading a Jira attachment
or opening a failing page in /investigate —
uses claude-in-chrome whenever it is available. It drives the user's real
Chrome through the browser extension, so the SSO sessions are already there and
no profile, LaunchAgent or debug port has to be stood up first. Load the tools
with ToolSearch (select:mcp__claude-in-chrome__tabs_context_mcp,…) before
using them.
If it is not available, skip the step and say so rather than asking the user to sign in — never enter credentials on their behalf.
How bond works under the hood.
What every session is told, and how
SessionStart prints shared/standing-rules.md into every session — the rules
that used to live in a personal ~/.claude/CLAUDE.md, so the plugin carries
them instead of the machine. A plugin has no declarative way to ship always-on
instructions (skills and agents load on demand, and there is no auto-loaded
plugin CLAUDE.md), so a hook is the mechanism.
Set BOND_USER_NAME to the name replies should open with; it falls back to
git config user.name, and the greeting rule is dropped when neither resolves.
The name is never committed — this repo is public.
shared/permissions-readonly.json is a read-only allowlist — the git reads
(status, log, diff, show, branch, rev-parse), ls, wc, jq,
head, tail, pnpm exec repograph, pnpm tasks report, pnpm tasks blockers
and node --test: the calls that look at a tree without changing it. A plugin
cannot apply permissions, so bond cannot switch them on for you — copy the
permissions.allow entries into a project's .claude/settings.json (or the
uncommitted .claude/settings.local.json). Until then every one of those calls
is approved by hand, one at a time.
Every hook and the rule it enforces
-
SessionStartrunshooks/standing-rules.mjs: the standing rules above. Registered with no matcher, so it fires on every start reason — startup, resume, clear, compact and fork alike. That is deliberate: rules that do not survive a compaction quietly stop applying halfway through a long session, and a matcher that failed to parse would drop them silently. -
SessionStartandUserPromptSubmitrunhooks/branch-guard.mjs: the branch the session opened on is recorded under<tmp>/bond/<session_id>.branch, and the first prompt submitted after the working tree has moved to another branch gets one line of context naming both branches and suggesting/clear. Once per recorded baseline — a session that carries two branches pays the first one on every turn of the second, but a reminder repeated every turn costs the tokens it is trying to save, so it is said once and re-armed by the nextSessionStart(resume and compact reuse the session id, and the drift after one of those deserves a second word). A detached HEAD, a directory that is no git tree or an unwritable tmp means no nudge, never a blocked prompt. The rule isdecidein that file, unit-tested intests/branch-guard.test.mjs. -
SessionStartrunshooks/node-guard.mjs: a tree that pins node in.nvmrc(or.node-version) gets one line of context when the shell its Bash calls land in runs another version — both versions and thePATH="$(nvm which <pin> | xargs dirname):$PATH"prefix everypnpm/nodecommand then needs. Each Bash call is a fresh shell that does not carry nvm's PATH, so the prefix is per command rather than per session; met mid-session instead of at the start, the same fact costs a failed command per call (ERR_UNKNOWN_FILE_EXTENSIONon every TypeScript entry point). A shorter pin covers the releases under it —24is satisfied by24.16.0,24.1is not — while an alias only nvm could resolve (lts/*) and a tree with neither file say nothing. nvm is never invoked: the line names the prefix, it does not run it. The rule ischeckin that file, unit-tested intests/node-guard.test.mjs. -
SessionStartrunshooks/context-mode-guard.mjs: when a running context-mode server (any…/context-mode/<version>/start.mjs) was started on another node major than this session, one line names both majors, the plugin root and the heal — only in a session below node 22.5, since from 22.5 context-mode opens its database throughnode:sqliteand the swap cannot reach it. context-mode keeps one activebetter_sqlite3.nodeper plugin version, shared by every session, and each session's hooks overwrite it with the build for their own node, so a server below 22.5 that opens its database after another session's swap fails everyctx_*call with "compiled against a different Node.js version" — and repositories pinning 16 to 24 make that the normal case. The heal is the plugin's ownhooks/ensure-deps.mjsensureNativeCompat, which copies the cached build back without a rebuild (npm rebuild better-sqlite3where a version no longer ships it). The binary each server runs comes from onelsofcall on macOS and/proc/<pid>/exeon Linux; a path that carries no version (/usr/local/bin/node) counts as unknown and says nothing. The rule ischeckin that file, unit-tested intests/context-mode-guard.test.mjs. -
PostToolUseruns prettier on any file edited viaEdit,Write, orMultiEdit(no-op when prettier is not available in the project), thenhooks/check-doc.mjsscans a just-written*.md|mdx|txtfor AI signatures and reports the lines back. -
PreToolUseonBashrunshooks/check-commit.mjs: agit commit/gh pr …whose message carries an AI signature (Co-Authored-Bynaming a tool,Claude-Session:, "generated with") or a non-Conventional-Commits subject is blocked with the reasons. Patterns live inhooks/ai-breadcrumbs.mjs; see theauthorship-conventionsskill. It runs on agent-base-kit, vendored ashooks/vendor/agent-base-kit.mjs(bond has nopackage.json), so it fails closed: a payload it cannot read or a check that throws blocks the command rather than letting it run unchecked.scripts/vendor-agent-base-kit.sh <version>refreshes the copy from npm. -
PreToolUseonBashrunshooks/check-pr.mjs: a PR whose title or body misses the shared Summary / Test plan shape, is not opened as a draft where the repo asks for one ("draft": truein.bond/project.json), or carries an AI signature is blocked with the reasons. See thepr-templateskill andshared/pr-template.md. The rule lives inhooks/pr-template.mjs: it recognises the PR command only where the shell would run one — not inside a heredoc body, a quoted string or a comment — and treats only the configured Jira project keys as ticket ids, soUTF-8andSHA-256are prose; it demands a## Jirasection only where.bond/project.jsonsays"tracker": "jira". -
PreToolUseonBashrunshooks/bash-budget.mjs: context only, never a decision — the command always runs. Ten calls in a row that each carry a single command get one line naming the three cheaper forms — chain the next ones with&∨, issue them in one message, or hand the loop to a subagent; measured over thirty days in one repo, 6481 Bash calls averaging 1.6 KB of output, where the price is not that output but the whole conversation being re-read on every one of them. Four shapes whose output has no bound get one line naming the bounded form —git logwithout-n/--oneline,catof one whole file,ls -Rorfindwithout-maxdepth, a test run without--reporter. At most one line per call and the batch nudge wins; the row is counted in<tmp>/bond/<session_id>.bash-singles, whose own mtime says whether the previous call came from a turn of its own — a counter written under a second ago means both calls were dispatched in one message, which is already the batching asked for, so that call does not lengthen the row. An unwritable tmp costs the batch nudge, never the call. Separators are read where the shell would run them, so"a && b"and a heredoc body carrying&&are each one command. The rule isjudgein that file, unit-tested intests/bash-budget.test.mjs. -
PreToolUseon the subagent dispatch —Agent, orTaskwhere the harness still names it that — runshooks/recon-router.mjs: context only, never a decision — the agent is dispatched either way. A prompt handed to the unnamed or the general-purpose agent that asks where something lives, what or who calls it, which file holds it or how it is wired gets one line naming who answers it cheaply —repo-scoutwhere the working tree has one under.claude/agents/, otherwiseExplorewith a breadth, either onmodel: "haiku". The general-purpose agent pays that search in full and returns everything it read; the other two return the conclusion. A dispatch to a reader (Explore,repo-scout,caveman:cavecrew-investigator) that names no model gets one line asking forhaiku; one that names a model has made the choice. So has a prompt that builds — one carryingimplement,add,write,refactor,fix,update,create,migrateoreditas a word — since the agent that writes the code reads it on the way there. The rule isroutein that file, unit-tested intests/recon-router.test.mjs. -
PreToolUseonBashrunshooks/gh-account-guard.mjs: agit pushor aghwrite (PR, issue, release, label, run,gh apiwith a write method) that would go out under the wrong GitHub account is blocked with the command that would not — a per-commandGH_TOKEN=$(gh auth token -u <account>), or for an SSH remote whose key belongs to the other account an HTTPS push with that token plus thegit fetchthat refreshesorigin/<branch>. Nevergh auth switch: the active account is machine-global. Configured byBOND_GH_ACCOUNTS(owner=account,…) andBOND_GH_SSH_ACCOUNT; with neither set it blocks nothing. Unit-tested intests/gh-account-guard.test.mjs. -
UserPromptSubmit, andPreToolUseonBash/Write/Edit/MultiEdit/NotebookEdit, runhooks/secrets-guard.mjs: a prompt carrying a live-looking secret — GitHub, Atlassian (API and access tokens), Figma, Slack (tokens and webhook URLs), OpenAI/Anthropic, AWS (key IDs and secret keys), PEM and PGP private keys, Telegram bot tokens, database URIs with an inline password (Postgres, MySQL, MongoDB, Redis, AMQP), JWTs — is blocked before it is stored in the session, naming the kind and never the value and without echoing the prompt back (suppressOriginalPrompt);#allow-secretsends it anyway. A tool call that would write a literal secret into a file git tracks, a commit, aghbody (inline, or in the--body-file/-Ffile it sends) or acurlline is blocked, unless the file already held that secret; a gitignored file, a loneecho/printf/catinto a gitignored.env*,claude mcp addoutside project scope, a$VARreference, a placeholder and a localhost dev URI pass. It runs on the vendored agent-base-kit, which fails the tool-call check closed (a payload it cannot read blocks the call) and the prompt check open (a fault lets the prompt through). Unit-tested intests/secrets-guard.test.mjs. -
SessionStart,PreCompactandSessionEndrunhooks/session-memory.mjs:save(PreCompact, SessionEnd) reads the transcript and keeps the last five prompts, each cut to one line, the files the session edited and its branch in~/.claude/bond/sessions/<repo>-<path>.json;restore(SessionStart) prints that note to the next session in the same directory — on startup or resume only, never after a compaction or a clear (the summary already carries it), never to the session that wrote it, and not once it is a week old. Small on purpose, because whatever SessionStart prints is re-read on every turn: five prompts of at most 160 characters, and a file list cut, with a "+N more" tail, so it never takes the note past 1 KB. An empty session does not overwrite a real one's note. The rules aredigestandrender, unit-tested intests/session-memory.test.mjs. Adapted from everything-claude-code's memory-persistence hooks. -
SessionStart(on a resume) andUserPromptSubmitrunhooks/resume-work.mjs: when the last turn was cut off by a usage limit, an API failure (lost connection, sleep, expired login) or a full disk (No space left on device/ENOSPCin the turn's last tool output), one line names the cause and tells the model to continue the task where it stopped — re-check state, skip finished steps, do not re-ask. A turn stopped with Esc is not resumed, and a disk still under the floor asks for space first. Each pause is announced once per session. The rules arefindPauseandadvise, unit-tested intests/resume-work.test.mjs. -
SessionStartandUserPromptSubmitrunhooks/disk-guard.mjsasync: under 5 GiB free on the home volume (BOND_DISK_MIN_FREE_GB), it runsscripts/disk-analyze.sh cleanon the caches that rebuild themselves — pnpm, npm, yarn, uv, pip, go, gradle, Xcode DerivedData; never the Trash or the Chrome debug profile — at most once an hour across sessions, and reports what it freed on the next turn. Still low after that, it points at/bond:disk-analyze. The rule isreport, unit-tested intests/disk-guard.test.mjs. -
PreToolUseon every tool runshooks/suggest-compact.mjs: context only. At the 50th call since the last compaction, and every 25th after, one line suggests/compactif a phase just ended — auto-compaction fires wherever the window fills, a chosen one keeps what the next phase needs.PreCompactresets the count in<tmp>/bond/<session_id>.tool-calls. The rule isjudge, unit-tested intests/suggest-compact.test.mjs. Adapted from everything-claude-code's strategic-compact hook.
The hook rules are unit-tested with the Node test runner — no dependencies, no
install step. Covered: the two matchers the commit and PR hooks decide on —
whether a command really runs git commit or gh pr, and whether a call really
opens a pull request — along with the AI-signature patterns they share with
check-doc.mjs; the ticket-key rule; the standing-rules rendering; and the
context hooks through their decision functions, decide (branch-guard), judge
(bash-budget, suggest-compact), digest/render (session-memory), route (recon-router), check (node-guard, context-mode-guard), findPause/advise (resume-work) and report (disk-guard), each with its
wrapper driven over stdin as well. Every SKILL.md is held
to its size limit and to the references/ files it links, and check-commit.mjs
is driven end to end over stdin:
node --test 'tests/**/*.test.mjs'bond-cockpit is tested by Claude Code's own plugin runner — the cache view
directly, and the status line, strip, band and pane on the terminal and desktop surfaces:
claude plugin validate plugins/bond-cockpit && claude plugin test plugins/bond-cockpitRepository tree
bond/
├── .claude-plugin/
│ ├── plugin.json # plugin manifest
│ └── marketplace.json # marketplace entry (single-plugin repo)
├── commands/ # slash commands
├── skills/ # each SKILL.md ≤ 3 KB; the detail sits in <skill>/references/
│ ├── readable-code-structure/ # small named functions, plain control flow, braces, one pass
│ ├── comment-hygiene/ # comment the why, delete the what
│ ├── testing-behavior/ # test the contract, not the implementation
│ ├── vertical-horizontal-review/ # two-pass review: depth + sibling sweep
│ ├── code-review/ # vendored stack guides (MIT) + OWASP security references (CC BY-SA 4.0)
│ ├── sharp-edges/ # vendored Trail of Bits: footgun APIs, malformed-input probes (CC BY-SA 4.0)
│ ├── differential-review/ # vendored Trail of Bits: risk triage, blame, blast radius (CC BY-SA 4.0)
│ ├── variant-analysis/ # vendored Trail of Bits: the same root cause elsewhere (CC BY-SA 4.0)
│ ├── insecure-defaults/ # Trail of Bits' corpus + bond's SKILL.md: fail-open config (CC BY-SA 4.0)
│ ├── pr-review-comment/ # that review as one PR comment, backed by prod data
│ ├── pr-template/ # one shared PR title + description + reviewers
│ ├── photo-jpeg-exiftool/ # JPEG via sips, metadata via exiftool, local only
│ ├── authorship-conventions/ # branch/commit/PR/doc naming, attribution, gh account
│ ├── routing-model-and-effort/ # (model, effort) pair per task phase
│ ├── routing-plugins/ # plugin and skill profile per session
│ └── routing-code-review/ # /code-review level, target and flags per diff
├── agents/
│ ├── docs-explorer.md # look up official docs before using a third-party API
│ ├── test-runner.md # run one check, return only the failures
│ ├── pr-status.md # one PR's CI, review and unhandled comments, compact
│ ├── pr-review.md # one PR's diff reviewed, verified findings ranked
│ ├── security-review.md # security defects in a diff, PR or path, verified
│ ├── pr-audit.md # a new PR audited in a fresh context: one review pass, fixes pushed
│ ├── ci-diagnose.md # one failed pipeline: class, cause, short excerpt
│ └── effort-{low,medium,high,xhigh,max}.md # one agent per effort level
├── shared/
│ ├── implement-flow.md # shared procedures used by /implement
│ ├── superpowers.md # which superpowers skill each implement phase runs
│ ├── merge-conflicts.md # detect, rebase or merge, resolve, prove — ship-pr, rebase, sweep-pr
│ ├── project-profile.md # per-repo host, base, tracker, reviewers
│ ├── standing-rules.md # always-on rules, printed by the SessionStart hook
│ ├── pr-review-flow.md # code review + bond's and Claude Code's security reviews, in parallel
│ ├── pr-template.md # single source of truth for PR title + description
│ └── permissions-readonly.json # read-only allowlist to copy into a project
├── scripts/
│ ├── disk-analyze.sh # disk usage report behind /disk-analyze
│ └── vendor-agent-base-kit.sh # refresh hooks/vendor/agent-base-kit.mjs from npm
├── hooks/
│ ├── hooks.json
│ ├── format-file.sh # PostToolUse: prettier on the edited file
│ ├── shell.mjs # is this command really *running* X?
│ ├── standing-rules.mjs # SessionStart: print the always-on rules
│ ├── render-rules.mjs # what the session actually reads
│ ├── branch-guard.mjs # SessionStart + UserPromptSubmit: the branch moved under this session
│ ├── bash-budget.mjs # PreToolUse: a row of one-command calls, output with no bound
│ ├── recon-router.mjs # PreToolUse: who answers a "where is X" prompt cheaply
│ ├── node-guard.mjs # SessionStart: the shell node is not the version .nvmrc pins
│ ├── resume-work.mjs # SessionStart (resume) + UserPromptSubmit: continue a turn a limit, API error or full disk cut off
│ ├── disk-guard.mjs # SessionStart + UserPromptSubmit (async): under 5 GiB free, clean rebuildable caches
│ ├── context-mode-guard.mjs # SessionStart: another session's context-mode server runs another node major
│ ├── session-memory.mjs # PreCompact/SessionEnd save, SessionStart restore: last session's note
│ ├── suggest-compact.mjs # PreToolUse: a /compact reminder at 50 calls and every 25 after
│ ├── ai-breadcrumbs.mjs # shared AI-signature patterns
│ ├── check-commit.mjs # PreToolUse: block git commit / gh pr with a signature
│ ├── gh-account-guard.mjs # PreToolUse: block a push or gh write from the wrong account
│ ├── vendor/agent-base-kit.mjs # hook runner (PreToolUse fails closed, prompt and Stop open), vendored from npm
│ ├── pr-template.mjs # the PR rule: command matcher, ticket keys, sections
│ ├── check-pr.mjs # PreToolUse: block a PR that breaks the shared template
│ └── check-doc.mjs # PostToolUse: flag a written md/txt with a signature
├── tests/
│ ├── pr-template.test.mjs # node --test 'tests/**/*.test.mjs'
│ ├── render-rules.test.mjs
│ ├── branch-guard.test.mjs
│ ├── bash-budget.test.mjs
│ ├── recon-router.test.mjs
│ ├── node-guard.test.mjs
│ ├── resume-work.test.mjs
│ ├── disk-guard.test.mjs
│ ├── context-mode-guard.test.mjs
│ ├── skill-size.test.mjs
│ ├── shell.test.mjs
├── plugins/bond-cockpit/ # implement-flow status line, strip + pane mod (function hooks)
├── plugins/bond-oss/ # open-source library create/audit/demo/release (enable globally)
│ └── .claude-plugin/plugin.json # depends on bond
├── LICENSE
└── README.md
Run the plugin from a local checkout:
Add this directory as a local marketplace:
/plugin marketplace add /path/to/bond
/plugin install bond@devmaxxxRun node --test 'tests/**/*.test.mjs' before a PR. Releases bump plugin.json and marketplace.json and tag through the repo's /release command.
MIT — see LICENSE — except the vendored Trail of Bits skills under skills/sharp-edges/, skills/differential-review/, skills/variant-analysis/ and skills/insecure-defaults/, which stay CC BY-SA 4.0 under their own LICENSE.