From Phone a Friend
Runs a structured Q&A rally between the host model and a backend model (Codex, Gemini, Ollama) seeded by topic for N rounds. Both sides must answer and question each round with strict prefix formatting.
How this skill is triggered — by the user, by Claude, or both
Slash command
/phone-a-friend:curiosity-engine --topic "<topic>" [--rounds N] [--backend codex|gemini|ollama]--topic "<topic>" [--rounds N] [--backend codex|gemini|ollama]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A structured ping-pong Q&A game between the host orchestrating model (the
A structured ping-pong Q&A game between the host orchestrating model (the agent running this skill — Claude in Claude Code, the OpenCode model in OpenCode) and a backend model. Both sides MUST produce an ANSWER: and a QUESTION: every round. The game is seeded with a topic and runs for N rounds (default 3, max 6).
phone-a-friend --to claude (or any other backend) to generate the
orchestrator's questions or answers — that would relay the orchestrator
role to a different model.--to
(e.g. phone-a-friend --to codex,gemini).curiosity-engine is a host slash command / Agent Skill, not a PaF CLI
subcommand. Never run phone-a-friend curiosity-engine.--backend is an argument to this skill, not a PaF CLI flag. Do not pass
--backend to phone-a-friend.PHONE_A_FRIEND_HOST=opencode so PaF detects the host deterministically.PHONE_A_FRIEND_HOST=codex
for the same reason. Do not select codex as the friend backend from
Codex; PaF will refuse with a recursion-guard error.git show, git diff,
git status, etc.) into the relay prompt. Curiosity rounds are seeded
with self-contained prompts; if the round needs file context,
repo-aware backends (codex, gemini) can read the repo via
--repo "$PWD". For ollama (no repo file access), pick a repo-aware
backend instead, or ask before sending a minimal excerpt. Inlining
repo content can leak uncommitted edits or committed secrets and is
not needed for a Q&A rally. The opening question and round
transcripts are narrative context that the orchestrator generates and
inlines into the relay prompt; that is the intended use, not file
dumping.$ARGUMENTScommand -v phone-a-friend
RELAY_MODE = binaryRELAY_MODE = directNo hard abort. The skill continues either way.
When RELAY_MODE = direct, call backend CLIs directly instead of using the
phone-a-friend binary:
| Backend | Direct command |
|---|---|
| Codex | codex exec -C "$PWD" --skip-git-repo-check --sandbox read-only "$(cat "$PROMPT_FILE")" < /dev/null |
| Gemini | gemini --sandbox --yolo --include-directories "$PWD" --output-format text -m <model> --prompt "$(cat "$PROMPT_FILE")" |
| Ollama | PROMPT_JSON="$(jq -Rs . < "$PROMPT_FILE")"; curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" -d "{\"model\":\"<model>\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}],\"stream\":false}" | jq -r '.message.content' |
In direct mode, build PROMPT_FILE from the relay prompt using this
template and the quoted-heredoc rule:
You are helping another coding agent by reviewing or advising on work in a local repository.
Repository path: <repo-path>
Use the repository files for context when needed.
Respond with concise, actionable feedback.
Request:
<relay-prompt>
No "Additional Context" section is needed for curiosity-engine (prompts are self-contained).
Note: do NOT pass PaF flags like --no-include-diff, --fast, or
--session in direct mode. They are CLI flags on the phone-a-friend
binary; the underlying backend CLIs do not accept them.
/curiosity-engine rounds use self-contained prompts; the working-tree diff
would be irrelevant noise. PaF reads defaults.include_diff from user
config, so without explicit suppression a user with include_diff = true
would silently leak the diff into every relay round.
The cleanest flag is --no-include-diff, added in phone-a-friend v2.2.0.
Older binaries reject the flag with unknown option '--no-include-diff'.
Probe once before Round 1, then reuse the gate across every binary-mode
relay (initial round, follow-up rounds, and the schema re-prompt):
if phone-a-friend relay --help 2>/dev/null | grep -q -- '--no-include-diff'; then
PAF_NO_DIFF="--no-include-diff"
else
export PHONE_A_FRIEND_INCLUDE_DIFF=false
PAF_NO_DIFF=""
fi
Append $PAF_NO_DIFF to every binary-mode phone-a-friend invocation in
the steps below. The env var fallback works in v1.7.2 and later; the
explicit flag is preferred when available.
Extract --topic, --rounds, and --backend from $ARGUMENTS.
--topic <string> — required. Everything after --topic up to the next flag. If missing, ask the user: "What topic should the Curiosity Engine explore?" Do not proceed until provided.--rounds N — optional, default 3, clamp to [1, 6].--backend codex|gemini|ollama — optional, default codex.If --backend value is not codex, gemini, or ollama: report error and stop.
Set:
codex)command -v codex # if BACKEND=codex
command -v gemini # if BACKEND=gemini
curl -sf http://localhost:11434/api/tags
If reachable and RELAY_MODE = direct: parse the JSON response to extract
model names from models[].name. Set OLLAMA_SELECTED_MODEL to the first
model in the list. If the list is empty, abort: "Ollama server is running but
has no models pulled. Install one with: ollama pull <model-name>". Report
the selected model to the user: "Ollama: using model <name>".
If RELAY_MODE = binary, the binary handles model selection internally.
| BACKEND | Available | Action |
|---|---|---|
| codex | yes | proceed |
| codex | no | abort: "codex CLI not found. Install: npm install -g @openai/codex" |
| gemini | yes | proceed |
| gemini | no | abort: "gemini CLI not found. Install: npm install -g @google/gemini-cli" |
| ollama | reachable | proceed (discover model if direct mode) |
| ollama | not reachable | abort: "Ollama not reachable at localhost:11434. Is Ollama running?" |
The orchestrating agent (the host model running this skill) serves first. It produces the opening move directly, without relaying to any backend:
ANSWER: N/A — I'm serving first.
QUESTION: <orchestrator's opening question on TOPIC — make it genuinely curious and specific>
Display to user:
--- Round 1 of <MAX_ROUNDS> | Topic: <TOPIC> ---
🤖 <orchestrator> QUESTION: <question>
<orchestrator> is the host model's display label (e.g., "Claude" in
Claude Code, the OpenCode model name in OpenCode). Pick one that the user
will recognize.
Then relay to backend. First build PROMPT_FILE so untrusted text such as
TOPIC and QUESTION is passed as data, not spliced into an inline shell
command:
PROMPT_FILE="$(mktemp)"
trap 'rm -f "$PROMPT_FILE" "${REPROMPT_FILE:-}"' EXIT
{
printf '%s\n' 'You are playing The Curiosity Engine — a structured Q&A rally with another agent.'
printf 'Topic: %s\n' "$TOPIC_SAFE"
printf 'Round: 1 of %s\n\n' "$MAX_ROUNDS"
printf '%s\n' "The orchestrating agent's question for you:"
printf '%s\n\n' "$QUESTION"
cat <<'PAF_CURIOSITY_PROMPT_EOF'
You MUST respond in EXACTLY this format — no exceptions, no extra text:
ANSWER: <your answer to the orchestrator's question, 2-4 sentences>
QUESTION: <a new question for the orchestrator on the same topic, that you are genuinely curious about>
Do not add any text before ANSWER: or after the QUESTION line.
PAF_CURIOSITY_PROMPT_EOF
} > "$PROMPT_FILE"
Binary mode (RELAY_MODE = binary):
phone-a-friend --to <BACKEND> --repo "$PWD" --sandbox read-only --fast $PAF_NO_DIFF [--model <model>] --prompt "$(cat "$PROMPT_FILE")"
Direct mode (RELAY_MODE = direct):
# Codex:
codex exec -C "$PWD" --skip-git-repo-check --sandbox read-only "$(cat "$PROMPT_FILE")" < /dev/null
# Gemini (always include -m):
gemini --sandbox --yolo --include-directories "$PWD" --output-format text -m <model> --prompt "$(cat "$PROMPT_FILE")"
# Ollama (use OLLAMA_SELECTED_MODEL from Step 2):
PROMPT_JSON="$(jq -Rs . < "$PROMPT_FILE")"
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" \
-d "{\"model\":\"<OLLAMA_SELECTED_MODEL>\",\"messages\":[{\"role\":\"user\",\"content\":${PROMPT_JSON}}],\"stream\":false}" \
| jq -r '.message.content'
If the relay call (binary or direct) produces no output, empty stdout, or a
non-zero exit code:
Display: ⚠️ Relay call failed for round <ROUND>. Ending game early.
Jump to Step 6 (Synthesis).
After each relay call, parse the response for ANSWER: and QUESTION: fields.
ANSWER: — extract everything after it (may be multiline until QUESTION: appears).QUESTION: — extract everything after it to end of response.QUESTION: is missing → schema violation. Execute re-prompt (see Step 4a).ANSWER: is missing → schema violation. Treat the same as missing QUESTION: — execute re-prompt (Step 4a).Send one correction relay if ANSWER: or QUESTION: is missing:
First create REPROMPT_FILE with a quoted heredoc:
REPROMPT_FILE="$(mktemp)"
cat > "$REPROMPT_FILE" <<'PAF_CURIOSITY_REPROMPT_EOF'
Your previous response did not follow the required format.
You MUST respond with EXACTLY this structure:
ANSWER: <your answer>
QUESTION: <your question for the orchestrator>
No other text. Try again.
PAF_CURIOSITY_REPROMPT_EOF
Binary mode (RELAY_MODE = binary):
phone-a-friend --to <BACKEND> --repo "$PWD" --sandbox read-only --fast $PAF_NO_DIFF [--model <model>] --prompt "$(cat "$REPROMPT_FILE")"
Direct mode (RELAY_MODE = direct):
# Codex:
codex exec -C "$PWD" --skip-git-repo-check --sandbox read-only "$(cat "$REPROMPT_FILE")" < /dev/null
# Gemini:
gemini --sandbox --yolo --include-directories "$PWD" --output-format text -m <model> --prompt "$(cat "$REPROMPT_FILE")"
# Ollama:
REPROMPT_JSON="$(jq -Rs . < "$REPROMPT_FILE")"
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" \
-d "{\"model\":\"<OLLAMA_SELECTED_MODEL>\",\"messages\":[{\"role\":\"user\",\"content\":${REPROMPT_JSON}}],\"stream\":false}" \
| jq -r '.message.content'
Parse again. If still missing QUESTION: → end game early. Display:
⚠️ <BACKEND> broke the chain on round <N> (missing QUESTION: after re-prompt).
Ending game early. Running synthesis on completed rounds.
Jump to Step 6 (Synthesis).
Display backend's response:
🔵 <BACKEND> ANSWER: <answer>
QUESTION: <question>
If this was the final round (ROUND == MAX_ROUNDS) → jump to Step 6 (Synthesis).
Otherwise, increment ROUND. The orchestrating agent (the host model) now responds directly — no relay:
🤖 <orchestrator> ANSWER: <orchestrator's genuine answer to backend's question, 2-4 sentences>
QUESTION: <orchestrator's new question for backend on TOPIC>
Relay the orchestrator's question to backend using this template (same structure as Step 3, substituting current values):
You are playing The Curiosity Engine — a structured Q&A rally with another agent.
Topic: <TOPIC>
Round: <ROUND> of <MAX_ROUNDS>
The orchestrating agent's question for you:
<QUESTION>
You MUST respond in EXACTLY this format — no exceptions, no extra text:
ANSWER: <your answer to the orchestrator's question, 2-4 sentences>
QUESTION: <a new question for the orchestrator on the same topic, that you are genuinely curious about>
Do not add any text before ANSWER: or after the QUESTION line.
Repeat Step 4 and Step 5 until MAX_ROUNDS reached or early termination.
Orchestrator discipline: the host model ALWAYS provides both ANSWER: and QUESTION: — never skips either field, never breaks the schema itself.
If ROUND == 1 and no backend response was ever successfully parsed (zero completed rounds):
Display: No rounds completed — cannot synthesize. Check backend availability and try again.
Stop.
Present the full session summary:
## Curiosity Engine — Session Complete
**Topic:** <TOPIC>
**Backend:** <BACKEND>
**Rounds completed:** <N> of <MAX_ROUNDS>
**Status:** <Converged naturally | Early termination — <BACKEND> broke chain on round N>
---
### Full Rally Transcript
<all rounds, formatted as displayed during play>
---
### Most Interesting Exchange
<orchestrator picks the sharpest Q&A pair from the transcript and explains in 2-3 sentences why it was the most interesting — what tension, insight, or surprise it revealed>
---
### Open Threads
<2-3 questions raised during the rally that weren't followed up on, worth exploring in a future session>
By default, omit --model for --to gemini and let Gemini CLI's
auto-routing pick. Set --model only when reproducibility, specific
capability, or debugging requires a pin.
Binary mode (preferred — when an explicitly pinned model returns a strong
404 / ModelNotFoundError, PaF caches it as unavailable for 24h at
~/.config/phone-a-friend/gemini-models.json and fails fast on subsequent
calls; no auto-substitution):
phone-a-friend --to gemini --repo "$PWD" --sandbox read-only --fast $PAF_NO_DIFF --prompt "$(cat "$PROMPT_FILE")"
To bypass the cache: PHONE_A_FRIEND_GEMINI_DEAD_CACHE=false. Or delete the
cache file to clear it.
Direct mode (no PaF wrapper — orchestrator handles retry):
gemini --sandbox --yolo --include-directories "$PWD" --output-format text --prompt "$(cat "$PROMPT_FILE")"
In direct mode, on capacity/transient errors (429, 500, 503), retry with a
different model before treating as round failure. On ModelNotFoundError,
surface immediately. Do NOT use aliases like auto, pro, or flash —
either omit --model entirely or pass a concrete model name.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub freibergergarcia/phone-a-friend