Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Agent details

Disclaimer: These documents reflect our current understanding of each agent’s hook system and extensibility surface. They are maintained as working references for symposium development, not as a substitute for each project’s official documentation. Details may be outdated or incomplete — always consult the primary sources linked in each agent’s page.

For each agent it supports, symposium needs to know:

  1. Hook registration — where and how to write config so the agent calls cargo-agents hook
  2. Hook I/O protocol — event names, input/output field names, exit code semantics
  3. Extension installation — where skill files go (project and global)
  4. Custom instructions — where the agent reads project-level instructions

The tables below summarize the answers for each agent. Individual agent pages contain the full reference. A ? indicates information we have not yet documented.

Hook registration

AgentProject config pathGlobal config pathFormat
Claude Code.claude/settings.json~/.claude/settings.jsonJSON, hooks key with matcher groups
GitHub Copilot.github/hooks/*.json~/.copilot/config.jsonJSON, version: 1 with hooks key
Gemini CLI.gemini/settings.json~/.gemini/settings.jsonJSON, hooks key with matcher groups
Codex CLI.codex/hooks.json~/.codex/hooks.jsonJSON, hooks key with matcher groups
Kiro.kiro/agents/*.json~/.kiro/agents/*.jsonJSON, hooks key in agent config
OpenCode.opencode/plugins/~/.config/opencode/plugins/JS/TS plugins (not shell hooks)
Goose(no hooks)(no hooks)N/A

Command field

AgentCommand fieldPlatform-specific?
Claude CodecommandNo
GitHub Copilotbash / powershellYes
Gemini CLIcommandNo
Codex CLIcommandNo
KirocommandNo
OpenCodeN/A (JS function)N/A
GooseN/AN/A

Timeout defaults

AgentDefault timeoutUnit
Claude Code600seconds
GitHub Copilot30seconds (timeoutSec)
Gemini CLI60,000milliseconds (timeout)
Codex CLI600seconds (timeout or timeoutSec)
Kiro30,000milliseconds (timeout_ms)
OpenCode60,000milliseconds (community hooks plugin)
GooseN/AN/A

Event names

Symposium registers hooks for four events. Each agent uses different names and casing conventions.

Symposium eventClaude CodeCopilotGemini CLICodex CLIKiro CLIOpenCodeGoose
pre-tool-usePreToolUsepreToolUseBeforeToolPreToolUsepreToolUsetool.execute.beforeN/A
post-tool-usePostToolUsepostToolUseAfterToolPostToolUsepostToolUsetool.execute.afterN/A
user-prompt-submitUserPromptSubmituserPromptSubmittedBeforeAgentUserPromptSubmituserPromptSubmitmessage.updated (filter by role)N/A
session-startSessionStartsessionStartSessionStartSessionStartagentSpawnsession.createdN/A

Blocking support

Not all events can block the action in all agents.

AgentPre-tool-use can block?Post-tool-use can block?User-prompt can block?Session-start can block?
Claude CodeYesNoYes (exit 2)No
GitHub CopilotYesNoNoNo
Gemini CLIYesYes (block result)Yes (deny discards message)No
Codex CLIYesYes (continue: false)Yes (continue: false)Yes (continue: false)
KiroYes (exit 2)NoNoNo
OpenCodeYes (throw Error)NoNo (observe only)No (observe only)
GooseN/AN/AN/AN/A

Hook I/O protocol

Input fields (pre-tool-use)

AgentTool name fieldTool args fieldSession/context fields
Claude Codetool_nametool_input (object)session_id, cwd, hook_event_name
GitHub CopilottoolNametoolArgs (JSON string)timestamp, cwd
Gemini CLItool_nametool_input (object)session_id, cwd, hook_event_name, timestamp
Codex CLItool_nametool_input (object)session_id, cwd, hook_event_name, model
Kirotool_nametool_input (object)hook_event_name, cwd
OpenCodetoolargs (mutable output object)sessionID, callID
GooseN/AN/AN/A

Output structure (pre-tool-use)

AgentPermission decision fieldDecision valuesModified input fieldNesting
Claude CodepermissionDecisionallow, deny, ask, deferupdatedInputnested in hookSpecificOutput
GitHub CopilotpermissionDecisionallow, deny, askmodifiedArgsflat
Gemini CLIdecisionallow, denytool_inputnested in hookSpecificOutput
Codex CLIdecision or permissionDecisionblock/deny(not yet implemented)flat or nested hookSpecificOutput
Kiro(exit code only)exit 0 = allow, exit 2 = block(not supported)N/A
OpenCode(throw to block)allow (return) / deny (throw)mutate output.argsJS mutation
GooseN/AN/AN/AN/A

Exit codes

All shell-based agents use the same convention (where applicable):

CodeMeaning
0Success; stdout parsed as JSON
2Block/deny; stderr used as reason
OtherNon-blocking warning, action proceeds

Exceptions: Copilot uses exit 0 = allow, non-zero = deny (no special meaning for exit 2). OpenCode uses JS exceptions, not exit codes.

Extension installation

Skill file paths

AgentProject skills pathGlobal skills path
Claude Code.claude/skills/<name>/SKILL.md~/.claude/skills/<name>/SKILL.md
GitHub Copilot.agents/skills/<name>/SKILL.md(none)
Gemini CLI.agents/skills/<name>/SKILL.md~/.gemini/skills/<name>/SKILL.md
Codex CLI.agents/skills/<name>/SKILL.md~/.agents/skills/<name>/SKILL.md
Kiro.kiro/skills/<name>/SKILL.md~/.kiro/skills/<name>/SKILL.md
OpenCode.agents/skills/<name>/SKILL.md~/.agents/skills/<name>/SKILL.md
Goose(N/A — uses MCP extensions)(N/A)

Symposium uses the vendor-neutral .agents/skills/ path whenever the agent supports it, falling back to agent-specific paths (e.g., .claude/skills/, .kiro/skills/) when required. Codex CLI and OpenCode also support .agents/skills/ natively.

Custom instructions

AgentProject instructionsGlobal instructions
Claude CodeCLAUDE.md, .claude/CLAUDE.md~/.claude/CLAUDE.md
GitHub Copilot.github/copilot-instructions.md, AGENTS.md~/.copilot/copilot-instructions.md
Gemini CLIGEMINI.md (walks up to .git)~/.gemini/GEMINI.md
Codex CLIAGENTS.md (each dir level)~/.codex/AGENTS.md
Kiro.kiro/steering/*.md, AGENTS.md~/.kiro/steering/*.md
OpenCodeAGENTS.md, CLAUDE.md~/.config/opencode/AGENTS.md
Goose.goosehints, AGENTS.md~/.config/goose/.goosehints

MCP server configuration

Relevant if symposium exposes functionality via MCP.

An agent’s MCP file is usually not the file its hooks live in, and the per-agent entry shapes differ more than they look. Agent::mcp_config_path encodes this table; every row marked verified was confirmed by asking the tool (<tool> mcp list reporting an entry symposium wrote, and <tool> mcp add showing which file it chooses), because a wrong guess here fails silently - symposium reports success for a file the agent never reads.

AgentProject scopeUser scopeEntry shapeVerified
Claude Code<project>/.mcp.json~/.claude.jsonmcpServers.<name> = {command, args}yes
Gemini CLI.gemini/settings.json~/.gemini/settings.jsonmcpServers.<name> = {command, args}yes
OpenCode<project>/opencode.json~/.config/opencode/opencode.jsonmcp.<name> = {type: "local", command: [bin, ...args], enabled, environment}yes
Codex CLI(none - user scope only)~/.codex/config.toml[mcp_servers.<name>] = command, argsyes
GitHub Copilot CLI(none - user scope only)~/.copilot/mcp-config.jsonmcpServers.<name> = {command, args}yes
Kiro.kiro/settings/mcp.json~/.kiro/settings/mcp.jsonmcpServers.<name> = {command, args}no (GUI only)
Goose(none - user scope only)~/.config/goose/config.yamlextensions.<name> = {name, type: stdio, cmd, args, enabled, envs}yes

Notes that cost real debugging time:

  • Claude Code ignores mcpServers in settings.json at both scopes - that file is hooks only. See Claude Code.
  • The Copilot CLI requires the mcpServers wrapper; entries written bare at the top level make it reject the whole file (mcpServers: Required), taking the user’s own servers down with it. .vscode/mcp.json belongs to the VS Code extension, a different product symposium does not currently target.
  • OpenCode rejects the entire config file for a wrong entry shape, so its serializer is separate: the command is one array, and env vars go under environment (an env key parses but never reaches the child).
  • Codex, Copilot CLI and Goose have no project-level MCP config, so project scope resolves to their user-level file rather than to a file nobody reads.
  • Goose uses cmd, not command, and requires a type; the nested provider: mcp / config: form it once got is rejected outright. Remote is type: streamable_http with uri - Goose has no sse variant.
  • env/headers must be maps. ACP models them as [{name, value}] pairs, and a list is skipped or rejected - silently, for an entry that differs from a working one only by carrying env. Codex takes env as a TOML table, Goose as envs, OpenCode as environment, everyone else as env.
  • The Copilot CLI also needs an explicit type (local/http/sse), or a remote entry never appears.