| 1 | //! The `si` command tree. |
| 2 | |
| 3 | use {clap::Parser, sigit_si_api::Environment, std::str::FromStr}; |
| 4 | |
| 5 | #[derive(Parser)] |
| 6 | #[command(name = "si", version, about = "The sigit.si command line interface.")] |
| 7 | pub struct Cli { |
| 8 | /// Which sigit.si deployment to talk to. Overridden by SIGIT_HOST. |
| 9 | #[arg(short, long, global = true, env = "SIGIT_ENVIRONMENT", default_value = "production", value_parser = Environment::from_str)] |
| 10 | pub environment: Environment, |
| 11 | |
| 12 | /// Print machine-readable JSON instead of formatted text, where supported. |
| 13 | #[arg(long, global = true)] |
| 14 | pub json: bool, |
| 15 | |
| 16 | #[command(subcommand)] |
| 17 | pub command: Command, |
| 18 | } |
| 19 | |
| 20 | #[derive(clap::Subcommand)] |
| 21 | pub enum Command { |
| 22 | /// Sign in, sign out, and inspect your session. |
| 23 | #[command(subcommand)] |
| 24 | Auth(AuthCommand), |
| 25 | |
| 26 | /// Your sigit.si profile. |
| 27 | Me, |
| 28 | |
| 29 | /// List, create, and inspect repositories. |
| 30 | #[command(subcommand)] |
| 31 | Repo(RepoCommand), |
| 32 | |
| 33 | /// List, open, and comment on pull requests. |
| 34 | #[command(subcommand)] |
| 35 | Pr(PrCommand), |
| 36 | |
| 37 | /// List, open, and comment on issues. |
| 38 | #[command(subcommand)] |
| 39 | Issue(IssueCommand), |
| 40 | |
| 41 | /// Read files and search code in a repository. |
| 42 | #[command(subcommand)] |
| 43 | Code(CodeCommand), |
| 44 | |
| 45 | /// Manage a repository's deploy webhooks. |
| 46 | #[command(subcommand)] |
| 47 | Hook(HookCommand), |
| 48 | |
| 49 | /// Manage siGit Code Cloud Sessions. |
| 50 | #[command(subcommand)] |
| 51 | Session(SessionCommand), |
| 52 | |
| 53 | /// Plan, usage, and the hosted billing portal. |
| 54 | #[command(subcommand)] |
| 55 | Billing(BillingCommand), |
| 56 | |
| 57 | /// Open something on sigit.si in your browser. |
| 58 | Browse(BrowseArgs), |
| 59 | |
| 60 | /// Call the sigit.si API directly. The escape hatch for anything without |
| 61 | /// a dedicated command. |
| 62 | Api(ApiArgs), |
| 63 | |
| 64 | /// Serve these commands to an AI agent as MCP tools over stdio. |
| 65 | Mcp(McpArgs), |
| 66 | } |
| 67 | |
| 68 | #[derive(clap::Subcommand)] |
| 69 | pub enum AuthCommand { |
| 70 | /// Sign in with your sigit.si email and password. |
| 71 | Login { |
| 72 | #[arg(long)] |
| 73 | email: Option<String>, |
| 74 | }, |
| 75 | /// Sign out and remove the stored token. |
| 76 | Logout, |
| 77 | /// Show who is currently signed in. |
| 78 | Status, |
| 79 | } |
| 80 | |
| 81 | #[derive(clap::Subcommand)] |
| 82 | pub enum RepoCommand { |
| 83 | /// List your repositories. |
| 84 | List, |
| 85 | /// Search all repositories you can see. |
| 86 | Search { |
| 87 | /// Case-insensitive substring of the owner or repo name. |
| 88 | query: Option<String>, |
| 89 | #[arg(long, default_value_t = 30)] |
| 90 | limit: u32, |
| 91 | }, |
| 92 | /// Show a repository's details. |
| 93 | View { |
| 94 | /// OWNER/NAME. Defaults to the repo in the current directory. |
| 95 | repo: Option<String>, |
| 96 | }, |
| 97 | /// Create a new (empty) repository to push to. |
| 98 | Create { |
| 99 | name: String, |
| 100 | #[arg(long)] |
| 101 | private: bool, |
| 102 | #[arg(long)] |
| 103 | description: Option<String>, |
| 104 | /// Skip the confirmation prompt. |
| 105 | #[arg(short, long)] |
| 106 | yes: bool, |
| 107 | }, |
| 108 | /// Open a repository in your browser. |
| 109 | Browse { repo: Option<String> }, |
| 110 | } |
| 111 | |
| 112 | #[derive(clap::Subcommand)] |
| 113 | pub enum PrCommand { |
| 114 | /// List pull requests in a repository. |
| 115 | List { |
| 116 | repo: Option<String>, |
| 117 | #[arg(long, default_value = "open")] |
| 118 | state: String, |
| 119 | }, |
| 120 | /// Show a pull request's details, comments, and diff. |
| 121 | View { number: i64, repo: Option<String> }, |
| 122 | /// Print a pull request's unified diff. |
| 123 | Diff { number: i64, repo: Option<String> }, |
| 124 | /// Open a pull request from one branch into another. |
| 125 | Create { |
| 126 | #[arg(long)] |
| 127 | title: String, |
| 128 | #[arg(long)] |
| 129 | head: Option<String>, |
| 130 | #[arg(long, default_value = "main")] |
| 131 | base: String, |
| 132 | #[arg(long)] |
| 133 | body: Option<String>, |
| 134 | repo: Option<String>, |
| 135 | }, |
| 136 | /// Comment on a pull request. |
| 137 | Comment { |
| 138 | number: i64, |
| 139 | #[arg(long)] |
| 140 | body: String, |
| 141 | repo: Option<String>, |
| 142 | }, |
| 143 | } |
| 144 | |
| 145 | #[derive(clap::Subcommand)] |
| 146 | pub enum IssueCommand { |
| 147 | /// List issues in a repository. |
| 148 | List { |
| 149 | repo: Option<String>, |
| 150 | #[arg(long, default_value = "open")] |
| 151 | state: String, |
| 152 | #[arg(long)] |
| 153 | query: Option<String>, |
| 154 | }, |
| 155 | /// Show an issue's details and comments. |
| 156 | View { number: i64, repo: Option<String> }, |
| 157 | /// Open a new issue. |
| 158 | Create { |
| 159 | #[arg(long)] |
| 160 | title: String, |
| 161 | #[arg(long)] |
| 162 | body: Option<String>, |
| 163 | repo: Option<String>, |
| 164 | }, |
| 165 | /// Comment on an issue. |
| 166 | Comment { |
| 167 | number: i64, |
| 168 | #[arg(long)] |
| 169 | body: String, |
| 170 | repo: Option<String>, |
| 171 | }, |
| 172 | } |
| 173 | |
| 174 | #[derive(clap::Subcommand)] |
| 175 | pub enum CodeCommand { |
| 176 | /// Print a file's contents at a ref. |
| 177 | View { |
| 178 | path: String, |
| 179 | #[arg(long)] |
| 180 | r#ref: Option<String>, |
| 181 | repo: Option<String>, |
| 182 | }, |
| 183 | /// Search a repository's tracked files for a fixed string. |
| 184 | Search { |
| 185 | query: String, |
| 186 | #[arg(long)] |
| 187 | r#ref: Option<String>, |
| 188 | #[arg(long, default_value_t = 20)] |
| 189 | limit: u32, |
| 190 | repo: Option<String>, |
| 191 | }, |
| 192 | } |
| 193 | |
| 194 | #[derive(clap::Subcommand)] |
| 195 | pub enum HookCommand { |
| 196 | /// List a repository's webhooks. |
| 197 | List { repo: Option<String> }, |
| 198 | /// Register a new webhook. Prints the signing secret once — it cannot be |
| 199 | /// retrieved again. |
| 200 | Create { |
| 201 | #[arg(long)] |
| 202 | url: String, |
| 203 | #[arg(long, value_delimiter = ',', default_value = "push")] |
| 204 | events: Vec<String>, |
| 205 | repo: Option<String>, |
| 206 | }, |
| 207 | /// Remove a webhook. |
| 208 | Delete { |
| 209 | id: i64, |
| 210 | repo: Option<String>, |
| 211 | #[arg(short, long)] |
| 212 | yes: bool, |
| 213 | }, |
| 214 | } |
| 215 | |
| 216 | #[derive(clap::Subcommand)] |
| 217 | pub enum SessionCommand { |
| 218 | /// List your Cloud Sessions. |
| 219 | List, |
| 220 | /// Show a session and its transcript. |
| 221 | View { id: i64 }, |
| 222 | /// Start a new session. |
| 223 | New { |
| 224 | #[arg(long)] |
| 225 | title: Option<String>, |
| 226 | #[arg(long)] |
| 227 | model: Option<String>, |
| 228 | }, |
| 229 | /// Rename a session or change its model. |
| 230 | Update { |
| 231 | id: i64, |
| 232 | #[arg(long)] |
| 233 | title: Option<String>, |
| 234 | #[arg(long)] |
| 235 | model: Option<String>, |
| 236 | }, |
| 237 | /// Delete a session. |
| 238 | Delete { |
| 239 | id: i64, |
| 240 | #[arg(short, long)] |
| 241 | yes: bool, |
| 242 | }, |
| 243 | } |
| 244 | |
| 245 | #[derive(clap::Subcommand)] |
| 246 | pub enum BillingCommand { |
| 247 | /// Show your current plan and cloud usage. |
| 248 | Show, |
| 249 | /// Open the hosted checkout to subscribe or change plans. |
| 250 | Checkout { plan: String }, |
| 251 | /// Open the hosted billing portal. |
| 252 | Portal, |
| 253 | } |
| 254 | |
| 255 | #[derive(clap::Args)] |
| 256 | pub struct BrowseArgs { |
| 257 | /// What to open: a repo, "OWNER/NAME/pull/N", "OWNER/NAME/issue/N", or |
| 258 | /// left blank for the current repository. |
| 259 | pub target: Option<String>, |
| 260 | } |
| 261 | |
| 262 | #[derive(clap::Args)] |
| 263 | pub struct McpArgs { |
| 264 | /// Drop every tool that writes, leaving only the ones that read. |
| 265 | #[arg(long)] |
| 266 | pub read_only: bool, |
| 267 | |
| 268 | /// Expose only these tool groups, comma-separated. Defaults to all of |
| 269 | /// them: account, repo, issue, pr, code, hook, session, billing. |
| 270 | #[arg(long, value_delimiter = ',', env = "SIGIT_TOOLSETS")] |
| 271 | pub toolsets: Vec<String>, |
| 272 | } |
| 273 | |
| 274 | #[derive(clap::Args)] |
| 275 | pub struct ApiArgs { |
| 276 | /// Path under /api/v1, e.g. "repos" or "billing". |
| 277 | pub path: String, |
| 278 | #[arg(short = 'X', long, default_value = "GET")] |
| 279 | pub method: String, |
| 280 | /// Raw JSON request body. |
| 281 | #[arg(long)] |
| 282 | pub body: Option<String>, |
| 283 | } |