AugmentClaude

ACP Debug

Debug ACP agents by spawning them, capturing JSON-RPC frames, and analyzing capabilities.

Installation

  1. Make sure Claude is on your device and in your terminal.

    Skills load from ~/.claude/skills/ when Claude Code starts up β€” so you need it on your machine first. If you don't have it yet, install it once with the command below, then run claude in any terminal to verify.

    One-time setup
    npm i -g @anthropic-ai/claude-code

    Already have it? Skip ahead.

  2. Paste into Claude Code or into your terminal.

    This copies the whole skill folder into ~/.claude/skills/acp-debug-kdlbs/ β€” the SKILL.md plus any scripts, reference docs, or templates the skill ships with. Safe default: works for every skill.

    Faster alternative (instruction-only skills)

    Skips the clone and grabs only the SKILL.md file. Don't use this if the skill ships Python scripts, reference markdowns, or asset templates β€” they won't be downloaded and the skill will fail when it tries to load them.

    Quick install (SKILL.md only)
    Sign up to copy
  3. Restart Claude Code.

    Quit and reopen Claude Code (or any other agent that loads from ~/.claude/skills/). New skills are picked up on startup.

  4. Just ask Claude.

    Skills auto-activate when your request matches the skill's description β€” no slash command needed. Trigger phrases live in the skill's own frontmatter; you can read them in the β€œWhat this skill does” section above.

Prefer to read the source first? Open on GitHub.

When Claude uses it

Debug an ACP agent CLI by spawning it, speaking raw JSON-RPC, and capturing every frame to a JSONL file. Use when the user asks to probe an agent's capabilities, compare agents, test a prompt against an agent, inspect raw ACP wire frames, or investigate why an agent fails to initialize.

What this skill does

ACP Debug

Run a headless ACP JSON-RPC session against any registered kandev agent (or an arbitrary command), record every wire frame to a JSONL file, and summarize the handshake. Backed by the acpdbg binary at apps/backend/bin/acpdbg.

When to use this skill

  • "what models does auggie advertise"
  • "probe claude-acp" / "probe all agents" / "run the matrix"
  • "debug why copilot-acp isn't starting"
  • "what does a session/new response actually look like for X"
  • "try this prompt against auggie with mode=ask"
  • "reproduce session/load for session <id> against claude-acp"
  • User mentions inspecting raw ACP wire payloads or a JSONL file from an earlier run

Before anything else: build the binary if missing

test -x apps/backend/bin/acpdbg || make -C apps/backend build-acpdbg

Sub-commands

acpdbg list                                            # enumerate registered ACP agents
acpdbg probe <agent>                                   # initialize + session/new + close
acpdbg probe --exec "<cmd> [args...]"                  # probe an arbitrary binary not in the registry
acpdbg mcp-probe <agent>                                # inject a temporary MCP sentinel into session/new
acpdbg prompt --prompt "..." [--model M] [--mode M] <agent>
acpdbg session-load --session-id <id> <agent>
acpdbg matrix                                          # probe every ACP agent in parallel

Flags MUST come before the positional <agent>. The CLI uses Go's stdlib flag parser, which stops at the first non-flag token β€” so any flag placed after <agent> is silently ignored (probe/matrix, e.g. a dropped --timeout) or errors (prompt β†’ --prompt is required). Always write acpdbg prompt --prompt "..." --timeout 240s <agent>, not acpdbg prompt <agent> --prompt ....

Shared flags (apply to every sub-command):

  • --out DIR β€” JSONL output directory (default ./acp-debug/)
  • --file PATH β€” exact JSONL path, overrides --out
  • --timeout DUR β€” overall run timeout (default 30s)
  • --workdir PATH β€” child cwd (default: fresh /tmp/kandev-acpdbg-<pid>-*)
  • --verbose β€” mirror frames to stderr
  • --stderr β€” capture child stderr into the JSONL

Steps

Create a task for each step below and mark them as completed as you go.

1. Pick the sub-command that matches the user's intent

  • "what models does X have" / "does X support modes" β†’ acpdbg probe X
  • "test a prompt against X" β†’ acpdbg prompt --prompt "..." X
  • "compare all agents" / "run the matrix" β†’ acpdbg matrix
  • "resume session <id>" β†’ acpdbg session-load --session-id <id> X
  • "did this agent attach the injected MCP server" β†’ acpdbg mcp-probe X
  • "try this random binary" β†’ acpdbg probe --exec "path/to/bin --acp"

2. Run the command

Always capture stdout β€” it contains the JSONL file path (and for matrix, the summary table and matrix-summary.json path).

apps/backend/bin/acpdbg probe --timeout 45s auggie

For matrix, prefer --timeout 60s so npx-spawned agents have time to cold-start.

For Claude ACP, the generated default workdir is an absolute temporary directory and is sent as the ACP cwd; no --workdir flag is required. If you override it, supply an existing absolute directory (for example, run mkdir -p /tmp/kandev-acpdbg-claude before passing that path to --workdir). An empty cwd can cause Claude to reject session/new; confirm the cwd in the recorded request frame before diagnosing a provider failure.

3. Read the JSONL file

The JSONL schema is:

directionMeaningExtra fields
metaacpdbg-generated markerevent (start / close), meta (map with agent, command, workdir for start; exit_code, reason for close)
sentFrame written to child's stdinframe (JSON-RPC request or reply)
receivedFrame read from child's stdoutframe (JSON-RPC request, response, or notification)
stderrChild stderr line (only when --stderr)line

Entries are strictly chronological. Each line is a single JSON object terminated by \n.

Useful jq recipes:

# Full initialize response
jq -c 'select(.direction == "received" and .frame.id == 1)' acp-debug/<file>.jsonl

# Full session/new response (models, modes, auth)
jq '.frame.result' acp-debug/<file>.jsonl | head -50

# Just the models advertised
jq -r 'select(.direction == "received") | .frame.result.models.availableModels[]?.modelId' acp-debug/<file>.jsonl

# Close event (exit code + reason)
jq -c 'select(.direction == "meta" and .event == "close")' acp-debug/<file>.jsonl

# All agent-initiated requests we auto-replied to
jq -c 'select(.direction == "received" and .frame.method and .frame.id)' acp-debug/<file>.jsonl

4. Summarize for the user

Give a concise markdown summary: agent, protocol version, models found (with currentModelId), modes found (with currentModeId), auth methods, any errors, and the JSONL path for deeper inspection. Example:

**auggie** (protocol v1, auggie 0.20.1)
Models (11): claude-sonnet-4-6 (current), claude-opus-4-6, gpt-5-4, …
Modes  (2):  default (current), ask
Auth methods: (none advertised)
JSONL: acp-debug/auggie-probe-20260409-183104.jsonl

5. If something looks wrong, walk the frames

Common failure modes:

SymptomLikely causeNext step
meta close exit_code: 127 immediatelychild binary not installedCheck which <cmd>; suggest install command
meta close before any received framechild crashed on startupRe-run with --stderr to capture the error
initialize response has populated authMethods but session/new failsauth requiredSurface the auth method ids; suggest setting env var / running CLI login
session/new hangs (context deadline exceeded)agent waiting on an unanswered agent-initiated requestCheck JSONL for received frames with method + id that we auto-replied to with method not found β€” the agent may be retrying
Response has no models / modes fieldsagent doesn't expose them over ACPNot a bug β€” document the gap

Re-run with --stderr whenever the child exits before the handshake completes; the stderr lines land in the JSONL and usually contain the root cause.

For mcp-probe, distinguish sentinel_delivered (the agent accepted the session/new configuration) from initialize_observed and tools_list_observed (the temporary endpoint received MCP traffic). An unobserved result is intentionally not a generic agent failure: a provider can attach lazily or ignore the supplied transport. Sentinel metadata has only opaque connection IDs and timestamps; the explicit acpdbg JSONL still contains raw ACP frames and must stay a developer-only artifact.

6. For matrix, read matrix-summary.json too

jq '.' acp-debug/matrix-summary.json

One entry per agent with status, models_count, current_model_id, auth_methods_count, duration_ms, and jsonl (the full per-agent JSONL file). Useful for answering "which agents succeeded / failed / need auth" without re-reading every JSONL.

What this skill does NOT do

  • No interactive UI. For ad-hoc side-by-side comparison, read the matrix-summary.json output or build a separate visualization tool on top of the JSONL.
  • No permission-request handling beyond canned replies. Agent-initiated requests (fs/read_text_file, session/request_permission, etc.) are answered with -32601 method not found so the session doesn't hang. If you need to exercise a real permission flow, use the full kandev backend.
  • No automatic credential bootstrap. The child inherits the parent shell's env; if an auth check fails the skill reports which methods were advertised and lets the user fix their local credentials.
  • No Docker / remote executor support. Standalone subprocess only β€” same as a manual auggie --acp invocation.

Related skills