From xp-agents
Pushes current free branch, creates GitHub PR if gh available, runs free-mode code review via xp-close-reviewer, merges to primary integration branch, and cleans up. For ad-hoc work outside sprints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/xp-agents:xp-free-closeThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
!`CLAUDE_PLUGIN_DATA="${CLAUDE_PLUGIN_DATA}" ${CLAUDE_SKILL_DIR}/scripts/preload.sh`
!CLAUDE_PLUGIN_DATA="${CLAUDE_PLUGIN_DATA}" ${CLAUDE_SKILL_DIR}/scripts/preload.sh
The preload above surfaces SMM_DIR, CURRENT_BRANCH, TARGET_BRANCH,
GH_AVAILABLE, and WORKTREE_CLEAN. Use these values verbatim — do
not recompute them. TARGET_BRANCH is the primary integration branch
(free-close merges a free branch → primary). The shared close pipeline
lives in ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py.
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py preflight \
--cwd . --current <CURRENT_BRANCH> --target <TARGET_BRANCH>
If the exit code is non-zero, stop. The script emitted the reason
on stderr (dirty worktree or <CURRENT_BRANCH> IS <TARGET_BRANCH>).
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py push \
--cwd . --branch <CURRENT_BRANCH>
Stdout is pushed: <branch> or skipped: no remote configured.
PR_OUTPUT=$(python3 ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py create-pr \
--cwd . --base <TARGET_BRANCH> --head <CURRENT_BRANCH> \
--title "<short title for the free work>" --body "<one-paragraph summary>")
PR_OUTPUT is a PR number or skipped: no gh on PATH.
Apply the shared ### Step 4: Security Review block above with
<close-mode> → free and <close-skill-name> → xp-free-close.
Compute the diff command:
DIFF_CMD=$(python3 ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py diff-command \
--pr-output "$PR_OUTPUT" --target <TARGET_BRANCH>)
Substitute the captured value as <DIFF_CMD> in the agent prompt below.
Agent(
subagent_type: "xp-agents:xp-close-reviewer",
prompt: "SMM_DIR=<SMM_DIR>\n\n## Mode\nfree\n\n## Source Branch\n<CURRENT_BRANCH>\n\n## Target Branch\n<TARGET_BRANCH>\n\n## Diff Command\n<DIFF_CMD>\n\n## Close Cycle ID\n<CLOSE_CYCLE_ID>\n\n## Context\nClosing free branch <CURRENT_BRANCH> into <TARGET_BRANCH>. PR <PR_OUTPUT or 'not created (no gh)'>.\n\n## Instructions\nRun the Diff Command, analyze cumulative diff with free-mode focus (standard quality review only — code quality, clarity, naming, obvious bugs, missing error handling at boundaries, test coverage gaps). Sprint/plan-level scope concerns do not apply. Return Keep / Concern / Block summary."
)
The shared close-pipeline reference (Steps 5, 5b, and 6) is emitted by
the preload at the top of this context — see
scripts/_close_pipeline_shared.md for the source. Apply those three
steps in order after Step 4.5, then continue with Step 7 below.
Free-close override for Step 6 (auto-merge gate): if ALL of these
hold, skip the shared Step 6's AskUserQuestion and proceed directly
to Step 7:
ASK_COUNT=$(python3 ${CLAUDE_PLUGIN_ROOT}/smm/smm_cli.py \
--smm-dir <SMM_DIR> count-classifications \
--route ask --cycle-id <CLOSE_CYCLE_ID> --since-ts <CLOSE_START_TS>)
<CLOSE_CYCLE_ID> and <CLOSE_START_TS> are emitted by the preload
above (captured at close-cycle start). --cycle-id is the strict
scoper — it prevents concurrent close-cycles in other teammate
worktrees from leaking concern_classify events into this count
(SMM is shared across worktrees). --since-ts is belt-and-
suspenders defense for the same-worktree resume case. The CLI
filters on metadata.action == "concern_classify" +
metadata.route == "ask" + metadata.close_cycle_id == CLOSE_CYCLE_ID
ts >= CLOSE_START_TS — structured fields, not regex. Test
numerically: [ "$ASK_COUNT" -gt 0 ] → fall through to the shared
Step 6 prompt.TEST_COMMAND=... line
(sourced from system_context.stack.test_command) AND running
that command AFTER all Step 5c fixes landed exits 0. Any non-zero
exit means tests aren't green — fall through to the shared Step 6
prompt.design_decision findings — even if the
classifier routed one to fix. Free-close merges to primary, and
architectural calls deserve a human checkpoint regardless of the
classifier's route choice. Verify via the same CLI:
DESIGN_DECISION_COUNT=$(python3 ${CLAUDE_PLUGIN_ROOT}/smm/smm_cli.py \
--smm-dir <SMM_DIR> count-classifications \
--category design_decision --cycle-id <CLOSE_CYCLE_ID> --since-ts <CLOSE_START_TS>)
Test numerically: [ "$DESIGN_DECISION_COUNT" -gt 0 ] → fall
through to the shared Step 6 prompt. (Story-close + sprint/plan
close don't need this guard — story-close merges into the sprint
branch, not primary; sprint+plan always confirm.)When TEST_COMMAND is empty (the project hasn't configured a test
command), the gate cannot fire. Print this two-line discovery hint
before falling through to the shared Step 6 prompt:
Auto-merge disabled — set stack.test_command in system_context.json to enable.
To set it, pipe the command (JSON-quoted) into the edit-stack-field CLI:
printf %s '"<your-test-command>"' | python3 ${CLAUDE_PLUGIN_ROOT}/smm/system_context_cli.py --smm-dir <SMM_DIR> edit-stack-field test_command
Substitute <your-test-command> with the project's test runner
invocation. The printf %s form avoids shell-quoting traps when the
command itself contains spaces or special characters.
When all four conditions hold, print exactly:
"All reviewer findings addressed and tests green — proceeding to merge
without confirmation."
then continue to Step 7. Otherwise apply the shared Step 6
AskUserQuestion as written. Free-close merges directly into primary,
so the deterministic green-tests gate AND the design_decision guard
are load-bearing — LLM judgment from Step 5c alone is not sufficient.
This matches the auto-accept pattern in /xp-accept.
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/close_common.py merge \
--cwd . --source <CURRENT_BRANCH> --target <TARGET_BRANCH>
The script chains: merge --no-ff → push target (if remote) → delete
source. Any step failing aborts the chain — the source branch always
survives a failed step so the user can resolve and retry. Conflicts
are never auto-resolved; the script's stderr will name the conflict.
Tell the user: free branch merged into primary, PR (if created) merged, local branch deleted. Free-close is complete.
npx claudepluginhub paulingalls/xp-agents --plugin xp-agentsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.