Skip to main content

aperture_cli/cli/
mod.rs

1pub mod commands;
2pub mod errors;
3pub mod legacy_execute;
4pub mod render;
5pub mod tracing_init;
6pub mod translate;
7
8use clap::{ArgAction, Parser, Subcommand, ValueEnum};
9
10#[derive(ValueEnum, Clone, Debug)]
11pub enum OutputFormat {
12    /// Output as JSON (default)
13    Json,
14    /// Output as YAML
15    Yaml,
16    /// Output as formatted table
17    Table,
18}
19
20#[derive(Parser, Debug)]
21#[allow(clippy::struct_excessive_bools)]
22#[command(
23    author,
24    version,
25    about = "Aperture: Dynamic CLI generator for OpenAPI specifications",
26    long_about = "Aperture dynamically generates commands from OpenAPI 3.x specifications.\n\
27                  It serves as a bridge between autonomous AI agents and APIs by consuming\n\
28                  OpenAPI specs and creating a rich command-line interface with built-in\n\
29                  security, caching, and agent-friendly features.\n\n\
30                  Examples:\n  \
31                  aperture config add myapi api-spec.yaml\n  \
32                  aperture api myapi users get-user --id 123\n  \
33                  aperture config list\n\n\
34                  Agent-friendly features:\n  \
35                  aperture api myapi --describe-json    # Get capability manifest\n  \
36                  aperture --json-errors api myapi ...  # Structured error output\n  \
37                  aperture api myapi --dry-run ...      # Show request without executing"
38)]
39pub struct Cli {
40    /// Output a JSON manifest of all available commands and parameters
41    #[arg(
42        long,
43        global = true,
44        help = "Output capability manifest as JSON (can be filtered with --jq)"
45    )]
46    pub describe_json: bool,
47
48    /// Output all errors as structured JSON to stderr
49    /// When used with batch operations, outputs a clean JSON summary at the end
50    #[arg(long, global = true, help = "Output errors in JSON format")]
51    pub json_errors: bool,
52
53    /// Suppress non-essential output (success messages, tips, hints)
54    /// Only outputs requested data and errors
55    #[arg(
56        long,
57        short = 'q',
58        global = true,
59        help = "Suppress informational output"
60    )]
61    pub quiet: bool,
62
63    /// Increase logging verbosity
64    #[arg(
65        short = 'v',
66        global = true,
67        action = ArgAction::Count,
68        help = "Increase logging verbosity (-v for debug, -vv for trace)"
69    )]
70    pub verbosity: u8,
71
72    /// Show the HTTP request that would be made without executing it
73    #[arg(long, global = true, help = "Show request details without executing")]
74    pub dry_run: bool,
75
76    /// Set the Idempotency-Key header for safe retries
77    #[arg(
78        long,
79        global = true,
80        value_name = "KEY",
81        help = "Set idempotency key header"
82    )]
83    pub idempotency_key: Option<String>,
84
85    /// Output format for response data
86    #[arg(
87        long,
88        global = true,
89        value_enum,
90        default_value = "json",
91        help = "Output format for response data"
92    )]
93    pub format: OutputFormat,
94
95    /// Apply JQ filter to response data, describe-json output, or batch results (with --json-errors)
96    #[arg(
97        long,
98        global = true,
99        value_name = "FILTER",
100        help = "Apply JQ filter to JSON output (e.g., '.name', '.[] | select(.active)', '.batch_execution_summary.operations[] | select(.success == false)')"
101    )]
102    pub jq: Option<String>,
103
104    /// Execute operations from a batch file
105    #[arg(
106        long,
107        global = true,
108        value_name = "PATH",
109        help = "Path to batch file (JSON or YAML) containing multiple operations"
110    )]
111    pub batch_file: Option<String>,
112
113    /// Maximum concurrent requests for batch operations
114    #[arg(
115        long,
116        global = true,
117        value_name = "N",
118        default_value = "5",
119        help = "Maximum number of concurrent requests for batch operations"
120    )]
121    pub batch_concurrency: usize,
122
123    /// Rate limit for batch operations (requests per second)
124    #[arg(
125        long,
126        global = true,
127        value_name = "N",
128        help = "Rate limit for batch operations (requests per second)"
129    )]
130    pub batch_rate_limit: Option<u32>,
131
132    /// Enable response caching
133    #[arg(
134        long,
135        global = true,
136        help = "Enable response caching (can speed up repeated requests)"
137    )]
138    pub cache: bool,
139
140    /// Disable response caching
141    #[arg(
142        long,
143        global = true,
144        conflicts_with = "cache",
145        help = "Disable response caching"
146    )]
147    pub no_cache: bool,
148
149    /// TTL for cached responses in seconds
150    #[arg(
151        long,
152        global = true,
153        value_name = "SECONDS",
154        help = "Cache TTL in seconds (default: 300)"
155    )]
156    pub cache_ttl: Option<u64>,
157
158    /// Use positional arguments for path parameters (legacy syntax)
159    #[arg(
160        long,
161        global = true,
162        help = "Use positional arguments for path parameters (legacy syntax)"
163    )]
164    pub positional_args: bool,
165
166    /// Automatically paginate through all pages, streaming results as NDJSON
167    ///
168    /// Detects the pagination strategy from the `OpenAPI` spec (cursor, offset,
169    /// or Link header) and loops until the last page, printing each page's
170    /// array items as newline-delimited JSON objects.
171    #[arg(
172        long,
173        global = true,
174        help = "Stream all pages as NDJSON (auto-detects pagination strategy)"
175    )]
176    pub auto_paginate: bool,
177
178    /// Maximum number of retry attempts for failed requests
179    #[arg(
180        long,
181        global = true,
182        value_name = "N",
183        help = "Maximum retry attempts (0 = disabled, overrides config)"
184    )]
185    pub retry: Option<u32>,
186
187    /// Initial delay between retries (e.g., "500ms", "1s")
188    #[arg(
189        long,
190        global = true,
191        value_name = "DURATION",
192        help = "Initial retry delay (e.g., '500ms', '1s', '2s')"
193    )]
194    pub retry_delay: Option<String>,
195
196    /// Maximum delay cap between retries (e.g., "30s", "1m")
197    #[arg(
198        long,
199        global = true,
200        value_name = "DURATION",
201        help = "Maximum retry delay cap (e.g., '30s', '1m')"
202    )]
203    pub retry_max_delay: Option<String>,
204
205    /// Force retry on non-idempotent requests without an idempotency key
206    #[arg(
207        long,
208        global = true,
209        help = "Allow retrying non-idempotent requests without idempotency key"
210    )]
211    pub force_retry: bool,
212
213    #[command(subcommand)]
214    pub command: Commands,
215}
216
217#[derive(Subcommand, Debug)]
218pub enum Commands {
219    /// Manage API specifications (add, list, remove, edit)
220    #[command(long_about = "Manage your collection of OpenAPI specifications.\n\n\
221                      Add specifications to make their operations available as commands,\n\
222                      list currently registered specs, remove unused ones, or edit\n\
223                      existing specifications in your default editor.")]
224    Config {
225        #[command(subcommand)]
226        command: ConfigCommands,
227    },
228    /// List available commands for an API specification
229    #[command(
230        long_about = "Display a tree-like summary of all available commands for an API.\n\n\
231                      Shows operations organized by tags, making it easy to discover\n\
232                      what functionality is available in a registered API specification.\n\
233                      This provides an overview without having to use --help on each operation.\n\n\
234                      Example:\n  \
235                      aperture list-commands myapi"
236    )]
237    ListCommands {
238        /// Name of the API specification context.
239        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
240        context: String,
241    },
242    /// Execute API operations for a specific context
243    #[command(
244        long_about = "Execute operations from a registered API specification.\n\n\
245                      The context refers to the name you gave when adding the spec.\n\
246                      Commands are dynamically generated based on the OpenAPI specification,\n\
247                      organized by tags (e.g., 'users', 'posts', 'orders').\n\n\
248                      Examples:\n  \
249                      aperture api myapi users get-user --id 123\n  \
250                      aperture api myapi posts create-post --body '{\"title\":\"Hello\"}'\n  \
251                      aperture api myapi --help  # See available operations"
252    )]
253    Api {
254        /// Name of the API specification context.
255        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
256        context: String,
257        /// Remaining arguments will be parsed dynamically based on the `OpenAPI` spec
258        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
259        args: Vec<String>,
260    },
261    /// Search for API operations across all specifications
262    #[command(long_about = "Search for API operations by keyword or pattern.\n\n\
263                      Search through all registered API specifications to find\n\
264                      relevant operations. The search includes operation IDs,\n\
265                      descriptions, paths, and HTTP methods.\n\n\
266                      Examples:\n  \
267                      aperture search 'list users'     # Find user listing operations\n  \
268                      aperture search 'POST create'     # Find POST operations with 'create'\n  \
269                      aperture search issues --api sm   # Search only in 'sm' API\n  \
270                      aperture search 'get.*by.*id'     # Regex pattern search")]
271    Search {
272        /// Search query (keywords, patterns, or regex)
273        query: String,
274        /// Limit search to a specific API context.
275        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
276        #[arg(long, value_name = "API", help = "Search only in specified API")]
277        api: Option<String>,
278        /// Show detailed results including paths and parameters
279        #[arg(long, help = "Show detailed information for each result")]
280        verbose: bool,
281    },
282    /// Execute API operations using shortcuts or direct operation IDs
283    #[command(
284        name = "exec",
285        long_about = "Execute API operations using shortcuts instead of full paths.\n\n\
286                      This command attempts to resolve shortcuts to their full command paths:\n\
287                      - Direct operation IDs: getUserById --id 123\n\
288                      - HTTP method + path: GET /users/123\n\
289                      - Tag-based shortcuts: users list\n\n\
290                      When multiple matches are found, you'll get suggestions to choose from.\n\n\
291                      Examples:\n  \
292                      aperture exec getUserById --id 123\n  \
293                      aperture exec GET /users/123\n  \
294                      aperture exec users list"
295    )]
296    Exec {
297        /// Shortcut command arguments
298        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
299        args: Vec<String>,
300    },
301    /// Get detailed documentation for APIs and commands
302    #[command(
303        long_about = "Get comprehensive documentation for APIs and operations.\n\n\
304                      This provides detailed information including parameters, examples,\n\
305                      response schemas, and authentication requirements. Use it to learn\n\
306                      about available functionality without trial and error.\n\n\
307                      Examples:\n  \
308                      aperture docs                        # Interactive help menu\n  \
309                      aperture docs myapi                  # API overview\n  \
310                      aperture docs myapi users get-user  # Detailed command help"
311    )]
312    Docs {
313        /// API name (optional, shows interactive menu if omitted).
314        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
315        api: Option<String>,
316        /// Tag/category name (optional)
317        tag: Option<String>,
318        /// Operation name (optional)
319        operation: Option<String>,
320        /// Show enhanced formatting with examples
321        #[arg(long, help = "Enhanced formatting with examples and tips")]
322        enhanced: bool,
323    },
324    /// Show API overview with statistics and quick start guide
325    #[command(
326        long_about = "Display comprehensive API overview with statistics and examples.\n\n\
327                      Shows operation counts, method distribution, available categories,\n\
328                      and sample commands to help you get started quickly with any API.\n\n\
329                      Examples:\n  \
330                      aperture overview myapi\n  \
331                      aperture overview --all  # Overview of all registered APIs"
332    )]
333    Overview {
334        /// API name (required unless using --all).
335        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
336        api: Option<String>,
337        /// Show overview for all registered APIs
338        #[arg(long, conflicts_with = "api", help = "Show overview for all APIs")]
339        all: bool,
340    },
341}
342
343#[derive(Subcommand, Debug)]
344pub enum ConfigCommands {
345    /// Add a new API specification from a file
346    #[command(
347        long_about = "Add an OpenAPI 3.x specification to your configuration.\n\n\
348                      This validates the specification, extracts operations, and creates\n\
349                      a cached representation for fast command generation. The spec name\n\
350                      becomes the context for executing API operations.\n\n\
351                      Supported formats: YAML (.yaml, .yml)\n\
352                      Supported auth: API Key, Bearer Token\n\n\
353                      Examples:\n  \
354                      aperture config add myapi ./openapi.yaml\n  \
355                      aperture config add myapi https://api.example.com/openapi.yaml"
356    )]
357    Add {
358        /// Name to identify this API specification (used as context in 'aperture api').
359        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
360        name: String,
361        /// Path to the `OpenAPI` 3.x specification file (YAML format) or URL
362        file_or_url: String,
363        /// Overwrite existing specification if it already exists
364        #[arg(long, help = "Replace the specification if it already exists")]
365        force: bool,
366        /// Reject specs with unsupported features instead of skipping endpoints
367        #[arg(
368            long,
369            help = "Reject entire spec if any endpoints have unsupported content types (e.g., multipart/form-data, XML). Default behavior skips unsupported endpoints with warnings."
370        )]
371        strict: bool,
372    },
373    /// List all registered API specifications
374    #[command(
375        long_about = "Display all currently registered API specifications.\n\n\
376                      Shows the names you can use as contexts with 'aperture api'.\n\
377                      Use this to see what APIs are available for command generation."
378    )]
379    List {
380        /// Show detailed information including skipped endpoints
381        #[arg(long, help = "Show detailed information about each API")]
382        verbose: bool,
383    },
384    /// Remove an API specification from configuration
385    #[command(
386        long_about = "Remove a registered API specification and its cached data.\n\n\
387                      This removes both the original specification file and the\n\
388                      generated cache, making the API operations unavailable.\n\
389                      Use 'aperture config list' to see available specifications."
390    )]
391    Remove {
392        /// Name of the API specification to remove.
393        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
394        name: String,
395    },
396    /// Edit an API specification in your default editor
397    #[command(
398        long_about = "Open an API specification in your default text editor.\n\n\
399                      Uses the $EDITOR environment variable to determine which editor\n\
400                      to use. After editing, you may need to re-add the specification\n\
401                      to update the cached representation.\n\n\
402                      Example:\n  \
403                      export EDITOR=vim\n  \
404                      aperture config edit myapi"
405    )]
406    Edit {
407        /// Name of the API specification to edit.
408        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
409        name: String,
410    },
411    /// Set base URL for an API specification
412    #[command(long_about = "Set the base URL for an API specification.\n\n\
413                      This overrides the base URL from the OpenAPI spec and the\n\
414                      APERTURE_BASE_URL environment variable. You can set a general\n\
415                      override or environment-specific URLs.\n\n\
416                      Examples:\n  \
417                      aperture config set-url myapi https://api.example.com\n  \
418                      aperture config set-url myapi --env staging https://staging.example.com\n  \
419                      aperture config set-url myapi --env prod https://prod.example.com")]
420    SetUrl {
421        /// Name of the API specification.
422        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
423        name: String,
424        /// The base URL to set
425        url: String,
426        /// Set URL for a specific environment (e.g., dev, staging, prod)
427        #[arg(long, value_name = "ENV", help = "Set URL for specific environment")]
428        env: Option<String>,
429    },
430    /// Get base URL configuration for an API specification
431    #[command(
432        long_about = "Display the base URL configuration for an API specification.\n\n\
433                      Shows the configured base URL override and any environment-specific\n\
434                      URLs. Also displays what URL would be used based on current\n\
435                      environment settings.\n\n\
436                      Example:\n  \
437                      aperture config get-url myapi"
438    )]
439    GetUrl {
440        /// Name of the API specification.
441        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
442        name: String,
443    },
444    /// List all configured base URLs
445    #[command(
446        long_about = "Display all configured base URLs across all API specifications.\n\n\
447                      Shows general overrides and environment-specific configurations\n\
448                      for each registered API. Useful for reviewing your URL settings\n\
449                      at a glance."
450    )]
451    ListUrls {},
452    /// Set secret configuration for an API specification security scheme
453    #[command(
454        long_about = "Configure authentication secrets for API specifications.\n\n\
455                      This allows you to set environment variable mappings for security\n\
456                      schemes without modifying the OpenAPI specification file. These\n\
457                      settings take precedence over x-aperture-secret extensions.\n\n\
458                      Examples:\n  \
459                      aperture config set-secret myapi bearerAuth --env API_TOKEN\n  \
460                      aperture config set-secret myapi apiKey --env API_KEY\n  \
461                      aperture config set-secret myapi --interactive"
462    )]
463    SetSecret {
464        /// Name of the API specification.
465        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
466        api_name: String,
467        /// Name of the security scheme (omit for interactive mode)
468        scheme_name: Option<String>,
469        /// Environment variable name containing the secret
470        #[arg(long, value_name = "VAR", help = "Environment variable name")]
471        env: Option<String>,
472        /// Interactive mode to configure all undefined secrets
473        #[arg(long, conflicts_with_all = ["scheme_name", "env"], help = "Configure secrets interactively")]
474        interactive: bool,
475    },
476    /// List configured secrets for an API specification
477    #[command(
478        long_about = "Display configured secret mappings for an API specification.\n\n\
479                      Shows which security schemes are configured with environment\n\
480                      variables and which ones still rely on x-aperture-secret\n\
481                      extensions or are undefined.\n\n\
482                      Example:\n  \
483                      aperture config list-secrets myapi"
484    )]
485    ListSecrets {
486        /// Name of the API specification.
487        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
488        api_name: String,
489    },
490    /// Remove a specific configured secret for an API specification
491    #[command(
492        long_about = "Remove a configured secret mapping for a specific security scheme.\n\n\
493                      This will remove the environment variable mapping for the specified\n\
494                      security scheme, causing it to fall back to x-aperture-secret\n\
495                      extensions or become undefined.\n\n\
496                      Examples:\n  \
497                      aperture config remove-secret myapi bearerAuth\n  \
498                      aperture config remove-secret myapi apiKey"
499    )]
500    RemoveSecret {
501        /// Name of the API specification.
502        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
503        api_name: String,
504        /// Name of the security scheme to remove
505        scheme_name: String,
506    },
507    /// Clear all configured secrets for an API specification
508    #[command(
509        long_about = "Remove all configured secret mappings for an API specification.\n\n\
510                      This will remove all environment variable mappings for the API,\n\
511                      causing all security schemes to fall back to x-aperture-secret\n\
512                      extensions or become undefined. Use with caution.\n\n\
513                      Examples:\n  \
514                      aperture config clear-secrets myapi\n  \
515                      aperture config clear-secrets myapi --force"
516    )]
517    ClearSecrets {
518        /// Name of the API specification.
519        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
520        api_name: String,
521        /// Skip confirmation prompt
522        #[arg(long, help = "Skip confirmation prompt")]
523        force: bool,
524    },
525    /// Re-initialize cached specifications
526    #[command(
527        long_about = "Regenerate binary cache files for API specifications.\n\n\
528                      This is useful when cache files become corrupted or when upgrading\n\
529                      between versions of Aperture that have incompatible cache formats.\n\
530                      You can reinitialize all specs or target a specific one.\n\n\
531                      Examples:\n  \
532                      aperture config reinit --all     # Reinitialize all specs\n  \
533                      aperture config reinit myapi     # Reinitialize specific spec"
534    )]
535    Reinit {
536        /// Name of the API specification to reinitialize (omit for --all).
537        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
538        context: Option<String>,
539        /// Reinitialize all cached specifications
540        #[arg(long, conflicts_with = "context", help = "Reinitialize all specs")]
541        all: bool,
542    },
543    /// Clear response cache
544    #[command(long_about = "Clear cached API responses to free up disk space.\n\n\
545                      You can clear cache for a specific API or all cached responses.\n\
546                      This is useful when you want to ensure fresh data from the API\n\
547                      or free up disk space.\n\n\
548                      Examples:\n  \
549                      aperture config clear-cache myapi     # Clear cache for specific API\n  \
550                      aperture config clear-cache --all     # Clear all cached responses")]
551    ClearCache {
552        /// Name of the API specification to clear cache for (omit for --all).
553        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
554        api_name: Option<String>,
555        /// Clear all cached responses
556        #[arg(long, conflicts_with = "api_name", help = "Clear all response cache")]
557        all: bool,
558    },
559    /// Show response cache statistics
560    #[command(long_about = "Display statistics about cached API responses.\n\n\
561                      Shows cache size, number of entries, and hit/miss rates.\n\
562                      Useful for monitoring cache effectiveness and disk usage.\n\n\
563                      Examples:\n  \
564                      aperture config cache-stats myapi     # Stats for specific API\n  \
565                      aperture config cache-stats           # Stats for all APIs")]
566    CacheStats {
567        /// Name of the API specification to show stats for (omit for all APIs).
568        /// Must start with a letter or digit; may contain letters, digits, dots, hyphens, or underscores (max 64 chars).
569        api_name: Option<String>,
570    },
571    /// Set a global configuration setting
572    #[command(long_about = "Set a global configuration setting value.\n\n\
573                      Supports dot-notation for nested settings and type-safe validation.\n\
574                      The configuration file comments and formatting are preserved.\n\n\
575                      Available settings:\n  \
576                      default_timeout_secs              (integer)  - Default timeout for API requests\n  \
577                      agent_defaults.json_errors        (boolean)  - Output errors as JSON by default\n  \
578                      retry_defaults.max_attempts       (integer)  - Max retry attempts (0 = disabled)\n  \
579                      retry_defaults.initial_delay_ms   (integer)  - Initial retry delay in ms\n  \
580                      retry_defaults.max_delay_ms       (integer)  - Maximum retry delay cap in ms\n\n\
581                      Examples:\n  \
582                      aperture config set default_timeout_secs 60\n  \
583                      aperture config set agent_defaults.json_errors true\n  \
584                      aperture config set retry_defaults.max_attempts 3")]
585    Set {
586        /// Setting key (use `config settings` to see all available keys)
587        key: String,
588        /// Value to set (validated against expected type)
589        value: String,
590    },
591    /// Get a global configuration setting value
592    #[command(
593        long_about = "Get the current value of a global configuration setting.\n\n\
594                      Supports dot-notation for nested settings.\n\
595                      Use `config settings` to see all available keys.\n\n\
596                      Examples:\n  \
597                      aperture config get default_timeout_secs\n  \
598                      aperture config get retry_defaults.max_attempts\n  \
599                      aperture config get default_timeout_secs --json"
600    )]
601    Get {
602        /// Setting key to retrieve (use `config settings` to see all available keys)
603        key: String,
604        /// Output as JSON
605        #[arg(long, help = "Output as JSON")]
606        json: bool,
607    },
608    /// List all available configuration settings
609    #[command(
610        long_about = "Display all available configuration settings and their current values.\n\n\
611                      Shows the key name, current value, type, and description for each\n\
612                      setting. Use this to discover available settings and their defaults.\n\n\
613                      Examples:\n  \
614                      aperture config settings\n  \
615                      aperture config settings --json"
616    )]
617    Settings {
618        /// Output as JSON
619        #[arg(long, help = "Output as JSON")]
620        json: bool,
621    },
622    /// Set a command mapping for an API specification
623    #[command(
624        name = "set-mapping",
625        long_about = "Customize the CLI command tree for an API specification.\n\n\
626                      Rename tag groups, rename operations, add aliases, or hide commands\n\
627                      without modifying the original OpenAPI spec. Changes take effect\n\
628                      after `config reinit`.\n\n\
629                      Examples:\n  \
630                      aperture config set-mapping myapi --group \"User Management\" users\n  \
631                      aperture config set-mapping myapi --operation getUserById --name fetch\n  \
632                      aperture config set-mapping myapi --operation getUserById --alias get\n  \
633                      aperture config set-mapping myapi --operation deleteUser --hidden"
634    )]
635    SetMapping {
636        /// Name of the API specification.
637        api_name: String,
638        /// Rename a tag group: `--group <original> <new_name>`
639        #[arg(long, num_args = 2, value_names = ["ORIGINAL", "NEW_NAME"], conflicts_with = "operation")]
640        group: Option<Vec<String>>,
641        /// Target an operation by its operationId
642        #[arg(long, value_name = "OPERATION_ID")]
643        operation: Option<String>,
644        /// Set the subcommand name for an operation
645        #[arg(long, requires = "operation", value_name = "NAME")]
646        name: Option<String>,
647        /// Set the group for an operation (overrides tag)
648        #[arg(long = "op-group", requires = "operation", value_name = "GROUP")]
649        op_group: Option<String>,
650        /// Add an alias for an operation
651        #[arg(long, requires = "operation", value_name = "ALIAS")]
652        alias: Option<String>,
653        /// Remove an alias from an operation
654        #[arg(long, requires = "operation", value_name = "ALIAS")]
655        remove_alias: Option<String>,
656        /// Mark an operation as hidden from help output
657        #[arg(long, requires = "operation")]
658        hidden: bool,
659        /// Mark an operation as visible (unhide)
660        #[arg(long, requires = "operation", conflicts_with = "hidden")]
661        visible: bool,
662    },
663    /// List command mappings for an API specification
664    #[command(
665        name = "list-mappings",
666        long_about = "Display all custom command mappings for an API specification.\n\n\
667                      Shows group renames, operation renames, aliases, and hidden flags.\n\n\
668                      Example:\n  \
669                      aperture config list-mappings myapi"
670    )]
671    ListMappings {
672        /// Name of the API specification.
673        api_name: String,
674    },
675    /// Remove a command mapping for an API specification
676    #[command(
677        name = "remove-mapping",
678        long_about = "Remove a custom command mapping for an API specification.\n\n\
679                      Removes a group rename or an operation mapping. Changes take effect\n\
680                      after `config reinit`.\n\n\
681                      Examples:\n  \
682                      aperture config remove-mapping myapi --group \"User Management\"\n  \
683                      aperture config remove-mapping myapi --operation getUserById"
684    )]
685    RemoveMapping {
686        /// Name of the API specification.
687        api_name: String,
688        /// Remove a group mapping by original tag name
689        #[arg(long, value_name = "ORIGINAL", conflicts_with = "operation")]
690        group: Option<String>,
691        /// Remove an operation mapping by operationId
692        #[arg(long, value_name = "OPERATION_ID")]
693        operation: Option<String>,
694    },
695}