YeonGyu-Kim 84466bbb6c fix: #247 classify prompt-related parse errors + unify JSON hint plumbing
Cycle #34 dogfood follow-through on Jobdori cycle #33 pinpoint (#247 filed
at fbcbe9d). Closes the two typed-error contract drifts surfaced in that
pinpoint against the Rust `claw` binary.

## What was wrong

1. `classify_error_kind()` (main.rs:~251) used substring matching but did
   NOT match two common prompt-related parse errors:
     - "prompt subcommand requires a prompt string"
     - "empty prompt: provide a subcommand..."
   Both fell through to `"unknown"`. §4.44 typed-error contract specifies
   `parse | usage | unknown` as distinct classes, so claws dispatching on
   `error.kind == "cli_parse"` missed those paths entirely.

2. JSON mode dropped the `Run `claw --help` for usage.` hint. Text mode
   appends it at stderr-print time (main.rs:~234) AFTER split_error_hint()
   has already serialized the envelope, so JSON consumers never saw it.
   Text-mode humans got an actionable pointer; machine consumers did not.

## Fix

Two small, targeted edits:

1. `classify_error_kind()`: add explicit branches for "prompt subcommand
   requires" and "empty prompt:" (the latter anchored with `starts_with`
   so it never hijacks unrelated error messages containing the word).
   Both route to `cli_parse`.

2. JSON error render path in `main()`: after calling split_error_hint(),
   if the message carried no embedded hint AND kind is `cli_parse` AND
   the short-reason does not already embed a `claw --help` pointer,
   synthesize the same `Run `claw --help` for usage.` trailer that
   text-mode stderr appends. The embedded-pointer check prevents
   duplication on the `empty prompt: ... (run `claw --help`)` message
   which already carries inline guidance.

## Verification

Direct repro on the compiled binary:

    $ claw --output-format json prompt
    {"error":"prompt subcommand requires a prompt string",
     "hint":"Run `claw --help` for usage.",
     "kind":"cli_parse","type":"error"}

    $ claw --output-format json ""
    {"error":"empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string",
     "hint":null,"kind":"cli_parse","type":"error"}

    $ claw --output-format json doctor --foo   # regression guard
    {"error":"unrecognized argument `--foo` for subcommand `doctor`",
     "hint":"Run `claw --help` for usage.",
     "kind":"cli_parse","type":"error"}

Text mode unchanged in shape; `[error-kind: ...]` prefix now reads
`cli_parse` for the two previously-misclassified paths.

## Regression coverage

- Unit test `classify_error_kind_covers_prompt_parse_errors_247`: locks
  both patterns route to `cli_parse` AND that generic "prompt"-containing
  messages still fall through to `unknown`.
- Integration tests in `tests/output_format_contract.rs`:
  * prompt_subcommand_without_arg_emits_cli_parse_envelope_with_hint_247
  * empty_positional_arg_emits_cli_parse_envelope_247
  * whitespace_only_positional_arg_emits_cli_parse_envelope_247
  * unrecognized_argument_still_classifies_as_cli_parse_247_regression_guard
- Full rusty-claude-cli test suite: 218 tests pass (180 bin unit + 15
  output_format_contract + 12 resume_slash + 7 compact + 3 mock + 1 cli).

## Family / related

Joins §4.44 typed-envelope contract gap family closure: #130, #179, #181,
and now **#247**. All four quartet items now have real fixes landed on
the canonical binary surface rather than only the Python harness.

ROADMAP.md: #247 marked CLOSED with before/after evidence preserved.
2026-04-22 22:43:14 +09:00
..

🦞 Claw Code — Rust Implementation

A high-performance Rust rewrite of the Claw Code CLI agent harness. Built for speed, safety, and native tool execution.

For a task-oriented guide with copy/paste examples, see ../USAGE.md.

Quick Start

# Inspect available commands
cd rust/
cargo run -p rusty-claude-cli -- --help

# Build the workspace
cargo build --workspace

# Run the interactive REPL
cargo run -p rusty-claude-cli -- --model claude-opus-4-6

# One-shot prompt
cargo run -p rusty-claude-cli -- prompt "explain this codebase"

# JSON output for automation
cargo run -p rusty-claude-cli -- --output-format json prompt "summarize src/main.rs"

Configuration

Set your API credentials:

export ANTHROPIC_API_KEY="sk-ant-..."
# Or use a proxy
export ANTHROPIC_BASE_URL="https://your-proxy.com"

Or provide an OAuth bearer token directly:

export ANTHROPIC_AUTH_TOKEN="anthropic-oauth-or-proxy-bearer-token"

Mock parity harness

The workspace now includes a deterministic Anthropic-compatible mock service and a clean-environment CLI harness for end-to-end parity checks.

cd rust/

# Run the scripted clean-environment harness
./scripts/run_mock_parity_harness.sh

# Or start the mock service manually for ad hoc CLI runs
cargo run -p mock-anthropic-service -- --bind 127.0.0.1:0

Harness coverage:

  • streaming_text
  • read_file_roundtrip
  • grep_chunk_assembly
  • write_file_allowed
  • write_file_denied
  • multi_tool_turn_roundtrip
  • bash_stdout_roundtrip
  • bash_permission_prompt_approved
  • bash_permission_prompt_denied
  • plugin_tool_roundtrip

Primary artifacts:

  • crates/mock-anthropic-service/ — reusable mock Anthropic-compatible service
  • crates/rusty-claude-cli/tests/mock_parity_harness.rs — clean-env CLI harness
  • scripts/run_mock_parity_harness.sh — reproducible wrapper
  • scripts/run_mock_parity_diff.py — scenario checklist + PARITY mapping runner
  • mock_parity_scenarios.json — scenario-to-PARITY manifest

Features

Feature Status
Anthropic / OpenAI-compatible provider flows + streaming
Direct bearer-token auth via ANTHROPIC_AUTH_TOKEN
Interactive REPL (rustyline)
Tool system (bash, read, write, edit, grep, glob)
Web tools (search, fetch)
Sub-agent / agent surfaces
Todo tracking
Notebook editing
CLAUDE.md / project memory
Config file hierarchy (.claw.json + merged config sections)
Permission system
MCP server lifecycle + inspection
Session persistence + resume
Cost / usage / stats surfaces
Git integration
Markdown terminal rendering (ANSI)
Model aliases (opus/sonnet/haiku)
Direct CLI subcommands (status, sandbox, agents, mcp, skills, doctor)
Slash commands (including /skills, /agents, /mcp, /doctor, /plugin, /subagent)
Hooks (/hooks, config-backed lifecycle hooks)
Plugin management surfaces
Skills inventory / install surfaces
Machine-readable JSON output across core CLI surfaces

Model Aliases

Short names resolve to the latest model versions:

Alias Resolves To
opus claude-opus-4-6
sonnet claude-sonnet-4-6
haiku claude-haiku-4-5-20251213

CLI Flags and Commands

Representative current surface:

claw [OPTIONS] [COMMAND]

Flags:
  --model MODEL
  --output-format text|json
  --permission-mode MODE
  --dangerously-skip-permissions
  --allowedTools TOOLS
  --resume [SESSION.jsonl|session-id|latest]
  --version, -V

Top-level commands:
  prompt <text>
  help
  version
  status
  sandbox
  acp [serve]
  dump-manifests
  bootstrap-plan
  agents
  mcp
  skills
  system-prompt
  init

claw acp is a local discoverability surface for editor-first users: it reports the current ACP/Zed status without starting the runtime. As of April 16, 2026, claw-code does not ship an ACP/Zed daemon entrypoint yet, and claw acp serve is only a status alias until the real protocol surface lands.

The command surface is moving quickly. For the canonical live help text, run:

cargo run -p rusty-claude-cli -- --help

Slash Commands (REPL)

Tab completion expands slash commands, model aliases, permission modes, and recent session IDs.

The REPL now exposes a much broader surface than the original minimal shell:

  • session / visibility: /help, /status, /sandbox, /cost, /resume, /session, /version, /usage, /stats
  • workspace / git: /compact, /clear, /config, /memory, /init, /diff, /commit, /pr, /issue, /export, /hooks, /files, /release-notes
  • discovery / debugging: /mcp, /agents, /skills, /doctor, /tasks, /context, /desktop
  • automation / analysis: /review, /advisor, /insights, /security-review, /subagent, /team, /telemetry, /providers, /cron, and more
  • plugin management: /plugin (with aliases /plugins, /marketplace)

Notable claw-first surfaces now available directly in slash form:

  • /skills [list|install <path>|help]
  • /agents [list|help]
  • /mcp [list|show <server>|help]
  • /doctor
  • /plugin [list|install <path>|enable <name>|disable <name>|uninstall <id>|update <id>]
  • /subagent [list|steer <target> <msg>|kill <id>]

See ../USAGE.md for usage examples and run cargo run -p rusty-claude-cli -- --help for the live canonical command list.

Workspace Layout

rust/
├── Cargo.toml              # Workspace root
├── Cargo.lock
└── crates/
    ├── api/                # Provider clients + streaming + request preflight
    ├── commands/           # Shared slash-command registry + help rendering
    ├── compat-harness/     # TS manifest extraction harness
    ├── mock-anthropic-service/ # Deterministic local Anthropic-compatible mock
    ├── plugins/            # Plugin metadata, manager, install/enable/disable surfaces
    ├── runtime/            # Session, config, permissions, MCP, prompts, auth/runtime loop
    ├── rusty-claude-cli/   # Main CLI binary (`claw`)
    ├── telemetry/          # Session tracing and usage telemetry types
    └── tools/              # Built-in tools, skill resolution, tool search, agent runtime surfaces

Crate Responsibilities

  • api — provider clients, SSE streaming, request/response types, auth (ANTHROPIC_API_KEY + bearer-token support), request-size/context-window preflight
  • commands — slash command definitions, parsing, help text generation, JSON/text command rendering
  • compat-harness — extracts tool/prompt manifests from upstream TS source
  • mock-anthropic-service — deterministic /v1/messages mock for CLI parity tests and local harness runs
  • plugins — plugin metadata, install/enable/disable/update flows, plugin tool definitions, hook integration surfaces
  • runtimeConversationRuntime, config loading, session persistence, permission policy, MCP client lifecycle, system prompt assembly, usage tracking
  • rusty-claude-cli — REPL, one-shot prompt, direct CLI subcommands, streaming display, tool call rendering, CLI argument parsing
  • telemetry — session trace events and supporting telemetry payloads
  • tools — tool specs + execution: Bash, ReadFile, WriteFile, EditFile, GlobSearch, GrepSearch, WebSearch, WebFetch, Agent, TodoWrite, NotebookEdit, Skill, ToolSearch, and runtime-facing tool discovery

Stats

  • ~20K lines of Rust
  • 9 crates in workspace
  • Binary name: claw
  • Default model: claude-opus-4-6
  • Default permissions: danger-full-access

License

See repository root.