1use clap::{Parser, Subcommand, ValueEnum};
2
3#[derive(ValueEnum, Clone, Debug)]
4pub enum OutputFormat {
5 Json,
7 Yaml,
9 Table,
11}
12
13#[derive(Parser, Debug)]
14#[allow(clippy::struct_excessive_bools)]
15#[command(
16 author,
17 version,
18 about = "Aperture: Dynamic CLI generator for OpenAPI specifications",
19 long_about = "Aperture dynamically generates commands from OpenAPI 3.x specifications.\n\
20 It serves as a bridge between autonomous AI agents and APIs by consuming\n\
21 OpenAPI specs and creating a rich command-line interface with built-in\n\
22 security, caching, and agent-friendly features.\n\n\
23 Examples:\n \
24 aperture config add myapi api-spec.yaml\n \
25 aperture api myapi users get-user --id 123\n \
26 aperture config list\n\n\
27 Agent-friendly features:\n \
28 aperture api myapi --describe-json # Get capability manifest\n \
29 aperture --json-errors api myapi ... # Structured error output\n \
30 aperture api myapi --dry-run ... # Show request without executing"
31)]
32pub struct Cli {
33 #[arg(
35 long,
36 global = true,
37 help = "Output capability manifest as JSON (can be filtered with --jq)"
38 )]
39 pub describe_json: bool,
40
41 #[arg(long, global = true, help = "Output errors in JSON format")]
44 pub json_errors: bool,
45
46 #[arg(
49 long,
50 short = 'q',
51 global = true,
52 help = "Suppress informational output"
53 )]
54 pub quiet: bool,
55
56 #[arg(long, global = true, help = "Show request details without executing")]
58 pub dry_run: bool,
59
60 #[arg(
62 long,
63 global = true,
64 value_name = "KEY",
65 help = "Set idempotency key header"
66 )]
67 pub idempotency_key: Option<String>,
68
69 #[arg(
71 long,
72 global = true,
73 value_enum,
74 default_value = "json",
75 help = "Output format for response data"
76 )]
77 pub format: OutputFormat,
78
79 #[arg(
81 long,
82 global = true,
83 value_name = "FILTER",
84 help = "Apply JQ filter to JSON output (e.g., '.name', '.[] | select(.active)', '.batch_execution_summary.operations[] | select(.success == false)')"
85 )]
86 pub jq: Option<String>,
87
88 #[arg(
90 long,
91 global = true,
92 value_name = "PATH",
93 help = "Path to batch file (JSON or YAML) containing multiple operations"
94 )]
95 pub batch_file: Option<String>,
96
97 #[arg(
99 long,
100 global = true,
101 value_name = "N",
102 default_value = "5",
103 help = "Maximum number of concurrent requests for batch operations"
104 )]
105 pub batch_concurrency: usize,
106
107 #[arg(
109 long,
110 global = true,
111 value_name = "N",
112 help = "Rate limit for batch operations (requests per second)"
113 )]
114 pub batch_rate_limit: Option<u32>,
115
116 #[arg(
118 long,
119 global = true,
120 help = "Enable response caching (can speed up repeated requests)"
121 )]
122 pub cache: bool,
123
124 #[arg(
126 long,
127 global = true,
128 conflicts_with = "cache",
129 help = "Disable response caching"
130 )]
131 pub no_cache: bool,
132
133 #[arg(
135 long,
136 global = true,
137 value_name = "SECONDS",
138 help = "Cache TTL in seconds (default: 300)"
139 )]
140 pub cache_ttl: Option<u64>,
141
142 #[arg(
144 long,
145 global = true,
146 help = "Use positional arguments for path parameters (legacy syntax)"
147 )]
148 pub positional_args: bool,
149
150 #[arg(
152 long,
153 global = true,
154 value_name = "N",
155 help = "Maximum retry attempts (0 = disabled, overrides config)"
156 )]
157 pub retry: Option<u32>,
158
159 #[arg(
161 long,
162 global = true,
163 value_name = "DURATION",
164 help = "Initial retry delay (e.g., '500ms', '1s', '2s')"
165 )]
166 pub retry_delay: Option<String>,
167
168 #[arg(
170 long,
171 global = true,
172 value_name = "DURATION",
173 help = "Maximum retry delay cap (e.g., '30s', '1m')"
174 )]
175 pub retry_max_delay: Option<String>,
176
177 #[arg(
179 long,
180 global = true,
181 help = "Allow retrying non-idempotent requests without idempotency key"
182 )]
183 pub force_retry: bool,
184
185 #[command(subcommand)]
186 pub command: Commands,
187}
188
189#[derive(Subcommand, Debug)]
190pub enum Commands {
191 #[command(long_about = "Manage your collection of OpenAPI specifications.\n\n\
193 Add specifications to make their operations available as commands,\n\
194 list currently registered specs, remove unused ones, or edit\n\
195 existing specifications in your default editor.")]
196 Config {
197 #[command(subcommand)]
198 command: ConfigCommands,
199 },
200 #[command(
202 long_about = "Display a tree-like summary of all available commands for an API.\n\n\
203 Shows operations organized by tags, making it easy to discover\n\
204 what functionality is available in a registered API specification.\n\
205 This provides an overview without having to use --help on each operation.\n\n\
206 Example:\n \
207 aperture list-commands myapi"
208 )]
209 ListCommands {
210 context: String,
212 },
213 #[command(
215 long_about = "Execute operations from a registered API specification.\n\n\
216 The context refers to the name you gave when adding the spec.\n\
217 Commands are dynamically generated based on the OpenAPI specification,\n\
218 organized by tags (e.g., 'users', 'posts', 'orders').\n\n\
219 Examples:\n \
220 aperture api myapi users get-user --id 123\n \
221 aperture api myapi posts create-post --body '{\"title\":\"Hello\"}'\n \
222 aperture api myapi --help # See available operations"
223 )]
224 Api {
225 context: String,
227 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
229 args: Vec<String>,
230 },
231 #[command(long_about = "Search for API operations by keyword or pattern.\n\n\
233 Search through all registered API specifications to find\n\
234 relevant operations. The search includes operation IDs,\n\
235 descriptions, paths, and HTTP methods.\n\n\
236 Examples:\n \
237 aperture search 'list users' # Find user listing operations\n \
238 aperture search 'POST create' # Find POST operations with 'create'\n \
239 aperture search issues --api sm # Search only in 'sm' API\n \
240 aperture search 'get.*by.*id' # Regex pattern search")]
241 Search {
242 query: String,
244 #[arg(long, value_name = "API", help = "Search only in specified API")]
246 api: Option<String>,
247 #[arg(long, help = "Show detailed information for each result")]
249 verbose: bool,
250 },
251 #[command(
253 name = "exec",
254 long_about = "Execute API operations using shortcuts instead of full paths.\n\n\
255 This command attempts to resolve shortcuts to their full command paths:\n\
256 - Direct operation IDs: getUserById --id 123\n\
257 - HTTP method + path: GET /users/123\n\
258 - Tag-based shortcuts: users list\n\n\
259 When multiple matches are found, you'll get suggestions to choose from.\n\n\
260 Examples:\n \
261 aperture exec getUserById --id 123\n \
262 aperture exec GET /users/123\n \
263 aperture exec users list"
264 )]
265 Exec {
266 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
268 args: Vec<String>,
269 },
270 #[command(
272 long_about = "Get comprehensive documentation for APIs and operations.\n\n\
273 This provides detailed information including parameters, examples,\n\
274 response schemas, and authentication requirements. Use it to learn\n\
275 about available functionality without trial and error.\n\n\
276 Examples:\n \
277 aperture docs # Interactive help menu\n \
278 aperture docs myapi # API overview\n \
279 aperture docs myapi users get-user # Detailed command help"
280 )]
281 Docs {
282 api: Option<String>,
284 tag: Option<String>,
286 operation: Option<String>,
288 #[arg(long, help = "Enhanced formatting with examples and tips")]
290 enhanced: bool,
291 },
292 #[command(
294 long_about = "Display comprehensive API overview with statistics and examples.\n\n\
295 Shows operation counts, method distribution, available categories,\n\
296 and sample commands to help you get started quickly with any API.\n\n\
297 Examples:\n \
298 aperture overview myapi\n \
299 aperture overview --all # Overview of all registered APIs"
300 )]
301 Overview {
302 api: Option<String>,
304 #[arg(long, conflicts_with = "api", help = "Show overview for all APIs")]
306 all: bool,
307 },
308}
309
310#[derive(Subcommand, Debug)]
311pub enum ConfigCommands {
312 #[command(
314 long_about = "Add an OpenAPI 3.x specification to your configuration.\n\n\
315 This validates the specification, extracts operations, and creates\n\
316 a cached representation for fast command generation. The spec name\n\
317 becomes the context for executing API operations.\n\n\
318 Supported formats: YAML (.yaml, .yml)\n\
319 Supported auth: API Key, Bearer Token\n\n\
320 Examples:\n \
321 aperture config add myapi ./openapi.yaml\n \
322 aperture config add myapi https://api.example.com/openapi.yaml"
323 )]
324 Add {
325 name: String,
327 file_or_url: String,
329 #[arg(long, help = "Replace the specification if it already exists")]
331 force: bool,
332 #[arg(
334 long,
335 help = "Reject entire spec if any endpoints have unsupported content types (e.g., multipart/form-data, XML). Default behavior skips unsupported endpoints with warnings."
336 )]
337 strict: bool,
338 },
339 #[command(
341 long_about = "Display all currently registered API specifications.\n\n\
342 Shows the names you can use as contexts with 'aperture api'.\n\
343 Use this to see what APIs are available for command generation."
344 )]
345 List {
346 #[arg(long, help = "Show detailed information about each API")]
348 verbose: bool,
349 },
350 #[command(
352 long_about = "Remove a registered API specification and its cached data.\n\n\
353 This removes both the original specification file and the\n\
354 generated cache, making the API operations unavailable.\n\
355 Use 'aperture config list' to see available specifications."
356 )]
357 Remove {
358 name: String,
360 },
361 #[command(
363 long_about = "Open an API specification in your default text editor.\n\n\
364 Uses the $EDITOR environment variable to determine which editor\n\
365 to use. After editing, you may need to re-add the specification\n\
366 to update the cached representation.\n\n\
367 Example:\n \
368 export EDITOR=vim\n \
369 aperture config edit myapi"
370 )]
371 Edit {
372 name: String,
374 },
375 #[command(long_about = "Set the base URL for an API specification.\n\n\
377 This overrides the base URL from the OpenAPI spec and the\n\
378 APERTURE_BASE_URL environment variable. You can set a general\n\
379 override or environment-specific URLs.\n\n\
380 Examples:\n \
381 aperture config set-url myapi https://api.example.com\n \
382 aperture config set-url myapi --env staging https://staging.example.com\n \
383 aperture config set-url myapi --env prod https://prod.example.com")]
384 SetUrl {
385 name: String,
387 url: String,
389 #[arg(long, value_name = "ENV", help = "Set URL for specific environment")]
391 env: Option<String>,
392 },
393 #[command(
395 long_about = "Display the base URL configuration for an API specification.\n\n\
396 Shows the configured base URL override and any environment-specific\n\
397 URLs. Also displays what URL would be used based on current\n\
398 environment settings.\n\n\
399 Example:\n \
400 aperture config get-url myapi"
401 )]
402 GetUrl {
403 name: String,
405 },
406 #[command(
408 long_about = "Display all configured base URLs across all API specifications.\n\n\
409 Shows general overrides and environment-specific configurations\n\
410 for each registered API. Useful for reviewing your URL settings\n\
411 at a glance."
412 )]
413 ListUrls {},
414 #[command(
416 long_about = "Configure authentication secrets for API specifications.\n\n\
417 This allows you to set environment variable mappings for security\n\
418 schemes without modifying the OpenAPI specification file. These\n\
419 settings take precedence over x-aperture-secret extensions.\n\n\
420 Examples:\n \
421 aperture config set-secret myapi bearerAuth --env API_TOKEN\n \
422 aperture config set-secret myapi apiKey --env API_KEY\n \
423 aperture config set-secret myapi --interactive"
424 )]
425 SetSecret {
426 api_name: String,
428 scheme_name: Option<String>,
430 #[arg(long, value_name = "VAR", help = "Environment variable name")]
432 env: Option<String>,
433 #[arg(long, conflicts_with_all = ["scheme_name", "env"], help = "Configure secrets interactively")]
435 interactive: bool,
436 },
437 #[command(
439 long_about = "Display configured secret mappings for an API specification.\n\n\
440 Shows which security schemes are configured with environment\n\
441 variables and which ones still rely on x-aperture-secret\n\
442 extensions or are undefined.\n\n\
443 Example:\n \
444 aperture config list-secrets myapi"
445 )]
446 ListSecrets {
447 api_name: String,
449 },
450 #[command(
452 long_about = "Remove a configured secret mapping for a specific security scheme.\n\n\
453 This will remove the environment variable mapping for the specified\n\
454 security scheme, causing it to fall back to x-aperture-secret\n\
455 extensions or become undefined.\n\n\
456 Examples:\n \
457 aperture config remove-secret myapi bearerAuth\n \
458 aperture config remove-secret myapi apiKey"
459 )]
460 RemoveSecret {
461 api_name: String,
463 scheme_name: String,
465 },
466 #[command(
468 long_about = "Remove all configured secret mappings for an API specification.\n\n\
469 This will remove all environment variable mappings for the API,\n\
470 causing all security schemes to fall back to x-aperture-secret\n\
471 extensions or become undefined. Use with caution.\n\n\
472 Examples:\n \
473 aperture config clear-secrets myapi\n \
474 aperture config clear-secrets myapi --force"
475 )]
476 ClearSecrets {
477 api_name: String,
479 #[arg(long, help = "Skip confirmation prompt")]
481 force: bool,
482 },
483 #[command(
485 long_about = "Regenerate binary cache files for API specifications.\n\n\
486 This is useful when cache files become corrupted or when upgrading\n\
487 between versions of Aperture that have incompatible cache formats.\n\
488 You can reinitialize all specs or target a specific one.\n\n\
489 Examples:\n \
490 aperture config reinit --all # Reinitialize all specs\n \
491 aperture config reinit myapi # Reinitialize specific spec"
492 )]
493 Reinit {
494 context: Option<String>,
496 #[arg(long, conflicts_with = "context", help = "Reinitialize all specs")]
498 all: bool,
499 },
500 #[command(long_about = "Clear cached API responses to free up disk space.\n\n\
502 You can clear cache for a specific API or all cached responses.\n\
503 This is useful when you want to ensure fresh data from the API\n\
504 or free up disk space.\n\n\
505 Examples:\n \
506 aperture config clear-cache myapi # Clear cache for specific API\n \
507 aperture config clear-cache --all # Clear all cached responses")]
508 ClearCache {
509 api_name: Option<String>,
511 #[arg(long, conflicts_with = "api_name", help = "Clear all response cache")]
513 all: bool,
514 },
515 #[command(long_about = "Display statistics about cached API responses.\n\n\
517 Shows cache size, number of entries, and hit/miss rates.\n\
518 Useful for monitoring cache effectiveness and disk usage.\n\n\
519 Examples:\n \
520 aperture config cache-stats myapi # Stats for specific API\n \
521 aperture config cache-stats # Stats for all APIs")]
522 CacheStats {
523 api_name: Option<String>,
525 },
526 #[command(long_about = "Set a global configuration setting value.\n\n\
528 Supports dot-notation for nested settings and type-safe validation.\n\
529 The configuration file comments and formatting are preserved.\n\n\
530 Available settings:\n \
531 default_timeout_secs (integer) - Default timeout for API requests\n \
532 agent_defaults.json_errors (boolean) - Output errors as JSON by default\n \
533 retry_defaults.max_attempts (integer) - Max retry attempts (0 = disabled)\n \
534 retry_defaults.initial_delay_ms (integer) - Initial retry delay in ms\n \
535 retry_defaults.max_delay_ms (integer) - Maximum retry delay cap in ms\n\n\
536 Examples:\n \
537 aperture config set default_timeout_secs 60\n \
538 aperture config set agent_defaults.json_errors true\n \
539 aperture config set retry_defaults.max_attempts 3")]
540 Set {
541 key: String,
543 value: String,
545 },
546 #[command(
548 long_about = "Get the current value of a global configuration setting.\n\n\
549 Supports dot-notation for nested settings.\n\
550 Use `config settings` to see all available keys.\n\n\
551 Examples:\n \
552 aperture config get default_timeout_secs\n \
553 aperture config get retry_defaults.max_attempts\n \
554 aperture config get default_timeout_secs --json"
555 )]
556 Get {
557 key: String,
559 #[arg(long, help = "Output as JSON")]
561 json: bool,
562 },
563 #[command(
565 long_about = "Display all available configuration settings and their current values.\n\n\
566 Shows the key name, current value, type, and description for each\n\
567 setting. Use this to discover available settings and their defaults.\n\n\
568 Examples:\n \
569 aperture config settings\n \
570 aperture config settings --json"
571 )]
572 Settings {
573 #[arg(long, help = "Output as JSON")]
575 json: bool,
576 },
577}