Install
$ agentstack add skill-ccomkhj-skills-pair-consult ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
pair-consult
TL;DR for a cold-woken peer
You were invoked via codex exec or claude -p with "Resume the pair-consult skill. Read .consult/STATE.md...". You are a one-shot peer: you do exactly ONE round (an even round — R2, R4, …) and exit. Do this:
cat .consult/STATE.md .consult/QUESTION.md .consult/USER_NOTES.md— orient.- Confirm
STATUS: WAITING:and readROUND:+ROUNDS:. You are B, so this round is a review — a fresh-critique review (R2-style) of A's latest proposal/response by default. It's a re-review (R4-style, accept/double-down) only whenROUNDS >= 5andROUND == ROUNDS-1(the last B round, which follows an A-response). Atn=3there's no A-response round, so the single B round is always a fresh review. See [Round protocol](#round-protocol--one-allowed-action-per-round). - Write
R.md, then updateSTATE.md: setSTATUS: WAITING:(the value of STATE.md'sA:line, e.g.WAITING: claude— never the literal letter "A") and bumpROUND. - EXIT. Do NOT call
consult_handofforconsult_wait. The orchestrator (the interactive session that woke you) is already blocked inconsult_waitand resumes the instant you flipSTATE.md. If you hand off, you spawn a duplicate orchestrator — two instances then write the same round and collide onSTATE.md. This is the bug. Don't be it.
You never do the final round. The final round (ROUND == ROUNDS) is always the orchestrator's synthesis. Only the orchestrator runs consult_handoff + consult_wait; see [Two roles](#two-roles) and [Handoff](#handoff).
No session memory across turns. State lives in .consult/. Templates for every file are in [reference/file-formats.md](reference/file-formats.md).
Overview
The default session is 5 rounds. --number n sets the round cap (ROUNDS: in STATE.md) — a max, since [early termination](#early-termination--skip-ahead-when-theres-consensus) can finish sooner; see [Flags](#flags). The 5-round shape:
| Round | Actor | Action | Output | After | |---|---|---|---|---| | R1 | A (orchestrator) | Propose | R1.md (+ code in repo if coding) | flip WAITING: B → consult_handoff B + consult_wait | | R2 | B (peer, one-shot) | Review proposal | R2.md (agreements + numbered critiques) | flip WAITING: → exit (no handoff, no wait) | | R3 | A (orchestrator) | Respond per critique (agree/partial/object) | R3.md | flip WAITING: B → consult_handoff B + consult_wait | | R4 | B (peer, one-shot) | Re-review (accept/double-down) | R4.md | flip WAITING: → exit (no handoff, no wait) | | R5 | A (orchestrator) | Synthesize, ask user | R5.md — user reads this | stop — STATUS: AWAITING_USER |
General rule for any odd n (ROUNDS): round 1 = A proposes; the final round n = A synthesizes (AWAITING_USER); interior rounds alternate — even rounds = B reviews, odd rounds = A responds. n is always odd (forced at init) so A is both the first proposer and the last synthesizer. At n=3 it's propose · review · synthesize (one B round, a fresh review — no re-review); at n=5 it's exactly the table above; at n=7 it's propose · review · respond · review · respond · re-review · synthesize.
After the final round, STATUS: AWAITING_USER and the loop stops. The user's reply (confirm / redirect / cancel) closes the session.
Two roles
The loop has an asymmetry that prevents duplicate instances — internalize it before anything else.
- Orchestrator = A = the interactive session that ran
/pair-consult. It is alive for the whole session. It does the odd rounds (R1, R3, … and the final round). After each non-final round it spawns the peer (consult_handoff B) and blocks inconsult_waituntil the peer flipsSTATE.mdback. After the final round it stops. - Peer = B = a fresh, headless, one-shot instance, cold-woken by the orchestrator's
consult_handoff. It does exactly one round (an even round — R2, R4, …), flipsSTATUS: WAITING:, and exits. It never callsconsult_handoffand never callsconsult_wait.
Why: consult_handoff always spawns a new instance of the named peer. If the peer (B) hands back with consult_handoff A, it spawns a second A — while the original orchestrator A is still alive in consult_wait. Both then see WAITING: , both act, both write the round, and they collide on STATE.md (Error editing file). The only safe shape is: only the orchestrator hands off and waits; the peer flips-and-exits. A peer that is alive does not need to be re-spawned — it's already waiting.
So consult_handoff + consult_wait are orchestrator-only verbs. If you were cold-woken by a resume prompt, you are the peer: flip and exit.
When to use
/pair-consult(fresh) or/pair-consult(resume).- Work fits in one round of proposal — not a 5-step plan. If it doesn't fit, break it into smaller questions or use a full planning workflow.
- User wants a structured second opinion ending in their own go/no-go.
- You were invoked as the peer by the active agent.
Don't use for: multi-step implementation (use a full planning workflow), open-ended exploration (use brainstorming), one-shot code review (use code-review), or solo work.
Round protocol — one allowed action per round
Each round is narrow on purpose. Stay in your lane. Templates for each round file are in [reference/file-formats.md](reference/file-formats.md); the semantics are below — each bullet's header names the rounds it covers (1:1 with R1–R5 at n=5; for larger odd n the review/respond actions repeat per the [Overview](#overview) rule).
- Propose (round 1, A). Read
QUESTION.md. For coding tasks, write the actual code in the repo, run the test, then writeR1.mdas the design rationale (not a code dump). - Review (even rounds, B). Read the latest
R*.mdand (for coding)git diff+ run the tests yourself. Open numbered critiques inR.md— do not edit A's artifacts. Your critiques drive A's next round. Then flipSTATUS: WAITING:, bumpROUND, and exit — you are one-shot; do not hand off. - Respond (odd interior rounds, A). For each numbered critique, write a verdict (
agree/partial/object), the action you took, and reasoning when not pure agreement. Address every critique — skipping one is a bug. If you applied code changes, re-run the test and note the result in the relevant C-block. - Re-review (round
n-1, the last B round — only whenn >= 5). Exists only when a preceding A-response round feeds it, so never atn=3(there the one B round is a fresh review). For each item where A objected or partial-applied, decideacceptordouble down. No fresh critiques — bugs A introduced in the prior round are double-downs with a sub-finding, not new Cs. This is B's last word. Then flipSTATUS: WAITING:, setROUND:, and exit — do not hand off (A is already waiting and writes the final round). - Synthesize (round
n, A). Write the user-facing close: what we landed on, where we agreed, unresolved tensions plainly stated, what we need from the user. ThenSTATUS: AWAITING_USERand stop.
Early termination — skip ahead when there's consensus
ROUNDS (default 5) is the max, not the requirement. Skip when the next round would be a rubber-stamp; when in doubt, do the round. The triggers are role-relative — they hold at any n:
| Trigger | What to do | |---|---| | A B-review has zero critiques (full agreement) | Skip the rest of the interior rounds. A jumps to the final synthesis round directly. | | An A-response is all agree AND adds no new code/claims/reasoning | Skip the next B-review. A jumps to synthesis directly. | | An A-response is all agree but introduces new substance | Run the next B-review — it catches regressions A introduced. (The skill's trial caught a real null-semantics bug this way.) | | An A-response has any object or partial | Run the next B-review. | | The last B re-review is done | Always run the final synthesis round — it's where the user is asked. |
When skipping ahead, jump straight to the final round (ROUND: , A synthesizes, AWAITING_USER) — early exit never lands the terminal step on B. Mark skipped round-log rows skipped, and call out the skip in the synthesis so the user sees the early exit.
Surfacing rounds in chat — keep the user in the loop
After every round (yours OR the peer's), print a 5-15 line digest in chat before doing your next action. This is what makes pair-consult feel like a conversation instead of a black-box file machine. Use consult_digest from [reference/handoff.sh](reference/handoff.sh) — it extracts agreements + critique titles + per-C verdicts + overall verdict, already truncated.
**B's R2:**
- ✓
- ! C1:
- ! C2:
**My R3:**
- ✓ Agreed on C1, C2, C3, C5
- ↳ C4: partial — kept the WITH ORDINALITY shape but added type placeholders
- One new claim: SQL-side dedupe preferable to app-side (going into R4)
The user can interrupt at any moment. Treat any user message as a steer — address it before continuing.
Shared state in .consult/
Create .consult/ at the repo root on init and append .consult/ to .gitignore. Files:
| File | Purpose | Written by | |---|---|---| | QUESTION.md | User's question + pinned context | Active agent on init | | STATE.md | ROUND, STATUS, A, B, round log | Every round | | R1.md … R.md | Round content | Actor of that round | | USER_NOTES.md | User-injected steers (created lazily) | consult_inject | | session.log + round--.log | Peer stdout | consult_handoff |
A and B are fixed for the session — whoever proposed in R1 is A. Full templates in [reference/file-formats.md](reference/file-formats.md).
Entry modes
Both modes accept the optional flags in [Flags](#flags) (--number n, --model high|xhigh). Parse them off the invocation first, then write the resolved ROUNDS: and EFFORT: into STATE.md at init.
Mode 1 — fresh question: /pair-consult "" [--number n] [--model high|xhigh]
Standard init. You are the orchestrator (A). Write QUESTION.md and STATE.md (ROUND: 1, ROUNDS: , STATUS: ACTIVE: , A: , B: , EFFORT: ), then do R1 as described in the Round protocol. After R1, ROUND: 2, STATUS: WAITING: , then consult_handoff + consult_wait and stay alive for the rest of A's rounds (every odd round through the final).
Mode 2 — continue from session: /pair-consult --from-session [--number n] [--model high|xhigh]
Use when you're mid-conversation with the user and you've just proposed something — code, an approach, a decision — and the user wants the peer to grill it. You are implicitly A; your most-recent proposal becomes R1 content.
- Write
QUESTION.mdfrom the user's recent turn — include enough context that B can act cold (B does NOT see chat history; only.consult/). - Write
R1.mdby summarizing your most-recent proposal — don't restate the conversation, extract the proposal into the R1 template. STATE.md:ROUND: 2,ROUNDS:,STATUS: WAITING:,A:,B:,EFFORT:. Round-log starts with the R1 row notingfrom-session.consult_handoff+consult_wait— B does R2 (one-shot) and flips back; yourconsult_waitresumes you for R3.
Flags
| Flag | Meaning | Default | |---|---|---| | --number n (alias --rounds n) | Requested round cap / depth, written to ROUNDS: — it's a max ([early termination](#early-termination--skip-ahead-when-theres-consensus) can end sooner). Normalized to odd and >= 3 so A both proposes and synthesizes: even n snaps up to n+1, n / claude --effort . Omitted → empty EFFORT:, each CLI uses its own default. | unset |
Note: only high and xhigh are accepted. Your ~/.codex/config.toml may already default codex to xhigh, so --model xhigh is often a no-op for the codex peer; --model high is what visibly steps it down.
Auto-detect Mode 2: when /pair-consult is invoked with no question argument AND no existing .consult/ AND the recent conversation contains a proposal you authored, default to Mode 2 without requiring --from-session explicitly. Otherwise prompt the user for a question.
If .consult/ already exists, don't overwrite — treat as resume; show consult_status and ask whether to continue or rm -rf .consult/ for a fresh start.
Handoff
consult_handoff + consult_wait are orchestrator-only. You run them only after your own non-final A round (R1, R3, … any odd round before the last) — never as a cold-woken peer (see [Two roles](#two-roles)). A peer flips STATE.md and exits; the orchestrator's existing consult_wait picks it up.
After writing your round (as the orchestrator) and updating STATE.md:
source ~/.claude/skills/pair-consult/reference/handoff.sh # or ~/.agents/...
consult_handoff codex # or: consult_handoff claude
consult_handoff does the headless invocation correctly (nohup + detach, -- terminator for claude, stdbuf for live logging, and injecting the EFFORT: flag per CLI). Do not call this at the final round (ROUND == ROUNDS) — the function will refuse and tell you to set STATUS: AWAITING_USER.
Then wait — the handoff isn't tracked by the harness
consult_handoff uses nohup ... & to detach the peer, which takes it outside the Claude Code harness's process tracking. The harness will NOT notify you when the peer's round lands. If you simply end your turn after consult_handoff, you will sit idle until the user pings (which surfaces a file-change reminder) or until a ScheduleWakeup fires — both unreliable and high-latency.
The fix: immediately after consult_handoff, call consult_wait so the harness has something to track:
# Claude Code (this is the important case):
Bash(
command="source ~/.claude/skills/pair-consult/reference/handoff.sh && consult_wait",
run_in_background=true,
)
# Codex CLI / inline: call consult_wait in the foreground — it will block until the peer flips STATE.md.
consult_wait polls .consult/STATE.md every 5s and exits the moment the STATUS line flips off WAITING: . When invoked via Bash(run_in_background=true), the harness tracks the poll-process and notifies you on exit — you learn about R at file-write time, not at next-user-ping time.
Behavior on exit:
- Exit 0 — your turn. Read the new round file, surface a digest, do your round.
- Exit 2 — timeout (default 540s, just under Bash's 600s ceiling). Re-invoke
consult_wait. A peer round legitimately taking >9 min is rare but possible (large code reviews, slow networks). - Exit 3 — the peer likely crashed (no process holds the round log open and the file is stale). Run
consult_statusandconsult_peer_statusto investigate before retrying.
Hazards
All guarded by consult_handoff — listed here so you don't reinvent them.
| Hazard | Rule | |---|---| | claude -p "" — flags after prompt hang the CLI | Flags first; use consult_handoff. | | claude --add-dir "" — variadic flag eats prompt | -- terminator before the prompt. | | claude -p --bare — fails auth unless ANTHROPIC_API_KEY is set | Don't use --bare. | | killall claude / pkill codex to recover | Kills the user's main session. Use consult_peer_status to list specific PIDs. |
Steering as a human
While the loop runs:
| Command | What it does | |---|---| | consult_watch | tail -F .consult/session.log across all rounds. | | consult_status | One-screen summary: STATE.md + open user notes + last 20 log lines. | | consult_inject "" | Append a USER_NOTE to .consult/USER_NOTES.md. Next agent must address it before their round. | | consult_takeover | Kill peer by PID (via lsof), set STATUS: BLOCKED: human-takeover. | | consult_resume | After takeover + manual edits,
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ccomkhj
- Source: ccomkhj/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.