Skip to content

Commit b61d65b

Browse files
NagyViktclaude
andauthored
feat(claude+pr): first-class Claude Code integration and gx pr command suite (#600)
- skill_guard.py: add claude/, codex/, cursor/ to default agent-branch prefixes (was agent/ only) so the harness-managed branches each tool uses are recognized without env-setup. Add GUARDEX_AGENT_BRANCH_PREFIXES_ONLY env to lock down namespaces. Generalize the in-hook regex allowlist for push/checkout to cover all four prefixes. - src/pr.js + src/cli/commands/pr.js: new `gx pr` command suite that centralizes PR-from-worktree plumbing — open (idempotent), status, sync, watch, list, ready, review. Detects base branch from origin/HEAD, generates titles/bodies from commits, supports auto-merge with configurable strategy, watches CI/merge state until merged / failed / timeout. - src/cli/commands/claude.js: new `gx claude install / check / doctor / uninstall`. Installs .claude/settings.json (deep-merging with any user-defined hooks so we never clobber custom entries), copies managed hooks + slash commands, ensures the gitguardex skill is present, and repairs the CLAUDE.md -> AGENTS.md symlink. `check` is read-only; `doctor` is `check --fix`. - .claude/commands/: ship /gx-status, /gx-doctor, /gx-pivot, /gx-pr, /gx-finish, /gx-setup slash commands documenting the canonical flow. - AGENTS.md: add a Claude Code quickstart block at the top covering branch awareness, slash commands, PR flow, and `gx claude install`. - Tests: claude-install (12 cases) and pr-module (8 cases) covering settings merge idempotency, hook/slash install, symlink repair, base-branch + title + body generation, and error paths. Updated skill-guard-hook test to reflect the new default prefixes (claude/, codex/, cursor/) and the new exclusive-mode env. Local repos in test helpers now disable commit signing so sandboxes with broken signing servers don't poison the suite. https://claude.ai/code/session_01P6xFS4dDv2MnJ63XyxRbid Co-authored-by: Claude <noreply@anthropic.com>
1 parent e1278e0 commit b61d65b

16 files changed

Lines changed: 1954 additions & 13 deletions

.claude/commands/gx-doctor.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# /gx-doctor
2+
3+
Run repo repair + verification.
4+
5+
Steps:
6+
1. `gx doctor` — installs/refreshes Guardex scaffolding, prunes stale worktrees, auto-finishes ready PRs.
7+
2. If issues remain, surface them with the relevant fix command (`gx setup`, `gx claude install`, `gx locks claim`, etc.).
8+
3. Report final state as `Repo is fully safe` or list outstanding errors/warnings.
9+
10+
Useful flags to pass through:
11+
- `--dry-run` — preview without writing.
12+
- `--current` — limit to the top-level repo (no recursive scan).
13+
- `--no-wait-for-merge` — skip the auto-finish PR-merge polling.
14+
- `--json` — machine-readable output for scripts.

.claude/commands/gx-finish.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# /gx-finish
2+
3+
Finish the current agent worktree task end-to-end: commit (if needed),
4+
push, open PR, wait for merge, prune the worktree.
5+
6+
Default invocation for the current agent branch:
7+
8+
```
9+
gx branch finish \
10+
--branch "$(git branch --show-current)" \
11+
--base main \
12+
--via-pr \
13+
--wait-for-merge \
14+
--cleanup
15+
```
16+
17+
To sweep every finished agent lane in this repo at once:
18+
19+
```
20+
gx finish --all
21+
```
22+
23+
Pre-conditions:
24+
- Working tree is clean OR commits are already in the worktree.
25+
- `gh auth status` is healthy (or `GITHUB_TOKEN` is set).
26+
- Tests for the touched area pass (run `npm test`/equivalent first).
27+
28+
If branch protection blocks the merge, the flow returns the PR URL and
29+
sets auto-merge. Re-run `/gx-pr watch` to follow it.
30+
31+
Do NOT replace this with standalone `git push` + `gh pr create` — those
32+
skip Guardex's cleanup, lock release, and worktree prune.

.claude/commands/gx-pivot.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# /gx-pivot
2+
3+
Pivot the current session onto a fresh agent worktree so edits stop being
4+
blocked by skill_guard on a protected/non-agent branch.
5+
6+
Usage:
7+
8+
```
9+
gx pivot "<task description>" "<agent-name>"
10+
```
11+
12+
Example:
13+
14+
```
15+
gx pivot "Add /pr open" "claude-pr-flow"
16+
```
17+
18+
Output prints `WORKTREE_PATH=...` and `BRANCH=agent/claude-pr-flow/<slug>`.
19+
`cd` into the worktree path, then continue editing.
20+
21+
When to use:
22+
- `skill_guard` blocked an edit/Bash on `main`, `dev`, or any non-agent branch.
23+
- Starting a brand-new task in a fresh session.
24+
25+
When NOT to use:
26+
- You're already inside an agent worktree and just want to continue. Stay put.
27+
- The task is a typo / 1-line tweak the user explicitly marked `quick:` /
28+
`tiny:` / `just:` — those skip orchestration.

.claude/commands/gx-pr.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# /gx-pr
2+
3+
Drive a pull request from the current agent worktree.
4+
5+
Default (`/gx-pr` with no args): show PR status for the current branch +
6+
CI rollup.
7+
8+
Subcommands:
9+
10+
| Subcommand | Effect |
11+
|---|---|
12+
| `gx pr` / `gx pr status` | Print PR number, URL, mergeability, CI summary. |
13+
| `gx pr open` | Idempotent: push branch + create draft PR if none exists. |
14+
| `gx pr sync` | Push, ensure PR exists, optionally `--auto-merge` / `--ready`. |
15+
| `gx pr watch` | Poll PR until merged / failed / timeout (`--timeout 600`). |
16+
| `gx pr ready` | Promote draft → ready for review. |
17+
| `gx pr list` | List open PRs in the current repo. |
18+
| `gx pr review --provider claude` | Run AI PR review. |
19+
20+
Flags:
21+
22+
- `--base <branch>` (default: detected from `origin/HEAD`)
23+
- `--title "..."`, `--body "..."`
24+
- `--no-draft`, `--no-push`
25+
- `--auto-merge`, `--merge-strategy squash|merge|rebase`
26+
- `--json` for machine-readable output
27+
28+
When to use vs `gx branch finish`:
29+
30+
- `gx pr ...` is the *low-level* PR plumbing for an agent that wants
31+
explicit control (status checks, ad-hoc PR creation outside a finish flow).
32+
- `gx branch finish --via-pr --wait-for-merge --cleanup` is still the
33+
default *end-of-task* command and handles PR + auto-merge + worktree
34+
cleanup in one shot. Use that when the work is genuinely done.

.claude/commands/gx-setup.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# /gx-setup
2+
3+
Install gitguardex scaffolding into the current repo (or a target one).
4+
5+
```
6+
gx setup # current repo
7+
gx setup --target /path/to/repo
8+
gx setup --dry-run # preview without writing
9+
```
10+
11+
What it does:
12+
13+
1. Installs companion tools (interactive prompt; skip with `--no-global-install`).
14+
2. Bootstraps `.omc/`, `.githooks/`, lock registry, OpenSpec scaffolding.
15+
3. Configures git hooks (`pre-commit`, `pre-push`, `post-checkout`) to defend
16+
the protected base branch.
17+
4. Optionally installs Spec Kit (`--no-speckit` to opt out).
18+
5. Runs a final scan and prints any remaining safety findings.
19+
20+
After `gx setup`, also run `gx claude install` (or `/gx-setup --claude`) to
21+
wire up Claude Code hooks, slash commands, and the agent skill.
22+
23+
Then: `gx pivot "<task>" "<agent>"` to start work, or `gx status` to verify.

.claude/commands/gx-status.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# /gx-status
2+
3+
Show the gitguardex status of the current repo: branch, guardex toggle,
4+
worktree count, agent-branch list, pending findings, and protected-base
5+
health.
6+
7+
```
8+
gx status # human-readable
9+
gx status --json # machine-readable
10+
gx status --strict # treat warnings as failures (exit 1 on any)
11+
```
12+
13+
When status reports findings, run `/gx-doctor` to repair.

.claude/hooks/skill_guard.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,20 @@ def emit_event(*_a: object, **_k: object) -> None:
2727
SHELL_GUARD_OVERRIDE_ENV = "ALLOW_BASH_ON_NON_AGENT_BRANCH"
2828
PRIMARY_WORKTREE_AGENT_EDIT_OVERRIDE_ENV = "ALLOW_CODE_EDIT_ON_PRIMARY_WORKTREE"
2929
# Extra agent-branch prefixes (comma- or space-separated). The hardcoded
30-
# "agent/" prefix is always recognized; this env var adds session-managed
31-
# prefixes like "claude/" without forcing repos to fork the hook.
30+
# defaults below are always recognized; this env var adds session-managed
31+
# prefixes without forcing repos to fork the hook.
32+
#
33+
# Defaults cover the common agentic-CLI branch namespaces:
34+
# - "agent/" — Guardex / Codex / generic agent branches
35+
# - "claude/" — Claude Code session branches (e.g. claude/improve-X-Segmk)
36+
# - "codex/" — Codex Cloud session branches
37+
# - "cursor/" — Cursor background-agent branches
38+
#
39+
# Repos that want to restrict to a single prefix should set
40+
# GUARDEX_AGENT_BRANCH_PREFIXES_ONLY=1 along with an explicit list.
3241
AGENT_BRANCH_PREFIXES_ENV = "GUARDEX_AGENT_BRANCH_PREFIXES"
33-
DEFAULT_AGENT_BRANCH_PREFIXES = ("agent/",)
42+
AGENT_BRANCH_PREFIXES_EXCLUSIVE_ENV = "GUARDEX_AGENT_BRANCH_PREFIXES_ONLY"
43+
DEFAULT_AGENT_BRANCH_PREFIXES = ("agent/", "claude/", "codex/", "cursor/")
3444
PATCH_FILE_HEADER_RE = re.compile(
3545
r"^\*\*\* (?:Update|Add|Delete) File:\s+(.+?)\s*$",
3646
re.MULTILINE,
@@ -51,13 +61,16 @@ def emit_event(*_a: object, **_k: object) -> None:
5161
re.compile(r"^git\s+pull(?:\s+--ff-only|\s+--rebase|\s+origin\s+\S+)?\s*$"),
5262
re.compile(r"^git\s+pull\s+--ff-only(?:\s+\S+){0,2}\s*$"),
5363
re.compile(r"^git\s+pull\s+--rebase(?:\s+\S+){0,2}\s*$"),
54-
# Pushing agent/* branches from any cwd is safe — guarded branch namespace.
55-
re.compile(r"^git\s+push(?:\s+(?:-u|--set-upstream))?\s+\S+\s+agent/[^\s]+(?:\s|$)"),
56-
re.compile(r"^git\s+push(?:\s+(?:-u|--set-upstream))?\s+\S+\s+HEAD:agent/[^\s]+(?:\s|$)"),
64+
# Pushing/checking out branches in any recognized agent namespace is safe.
65+
# The capture group lists each default prefix; extra prefixes from
66+
# GUARDEX_AGENT_BRANCH_PREFIXES are honored at branch-detection time, so
67+
# these regexes only need to cover the built-in namespaces.
68+
re.compile(r"^git\s+push(?:\s+(?:-u|--set-upstream))?\s+\S+\s+(?:agent|claude|codex|cursor)/[^\s]+(?:\s|$)"),
69+
re.compile(r"^git\s+push(?:\s+(?:-u|--set-upstream))?\s+\S+\s+HEAD:(?:agent|claude|codex|cursor)/[^\s]+(?:\s|$)"),
5770
re.compile(
5871
r"^gh\s+(?:auth\s+status|repo\s+view|pr\s+(?:list|view|checks|status|create|edit|comment|review|ready|reopen|merge)|issue\s+(?:list|view|status|create|comment)|run\s+(?:list|view|watch)|workflow\s+(?:list|view|run))\b"
5972
),
60-
re.compile(r"^git\s+(?:checkout|switch)\s+agent/[^\s]+(?:\s|$)"),
73+
re.compile(r"^git\s+(?:checkout|switch)\s+(?:agent|claude|codex|cursor)/[^\s]+(?:\s|$)"),
6174
re.compile(r"^(?:ls|cat|head|tail|wc|nl|sed\s+-n|rg|find|stat|du|df|ps|ss|which|command\s+-v)\b"),
6275
# All gitguardex CLI subcommands are themselves safety-aware; trust them on protected branches.
6376
re.compile(r"^(?:gx|guardex|gitguardex|multiagent-safety)\s+\S+\b"),
@@ -297,11 +310,20 @@ def _parse_branch_prefixes(raw: str) -> tuple[str, ...]:
297310

298311

299312
def agent_branch_prefixes() -> tuple[str, ...]:
300-
"""Active agent-branch prefixes: defaults plus GUARDEX_AGENT_BRANCH_PREFIXES."""
313+
"""Active agent-branch prefixes: defaults plus GUARDEX_AGENT_BRANCH_PREFIXES.
314+
315+
If GUARDEX_AGENT_BRANCH_PREFIXES_ONLY=1, only the explicit env list is used
316+
(defaults are dropped). This is for repos that want to lock down which
317+
namespaces count as agent-managed.
318+
"""
301319
extras = _parse_branch_prefixes(os.environ.get(AGENT_BRANCH_PREFIXES_ENV, ""))
320+
exclusive = os.environ.get(AGENT_BRANCH_PREFIXES_EXCLUSIVE_ENV, "").strip().lower() in {
321+
"1", "true", "yes", "on",
322+
}
323+
base = () if exclusive else DEFAULT_AGENT_BRANCH_PREFIXES
302324
seen: set[str] = set()
303325
out: list[str] = []
304-
for prefix in (*DEFAULT_AGENT_BRANCH_PREFIXES, *extras):
326+
for prefix in (*base, *extras):
305327
if prefix not in seen:
306328
seen.add(prefix)
307329
out.append(prefix)

AGENTS.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ This document is the agent contract for this repo. It applies identically to Cod
77
- Optimize for task completion with low token use.
88
- Prefer phase-based execution over conversational micro-steps.
99

10+
## Claude Code quickstart
11+
12+
If you are a Claude Code session arriving in this repo for the first time:
13+
14+
1. **Branch awareness**`skill_guard.py` accepts `agent/*`, `claude/*`,
15+
`codex/*`, and `cursor/*` as agent-managed branch namespaces by default.
16+
Your harness-assigned `claude/<...>` branch is recognized; you don't need
17+
to set `GUARDEX_AGENT_BRANCH_PREFIXES`. If you ever do need to lock down
18+
namespaces, set `GUARDEX_AGENT_BRANCH_PREFIXES_ONLY=1` plus an explicit
19+
list.
20+
2. **Slash commands**`/gx-status`, `/gx-doctor`, `/gx-pivot`,
21+
`/gx-pr`, `/gx-finish`, `/gx-setup` are available out of the box. See
22+
`.claude/commands/`.
23+
3. **PR flow** — when you need explicit PR control, use `gx pr open`,
24+
`gx pr status`, `gx pr sync`, or `gx pr watch`. For end-of-task
25+
commit + push + PR + merge + cleanup, still use the non-negotiable
26+
`gx branch finish --via-pr --wait-for-merge --cleanup`.
27+
4. **Repo wiring**`gx claude install` writes `.claude/settings.json`,
28+
hooks, slash commands, and the gitguardex skill into a target repo.
29+
`gx claude check` diagnoses drift without writing; `gx claude doctor`
30+
diagnoses and repairs.
31+
1032
## ExecPlans
1133

1234
When writing complex features or significant refactors, use an ExecPlan (as described in `.agent/PLANS.md`) from design to implementation.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)