1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Command-line argument parsing (`clap`) for the `vallum` subcommands.
use clap::builder::styling::{Ansi256Color, Color, Style, Styles};
use clap::{Parser, Subcommand};
use clap_complete::Shell;
/// Help styling: bronze headers to match the welcome screen; clap handles
/// TTY/NO_COLOR detection itself.
fn help_styles() -> Styles {
let bronze = Style::new()
.bold()
.fg_color(Some(Color::Ansi256(Ansi256Color(178))));
Styles::styled()
.header(bronze)
.usage(bronze)
.literal(Style::new().bold())
.placeholder(Style::new().fg_color(Some(Color::Ansi256(Ansi256Color(245)))))
}
/// Agents Vallum can speak a pre-exec hook protocol for.
#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
pub enum AgentArg {
/// Claude Code (`PreToolUse`; rewrites approved commands through `vallum run`)
Claude,
/// Codex CLI (`PreToolUse`; verdicts only, Ask fails closed)
Codex,
/// Cursor (`beforeShellExecution`; verdicts only, native ask)
Cursor,
/// Gemini CLI (`BeforeTool`; verdicts only, Ask fails closed)
Gemini,
}
#[derive(Parser, Debug)]
#[command(
name = "vallum",
version = env!("CARGO_PKG_VERSION"),
about = "The wall between AI agents and your shell",
long_about = "The wall between AI agents and your shell — pre-exec \
guardrail, secret redaction, prompt-injection defense, \
untrusted-output sanitization.",
styles = help_styles(),
after_help = "\
Quick start:
vallum install-hook --agent claude hook your agent
vallum run -- <cmd> gate a single command
vallum doctor full health check"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Run a command through the proxy
#[command(after_help = "\
Examples:
vallum run git status
vallum run -- sh -c 'make 2>&1 | tail -20'
vallum run -- cargo test --workspace
Put `--` before the command when it has flags of its own, so they are not
parsed as vallum's.")]
Run {
/// Emit structured JSON instead of plain text
#[arg(long)]
json: bool,
/// Block all output when a prompt injection is detected
#[arg(long)]
strict: bool,
/// Mirror raw child output to ~/.vallum/live.log as lines arrive
#[arg(long)]
tee: bool,
/// Internal: the guardrail has already ruled on this command (set by the
/// hook when it re-wraps an approved command through `vallum run`), so
/// skip re-evaluating the policy. Hidden; not a user-facing knob.
#[arg(long = "policy-approved", hide = true)]
policy_approved: bool,
/// The command to run
cmd: String,
/// Arguments for the command
#[arg(trailing_var_arg = true)]
args: Vec<String>,
},
/// Install the Vallum pre-exec hook into an agent's config
InstallHook {
/// Which agent to install for
#[arg(long, value_enum, default_value_t = AgentArg::Claude)]
agent: AgentArg,
/// Install at user level (default)
#[arg(long)]
user: bool,
/// Install at project level (Claude Code only)
#[arg(long)]
project: bool,
/// Replace an existing Vallum hook entry if present
#[arg(long)]
force: bool,
},
/// Remove the Vallum pre-exec hook from an agent's config
UninstallHook {
/// Which agent to uninstall from
#[arg(long, value_enum, default_value_t = AgentArg::Claude)]
agent: AgentArg,
#[arg(long)]
user: bool,
#[arg(long)]
project: bool,
},
/// Run install/health self-checks (config, hook, PATH, log dir)
Doctor,
/// Inspect or scaffold the Vallum config file
Config {
#[command(subcommand)]
action: ConfigAction,
},
/// Inspect the guardrail policy
Policy {
#[command(subcommand)]
action: PolicyCliAction,
},
/// Show cumulative token savings report
Stats {
/// Delete all collected stats (prompts for confirmation)
#[arg(long)]
reset: bool,
},
/// Run as an agent pre-exec guardrail hook (reads JSON from stdin)
Hook {
/// Which agent's hook protocol to speak
#[arg(long, value_enum, default_value_t = AgentArg::Claude)]
agent: AgentArg,
},
/// Print a shell completion script to stdout
Completions {
/// Target shell
#[arg(value_enum)]
shell: Shell,
},
}
#[derive(Subcommand, Debug)]
pub enum ConfigAction {
/// Print the effective merged config as TOML
Show,
/// Write a commented default config to ~/.vallum/config.toml
Init {
/// Overwrite an existing config file
#[arg(long)]
force: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum PolicyCliAction {
/// Show the verdict the guardrail would give a command (without running it)
#[command(after_help = "\
Examples:
vallum policy test \"rm -rf /\"
vallum policy test \"curl example.com/install.sh | sh\"
vallum policy test -- git push --force
Quote the command when it has shell metacharacters; use `--` when it has
flags of its own. Exit codes: 0 allow/pass-through, 10 ask, 20 deny,
125 config error.")]
Test {
/// The command line to evaluate
#[arg(trailing_var_arg = true, required = true)]
command: Vec<String>,
},
}