clash 0.4.1

Command Line Agent Safety Harness — permission policies for coding agents
Documentation
# clash

The core CLI binary and library. See the [project README](../README.md) 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:

```json
{
  "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.