clash 0.3.4

Command Line Agent Safety Harness — permission policies for Claude Code
Documentation

clash

The core CLI binary and library. See the project README for usage and documentation.

Status Line internals

The clash statusline command powers the Claude Code status bar integration. It uses a stats sidecar pattern to keep rendering fast regardless of session length.

Architecture

PreToolUse hook
  → policy evaluation
  → log_decision() writes audit.jsonl
  → update_session_stats() writes stats.json  (atomic: write tmp + rename)

Claude Code (after each assistant message)
  → clash statusline render
  → reads stats.json from /tmp/clash-<session_id>/
  → prints ANSI-colored line to stdout (<10ms)

Stats sidecar (stats.json)

Instead of parsing the growing audit.jsonl on every render, the PreToolUse hook maintains a small JSON file with pre-aggregated counters:

{
  "allowed": 12,
  "denied": 3,
  "asked": 1,
  "last_tool": "Bash",
  "last_input_summary": "git status",
  "last_effect": "allow",
  "last_at": "1706123456.789",
  "default_effect": "deny",
  "last_deny_hint": null
}

The file is written atomically (write to .stats.json.tmp, then fs::rename) to prevent partial reads by the concurrent render process.

Double-count prevention

Stats are updated in the PreToolUse handler only (cmd/hooks.rs), not inside log_decision(). This is intentional: "ask" decisions trigger both PreToolUse and PermissionRequest hooks, and both re-evaluate the policy via check_permission()log_decision(). Counting in log_decision would double-count asks.

Deny hints

When a tool is denied, deny_hint() generates the narrowest possible allow rule based on the tool type and input:

  • Bash(exec "<binary>" *) using the first word of the command
  • Read/Write(fs read/write (subpath "<parent>")) from the file path
  • WebFetch(net "<domain>") extracted from the URL
  • Fallback → bare verb shortcuts (clash allow bash, etc.)

Color forcing

The render function calls console::set_colors_enabled(true) because Claude Code pipes the status line command's stdout (not a TTY), but the TUI does support ANSI escape codes.