A Claude Code plugin bundling the git/GitHub delivery workflow as six composable skills. Commit mechanics, the single-PR lifecycle, the tiered babysit fleet loop, worktree lifecycle management, convention setup, and merge-conflict resolution.
Builds a commit the safe way: drafts a subject matching the resolved
convention, the layered source-control.md config (written by
/source-control:setup) → the consuming project's own
CLAUDE.md/rules/commit-msg hook → Conventional Commits (11-type vocabulary)
as the default. Pre-checks it against the
pattern before git runs, appends a Co-authored-by: Claude … trailer, and
feeds the message via Bash heredoc (git commit -F - --cleanup=verbatim),
never PowerShell here-strings, never scratch files in .git/. Stages
surgically (git add <path>, never -A), and supports pathspec-limited
commits when the index is shared with a concurrent session. Right after
staging, it fixes the exec bit on newly-added shebang files and runs the
consuming repo's own formatter/linter (when one is discoverable) against the
staged files. Catching what CI's exec-bit and lint lanes would otherwise
catch after the push round-trip.
Orchestrates the PR lifecycle with two non-negotiable gates, every review finding is verified before it is presented, and every CI fix is research-gated:
- prep. Review the branch diff (via your review agents/skills when installed, inline otherwise), verify findings, simplify, then run the project's build+test+lint gate as a hard block.
- create. Branch-name check, default-branch rebase, unrelated-changes
triage,
Closes #Nderivation from the branch name (validated against the live issue), safely-assembled PR body,gh pr create. - monitor. Async event loop over CI checks + review comments. Event
delivery prefers a push channel when your environment ships one, falls back
to a session-persistent Monitor watch (30s
ghpoll), or plainghpolling in cloud sessions. CI failures are read from complete logs via the bundled annotation/ZIP fetch scripts (gh run view --log-failedtruncates); every reviewer comment gets explore → research → classify → react → reply → fix → verify-on-GitHub treatment. - merge. 6-Gate readiness re-verification, squash merge, worktree reuse/cleanup, post-merge CI health check. Never auto-merges.
- fetch-logs. Tiered CI-log retrieval (annotations → full untruncated ZIP via the REST API → per-job text).
Tiered, self-pacing fleet loop over your own open PRs (designed for
/loop /source-control:babysit-prs):
- safe (default). Discovers YOUR open PRs under the current repo's
owner (or the configured watched owners), checks each out, keeps the
branch fresh, processes every review finding individually with
GitHub-verified evidence per the plugin-scope shared review discipline,
finding classification mechanically gated by the bundled
babysit-readiness-gate.sh. Never resolves threads, never merges, an engine-backed run reports merge-readiness from a read-only merge-gate run; the Python-free degrade has no merge gate and reports it unchecked. - worker (explicit keyword). Everything safe does, plus auto-resolving
pre-push-outdated bot threads and merging PRs a deterministic gate proves
100% ready (
mergeStateStatus == CLEANplus explicit cross-checks, with an expected-head pin carried to GitHub's server-side match-head-commit guard). Requires Python 3 (stdlib only). - autopilot (explicit keyword). Maximum autonomy for a solo owner: every author under the watched owners, resolves any thread it has addressed, merges through the same gate, escalates only what it genuinely cannot solve. Requires Python 3.
Cross-tier invariants: never an unprotected force-push, never --admin,
never GitHub settings or branch-protection changes, never a repository
outside the watched owners, and dependency-manager-authored PRs
(Dependabot/Renovate-class) are never merged autonomously in any tier. A
merge-capable tier engages only when the invocation names it, the
configured default_tier applies to explicitly typed invocations only,
never to auto-routed conversational matches. The two guarded mutations run
only through the plugin's pinned wrappers (source-control-babysit-merge,
source-control-babysit-resolve-thread), which fail closed without an
owner allowlist and reject unattended unpinned merges.
Git worktree lifecycle for parallel-session isolation: create (guided
naming, EnterWorktree, post-create setup checks), status (porcelain parse,
batched PR cross-reference, staleness classification, unclaimed-lock
report), cleanup
(file-lock-aware removal that never counts a Windows husk as deleted, emits
destructive branch deletion for the user), audit (configuration health,
including linked worktrees with no lock reason).
Supported interface: scripts/worktree-create.sh --existing-branch <name> checks out
an existing local branch into a new worktree instead of creating one, and cannot be
combined with --base-ref. Exit codes are listed in the script header. Known
consumer: /repo-fleet-hygiene:sync. A consumer outside this plugin must locate the
script through the installed plugin root, never a monorepo-relative path.
check (read-only, default) reports the effective commit-subject / PR-title
convention, one row per key with the config layer that supplied it, and the
babysit-prs userConfig surface. apply interviews the repo and writes the
convention config. Inferring first from the repo's own CLAUDE.md/rules,
commit-msg hook, or git log history before asking, and offering Conventional
Commits (11-type vocabulary) as the recommended default or a custom pattern
(e.g. a ticket-prefix regex) for orgs that don't use Conventional Commits.
Supply subject_pattern= to write it non-interactively, and
layer=user|team|local to pick which layer receives it (default: the tracked
team file). Re-runnable to reconfigure.
Resolves in-progress merge/rebase/cherry-pick conflicts intent-first: reads
the history behind BOTH sides of every hunk before editing (log/blame,
commit messages, PR/issue context via gh when available), composes both
changes by default, and drops a side only with evidence, never mechanical
--ours/--theirs picking. After the markers are gone it sweeps for
semantic conflicts the merge machinery can't flag (renamed symbol vs new
call site) and runs the project's build/test gates before concluding via
--continue. --abort is never a resolution strategy. Only an explicit
user decision to abandon the integration.
A PreToolUse hook on the Bash tool. When a gh pr create / gh pr edit
carries a PR body the hook can read statically, it validates that body against
the same contract the repository's required PR-contract check enforces,
a closing keyword (or an explicit no-linked-issue marker) plus four non-empty
contract sections (## Summary, ## Fix, ## Verification, ## Related),
and blocks the call with every missing or empty requirement named, so the
failure surfaces before the PR exists rather than a CI round trip later.
/source-control:pull-request create already gates its own body; this covers
the calls that bypass the skill.
Enforcement is keyed to the consuming repository's own policy: it runs only
where one of the repo's .github/workflows/*.yml / *.yaml files uses: the
pr-contract composite step (the SHA-pinned
melodic-software/ci-workflows/.github/actions/pr-contract@<sha> form, or
ci-workflows' own local ./.github/actions/pr-contract). A body the hook
cannot read statically always passes: an unexpanded variable, an absent body
flag, a body flag with no value, an unreadable file, a --repo-targeted
invocation, or a call following a cd/pushd on the same command line, which
moves the directory the workflow scan and any relative --body-file resolved
against. Set pr_body_linkage_gate_enabled to false to turn it off.
The registration carries an if filter, Bash(*gh *), the same shape as the
Bash(*worktree*) filter on the worktree gates, so the hook process is spawned only
for a command line that carries gh followed by a space somewhere in its text (Claude Code checks each
subcommand of a compound command, and runs the hook regardless when it cannot tell what
a command expands to). The leading wildcard is deliberate: the if field matches the
command name, so the narrower Bash(gh *) never launched the gate for a wrapped call
such as env GH_TOKEN=x gh pr create, sudo gh pr create or
bash -c "cd x && gh pr create", whose first word is not gh. The wider filter is a
superset of the hook's own first check (a gh word anywhere on the line), so nothing
it would have judged is skipped; a plain git status still does not pay for it, and a
non-gh line that happens to contain gh followed by a space (echo high tide) pays one bash start
before the hook's own jq-free regex pre-filter dismisses it. What the filter still
cannot see is a gh that only appears after a $(), a backtick or a $VAR expands;
Claude Code spawns the hook regardless for such a command, so the gate still judges
it, and the dotfiles fan-out harness reports those spawns as RAN(best-effort) on its
$() sample.
Its share of the hook budget,
counted as kernel process creations rather than wall time: the host that
reported this hook timing out pays 0.3-0.9 s per spawn, so wall time there says
more about contention than about the hook. Counted with
strace -f -e trace=clone,clone3,fork,vfork,execve, telemetry sink off:
| Path | clone-family | execve |
|---|---|---|
A gh call with no pr in it |
7 | 2 |
gh pr create with a readable body (allow or block) |
11 | 3 |
pr-linkage-mcp-gate, an MCP create (allow or block) |
11 | 4 |
Two of each execve count belong to lib/hook-utils.sh, not to these hooks:
one jq -e . validating the payload and one git rev-parse resolving the repo
root. The gates themselves spend one batched jq over the whole payload, plus,
on the MCP surface only, the git remote get-url its origin-match scope guard
needs. hooks/pr-linkage-spawn-budget.test.sh holds these numbers as ceilings
with no headroom, and proves itself non-vacuous against three mutants.
The hook emits one structured
hook-telemetry envelope per
run to whatever HOOK_TELEMETRY_SINK names. Carrying status (blocked on a
block, ok otherwise), duration_ms, and a data payload of labels only: the
outcome and which body form was read (body-literal, body-file,
stdin-heredoc, body-substitution). Never the PR body, the command, or a
path. Unset HOOK_TELEMETRY_SINK → no-op.
The MCP-surface sibling of pr-body-linkage-gate: a PreToolUse hook on the
GitHub MCP server's create_pull_request / update_pull_request tools, which
is how cloud/remote sessions, where the gh CLI doesn't exist, open PRs. It
covers both the mcp__github__<tool> names and the
mcp__plugin_<plugin>_github__<tool> names of a plugin-bundled server.
Same contract, same authority (a workflow in the consuming repository's own
.github/workflows/ that uses: the pr-contract composite step), same
block-with-the-fix-named behavior. The MCP payload hands over the body as a plain JSON field, so the
Bash sibling's static-readability caveats don't apply here; the scope guards
that remain are the workflow scan above, an origin-remote match on the call's
owner/repo (another repository's PR is not this repo's policy), and an
update_pull_request that carries no body field, which changes nothing CI
already validated and passes. A create_pull_request with no body at all
blocks. GitHub would open the PR with an empty body, which the CI check
rejects. Set pr_linkage_mcp_gate_enabled to false to turn it off.
Telemetry matches the sibling's: one envelope per run (ok/blocked,
duration_ms, and the tool name as its only data label), only when
HOOK_TELEMETRY_SINK is set.
A PostToolUse hook on the Bash tool. After a raw git worktree add it
claims the parsed add target (same tokenizer / git -C / wrapper
chdir composition as the containment sibling) with a session-distinct
reason. It does not claim every unlocked tree. Two concurrent adds
must not assign both to whichever hook runs first. echo git worktree add is not a git call. worktree-create.sh already locks the trees it
creates; this hook is the route for the adds that bypass the helper.
Existing reasons are never rewritten. The lock is a claim other agents
can read; it does not block concurrent writes (git-worktree(1)).
The worktree scripts (worktree-claim.sh, landed-work.sh, worktree-facts.sh list)
require git 2.36.0 or newer for git worktree list --porcelain -z; on older git they
fail closed with a message naming the floor and the installed version.
scripts/worktree-claim.sh report lists unclaimed linked worktrees;
check-enter <path> --session-id <id> surfaces a foreign live claim and
stops; release <path> --session-id <id> unlocks a claim this session armed
and refuses a foreign one; stale <path> is a read-only test (exit 0) that a
claim's session has no live transcript on this host, which cleanup combines
with a merged or landed branch before offering to remove a locked worktree. Set worktree_add_claim_gate_enabled to false to turn the hook
off; the script remains the documented gate.
This hook and its PreToolUse sibling worktree-add-containment-gate are registered
with the if filter Bash(*worktree*): the hook process is spawned only for a command
whose text carries worktree, which is also each hook's own first check, so every
git worktree add spelling they judged before (including git -C <dir> worktree add
and wrapped forms) still reaches them, and every other Bash call no longer pays for
two hook processes. The same best-effort caveat applies: a command containing $(), a
backtick or $VAR spawns both processes whatever its text, since the filter cannot see
what the substitution expands to.
The two Bash-matcher worktree gates and the WorktreeCreate gate, counted as kernel
process creations rather than wall time: the host that reported these hooks timing out
pays 0.3-0.9 s per spawn, so wall time there describes contention more than it describes
the hook. Counted with strace -f -e trace=clone,clone3,fork,vfork,execve:
| Path | clone-family | execve |
|---|---|---|
Either Bash gate, a worktree command that is not an add |
7 | 2 |
worktree-add-containment-gate, an add it allows |
18 | 5 |
worktree-add-containment-gate, an add it blocks |
18-22 | 5-7 |
worktree-add-claim-gate, a parsed add target |
22 | 6 |
worktree-create-gate, before it reaches the placement helper |
13 | 4 |
Four of the seven creations on the not-an-add path belong to
lib/hook-utils.sh, not to these gates: the
hook::buffer_stdin substitution and hook::json_complete's printf | jq -e .. The
gates' own share of that path is one printf '%s' "$INPUT" | jq field read, 3 creations
and 1 execve. That feed is the form the library prescribes and is kept on purpose: a
here-string would cost 1 creation, but bash fills a here-string's pipe itself and
deadlocks at the pipe capacity on Git Bash (#1587), which on a hook is the timeout. The
block-path spread is the ancestor walk: a .git-directory target asks git rev-parse a
third time, and a block message that names a configured root reads the
worktreeroot.path key. hooks/worktree-gates-spawn-budget.test.sh holds these
numbers as ceilings, proves itself non-vacuous against seven mutants, one per change, and
fails when a gate feeds its payload to a reader by here-string.
- Self-contained. Everything else runs on
git,gh(authenticated),jq, and Bash scripts bundled under${CLAUDE_PLUGIN_ROOT}(Git Bash on native Windows);unzipis additionally required by the CI-log fetch path (fetch-failed-logs), which exits with a remediation message when it is absent. Transient CI-log scratch goes to${CLAUDE_PLUGIN_DATA}(ormktemp). - Graceful degrade. Adjacent capabilities (review agents, a simplifier, a verify skill, a research skill, a work-item tracker, a CI-log-audit agent, a GitHub-events push channel) are used when your environment provides them and replaced by inline guidance when absent. No phase blocks on a missing tool.
- Reads your conventions, assumes none. Commit-subject / PR-title
convention resolves from the
source-control.mdconfig (written by/source-control:setup) first, a~/.claudeuser-global file, the tracked team file, and a gitignored.claude/source-control.local.mdpersonal overlay, merged per key, then the consuming project's ownCLAUDE.md, rules, and hooks; branch naming, PR template, merge style, and bot-identity wrappers also come from the project's ownCLAUDE.mdand rules. Defaults (Conventional Commits, squash merge) apply only when the project declares nothing.
- Node.js on
PATH. Every hook row launches throughnode hooks/exec-bash.mjs, and Claude Code's native binary neither ships nor uses Node (setup, fetched 2026-09-29). Withoutnodethe hooks do not launch and the PR-linkage and worktree gates are not enforced. The setupcheckreports whethernoderesolves.
/plugin marketplace add melodic-software/claude-code-plugins
/plugin install source-control@<marketplace>/source-control:babysit-prs is configured through the plugin's native
userConfig surface, the /plugin dialog, or
claude plugin install --config KEY=VALUE for headless installs; run
/source-control:setup for guided check/apply. Every key is optional:
zero-config behavior is the safe tier over your own PRs under the current
repo's owner.
| Key | Type | Default / absent behavior |
|---|---|---|
lane_instance |
string | sanitized lowercased hostname (writer identity suffixing babysit-loop's telemetry marker; must be distinct across concurrent lane instances) |
pr_body_linkage_gate_enabled |
boolean | true (the PR-body hook above; inert in a repo with no workflow using the pr-contract step) |
pr_linkage_mcp_gate_enabled |
boolean | true (the MCP-surface sibling; inert in a repo with no workflow using the pr-contract step) |
babysit_watched_owners |
string (multiple) | infer the current repo's owner |
babysit_self_logins |
string (multiple) | your gh api user login (extras add to it) |
babysit_default_tier |
string | safe (explicit invocations only) |
babysit_merge_method |
string | repo convention, then squash |
babysit_review_trigger_phrase |
string | review-trigger module dormant |
babysit_review_bot_logins |
string (multiple) | review-trigger module dormant; merge gate's review-settle hold dormant |
babysit_review_gate_context |
string | review gate treated as absent |
babysit_review_settle_minutes |
string | review-settle hold dormant (pair it with babysit_review_bot_logins) |
babysit_ci_gateway_context |
string | gateway check unused |
babysit_extra_bot_logins |
string (multiple) | structural bot detection only |
babysit_extra_dependency_manager_logins |
string (multiple) | built-in dependabot/renovate dependency-manager set only |
babysit_approval_downgrade_logins |
string (multiple) | an approval carrying blocking-looking prose is downgraded to ignored structurally (every bot); a named login instead surfaces its own as material. Real APPROVED-state reviews and plain clean approvals are ignored regardless. |
babysit_skip_downgrade_logins |
string (multiple) | downgrade heuristic dormant |
babysit_max_quiet_recheck_seconds |
number | 14400 |
babysit_stuck_check_age_seconds |
number | 1800 (min age before a pending non-required check under UNSTABLE reports stuck) |
babysit_advisory_fix_round_cap |
number | 100 |
babysit_worker_concurrency_cap |
number | 10 |
babysit_worktree_root |
directory | worktrees/ under the plugin data dir |
promotion_evidence_binding |
file | no promotion-evidence bootstrap; every promotable cell stays effective-unpromoted (absolute path to the security binding document, outside the target repository, read-only to the lane; contract: promotion-evidence-bootstrap.md) |
promotion_evidence_root |
directory | no probe evidence root; every promotable cell stays effective-unpromoted (absolute path to the protected --probe-evidence-root directory, outside the target repository, read-only to the lane) |
promotion_evidence_source |
file | no evidence source; every promotable cell stays effective-unpromoted (absolute path to the operator-published epoch-scoped events file, outside the target repository, read-only to the lane) |
worktree_root |
directory | worktrees/ under the plugin data dir (external root for /source-control:worktree create; never inside a repository or a repository-discovery root) |
worktree_stale_days |
number | 14 (staleness threshold for /source-control:worktree status) |
worktree_reap_after_hours |
number | 48 (last-commit age past which /source-control:worktree cleanup proposes a safe worktree by default) |
fetch_logs_max_bytes |
number | 52428800 (CI-log ZIP size cap for fetch-logs) |
branch_issue_pattern |
string | deprecated fallback: set the branch_issue_pattern key on the layered .claude/source-control.md surface instead, which wins when present. Absent in both: the built-in <type>/<N>-<slug> (and routine-issue-<N>) branch-to-issue grammar; set an ERE (last capture group = the numeric GitHub issue number) for a scheme that places the number differently, e.g. ^[^/]+/([0-9]+)- (alice/1234-slug) or -([0-9]+)$ (feat/add-widget-1234) |
setup_inference_window |
string | 1 year (git log --since window for /source-control:setup's commit-history convention inference; any git-approxidate) |
setup_inference_recency_days |
number | 90 (recent-vs-older split boundary in the inference report) |
setup_inference_min_commits |
number | 50 (below this many classifiable subjects, inference widens to full history and reports low confidence) |
The commit-subject / PR-title convention is separate: run
/source-control:setup to interview your repo and write the
source-control.md config. Idempotent and safe to re-run. apply layer=team
appends the recursive .claude/**/*.local.* line to your .gitignore when it is missing, so the
personal overlay layer stays out of version control; layer=local never edits .gitignore and
fails with a recommendation when the overlay is exposed.
Remaining optional environment variables:
| Variable | Used by | Effect |
|---|---|---|
FETCH_LOGS_SCRATCH / FETCH_LOGS_REPO |
fetch-logs |
Scratch dir and repo override |
The plugin-scope finding-classification gate accepts extra posting identities via its
--extra-self flag (fed from babysit_self_logins), added to your
gh api user login; its --self flag still provides a full override.
Generated from this plugin's .claude-plugin/plugin.json. Every option Claude Code
will prompt for when the plugin is enabled, with the environment variable each hook
reads it from.
| Option | Type | Default | Environment variable | Description |
|---|---|---|---|---|
lane_instance |
string | (none) | CLAUDE_PLUGIN_OPTION_LANE_INSTANCE |
Writer identity for this machine's loop-lane telemetry, per the loop-lane convention's lane-instance identity rule. It becomes the suffix of the babysit-loop telemetry sentinel marker (source-control:babysit-loop@<id>), so each concurrently running lane instance owns its own comment and none can overwrite another's durable state. Must match ^[a-z0-9][a-z0-9-]{0,31}$, be stable across restarts, and be distinct across concurrent instances; two lanes on one machine each need an explicit value. Absent: the sanitized lowercased hostname. The value appears verbatim in tracker comments. Set an opaque id if a machine name should not be published in a public tracker. |
pr_body_linkage_gate_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_PR_BODY_LINKAGE_GATE_ENABLED |
Block a gh pr create/gh pr edit whose statically-readable PR body would fail the repository's required PR-contract check (missing a closing keyword, or a missing/empty ## Summary, ## Fix, ## Verification, or ## Related section). Enforced only in a repository whose .github/workflows carry a workflow that uses the pr-contract composite step; a body the hook cannot read statically always passes. |
pr_linkage_mcp_gate_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_PR_LINKAGE_MCP_GATE_ENABLED |
Block a GitHub MCP create_pull_request/update_pull_request whose PR body would fail the repository's required PR-contract check (closing keyword plus non-empty ## Summary, ## Fix, ## Verification, and ## Related), the MCP-surface sibling of pr-body-linkage-gate, covering cloud/remote sessions that open PRs without the gh CLI. Same policy scope: enforced only in a repository whose .github/workflows carry a workflow that uses the pr-contract composite step, and only for the repository the origin remote names. |
worktree_add_containment_gate_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_WORKTREE_ADD_CONTAINMENT_GATE_ENABLED |
Block a raw Bash git worktree add whose resolved target lands inside a git repository, meaning a working tree or a .git / bare directory, with a message naming the configured external root (worktreeroot.path git config key, then the worktree_root plugin option, then the plugin data dir). Blocks ONLY the nesting class: a conforming target passes silently, with no advisory, and a target the hook cannot resolve statically (dynamic path, prior cd, unreadable payload) always passes. The nesting invariant's measurement, disputed arms and expiry live in exactly one place: skills/worktree/SKILL.md § "The nesting invariant, dated measurement". |
worktree_add_claim_gate_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_WORKTREE_ADD_CLAIM_GATE_ENABLED |
After a raw Bash git worktree add, lock the parsed add target with a session-distinct claim (host + session id + timestamp). Only that path is claimed, not every currently unlocked linked worktree, so two concurrent adds cannot steal each other's trees. Existing reasons, including the worktree-create.sh helper string, are never rewritten. The lock is a claim other agents can read, not a write mutex. Turning this OFF leaves plain-add trees unclaimed; scripts/worktree-claim.sh report still lists them and check-enter still surfaces a foreign live claim. Kill switch only: worktree_add_claim_gate_enabled. |
worktree_create_gate_enabled |
boolean | true |
CLAUDE_PLUGIN_OPTION_WORKTREE_CREATE_GATE_ENABLED |
Redirect a WorktreeCreate away from Claude Code's default location, which may be inside the repository, to the configured worktree_root. Turning this OFF does NOT hand placement back to Claude Code: a WorktreeCreate hook has no 'not applicable' channel, and measured on Claude Code 2.1.228, a non-zero exit and an exit-0-without-a-path both fail the creation. That is why false makes the gate refuse out loud, and every harness-driven creation path (claude --worktree, a subagent with isolation: "worktree", a background session) fails with a message naming the real stand-downs. To let Claude Code place worktrees itself, set worktree.bgIsolation to "none" in settings, or disable this plugin. Probe, verbatim harness output and the as-of stamp: skills/worktree/fixtures/README.md. |
babysit_watched_owners |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_WATCHED_OWNERS |
GitHub owners (users/orgs) babysit-prs may act under. Absent: the current repo's owner is inferred per run. |
babysit_self_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_SELF_LOGINS |
Extra GitHub posting identities (e.g. a project bot account) added to your gh api user login, forming the self set babysit-prs treats as its own: self-comment suppression, same-login classification, readiness-gate classification rows, the merge-gate self-exemption, and the resolve-thread bot-only test (a self-authored reply to a bot thread no longer counts as a disqualifying human participant). Not a discovery filter. Which authors' PRs the queue discovers is --author's job, independent of this set. Absent: your gh login alone. |
babysit_intended_write_identity |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_INTENDED_WRITE_IDENTITY |
The single GitHub login babysit-prs's own writes are intended to land under, typically the bot posting identity. When a write the orchestrator recorded performing lands under a different babysit_self_logins identity (e.g. a bot-token mint failed and the write silently fell back to your personal login), the cycle status surfaces an attribution-drift material finding instead of proceeding silently. Set it to one of your self logins; a value that is not actually a posting identity would flag every write. Absent: the check is dormant. |
babysit_default_tier |
string | "safe" |
CLAUDE_PLUGIN_OPTION_BABYSIT_DEFAULT_TIER |
Tier an explicit bare /source-control:babysit-prs invocation runs: safe, worker, or autopilot. Never applies to auto-routed invocations. |
babysit_merge_method |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_MERGE_METHOD |
Merge method for gate-proven merges: merge, squash, or rebase. Absent: repo convention, then squash. |
babysit_autopilot_merge_tier |
boolean | false |
CLAUDE_PLUGIN_OPTION_BABYSIT_AUTOPILOT_MERGE_TIER |
Enable the #476 autopilot merge tier: a distinct bot account submits a genuine approving review, then the gate merges only when every criterion holds (issue-linked, lane-authored, no do-not-merge label, distinct-bot approval on the live head, no human blocking comment). Ships DISABLED; a deliberate operator opt-in. Requires babysit_lane_logins, babysit_approver_bot_logins, and babysit_merge_block_labels to be set. Absent/false: the tier does not exist and PRs go to the human merge-ready list. |
babysit_lane_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_LANE_LOGINS |
Author logins recognized as pipeline lanes for the autopilot merge tier's lane-authored criterion. Absent: the tier (when enabled) refuses fail-closed. |
babysit_approver_bot_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_APPROVER_BOT_LOGINS |
Bot logins whose approving review satisfies the autopilot merge tier's author != approver criterion. Absent: the tier (when enabled) refuses fail-closed. |
babysit_merge_block_labels |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_MERGE_BLOCK_LABELS |
Labels that veto an autopilot-merge-tier merge, e.g. do-not-merge. Absent: the tier (when enabled) refuses fail-closed. |
babysit_review_trigger_phrase |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_REVIEW_TRIGGER_PHRASE |
Comment phrase that requests an AI re-review (posted and recognized). Absent: the review-trigger module stays dormant. |
babysit_review_bot_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_REVIEW_BOT_LOGINS |
Logins of the AI review bots the trigger phrase addresses, and whose review of the live head the merge gate waits for. Absent: the review-trigger module stays dormant and the merge gate's review-settle hold stays dormant. |
babysit_review_settle_minutes |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_REVIEW_SETTLE_MINUTES |
How long after a head appears a review bot's re-review may still be in flight. The merge gate holds a head that bot has not reviewed yet until the window elapses, then stops waiting. Requires babysit_review_bot_logins; absent, the hold stays dormant. Set it above the reviewer's observed latency. |
babysit_review_gate_context |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_REVIEW_GATE_CONTEXT |
Check/status context name of the AI-review gate. Absent: gate treated as absent (degrade). |
babysit_ci_gateway_context |
string | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_CI_GATEWAY_CONTEXT |
Check/status context name of a CI gateway check. Absent: gateway classification unused. |
babysit_extra_bot_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_EXTRA_BOT_LOGINS |
Additional logins to treat as bots when structural detection cannot identify them. Absent: structural detection only. |
babysit_extra_dependency_manager_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_EXTRA_DEPENDENCY_MANAGER_LOGINS |
Additional dependency-manager bot logins beyond the built-in dependabot/renovate set whose PRs the merge gate holds absent --allow-dependency, the same as the built-ins. Absent: built-in dependency-manager set only. |
babysit_approval_downgrade_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_APPROVAL_DOWNGRADE_LOGINS |
AI reviewer logins whose approval is surfaced as a material finding instead of ignored in the one case the structural approval-downgrade reaches: a review body carrying blocking-looking prose that still parses as an approval verdict (no CRITICAL/IMPORTANT or required-fix marker). Every bot's such approval is downgraded to non-blocking regardless; naming a login opts its own into the more-conservative material bucket rather than being ignored. Does not affect a review already in the APPROVED state or a plain clean approval with no blocking-looking prose. Both are ignored regardless. Absent: such approvals are ignored for every bot. |
babysit_skip_downgrade_logins |
string (multiple) | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_SKIP_DOWNGRADE_LOGINS |
AI reviewer logins whose skip/no-op review is not treated as an approval. Absent: the downgrade heuristic stays dormant. |
babysit_max_quiet_recheck_seconds |
number | 14400 |
CLAUDE_PLUGIN_OPTION_BABYSIT_MAX_QUIET_RECHECK_SECONDS |
Longest a quiet PR may go without a worker recheck. |
babysit_stuck_check_age_seconds |
number | 1800 |
CLAUDE_PLUGIN_OPTION_BABYSIT_STUCK_CHECK_AGE_SECONDS |
Minimum age before a pending non-required check under UNSTABLE is reported stuck (stuck_queued / never_settling material finding). Orphaned status contexts with no backing run are detected structurally and ignore this threshold. |
babysit_advisory_fix_round_cap |
number | 100 |
CLAUDE_PLUGIN_OPTION_BABYSIT_ADVISORY_FIX_ROUND_CAP |
Per-PR cap on advisory-only fix rounds (never caps blocking defects). |
babysit_worker_concurrency_cap |
number | 10 |
CLAUDE_PLUGIN_OPTION_BABYSIT_WORKER_CONCURRENCY_CAP |
Maximum per-PR workers dispatched concurrently in one cycle. |
babysit_worktree_root |
directory | (none) | CLAUDE_PLUGIN_OPTION_BABYSIT_WORKTREE_ROOT |
Root directory for babysit-managed ephemeral worktrees. Absent: the worktrees/ subdirectory of the plugin data dir. |
promotion_evidence_binding |
file | (none) | CLAUDE_PLUGIN_OPTION_PROMOTION_EVIDENCE_BINDING |
Absolute path to the security binding document the babysit-loop promotion-evidence seam reads (the binding argument of the autonomy plugin's check-security-binding.mjs). It must sit outside the target repository and every lane worktree, and the lane must be able to read it but not write it. Honored from user, --settings, or managed plugin settings only, never from .claude/source-control.md or any repository file. Contract: skills/babysit-loop/reference/promotion-evidence-bootstrap.md. Absent: no promotion-evidence bootstrap is configured and every promotable cell stays effective-unpromoted. |
promotion_evidence_root |
directory | (none) | CLAUDE_PLUGIN_OPTION_PROMOTION_EVIDENCE_ROOT |
Absolute path to the directory passed as --probe-evidence-root: the protected evidence surface the seam resolves isolation probe transcripts against. It must sit outside the target repository and every lane worktree, and the lane must be able to read it but not write it. Honored from user, --settings, or managed plugin settings only, never from .claude/source-control.md or any repository file. Contract: skills/babysit-loop/reference/promotion-evidence-bootstrap.md. Absent: no probe evidence root is configured and every promotable cell stays effective-unpromoted. |
promotion_evidence_source |
file | (none) | CLAUDE_PLUGIN_OPTION_PROMOTION_EVIDENCE_SOURCE |
Absolute path to the epoch-scoped promotion-evidence events file passed as --evidence, published by an operator-side process the lane can read but not write. It must sit outside the target repository and every lane worktree. Honored from user, --settings, or managed plugin settings only, never from .claude/source-control.md or any repository file. Contract: skills/babysit-loop/reference/promotion-evidence-bootstrap.md. Absent: no evidence source is configured and every promotable cell stays effective-unpromoted. |
worktree_root |
directory | (none) | CLAUDE_PLUGIN_OPTION_WORKTREE_ROOT |
External root under which /worktree create places worktrees, as /--, a path OUTSIDE every repository (on Windows, the same drive as the repo). Absent: the worktrees/ subdirectory of the plugin data dir, which the skill supplies explicitly rather than reading from the environment (not per-plugin in a Bash-tool subprocess). Deliberately outside the repository tree AND outside repository-discovery roots such as a ghq root, which a checkout-relative default would land inside. Never the in-repo .claude/worktrees/ default, whose nested placement the nesting invariant forbids. That claim is stated, measured, dated and given an expiry in exactly one place: skills/worktree/SKILL.md § "The nesting invariant, dated measurement". |
worktree_stale_days |
number min 1 |
14 |
CLAUDE_PLUGIN_OPTION_WORKTREE_STALE_DAYS |
Days since last commit before /worktree status classifies a worktree as stale |
worktree_reap_after_hours |
number min 1 |
48 |
CLAUDE_PLUGIN_OPTION_WORKTREE_REAP_AFTER_HOURS |
Hours since last commit before /worktree cleanup proposes an already-safe worktree for removal by default |
fetch_logs_max_bytes |
number min 1 |
52428800 |
CLAUDE_PLUGIN_OPTION_FETCH_LOGS_MAX_BYTES |
Abort a CI-log ZIP fetch larger than this |
branch_issue_pattern |
string | (none) | CLAUDE_PLUGIN_OPTION_BRANCH_ISSUE_PATTERN |
Deprecated: set branch_issue_pattern on the layered .claude/source-control.md surface instead; this value is read only as a fallback. POSIX ERE for extracting the numeric GitHub issue number from the current branch name; the LAST capture group holds it and must resolve to digits (Closes #N honors only a numeric issue). Set this for a non-default branch scheme that places the number differently, e.g. '^[^/]+/([0-9]+)-' for 'alice/1234-slug' or '-([0-9]+)$' for 'feat/add-widget-1234'. Absent: the built-in '/-' (and routine-issue-) convention. |
setup_inference_window |
string | "1 year" |
CLAUDE_PLUGIN_OPTION_SETUP_INFERENCE_WINDOW |
git log --since window /source-control:setup samples for commit-subject convention inference (any git-approxidate, e.g. '1 year', '6 months'). Absent: 1 year. |
setup_inference_recency_days |
number min 1 |
90 |
CLAUDE_PLUGIN_OPTION_SETUP_INFERENCE_RECENCY_DAYS |
Boundary for the recency split in /source-control:setup's convention-inference report: subjects newer than this many days are the 'recent' bucket, weighted as the live convention when its share diverges from the older bucket. Absent: 90. |
setup_inference_min_commits |
number min 1 |
50 |
CLAUDE_PLUGIN_OPTION_SETUP_INFERENCE_MIN_COMMITS |
Below this many classifiable subjects in the window, /source-control:setup widens inference to full history; still below it, the inference is reported low-confidence rather than authoritative. Absent: 50. |
Three supported routes, in the order most people want them:
-
Interactively. Claude Code prompts for declared options when you enable the plugin. To change them later:
/plugin configure source-control@<marketplace>. -
Headless. Repeat
--configfor each option. Replace<marketplace>with the marketplace you installed this plugin from:claude plugin install source-control@<marketplace> -s <scope> --config lane_instance=<value>
The same command reconfigures a plugin that is already installed: it prints
already installedand still writes the value. The short-circuit message is about the install, not the config write. Do notclaude plugin uninstallto reconfigure: uninstalling drops this plugin's whole storedpluginConfigsentry, resetting every option in the table above to its default.-sdefaults touser, so pass the scopeclaude plugin listreports for this plugin. The verified-version record lives in the plugin-reconfiguration convention.The value is stored immediately; the session you are in does not change. Hooks are handed their
CLAUDE_PLUGIN_OPTION_*when the session starts, so start a fresh Claude Code session before expecting new behavior. A check run in the old session still reports the old value, and that is not a failed write. -
By hand, in settings. Add the value under
pluginConfigsin your user settings (~/.claude/settings.json):{ "pluginConfigs": { "source-control@<marketplace>": { "options": { "lane_instance": <value> } } } }Plugin option values are read from user,
--settings, and managed settings only, not from a project's.claude/settings.json. To vary behavior per repository, enable or disable the plugin in that project'senabledPluginsinstead of setting an option there.
Do not set the CLAUDE_PLUGIN_OPTION_* variables yourself. They are how Claude Code
hands a configured value to a hook process; the value comes from the routes above.
- User configuration: the
userConfigschema and theCLAUDE_PLUGIN_OPTION_<KEY>export - Plugin install options: the
--configflag's reference entry - Plugins and skills settings:
enabledPlugins,extraKnownMarketplaces,pluginConfigs - Settings files and who they affect: user vs project vs local precedence
- Manage installed plugins: enabling, disabling,
/plugin list
- One local hook (
pr-body-linkage-gate, above), no MCP servers, no telemetry unless you opt in (see below), no outbound network beyondgitandghagainst the repository the session already targets. The hook reads only the Bash command it is gating, the body file that command names, and the repository's own workflow directory; it never runsgit,gh, or any network call. - Writes to GitHub (comments, reactions, thread resolution, PR creation,
merge) happen only inside the documented
/source-control:pull-requestphases and the/source-control:babysit-prsloop./source-control:babysit-prsmerges only in its explicitworker/autopilotopt-in tiers, and only through a deterministic merge gate (expected-head pin, fail-closed owner allowlist, dependency-PR and unprotected-repo refusals), the safe default never resolves threads or merges, and configuration alone can never grant an auto-routed invocation merge authority. - Bundled scripts are read-only against the GitHub API except where the skill body documents a write.