clash
Stop babysitting your agents, go touch grass.
Command Line Agent Safety Harness
curl -fsSL https://raw.githubusercontent.com/empathic/clash/main/install.sh | bash
clash init
claude
The problem
Coding agents operate with broad tool access — executing commands, editing files, and making network requests on your behalf. Their permission models tend to be all-or-nothing: either you allow a tool entirely or get prompted every time.
You end up clicking "yes" hundreds of times a session, or giving blanket approval and hoping for the best.
Clash gives you granular control. Write policy rules that decide what to allow, deny, or ask about — then let the agent work freely on safe operations while blocking dangerous ones. On Linux, rules can generate kernel-enforced filesystem sandboxes so even allowed commands can only touch the files you specify.
Three concepts
Rule
An effect paired with a capability matcher. exe("git").allow() lets the agent run any git command. exe("git", args=["push"]).deny() blocks pushes. Rules use first-match semantics — put specific rules before broad ones.
Domain
Rules target one of three capability domains: exec (shell commands), fs (file operations), and net (network access). Clash speaks in capabilities, not tool names — a single rule can cover multiple agent tools.
Layer
Policies stack in three levels: User (personal defaults), Project (repo-specific rules), and Session (temporary overrides). Higher layers shadow lower layers. Session > Project > User.
Without Clash vs. with Clash
| Without Clash | With Clash |
|---|---|
| Click "yes" hundreds of times per session | Auto-approve safe operations, block dangerous ones |
| All-or-nothing tool permissions | Granular rules by command, file path, and domain |
| No visibility into what the agent is doing | Status line shows live allow/deny/ask counts |
| Trust the agent with everything or nothing | Kernel-enforced sandboxes constrain even allowed commands |
| Same permissions for every project | Per-project and per-session policy layers |
A minimal policy
# ~/.clash/policy.star (user level)
load("@clash//builtin.star", "base")
load("@clash//std.star", "exe", "policy", "cwd", "domains")
def main():
my_rules = policy(default = ask, rules = [
cwd(follow_worktrees = True).allow(read = True, write = True),
exe("cargo").allow(),
exe("git", args = ["push"]).deny(),
exe("git", args = ["reset", "--hard"]).deny(),
exe("git").allow(),
domains({"github.com": allow}),
])
return my_rules.merge(base)
Compiled JSON IR
{
"schema_version": 5,
"default_effect": "ask",
"sandboxes": {},
"tree": [
{ "condition": { "observe": "tool_name", "pattern": { "literal": { "literal": "Bash" } },
"children": [
{ "condition": { "observe": { "positional_arg": 0 }, "pattern": { "literal": { "literal": "cargo" } },
"children": [{ "decision": { "allow": null } }] } },
{ "condition": { "observe": { "positional_arg": 0 }, "pattern": { "literal": { "literal": "git" } },
"children": [
{ "condition": { "observe": { "positional_arg": 1 }, "pattern": { "literal": { "literal": "push" } },
"children": [{ "decision": "deny" }] } },
{ "condition": { "observe": { "positional_arg": 1 }, "pattern": { "literal": { "literal": "reset" } },
"children": [
{ "condition": { "observe": { "positional_arg": 2 }, "pattern": { "literal": { "literal": "--hard" } },
"children": [{ "decision": "deny" }] } }
] } },
{ "decision": { "allow": null } }
] } }
] } }
]
}
Three effects: allow auto-approves, deny blocks, ask prompts you. First matching rule wins — put specific rules before broad ones. Edits take effect immediately — no restart needed.
Quick start
-
Install clash
Downloads the latest release binary for your platform (Apple Silicon Mac, Linux x86_64, Linux aarch64). On Intel Mac:curl -fsSL https://raw.githubusercontent.com/empathic/clash/main/install.sh | bashcargo install clash. -
Initialize
Writes a default policy, installs the Claude Code plugin, configures the status line, and walks you through initial setup.clash init -
Launch your agent
Every Claude Code session now loads clash automatically. Useclaude/clash:statusinside a session to see your policy in action.
Agent support
Clash is designed to be agent-agnostic — a universal safety harness for any coding agent that executes tools on your behalf. The policy language and capability model are agent-independent; only the integration layer is specific to each agent.
| Agent | Status |
|---|---|
| Claude Code | Supported |
| Codex CLI | Planned |
| Gemini CLI | Planned |
| OpenCode | Planned |
Currently, the Claude Code integration is the most mature. If you'd like to help bring Clash to another agent, contributions are welcome.
How it works
Clash integrates via the agent's plugin system. For Claude Code, a plugin registers hooks that intercept every tool call. Each invocation is evaluated against your policy and returns an allow, deny, or ask decision — before the agent executes anything.
The policy is read on every tool call, so edits take effect immediately. On Linux, allowed exec rules can generate Landlock sandboxes; on macOS, Seatbelt profiles constrain filesystem and network access at the kernel level.