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.
npx agentmods add commands/antonbabenko/deliberation/setupgit clone --depth 1 https://github.com/antonbabenko/deliberationWhat 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.
| Model | Per session | Once 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 |
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) 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 aCRITICALblock 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."
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.
- 2d ago First seen · 307 lines · 21 tokens per session scan B c2110bf5f8be
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.
Other commands, from other repositories
ask
Query multiple AI agents (Gemini, OpenAI, Grok, Perplexity, Kimi, and a local ollama model) for diverse perspectives on architecture decisions, technology choices, debugging dead-ends, and security tradeoffs. Use this whenever the user names the council directly, whatever the topic — ask the council, council review…
result
Fetch, list, or cancel background council jobs started with --async.
status
Check connectivity and configuration status of all council providers.
setup
First-run wizard — pick AI providers, walk through CLI install + auth, verify each, save settings.
codex-update
Install or update the upstream openai/codex-plugin-cc to the pinned tag.
settings
Show or change which AI providers are enabled, the default, and the /ai:compare set.