setup

A setup command for connecting Codex with GPT, Gemini, Grok, and OpenRouter expert assistants through MCP, a standard way for tools and applications to communicate. It also installs the related deliberation rules and can add command shortcuts.

In plain words
What is it for?
Use it when configuring expert subagents for development discussions, installing the deliberation rules, optionally adding short command aliases, or checking the resulting setup.
Why use it?
It sets up the required configuration in one guided process instead of requiring each server and rule to be configured separately. It also checks whether the setup completed correctly.

Command

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add commands/antonbabenko/deliberation/setup
Clone the repo
git clone --depth 1 https://github.com/antonbabenko/deliberation
Per session 21 Only the description is in the session, so the agent can decide to use it. The body loads when it is invoked.
When invoked 4,845 The whole file, excluding the scripts and references it only reads on demand.
Security scan B 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00021 $0.04845
Opus 5 $0.00010 $0.02423
Sonnet 5 $0.00004 $0.00969
Haiku 4.5 $0.00002 $0.00485

Measured 2d ago against content hash c2110bf5f8be, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade B, and why

setup scanned grade B with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Reads agent configuration directoriesmediumAgent snooping

.claude/, .codex/, .gemini/ hold keys, settings and other credentials a mod has no legitimate need for.

c=$(find "$HOME/.claude/plugins/cache" -maxdepth 6 -path '*/deliberation/*/server/mcp/index.js' -type f 2>/dev/null | sort -V | tail -1)
commands/setup.md · 307 lines

How it starts

The opening of the file, as written. The whole thing — 307 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Setup

Configure GPT (via Codex), Gemini, Grok, and OpenRouter as expert subagents via MCP, install the orchestration rules, and (optionally) the short command aliases. Grok and OpenRouter are advisory-only.

This command runs in three phases: ONE main Bash call (checks + seed config + migrate + install rules + status), then isolated question turns for the optional aliases and the optional GitHub star. Do not batch a Bash call with an AskUserQuestion, and do not split the main block.

Step 1: Run setup

Run the block below as ONE Bash call. Do NOT split it into smaller calls, and do NOT batch it with any other tool call. It is idempotent - safe to re-run.

Run it with the Bash sandbox DISABLED. The block writes ~/.claude/rules/deliberation/ and ~/.claude.json, both outside a typical sandbox write allowlist. Under a sandbox those writes fail silently; the block verifies the result at the end and prints a CRITICAL block telling you to re-run unsandboxed if it detects a problem.

The MCP servers are registered by the plugin manifest (inline mcpServers in .claude-plugin/plugin.json), so they load automatically when the plugin is enabled and update with /plugin marketplace update antonbabenko + /reload-plugins. This block is non-interactive: it seeds a default config.json, checks the provider CLIs, installs the rules, and prints a status report.

set -u

# --- resolve plugin root: env var -> marketplace cache (highest semver) -> current checkout ---
# A candidate is valid only if it contains server/mcp/index.js.
resolve_plugin_root() {
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -f "$CLAUDE_PLUGIN_ROOT/server/mcp/index.js" ]; then
    printf '%s' "$CLAUDE_PLUGIN_ROOT"; return 0; fi
  # marketplace cache, highest version (use find, not a glob - a failed zsh glob warns on stderr)
  local c
  c=$(find "$HOME/.claude/plugins/cache" -maxdepth 6 -path '*/deliberation/*/server/mcp/index.js' -type f 2>/dev/null | sort -V | tail -1)
  if [ -n "$c" ]; then printf '%s' "${c%/server/mcp/index.js}"; return 0; fi
  if [ -f "$PWD/server/mcp/index.js" ] && grep -q '"name": "deliberation"' "$PWD/.claude-plugin/plugin.json" 2>/dev/null; then
    printf '%s' "$PWD"; return 0; fi
  return 1
}
PLUGIN_ROOT="$(resolve_plugin_root)" || { echo "Error: cannot locate the deliberation plugin root. Install via /plugin, run from the plugin checkout, or set CLAUDE_PLUGIN_ROOT."; exit 1; }

# --- config path: env override > canonical XDG ---
# Mirrors core/paths.js: DELIBERATION_CONFIG wins; else the canonical
# ${XDG_CONFIG_HOME or ~/.config}/deliberation/config.json. Per the XDG spec a
# RELATIVE XDG_CONFIG_HOME is ignored and the default used.
if [ -n "${DELIBERATION_CONFIG:-}" ]; then
  CFG="$DELIBERATION_CONFIG"
else
  if [ -n "${XDG_CONFIG_HOME:-}" ] && [ "${XDG_CONFIG_HOME#/}" != "${XDG_CONFIG_HOME}" ]; then
    XDG_BASE="$XDG_CONFIG_HOME"
  else
    XDG_BASE="$HOME/.config"
  fi
  CFG="$XDG_BASE/deliberation/config.json"
fi

# --- sessions store dir: env override > canonical XDG cache ---
# Mirrors core/paths.js resolveSessionsDir / canonicalCacheDir: DELIBERATION_SESSIONS
# wins; else ${XDG_CACHE_HOME or ~/.cache}/deliberation/sessions. A RELATIVE
# XDG_CACHE_HOME is ignored (XDG spec) and the default used.
if [ -n "${DELIBERATION_SESSIONS:-}" ]; then
  SESSIONS_DIR="$DELIBERATION_SESSIONS"
else
  if [ -n "${XDG_CACHE_HOME:-}" ] && [ "${XDG_CACHE_HOME#/}" != "${XDG_CACHE_HOME}" ]; then
    CACHE_BASE="$XDG_CACHE_HOME"
  else
    CACHE_BASE="$HOME/.cache"
  fi
  SESSIONS_DIR="$CACHE_BASE/deliberation/sessions"
fi

# --- seed a default config on first run (never clobber an existing file) ---
# Codex/Gemini/Grok enabled; OpenRouter disabled with two example model records
# (also disabled). Edit $CFG to turn OpenRouter / the models on, then re-run setup.
CONFIG_CREATED=0
if [ ! -f "$CFG" ]; then
  mkdir -p "$(dirname "$CFG")"
  if cp "$PLUGIN_ROOT/config/config.default.json" "$CFG" 2>/dev/null; then
    CONFIG_CREATED=1
  else
    echo "WARN: could not seed default config at $CFG"
  fi
fi

# Helpers take their first arg WITHOUT the literal $1/$2 tokens: Claude Code
# interpolates $1..$9 / $ARGUMENTS in a command body before bash runs, and this is a
# no-arg command, so any $1 here would be blanked. `for x in "$@"; do break; done`
# binds x to the first arg; the guarded shift drops it so "$@" is the remainder.
# `$@` is NOT a slash-command placeholder, so it survives intact.
json_eval() {
  local prog="" ; for prog in "$@"; do break; done ; [ "$#" -gt 0 ] && shift
  node -e "$prog" "$CFG" "$@" 2>/dev/null
}
# openrouter on iff providers.openrouter.enabled!=false AND (>=1 models record OR defaultModel).
# Unified v1 shape: connection lives under providers.openrouter; models is the top-level map.
openrouter_enabled() {
  json_eval 'try{const c=require(process.argv[1]);const p=(c.providers&&c.providers.openrouter)||{};const hasModel=(c.models&&typeof c.models==="object"&&Object.keys(c.models).length)||p.defaultModel;const on=p.enabled!==false&&hasModel;process.stdout.write(on?"1":"0")}catch(e){process.stdout.write("0")}'
}
or_key_env() {
  json_eval 'try{const c=require(process.argv[1]);const p=(c.providers&&c.providers.openrouter)||{};process.stdout.write(p.apiKeyEnv||"OPENROUTER_API_KEY")}catch(e){process.stdout.write("OPENROUTER_API_KEY")}'
}
# sessions: "ON|OFF" + max records + max age, rendering -1 as "unlimited". Missing
# config or block => default OFF / 200 / 30d. Output shape: "<ON|OFF>|<recs>|<age>".
sessions_summary() {
  json_eval 'try{const c=require(process.argv[1]);const s=c.sessions||{};const on=s.persist===true?"ON":"OFF";const mr=Number.isInteger(s.maxRecords)?s.maxRecords:200;const md=Number.isInteger(s.maxAgeDays)?s.maxAgeDays:30;const recs=mr===-1?"unlimited":String(mr);const age=md===-1?"unlimited":md+"d";process.stdout.write(on+"|"+recs+"|"+age)}catch(e){process.stdout.write("OFF|200|30d")}'
}

# Remove a user-scope MCP registration so it cannot shadow the manifest entry. Tolerant of absence.
remove_mcp() {
  local name="" ; for name in "$@"; do break; done
  claude mcp remove --scope user "$name" >/dev/null 2>&1 || true
}
# List any user-scope deliberation-* / deliberation entries in ~/.claude.json (a sandbox blocks
# WRITES to it, not reads, so this read is the source of truth). Echoes a space-joined list.
stale_userscope() {
  node -e 'const fs=require("fs"),h=require("os").homedir();try{const j=JSON.parse(fs.readFileSync(h+"/.claude.json","utf8"));const m=j.mcpServers||{};process.stdout.write(Object.keys(m).filter(k=>k==="deliberation"||k.indexOf("deliberation-")===0).join(" "))}catch(e){process.stdout.write("")}'
}

# --- CLI presence (external tools; bridges ship with the plugin so are not checked) ---
# Read --version from stdout only. Folding in stderr (2>&1) would capture codex's
# "WARNING: proceeding, even though we could not update PATH" line (emitted when codex
# cannot rewrite PATH, e.g. under a sandbox) and report it as the version. The grep
# fallback covers builds that print the version on stderr while still dropping the warning.
if command -v codex >/dev/null 2>&1; then
  CODEX_STATUS="$(codex --version 2>/dev/null | head -1)"
  [ -z "$CODEX_STATUS" ] && CODEX_STATUS="$(codex --version 2>&1 | grep -ivE 'warning|could not update path' | head -1)"
  [ -z "$CODEX_STATUS" ] && CODEX_STATUS="installed"
else
  CODEX_STATUS="MISSING (npm i -g @openai/codex)"
fi
command -v agy   >/dev/null 2>&1 && AGY_STATUS="installed" || AGY_STATUS="MISSING (https://antigravity.google)"

# --- keep the plugin manifest as the only MCP registration ---
# The manifest (inline mcpServers in .claude-plugin/plugin.json) registers the servers with
# ${CLAUDE_PLUGIN_ROOT}, which Claude Code re-resolves on each load. Clear any user-scope copies
# so they cannot shadow it.
# Per-provider enable/disable is gated in config via the unified server's fan-out (/ask-all,
# /consensus); the direct provider tools always load.
for s in deliberation deliberation-codex deliberation-gemini deliberation-grok deliberation-openrouter; do
  remove_mcp "$s"
done

# OpenRouter auth note (the manifest registers the server; this is only a key reminder).
if [ "$(openrouter_enabled)" = "1" ]; then
  KEYENV="$(or_key_env)"; [ -z "$(printenv "$KEYENV" 2>/dev/null)" ] && echo "Note: \$$KEYENV is empty; OpenRouter calls return auth errors until you export it."
fi

# --- install orchestration rules (copy only; never deletes) ---
mkdir -p "$HOME/.claude/rules/deliberation"
cp "$PLUGIN_ROOT"/rules/*.md "$HOME/.claude/rules/deliberation/" 2>/dev/null || true
RULE_COUNT=$(find "$HOME/.claude/rules/deliberation" -maxdepth 1 -name '*.md' 2>/dev/null | wc -l | tr -d ' ')

# --- confirm no user-scope entry shadows the manifest (catches a sandbox-blocked write) ---
# A sandbox blocks the WRITE to ~/.claude.json, so a user-scope entry can survive the removal
# above. Collect anything that remains; clean state is empty.
USERSCOPE_LEFT="$(stale_userscope)"

# --- status ---
echo
echo "deliberation setup"
echo "--------------------------------------------------"
echo "Codex CLI:       $CODEX_STATUS"
echo "Antigravity CLI: $AGY_STATUS"
echo "Config file:     $CFG"
[ "$CONFIG_CREATED" = "1" ] && echo "                 (created from default - codex/gemini/grok on, OpenRouter off, 2 example models off)"
echo "                 Edit it to enable OpenRouter and the example models, then re-run /deliberation:setup."
SESS="$(sessions_summary)"; SESS_STATE="${SESS%%|*}"; SESS_REST="${SESS#*|}"; SESS_RECS="${SESS_REST%%|*}"; SESS_AGE="${SESS_REST#*|}"
echo "Sessions:        persistence $SESS_STATE (opt-in; default OFF)"
[ "$SESS_STATE" = "OFF" ] && echo "                 turn on: set \"sessions\": { \"persist\": true } in $CFG"
echo "                 store: $SESSIONS_DIR (max records: $SESS_RECS, max age: $SESS_AGE; -1 = unlimited)"
echo "Rules:           $RULE_COUNT files in ~/.claude/rules/deliberation/"
echo "Grok auth:       $([ -n "${XAI_API_KEY:-}" ] && echo "XAI_API_KEY set" || echo "XAI_API_KEY not set (calls return missing-auth)")"
echo "OpenRouter auth: $([ -n "${OPENROUTER_API_KEY:-}" ] && echo set || echo "not set")"
if [ -n "$USERSCOPE_LEFT" ]; then
  echo "MCP servers:     CRITICAL - user-scope entries shadow the manifest:$USERSCOPE_LEFT"
  echo "                 A Bash sandbox most likely blocked the write to ~/.claude.json. Re-run"
  echo "                 /deliberation:setup with the sandbox DISABLED (see /sandbox)."
else
  echo "MCP servers:     registered by the plugin manifest (inline in .claude-plugin/plugin.json); load on enable."
  echo "                 'claude mcp list' shows them once the plugin loads."
fi
echo
echo "Update flow: '/plugin update' then '/reload-plugins' (or restart Claude Code)."
echo "If the deliberation-* tools are not visible yet, run /reload-plugins or restart Claude Code."

Read the full file on GitHub · 307 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 2d ago First seen · 307 lines · 21 tokens per session scan B c2110bf5f8be

Subscribe to this mod's changes

setup is a command published in the GitHub repository antonbabenko/deliberation (138 stars, last pushed 4d ago), licensed MIT. It adds 21 tokens to every session and 4,845 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it B with 1 finding (reads agent configuration directories). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.