| name | voice:status |
| description | Report the current state of the voice channel connection, last utterance, and any errors. |
| allowed-tools | ["Read","Bash(echo *)"] |
/voice:status
Report the current state of the voice channel connection.
Resolve the state dir
First run:
echo "${VOICE_STATE_DIR:-${CLAUDE_PROJECT_DIR:-$(pwd)}/.claude/channels/voice}"
Use the output as <STATE_DIR> for all file paths below.
What you do
Read <STATE_DIR>/status.json and <STATE_DIR>/config.json and report:
- Connection state —
connecting, connected, disconnected, or error (from status.json)
- Dispatcher URL — from
config.json, or "not configured" if no config exists
- Agent ID — from
config.json
- TLS — if
dispatcher_url starts with wss://, show "enabled (pinned)"; if ws://, show "disabled (plaintext)"
- Permission relay — enabled or disabled
- Last activity —
last_utterance_id and ts from status.json if present
- Last error —
last_close_code, last_close_reason, or last_error if present
Error states
Certificate pin failure
If status.json has state: 'error' and last_error contains "cert pin mismatch" or "pin",
render it prominently as:
⚠ Certificate pin failure — token was NOT sent to the dispatcher.
The dispatcher's TLS certificate does not match the pinned certificate.
Fix: re-run /voice:configure with the correct pairing string from the dispatcher.
(Run 'voice-dispatcher config rotate-token <id>' to generate a fresh v2 pairing string.)
Do NOT treat this as a generic disconnect. It means the plugin detected an
impersonation attempt or stale pinned certificate and refused to send the bearer token.
Authentication failure
If status.json has last_close_code: 4001 or last_error contains "token was rejected" or
"authentication failed", render it prominently as:
⚠ Authentication failure — dispatcher rejected this agent token.
Fix: on the dispatcher host, run 'voice-dispatcher config rotate-token <agent_id>'
and then re-run /voice:configure on this Claude Code host with the new pairing string.
Do NOT keep presenting this as a transient reconnecting state. It means the local plugin config is
stale or belongs to a different dispatcher agent.
Generic disconnect
If state is disconnected or error without a pin-related last_error, show
the close code, reason, and last error normally.
If status.json does not exist
The MCP server has not started yet. Tell the user to:
- Ensure
/voice:configure has been run
- Restart the Claude Code session (the MCP server starts automatically from
.mcp.json)
If config.json does not exist
The plugin is not configured. Tell the user to run /voice:configure.
If VOICE_STATE_DIR is unset, also note the migration case: configs created before the
project-local default lived at ~/.claude/channels/voice, which the MCP server still uses as its
fallback. So voice may actually be working even though this skill looks in the project dir and
reports "not configured" — re-running /voice:configure migrates the config to the project-local
path and pins VOICE_STATE_DIR, ending the mismatch.
Example output — connected
Voice channel status
────────────────────
State: connected
Dispatcher: wss://192.168.1.50:7355
Agent ID: jarvis
TLS: enabled (pinned)
Permission relay: disabled
Last utterance: u-1748012345 (2026-05-24T10:30:00Z)
Example output — pin failure
Voice channel status
────────────────────
State: error
Dispatcher: wss://192.168.1.50:7355
⚠ Certificate pin failure — token was NOT sent to the dispatcher.
The dispatcher's TLS certificate does not match the pinned certificate.
Fix: re-run /voice:configure with the correct pairing string from the dispatcher.