Skip to main content

safe_chains/
cli.rs

1use clap::Parser;
2
3const EXAMPLES: &str = "\
4EXAMPLES:
5  # Check whether a command would auto-approve (exit 0 = allowed):
6  safe-chains \"git status\"
7
8  # See a per-segment breakdown of why a command does or doesn't approve:
9  safe-chains --explain \"grep foo . && ./deploy.sh\"
10
11  # Offer to support a command safe-chains doesn't recognize. This writes or
12  # upgrades the local .safe-chains.toml, then prints the pin to hand-add to
13  # ~/.config/safe-chains.toml so the definition takes effect:
14  safe-chains --suggest \"mytool sync --dry-run\"
15";
16
17#[derive(Parser)]
18#[command(name = "safe-chains")]
19#[command(about = "Auto-allow safe bash commands in agentic coding tools")]
20#[command(version, disable_version_flag = true)]
21#[command(after_help = EXAMPLES)]
22#[allow(clippy::struct_excessive_bools)]
23pub struct Cli {
24    /// Command string to check (omit for Claude hook mode via stdin)
25    pub command: Option<String>,
26
27    /// Print version information.
28    #[arg(short = 'v', short_alias = 'V', long, action = clap::ArgAction::Version)]
29    pub version: Option<bool>,
30
31    /// Safety level threshold; only commands at or below it auto-approve. Levels, locked → open:
32    /// paranoid, reader, editor, developer, local-admin, network-admin, yolo. The legacy names
33    /// inert / safe-read / safe-write still work (mapped to paranoid / reader / developer, with a
34    /// notice). Default: developer.
35    #[arg(long)]
36    pub level: Option<String>,
37
38    /// Working directory to resolve relative paths against (as a harness hook would pass).
39    /// Pair with --root so e.g. `cd`-relative writes classify against the real directory.
40    #[arg(long)]
41    pub cwd: Option<String>,
42
43    /// Project root, so a relative path under it is worktree-local and one outside it (the
44    /// cwd having escaped the project) is scored as its real absolute target.
45    #[arg(long)]
46    pub root: Option<String>,
47
48    /// The harness session id (as a hook would pass), used to recognize this session's scratchpad
49    /// under a temp root as a trusted working area rather than anonymous `/tmp`.
50    #[arg(long, value_name = "ID")]
51    pub session_id: Option<String>,
52
53    /// Print a per-segment breakdown of why a command would or would not auto-approve.
54    #[arg(long)]
55    pub explain: bool,
56
57    /// Offer to support a command safe-chains doesn't recognize yet. Pass the command as the
58    /// argument: `safe-chains --suggest "<command>"`. It writes (or upgrades) the project's local
59    /// `.safe-chains.toml` with a definition for the unrecognized command, then prints the
60    /// `[[trusted]]` pin for you to add to ~/.config/safe-chains.toml so that file takes effect.
61    #[arg(long)]
62    pub suggest: bool,
63
64    /// List all supported commands in Markdown format
65    #[arg(long)]
66    pub list_commands: bool,
67
68    /// Generate mdBook command reference pages in docs/src/commands/
69    #[arg(long)]
70    pub generate_book: bool,
71
72    /// Configure the hook for the named tool (default: claude). Use --auto-detect for every installed tool.
73    #[arg(long)]
74    pub setup: bool,
75
76    /// Pair with --setup to select the target tool by name. See --list-tools.
77    #[arg(long, value_name = "NAME")]
78    pub tool: Option<String>,
79
80    /// Pair with --setup to install for every installed tool detected on this machine.
81    #[arg(long)]
82    pub auto_detect: bool,
83
84    /// Print the names of every supported integration target.
85    #[arg(long)]
86    pub list_tools: bool,
87
88    /// Hook subcommand: read this tool's stdin envelope, validate the command, write the response.
89    #[command(subcommand)]
90    pub subcommand: Option<Subcommand>,
91}
92
93#[derive(clap::Subcommand)]
94pub enum Subcommand {
95    /// Run as a runtime hook for the named tool.
96    Hook {
97        /// Tool to read/write the hook envelope for. See --list-tools.
98        #[arg(value_name = "TOOL")]
99        tool: String,
100    },
101}