completers/claude_completer/claude_completer.ps1
|
<# .SYNOPSIS Argument completer for claude / claude.exe (Claude Code CLI). .DESCRIPTION Provides tab completion for claude subcommands, flags, flag values, and the --flag=value inline syntax. Dot-source this file from your $PROFILE to enable completion for both invocation forms. Safe to source multiple times (re-registration simply replaces the completer). .EXAMPLE . "$PSScriptRoot\claude_completer.ps1" #> Set-StrictMode -Version Latest function Initialize-ClaudeCompleterData { if (Get-Variable -Name ClaudeTopCommands -Scope Script -ErrorAction Ignore) { return } # Top-level subcommands. $script:ClaudeTopCommands = @( 'agents', 'attach', 'auth', 'auto-mode', 'doctor', 'gateway', 'import', 'install', 'logs', 'mcp', 'plugin', 'purge', 'respawn', 'rm', 'setup-token', 'stop', 'ultrareview', 'update' ) # Command alias normalisation (alias -> canonical) applied when walking args. # Top-level: plugins == plugin, upgrade == update, kill == stop. $script:ClaudeCmdAliases = @{ 'plugins' = 'plugin' 'upgrade' = 'update' 'kill' = 'stop' } # Level-2 subcommand aliases keyed by "cmd": alias -> canonical. $script:ClaudeSubAliases = @{ 'plugin' = @{ 'i' = 'install' 'new' = 'init' 'remove' = 'uninstall' 'autoremove' = 'prune' } } # Level-3 subcommand aliases keyed by "cmd.subcmd": alias -> canonical. $script:ClaudeL3Aliases = @{ 'plugin.marketplace' = @{ 'rm' = 'remove' } } # Level-2 subcommands per top-level command. $script:ClaudeSubSubcommands = @{ 'auth' = @('login', 'logout', 'status', 'help') 'auto-mode' = @('config', 'critique', 'defaults', 'reset', 'help') 'mcp' = @('add', 'add-json', 'add-from-claude-desktop', 'get', 'list', 'login', 'logout', 'remove', 'reset-project-choices', 'serve', 'help') 'plugin' = @('configure', 'details', 'disable', 'enable', 'eval', 'init', 'install', 'list', 'marketplace', 'prune', 'tag', 'test', 'uninstall', 'update', 'validate', 'help') # Commands with no L2 subcommands (own options or positionals only). 'agents' = @() 'attach' = @() 'doctor' = @() 'gateway' = @() 'import' = @() 'install' = @() 'logs' = @() 'purge' = @() 'respawn' = @() 'rm' = @() 'setup-token' = @() 'stop' = @() 'ultrareview' = @() 'update' = @() } # Level-3 subcommands: "cmd.subcmd" -> @(subsubcmds). $script:ClaudeL3Subcommands = @{ 'plugin.marketplace' = @('add', 'list', 'remove', 'update', 'help') } # ---- Flag categories ----------------------------------------------------------------------- # Global enum flags with fixed choice sets. $script:ClaudeEnumFlags = @{ '--effort' = @('low', 'medium', 'high', 'xhigh', 'max') '--permission-mode' = @('acceptEdits', 'auto', 'bypassPermissions', 'manual', 'dontAsk', 'plan') '--output-format' = @('text', 'json', 'stream-json') '--input-format' = @('text', 'stream-json') '--prompt-suggestions' = @('true', 'false', '1', '0', 'yes', 'no', 'on', 'off') '--autocompact' = @('auto') '--permission-prompts' = @('host', 'none') '--system-prompt-snapshot' = @('on', 'off') } # Boolean / switch flags (accept no value). $script:ClaudeBoolFlags = @( '--allow-dangerously-skip-permissions' '--ax-screen-reader' '--background' '--bare' '--bg' '--brief' '--chrome' '--continue' '--dangerously-skip-permissions' '--disable-slash-commands' '--exclude-dynamic-system-prompt-sections' '--fork-session' '--forward-subagent-text' '--help' '--ide' '--include-hook-events' '--include-partial-messages' '--no-chrome' '--no-session-persistence' '--print' '--replay-user-messages' '--restricted' '--safe-mode' '--strict-mcp-config' '--tmux' '--verbose' '--version' ) # Optional-value flags: behave like booleans for token-consumption purposes # (do NOT consume the next token as a value), but still offer enum/value # completion when the user explicitly types --flag=. $script:ClaudeOptionalValueFlags = @( '--cloud' '--debug' '--resume' '--teleport' '--worktree' '--remote-control' '--from-pr' '--prompt-suggestions' ) # Free-form string flags (single value, no fixed choices). $script:ClaudeStringFlags = @( '--agent' '--agents' '--append-system-prompt' '--environment' '--json-schema' '--name' '--remote-control-session-name-prefix' '--session-id' '--system-prompt' ) # Numeric flags. $script:ClaudeNumberFlags = @( '--max-budget-usd' ) # Array / multi-value flags. $script:ClaudeArrayFlags = @( '--add-dir' '--allowedTools' '--allowed-tools' '--betas' '--disallowedTools' '--disallowed-tools' '--file' '--mcp-config' '--plugin-dir' '--plugin-url' '--setting-sources' '--settings' '--tools' ) # Flags whose value is a model name (free-form, but with hints). The aliases and the # full-name example come from 'claude --help' ('fable', 'opus', 'sonnet', 'claude-fable-5'). $script:ClaudeModelFlags = @('--model', '--fallback-model') $script:ClaudeModelHints = @( 'fable', 'opus', 'sonnet', 'haiku', 'claude-fable-5' ) # Flags whose value has comma-separated hints (free-form). $script:ClaudeHintFlags = @{ '--setting-sources' = @('user', 'project', 'local') } # Flags that produce file-path completion. $script:ClaudePathFlags = @( '--debug-file' '--mcp-config' '--settings' ) # Flags that produce directory-path completion. $script:ClaudeDirFlags = @( '--add-dir' '--plugin-dir' ) # ---- Context tables ------------------------------------------------------------------------ # L2/L3 context-specific flags (beyond the global set): "cmd.subcmd" or # "cmd.subcmd.subsubcmd" -> @(flags). $script:ClaudeContextFlags = @{ 'auth.login' = @('--claudeai', '--console', '--email', '--sso') 'auth.status' = @('--json', '--text') 'auto-mode.critique' = @('--model') 'mcp.add' = @('--callback-port', '--client-id', '--client-secret', '--env', '--header', '--scope', '--transport') 'mcp.add-json' = @('--client-secret', '--scope') 'mcp.add-from-claude-desktop' = @('--scope') 'mcp.login' = @('--no-browser') 'mcp.remove' = @('--scope') 'mcp.serve' = @('--debug', '--verbose') 'plugin.configure' = @('--json', '--values-stdin') 'plugin.disable' = @('--all', '--json', '--scope') 'plugin.enable' = @('--json', '--scope') 'plugin.eval' = @('--ablation', '--allow-real-servers', '--allow-tools', '--case', '--concurrency', '--eval-dir', '--json', '--judge-model', '--keep-temp', '--max-cost-usd', '--mocks', '--model', '--no-publish', '--no-scaffold', '--output-dir', '--publish-report', '--report', '--runs', '--scaffold', '--tag', '--threshold', '--trust-plugin', '--verbose') 'plugin.init' = @('--author', '--author-email', '--description', '--force', '--with') 'plugin.install' = @('--accept-command', '--config', '--json', '--marketplace', '--registry', '--scope', '--yes') 'plugin.list' = @('--available', '--data-size', '--json') 'plugin.prune' = @('--dry-run', '--scope', '--yes') 'plugin.tag' = @('--dry-run', '--force', '--message', '--push', '--remote') 'plugin.uninstall' = @('--json', '--keep-data', '--prune', '--scope', '--yes') 'plugin.update' = @('--accept-command', '--json', '--scope', '--yes') 'plugin.validate' = @('--json', '--strict') 'plugin.marketplace.add' = @('--claudeai', '--json', '--scope', '--sparse') 'plugin.marketplace.list' = @('--json') 'plugin.marketplace.remove' = @('--json', '--scope') 'plugin.marketplace.update' = @('--json') } # Flags for commands that take flags directly (no L2 subcommand): "cmd" -> @(flags). $script:ClaudeCmdContextFlags = @{ 'agents' = @('--add-dir', '--agent', '--all', '--allow-dangerously-skip-permissions', '--cwd', '--dangerously-skip-permissions', '--effort', '--json', '--mcp-config', '--model', '--permission-mode', '--plugin-dir', '--restricted', '--setting-sources', '--settings', '--strict-mcp-config') 'gateway' = @('--config') 'import' = @('--dry-run', '--yes') 'install' = @('--force') 'purge' = @('--all', '--dry-run', '--interactive', '--yes') 'respawn' = @('--all') 'rm' = @('--discard-unpushed', '--force-remove-worktree') 'ultrareview' = @('--json', '--no-post', '--post', '--timeout') } # Context-specific boolean flags (take no value when used in a subcommand). # 'plugin list --data-size [plugin]' takes an optional value, so like the global # optional-value flags it never consumes the next word and stays unregistered. $script:ClaudeContextBoolFlagSet = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) @('--claudeai', '--console', '--sso', '--json', '--text', '--client-secret', '--verbose', '--debug', '--all', '--available', '--dry-run', '--yes', '--force', '--push', '--strict', '--interactive', '--no-browser', '--post', '--no-post', '--restricted', '--allow-real-servers', '--keep-temp', '--no-publish', '--no-scaffold', '--publish-report', '--scaffold', '--trust-plugin', '--keep-data', '--prune', '--values-stdin') | ForEach-Object { $null = $script:ClaudeContextBoolFlagSet.Add($_) } # Context-specific enum values: "cmd.subcmd.--flag" or "cmd.subcmd.subsubcmd.--flag" -> @(choices). $script:ClaudeContextEnumFlags = @{ 'mcp.add.--scope' = @('local', 'user', 'project') 'mcp.add.--transport' = @('stdio', 'sse', 'http') 'mcp.add-json.--scope' = @('local', 'user', 'project') 'mcp.add-from-claude-desktop.--scope' = @('local', 'user', 'project') 'mcp.remove.--scope' = @('local', 'user', 'project') 'plugin.disable.--scope' = @('user', 'project', 'local') 'plugin.enable.--scope' = @('user', 'project', 'local') 'plugin.eval.--ablation' = @('none', 'with-without') 'plugin.eval.--mocks' = @('record', 'off') 'plugin.install.--scope' = @('user', 'project', 'local') 'plugin.prune.--scope' = @('user', 'project', 'local') 'plugin.uninstall.--scope' = @('user', 'project', 'local') 'plugin.update.--scope' = @('user', 'project', 'local', 'managed') 'plugin.marketplace.add.--scope' = @('user', 'project', 'local') 'plugin.marketplace.remove.--scope' = @('user', 'project', 'local') } # Context-specific value-taking (string/number) flags. $script:ClaudeContextValueFlagSet = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) @('--callback-port', '--client-id', '--email', '--config', '--message', '--remote', '--timeout', '--discard-unpushed', '--force-remove-worktree', '--allow-tools', '--case', '--concurrency', '--eval-dir', '--judge-model', '--max-cost-usd', '--output-dir', '--report', '--runs', '--tag', '--threshold', '--author', '--author-email', '--description', '--with', '--accept-command', '--marketplace', '--registry', '--sparse') | ForEach-Object { $null = $script:ClaudeContextValueFlagSet.Add($_) } # Context-specific numeric flags. $script:ClaudeContextNumberFlagSet = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) @('--callback-port', '--timeout', '--max-budget-usd', '--concurrency', '--max-cost-usd', '--runs', '--threshold') | ForEach-Object { $null = $script:ClaudeContextNumberFlagSet.Add($_) } # Context short aliases keyed by "cmd.subcmd" or "cmd". $script:ClaudeContextShortAliases = @{ 'mcp.add' = @{ '-e' = '--env'; '-H' = '--header'; '-s' = '--scope'; '-t' = '--transport' } 'mcp.add-json' = @{ '-s' = '--scope' } 'mcp.add-from-claude-desktop' = @{ '-s' = '--scope' } 'mcp.remove' = @{ '-s' = '--scope' } 'mcp.serve' = @{ '-d' = '--debug' } 'plugin.disable' = @{ '-a' = '--all'; '-s' = '--scope' } 'plugin.enable' = @{ '-s' = '--scope' } 'plugin.eval' = @{ '-j' = '--concurrency' } 'plugin.init' = @{ '-f' = '--force' } 'plugin.install' = @{ '-s' = '--scope'; '-y' = '--yes' } 'plugin.prune' = @{ '-s' = '--scope'; '-y' = '--yes' } 'plugin.tag' = @{ '-f' = '--force'; '-m' = '--message' } 'plugin.uninstall' = @{ '-s' = '--scope'; '-y' = '--yes' } 'plugin.update' = @{ '-s' = '--scope'; '-y' = '--yes' } 'purge' = @{ '-i' = '--interactive'; '-y' = '--yes' } } # Positional path completion for specific subcommand paths (first positional). $script:ClaudePathPositionalL2 = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) @('plugin.tag', 'plugin.test', 'plugin.validate', 'purge') | ForEach-Object { $null = $script:ClaudePathPositionalL2.Add($_) } # The subset of those path slots that take a directory ('plugin test [dir]', 'purge [path]'). $script:ClaudeDirPositionalL2 = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) @('plugin.test', 'purge') | ForEach-Object { $null = $script:ClaudeDirPositionalL2.Add($_) } # Positional enum values for specific subcommand paths. $script:ClaudePositionalEnums = @{ 'install' = @('stable', 'latest') 'import' = @('codex', 'gemini', 'cursor') } # Placeholder values for free-form positional slots. $script:ClaudePositionalPlaceholders = @{ 'attach' = @('<id>') 'install' = @('<target>') 'logs' = @('<id>') 'respawn' = @('<id>') 'rm' = @('<id>') 'stop' = @('<id>') 'ultrareview' = @('<target>') 'mcp.add' = @('<name>', '<commandOrUrl>', '<arg>') 'mcp.add-json' = @('<name>', '<json>') 'mcp.get' = @('<name>') 'mcp.login' = @('<name>') 'mcp.logout' = @('<name>') 'mcp.remove' = @('<name>') 'plugin.configure' = @('<plugin>') 'plugin.details' = @('<name>') 'plugin.disable' = @('<plugin>') 'plugin.enable' = @('<plugin>') 'plugin.eval' = @('<target>') 'plugin.init' = @('<name>') 'plugin.install' = @('<plugin>') 'plugin.uninstall' = @('<plugin>') 'plugin.update' = @('<plugin>') 'plugin.marketplace.add' = @('<source>') 'plugin.marketplace.remove' = @('<name>') 'plugin.marketplace.update' = @('<name>') } # ---- Global short alias maps --------------------------------------------------------------- $script:ClaudeShortAliases = @{ '-c' = '--continue' '-d' = '--debug' '-h' = '--help' '-n' = '--name' '-p' = '--print' '-r' = '--resume' '-v' = '--version' '-w' = '--worktree' } # Reverse map (long -> short) built once at load time. $script:ClaudeAliasReverse = @{} $script:ClaudeShortAliases.GetEnumerator() | ForEach-Object { $script:ClaudeAliasReverse[$_.Value] = $_.Key } $script:ClaudeContextAliasReverse = @{} foreach ($entry in $script:ClaudeContextShortAliases.GetEnumerator()) { $reverse = @{} foreach ($alias in $entry.Value.GetEnumerator()) { $reverse[$alias.Value] = $alias.Key } $script:ClaudeContextAliasReverse[$entry.Key] = $reverse } # ---- Descriptions -------------------------------------------------------------------------- $script:ClaudeCmdDesc = @{ 'agents' = 'Manage background agents' 'attach' = 'Open a background session in this terminal' 'auth' = 'Manage authentication' 'auto-mode' = 'Inspect or reset auto mode classifier configuration' 'doctor' = 'Diagnose and verify the installation' 'gateway' = 'Run the enterprise auth/telemetry gateway' 'import' = 'Import config from another AI coding agent' 'install' = 'Install the native Claude Code build' 'logs' = 'Print a background session''s recent terminal output' 'mcp' = 'Configure and manage MCP servers' 'plugin' = 'Manage plugins and marketplaces' 'purge' = 'Delete all Claude Code state for a project (transcripts, tasks, file history, config entry)' 'respawn' = 'Restart a background session (or all with --all)' 'rm' = 'Delete a background session and its worktree' 'setup-token' = 'Set up a long-lived authentication token' 'stop' = 'Stop a background session (alias: kill)' 'ultrareview' = 'Run an ultra-deep code review' 'update' = 'Update Claude Code to the latest version' } $script:ClaudeSubcmdDesc = @{ 'auth.login' = 'Log in to Claude' 'auth.logout' = 'Log out of Claude' 'auth.status' = 'Show current authentication status' 'auth.help' = 'Show help for auth' 'auto-mode.config' = 'Configure auto mode' 'auto-mode.critique' = 'Run an auto-mode critique' 'auto-mode.defaults' = 'Show or reset auto-mode defaults' 'auto-mode.reset' = 'Reset auto mode configuration to the shipped defaults' 'auto-mode.help' = 'Show help for auto-mode' 'mcp.add' = 'Add an MCP server' 'mcp.add-json' = 'Add an MCP server from a JSON definition' 'mcp.add-from-claude-desktop' = 'Import MCP servers from Claude Desktop' 'mcp.get' = 'Show details for an MCP server' 'mcp.list' = 'List configured MCP servers' 'mcp.login' = 'Authenticate with an MCP server' 'mcp.logout' = 'Clear stored OAuth credentials for an MCP server' 'mcp.remove' = 'Remove an MCP server' 'mcp.reset-project-choices' = 'Reset per-project MCP trust choices' 'mcp.serve' = 'Run Claude Code as an MCP server' 'mcp.help' = 'Show help for mcp' 'plugin.configure' = 'Show a plugin''s options and which are unset, or save values from stdin' 'plugin.details' = 'Show details for a plugin' 'plugin.disable' = 'Disable a plugin' 'plugin.enable' = 'Enable a plugin' 'plugin.eval' = 'Run eval cases against a plugin and report scored results' 'plugin.init' = 'Scaffold a new plugin (alias: new)' 'plugin.install' = 'Install a plugin (alias: i)' 'plugin.list' = 'List plugins' 'plugin.marketplace' = 'Manage plugin marketplaces' 'plugin.prune' = 'Remove orphaned plugins (alias: autoremove)' 'plugin.tag' = 'Tag a plugin release' 'plugin.test' = 'Run a mod''s tests' 'plugin.uninstall' = 'Uninstall a plugin (alias: remove)' 'plugin.update' = 'Update a plugin' 'plugin.validate' = 'Validate a plugin or marketplace manifest, or a directory' 'plugin.help' = 'Show help for plugin' 'plugin.marketplace.add' = 'Add a marketplace source' 'plugin.marketplace.list' = 'List configured marketplaces' 'plugin.marketplace.remove' = 'Remove a marketplace (alias: rm)' 'plugin.marketplace.update' = 'Update marketplace metadata' 'plugin.marketplace.help' = 'Show help for marketplace' } $script:ClaudeFlagDesc = @{ # Global boolean flags '--allow-dangerously-skip-permissions' = '[boolean] Allow bypassing all permission checks' '--ax-screen-reader' = '[boolean] Render screen-reader friendly output' '--background' = '[boolean] Start the session in the background and print its id (--bg)' '--bg' = '[boolean] Start the session in the background and print its id (--background)' '--bare' = '[boolean] Minimal output mode' '--brief' = '[boolean] Brief output mode' '--chrome' = '[boolean] Use Chrome integration' '--continue' = '[boolean] Continue the most recent conversation' '--dangerously-skip-permissions' = '[boolean] Skip permission prompts (dangerous)' '--disable-slash-commands' = '[boolean] Disable slash commands' '--exclude-dynamic-system-prompt-sections' = '[boolean] Exclude dynamic system prompt sections' '--fork-session' = '[boolean] Fork into a new session' '--forward-subagent-text' = '[boolean] Forward subagent text and thinking blocks (stream-json only)' '--help' = '[boolean] Show help' '--ide' = '[boolean] Connect to an IDE' '--include-hook-events' = '[boolean] Include hook events in output' '--include-partial-messages' = '[boolean] Include partial assistant messages' '--no-chrome' = '[boolean] Disable Chrome integration' '--no-session-persistence' = '[boolean] Do not persist the session' '--print' = '[boolean] Print response and exit (-p)' '--replay-user-messages' = '[boolean] Replay user messages' '--safe-mode' = '[boolean] Start with all customizations disabled' '--strict-mcp-config' = '[boolean] Use only the specified MCP config' '--tmux' = '[boolean] Use tmux integration' '--verbose' = '[boolean] Verbose output' '--version' = '[boolean] Show version' # Optional-value flags '--cloud' = '[optional] Create a cloud session (description) or attach to one (session id or URL)' '--debug' = '[optional] Enable debug mode (optional filter)' '--resume' = '[optional] Resume a session (optional id)' '--teleport' = '[optional] Resume a teleport session (optional session id)' '--worktree' = '[optional] Use a git worktree (optional name)' '--remote-control' = '[optional] Enable remote control (optional name)' '--from-pr' = '[optional] Start from a PR (optional value)' '--prompt-suggestions' = '[optional] Prompt suggestions: true|false|...' # Global enum flags '--autocompact' = '[string] Auto-compact window size: auto, or 100k-1M tokens' '--effort' = '[string] Reasoning effort: low|medium|high|xhigh|max' '--permission-mode' = '[string] Permission mode: acceptEdits|auto|bypassPermissions|manual|dontAsk|plan' '--permission-prompts' = '[string] Who answers permission prompts with --print: host|none' '--output-format' = '[string] Output format: text|json|stream-json' '--input-format' = '[string] Input format: text|stream-json' '--system-prompt-snapshot' = '[string] Record the system prompt once per conversation: on|off' # Global string flags '--agent' = '[string] Agent to run' '--agents' = '[string] Agents definition (json)' '--append-system-prompt' = '[string] Append text to the system prompt' '--environment' = '[string] Self-hosted cloud environment id (ccpool_...)' '--fallback-model' = '[string] Fallback model(s), comma-separated, tried in order' '--json-schema' = '[string] JSON schema for structured output' '--name' = '[string] Session or run name (-n)' '--remote-control-session-name-prefix' = '[string] Remote-control session name prefix' '--session-id' = '[string] Session ID (uuid)' '--system-prompt' = '[string] Override the system prompt' '--model' = '[string] Model to use' # Numeric flags '--max-budget-usd' = '[number] Maximum budget in USD' # Array flags '--add-dir' = '[array] Additional working directory' '--allowedTools' = '[array] Allowed tools' '--allowed-tools' = '[array] Allowed tools' '--betas' = '[array] API beta features' '--disallowedTools' = '[array] Disallowed tools' '--disallowed-tools' = '[array] Disallowed tools' '--file' = '[array] Input file(s)' '--mcp-config' = '[array] MCP config file or JSON' '--plugin-dir' = '[array] Plugin directory' '--plugin-url' = '[array] Plugin URL' '--setting-sources' = '[array] Setting sources: user,project,local' '--settings' = '[array] Settings file or JSON' '--tools' = '[array] Tools definition' # Context-specific flags '--claudeai' = '[boolean] Use claude.ai login' '--console' = '[boolean] Use Anthropic Console login' '--email' = '[string] Email address for login' '--sso' = '[boolean] Use SSO login' '--json' = '[boolean] JSON output' '--text' = '[boolean] Text output' '--callback-port' = '[number] OAuth callback port' '--client-id' = '[string] OAuth client ID' '--client-secret' = '[boolean] Prompt for an OAuth client secret' '--env' = '[array] Environment variable: KEY=value' '--header' = '[array] HTTP header' '--scope' = '[string] Configuration scope' '--transport' = '[string] MCP transport: stdio|sse|http' '--all' = '[boolean] Apply to all' '--available' = '[boolean] Show available plugins' '--config' = '[string] Plugin config: key=value' '--dry-run' = '[boolean] Show what would happen without doing it' '--yes' = '[boolean] Assume yes to prompts' '--force' = '[boolean] Force the operation' '--message' = '[string] Tag message' '--push' = '[boolean] Push the tag' '--remote' = '[string] Git remote name' '--strict' = '[boolean] Strict validation' '--interactive' = '[boolean] Interactive mode' '--cwd' = '[string] Working directory' '--timeout' = '[number] Timeout in minutes' '--no-browser' = '[boolean] Print the authorization URL instead of opening a browser' '--post' = '[boolean] Post the findings to the PR' '--no-post' = '[boolean] Do not post the findings to the PR' '--restricted' = '[boolean] Restricted mode: remove the command/code-running tools and WebFetch (agents: start dispatched sessions restricted)' '--discard-unpushed' = '[string] <commit>@<worktree-id> to discard unpushed commits' '--force-remove-worktree' = '[string] <worktree-id> to delete even if removal failed' '--ablation' = '[string] Baseline arm: none|with-without' '--mocks' = '[string] MCP mock stand-ins: record|off' '--concurrency' = '[number] Concurrent agent runs (1-8, -j)' '--eval-dir' = '[string] Directory holding the eval cases' '--trust-plugin' = '[boolean] Skip the first-run trust prompt' '--author' = '[string] Author name' '--author-email' = '[string] Author email' '--description' = '[string] Manifest description' '--with' = '[array] Components to scaffold: skills, agents, hooks, mcp, ...' '--accept-command' = '[string] sha256 of the marketplace-declared command to accept' '--marketplace' = '[string] Marketplace source to install from: owner/repo, git/https URL, or path' '--registry' = '[string] npm registry URL for a <package>@npm install' '--keep-data' = '[boolean] Preserve the plugin''s persistent data directory' '--prune' = '[boolean] Also remove auto-installed dependencies no longer needed' '--data-size' = '[optional] Measure installed plugins'' data directories, or one plugin''s (requires --json)' '--values-stdin' = '[boolean] Read option values from stdin as a JSON object' '--sparse' = '[array] Limit checkout to these directories (git sparse-checkout)' } } #region -- Helpers ------------------------------------------------------------------------------ function New-ClaudeCompletion { param( [Parameter(Mandatory)] [string]$CompletionText, [string]$ListItemText = '', [string]$Tooltip = '', [System.Management.Automation.CompletionResultType] $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue ) if ([string]::IsNullOrEmpty($ListItemText)) { $ListItemText = $CompletionText } if ([string]::IsNullOrEmpty($Tooltip)) { $Tooltip = $ListItemText } [System.Management.Automation.CompletionResult]::new( $CompletionText, $ListItemText, $ResultType, $Tooltip ) } # Normalises a command/subcommand alias to its canonical name. function Resolve-ClaudeCmdAlias { param([string]$Token) $script:ClaudeCmdAliases[$Token] ?? $Token } function Resolve-ClaudeSubAlias { param([string]$Sub, [string]$Token) $map = $script:ClaudeSubAliases[$Sub] if ($map -and $map.ContainsKey($Token)) { return $map[$Token] } return $Token } function Resolve-ClaudeL3Alias { param([string]$Sub, [string]$SubSub, [string]$Token) $map = $script:ClaudeL3Aliases["$Sub.$SubSub"] if ($map -and $map.ContainsKey($Token)) { return $map[$Token] } return $Token } function Resolve-ClaudeFlagName { param( [string]$FlagName, [string]$Sub, [string]$SubSub ) foreach ($ctxKey in @( if ($Sub -and $SubSub) { "$Sub.$SubSub" } if ($Sub) { $Sub } )) { $ctxMap = $script:ClaudeContextShortAliases[$ctxKey] if ($ctxMap -and $ctxMap.ContainsKey($FlagName)) { return $ctxMap[$FlagName] } } $script:ClaudeShortAliases[$FlagName] ?? $FlagName } function Get-ClaudeShortAlias { param( [string]$FlagName, [string]$Sub, [string]$SubSub ) foreach ($ctxKey in @( if ($Sub -and $SubSub) { "$Sub.$SubSub" } if ($Sub) { $Sub } )) { $ctxReverse = $script:ClaudeContextAliasReverse[$ctxKey] if ($ctxReverse -and $ctxReverse.ContainsKey($FlagName)) { return $ctxReverse[$FlagName] } } return $script:ClaudeAliasReverse[$FlagName] } # Returns $true when the flag consumes the next token as its value. function Test-ClaudeFlagTakesValue { param( [string]$FlagName, [string]$Sub, [string]$SubSub, [string]$Sub3 ) $r = Resolve-ClaudeFlagName -FlagName $FlagName -Sub $Sub -SubSub $SubSub # Optional-value flags never consume the next token (conservative). if ($script:ClaudeOptionalValueFlags -contains $r) { return $false } # Global enum flags always take a value. if ($null -ne $script:ClaudeEnumFlags[$r]) { return $true } # Global value-taking flag categories. if ($script:ClaudeStringFlags -contains $r) { return $true } if ($script:ClaudeNumberFlags -contains $r) { return $true } if ($script:ClaudeArrayFlags -contains $r) { return $true } if ($script:ClaudeModelFlags -contains $r) { return $true } if ($script:ClaudePathFlags -contains $r) { return $true } if ($script:ClaudeDirFlags -contains $r) { return $true } # Global boolean flags never take a value. if ($script:ClaudeBoolFlags -contains $r) { return $false } # Context-specific enum flags. if ($Sub -and $SubSub) { $k2 = if ($Sub3) { "$Sub.$SubSub.$Sub3.$r" } else { "$Sub.$SubSub.$r" } if ($null -ne $script:ClaudeContextEnumFlags[$k2]) { return $true } } # Context-specific value-taking (string/number) flags. if ($script:ClaudeContextValueFlagSet.Contains($r)) { return $true } # Context-specific boolean flags. if ($script:ClaudeContextBoolFlagSet.Contains($r)) { return $false } return $false } # Returns the known enum choices for a flag in the given context, or $null. function Get-ClaudeEnumValues { param( [string]$FlagName, [string]$Sub, [string]$SubSub, [string]$Sub3 ) $r = Resolve-ClaudeFlagName -FlagName $FlagName -Sub $Sub -SubSub $SubSub # Global enum first. $vals = $script:ClaudeEnumFlags[$r] if ($vals) { return $vals } # L2 / L3 context. if ($Sub -and $SubSub) { $ctxKey = if ($Sub3) { "$Sub.$SubSub.$Sub3" } else { "$Sub.$SubSub" } $vals = $script:ClaudeContextEnumFlags["$ctxKey.$r"] if ($vals) { return $vals } } return $null } # Builds the full flag set for the current command context. function Get-ClaudeFlagSet { param([string]$Sub, [string]$SubSub, [string]$Sub3) $set = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::Ordinal ) # Root command: the full global flag surface. if (-not $Sub) { $script:ClaudeEnumFlags.Keys | ForEach-Object { $null = $set.Add($_) } $script:ClaudeBoolFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeOptionalValueFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeStringFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeNumberFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeArrayFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeModelFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudePathFlags | ForEach-Object { $null = $set.Add($_) } $script:ClaudeDirFlags | ForEach-Object { $null = $set.Add($_) } return $set } # Commander subcommands do not inherit root options: only the context # table for this exact command level plus --help applies. $null = $set.Add('--help') if ($SubSub) { $ctxKey = if ($Sub3) { "$Sub.$SubSub.$Sub3" } else { "$Sub.$SubSub" } $ctxFlags = $script:ClaudeContextFlags[$ctxKey] if ($ctxFlags) { $ctxFlags | ForEach-Object { $null = $set.Add($_) } } } else { $cmdFlags = $script:ClaudeCmdContextFlags[$Sub] if ($cmdFlags) { $cmdFlags | ForEach-Object { $null = $set.Add($_) } } } return $set } function Get-ClaudePositionalPlaceholder { param( [string]$ContextKey, [int]$PositionIndex ) $placeholders = $script:ClaudePositionalPlaceholders[$ContextKey] if (-not $placeholders) { return $null } if ($PositionIndex -lt $placeholders.Count) { return $placeholders[$PositionIndex] } return $placeholders[-1] } function Write-ClaudeLongFlagResults { param( [string]$WordToComplete, [string]$Sub, [string]$SubSub, [string]$Sub3 ) $flagSet = Get-ClaudeFlagSet -Sub $Sub -SubSub $SubSub -Sub3 $Sub3 foreach ($flag in $flagSet) { if ($flag -notlike ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { continue } $desc = $script:ClaudeFlagDesc[$flag] ?? '' $short = Get-ClaudeShortAlias -FlagName $flag -Sub $Sub -SubSub $SubSub $tip = if ($short) { "$flag (alias: $short): $desc" } else { "${flag}: $desc" } New-ClaudeCompletion $flag -ResultType ParameterName -Tooltip $tip } } # Emits value completion for a value-taking flag (enum / model / hint / path / number / generic). function Write-ClaudeFlagValue { param( [string]$Flag, [string]$WordToComplete, [string]$Sub, [string]$SubSub, [string]$Sub3, [string]$InlinePrefix = '' # e.g. "--flag=" for inline syntax, '' for space-separated ) $enumVals = Get-ClaudeEnumValues -FlagName $Flag -Sub $Sub -SubSub $SubSub -Sub3 $Sub3 if ($enumVals) { $enumVals | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { $tip = ($script:ClaudeFlagDesc[$Flag] ?? $_) if ($InlinePrefix) { New-ClaudeCompletion "$InlinePrefix$_" -ListItemText $_ -Tooltip $tip } else { New-ClaudeCompletion $_ -Tooltip $tip } } return } # Model hints. if ($script:ClaudeModelFlags -contains $Flag) { $script:ClaudeModelHints | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { if ($InlinePrefix) { New-ClaudeCompletion "$InlinePrefix$_" -ListItemText $_ -Tooltip "Model: $_" } else { New-ClaudeCompletion $_ -Tooltip "Model: $_" } } return } # Comma-separated hint flags. $hints = $script:ClaudeHintFlags[$Flag] if ($hints) { $hints | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { if ($InlinePrefix) { New-ClaudeCompletion "$InlinePrefix$_" -ListItemText $_ -Tooltip "Value: $_" } else { New-ClaudeCompletion $_ -Tooltip "Value: $_" } } return } # Path / dir completion. --add-dir takes directories only; --plugin-dir a directory or a .zip. if ($script:ClaudePathFlags -contains $Flag -or $script:ClaudeDirFlags -contains $Flag) { $directoriesOnly = $script:ClaudeDirFlags -contains $Flag $allowZip = $Flag -eq '--plugin-dir' [System.Management.Automation.CompletionCompleters]::CompleteFilename($WordToComplete) | Where-Object { (-not $directoriesOnly) -or ($_.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderContainer) -or ($allowZip -and $_.CompletionText -like '*.zip*') } | ForEach-Object { if ($InlinePrefix) { New-ClaudeCompletion "$InlinePrefix$($_.CompletionText)" ` -ListItemText $_.ListItemText ` -ResultType $_.ResultType ` -Tooltip $_.ToolTip } else { $_ } } return } # Number placeholder. if ($script:ClaudeNumberFlags -contains $Flag -or $script:ClaudeContextNumberFlagSet.Contains($Flag)) { if ('' -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { New-ClaudeCompletion '<n>' -Tooltip "Numeric value for $Flag" } return } # Generic placeholder — suppresses filesystem fallback for string/array flags. if ('' -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { New-ClaudeCompletion '<value>' -Tooltip "Value for $Flag" } } #endregion #region -- Completer scriptblock ---------------------------------------------------------------- function Complete-ClaudeNative { param( $CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameter ) Initialize-ClaudeCompleterData # ------------------------------------------------------------------------- # Detect the native ReadLine calling convention vs TabExpansion2. # # Native convention (ReadLine): # A) after trailing space: $CommandName='', $ParameterName='<full line>', # $WordToComplete='<cursor col>' # B) mid-token: $CommandName='<partial>', $ParameterName='<full line>', # $WordToComplete='<cursor col>' # # TabExpansion2 native path: # $CommandName='<wordToComplete>', $ParameterName=<CommandAst object>, # $WordToComplete='<cursorPosition int>' # # Heuristic: $WordToComplete is a pure integer AND $ParameterName coerces # to something that looks like a command line (starts with a non-space word). # ------------------------------------------------------------------------- $isNativeConvention = $WordToComplete -match '^\d+$' -and $ParameterName -match '^\s*\S+' # Line-absolute cursor offset (native convention only); $null otherwise. $cursorCol = $null # True when the cursor sits inside (or at the end of) the last element left of it: that # element is the word being completed, never an already-committed argument. $cursorInsideWord = $false if ($isNativeConvention) { $nativePartialWord = $CommandName $cursorCol = [int]$WordToComplete # $ParameterName is a CommandAst (TabExpansion2) or a string (ReadLine). if ($ParameterName -is [System.Management.Automation.Language.CommandAst]) { # TabExpansion2: we already have the CommandAst — reuse it directly. $CommandAst = $ParameterName } else { $tokens = $null $parseErrs = $null $parsedAst = [System.Management.Automation.Language.Parser]::ParseInput( [string]$ParameterName, [ref]$tokens, [ref]$parseErrs) if ($parsedAst.EndBlock.Statements.Count -gt 0) { $pipeline = $parsedAst.EndBlock.Statements[0] if ($pipeline -is [System.Management.Automation.Language.PipelineAst] -and $pipeline.PipelineElements.Count -gt 0 -and $pipeline.PipelineElements[0] -is [System.Management.Automation.Language.CommandAst]) { $CommandAst = $pipeline.PipelineElements[0] } } } if ($null -ne $CommandAst -and $CommandAst.CommandElements.Count -gt 1) { # Everything is derived from the text LEFT of the cursor. $cursorCol is # line-absolute while Extent.Text is command-relative, so rebase first. $line = $CommandAst.Extent.Text $relCursor = $cursorCol - $CommandAst.Extent.StartOffset $hasTrailingSpace = ($relCursor -le 0) -or ($relCursor -gt $line.Length) -or [char]::IsWhiteSpace($line[$relCursor - 1]) $cursorInsideWord = -not $hasTrailingSpace if ($hasTrailingSpace) { $WordToComplete = '' } elseif (-not [string]::IsNullOrEmpty($nativePartialWord)) { $WordToComplete = $nativePartialWord } else { $pfx = $line.Substring(0, $relCursor) $spc = $pfx.LastIndexOf(' ') $WordToComplete = if ($spc -ge 0) { $pfx.Substring($spc + 1) } else { '' } } } else { $WordToComplete = '' } } if ($null -eq $WordToComplete) { $WordToComplete = '' } if ($null -eq $CommandAst) { return } $allElements = @($CommandAst.CommandElements) if ($null -ne $cursorCol) { # Ignore every element that starts at or after the cursor. $allElements = @($allElements | Where-Object { $_.Extent.StartOffset -lt $cursorCol }) } if ($allElements.Count -eq 0) { return } # ------------------------------------------------------------------------- # Build the committed argument list. Use Extent.Text for all nodes to stay # safe under StrictMode (CommandParameterAst may lack a .Value property). # ------------------------------------------------------------------------- function Get-ClaudeTokenText { param($el) $el.Extent.Text } $allArgs = @(foreach ($el in ($allElements | Select-Object -Skip 1)) { Get-ClaudeTokenText -el $el }) # The word being completed is never a committed argument, flag or not: keeping a # value-taking flag such as '--effort' in the list made the walk expect its value # and the flag name itself could no longer be completed. Once the user types the # space after it, the flag is left of the cursor and becomes committed normally. $committedArgs = $allArgs if ($allArgs.Count -gt 0) { $lastIsCurrentWord = if ($null -ne $cursorCol) { $cursorInsideWord } else { $allArgs[-1] -eq $WordToComplete } if ($lastIsCurrentWord) { $committedArgs = @($allArgs | Select-Object -First ($allArgs.Count - 1)) } } # ------------------------------------------------------------------------- # State machine: walk committed args to determine context. # Tracks up to 3 command levels plus expectingValue / currentFlag. # Command aliases are normalised as they are matched. # ------------------------------------------------------------------------- $sub = $null # e.g. 'mcp' $subsub = $null # e.g. 'add' $sub3 = $null # e.g. 'add' under plugin.marketplace $expectingValue = $false $currentFlag = $null $positionalCount = 0 # non-flag positionals seen after sub/subsub/sub3 foreach ($token in $committedArgs) { if ($expectingValue) { $expectingValue = $false $currentFlag = $null continue } if ($token -like '-*') { if ($token -like '*=*') { continue } # inline --flag=value; value already consumed if (Test-ClaudeFlagTakesValue -FlagName $token -Sub $sub -SubSub $subsub -Sub3 $sub3) { $expectingValue = $true $currentFlag = Resolve-ClaudeFlagName -FlagName $token -Sub $sub -SubSub $subsub } continue } # Positional token — try to match command levels first. $matched = $false if ($null -eq $sub) { $canon = Resolve-ClaudeCmdAlias -Token $token if ($script:ClaudeTopCommands -contains $canon) { $sub = $canon $matched = $true } } elseif ($null -eq $subsub) { $subs = $script:ClaudeSubSubcommands[$sub] $canon = Resolve-ClaudeSubAlias -Sub $sub -Token $token if ($subs -and $subs -contains $canon) { $subsub = $canon $matched = $true } } elseif ($null -eq $sub3) { $l3k = "$sub.$subsub" $l3cs = $script:ClaudeL3Subcommands[$l3k] $canon = Resolve-ClaudeL3Alias -Sub $sub -SubSub $subsub -Token $token if ($l3cs -and $l3cs -contains $canon) { $sub3 = $canon $matched = $true } } if (-not $matched) { $positionalCount++ } } # ========================================================================= # 1. Inline --flag=value completion # ========================================================================= if ($WordToComplete -like '*=*') { $eqIdx = $WordToComplete.IndexOf('=') $flagPart = $WordToComplete.Substring(0, $eqIdx) $valPfx = $WordToComplete.Substring($eqIdx + 1) $resolved = Resolve-ClaudeFlagName -FlagName $flagPart -Sub $sub -SubSub $subsub Write-ClaudeFlagValue -Flag $resolved -WordToComplete $valPfx ` -Sub $sub -SubSub $subsub -Sub3 $sub3 -InlinePrefix "$flagPart=" return } # ========================================================================= # 2. Value completion after space-separated value-taking flag # ========================================================================= if ($expectingValue) { Write-ClaudeFlagValue -Flag $currentFlag -WordToComplete $WordToComplete ` -Sub $sub -SubSub $subsub -Sub3 $sub3 return } # ========================================================================= # 3. Flag completion (word starts with - or --) # ========================================================================= if ($WordToComplete -like '-*') { $flagSet = Get-ClaudeFlagSet -Sub $sub -SubSub $subsub -Sub3 $sub3 $isShort = $WordToComplete -notlike '--*' foreach ($flag in $flagSet) { $desc = $script:ClaudeFlagDesc[$flag] ?? '' $short = Get-ClaudeShortAlias -FlagName $flag -Sub $sub -SubSub $subsub if ($isShort) { if (-not $short) { continue } if ($short -notlike ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { continue } New-ClaudeCompletion $short ` -ResultType ParameterName ` -Tooltip "$short -> ${flag}: $desc" } else { if ($flag -notlike ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { continue } $tip = if ($short) { "$flag (alias: $short): $desc" } else { "${flag}: $desc" } New-ClaudeCompletion $flag -ResultType ParameterName -Tooltip $tip } } return } # ========================================================================= # 4. Subcommand / positional completion # ========================================================================= # --- 4a. Have sub + subsub → offer L3 subcommands (or positional values). --- if ($sub -and $subsub -and $null -eq $sub3) { $l3k = "$sub.$subsub" $l3cs = $script:ClaudeL3Subcommands[$l3k] if ($l3cs) { $l3cs | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { $tip = $script:ClaudeSubcmdDesc["$sub.$subsub.$_"] ?? $_ New-ClaudeCompletion $_ -Tooltip $tip } return } # No L3 commands → offer positional enum / path if applicable. $emitted = $false $posVals = $script:ClaudePositionalEnums[$l3k] if ($posVals) { $posVals | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { New-ClaudeCompletion $_ -Tooltip "Positional: $_" $emitted = $true } } if ($script:ClaudePathPositionalL2.Contains($l3k) -and $positionalCount -eq 0) { $directoriesOnly = $script:ClaudeDirPositionalL2.Contains($l3k) [System.Management.Automation.CompletionCompleters]::CompleteFilename($WordToComplete) | Where-Object { -not $directoriesOnly -or $_.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderContainer } $emitted = $true } $placeholder = Get-ClaudePositionalPlaceholder -ContextKey $l3k -PositionIndex $positionalCount if ($placeholder -and $placeholder -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { New-ClaudeCompletion $placeholder -Tooltip "Positional value for $l3k" $emitted = $true } if ([string]::IsNullOrEmpty($WordToComplete)) { Write-ClaudeLongFlagResults -WordToComplete '' -Sub $sub -SubSub $subsub return } if ($emitted) { return } return } # --- 4b. Have sub + subsub + sub3 → positional values only. --- if ($sub -and $subsub -and $sub3) { $posKey = "$sub.$subsub.$sub3" $emitted = $false $posVals = $script:ClaudePositionalEnums[$posKey] if ($posVals) { $posVals | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { New-ClaudeCompletion $_ -Tooltip "Positional: $_" $emitted = $true } } $placeholder = Get-ClaudePositionalPlaceholder -ContextKey $posKey -PositionIndex $positionalCount if ($placeholder -and $placeholder -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { New-ClaudeCompletion $placeholder -Tooltip "Positional value for $posKey" $emitted = $true } if ([string]::IsNullOrEmpty($WordToComplete)) { Write-ClaudeLongFlagResults -WordToComplete '' -Sub $sub -SubSub $subsub -Sub3 $sub3 return } if ($emitted) { return } return } # --- 4c. Have sub only → offer L2 subcommands (or positional / cmd flags). --- if ($sub -and $null -eq $subsub) { $subs = $script:ClaudeSubSubcommands[$sub] if ($subs -and $subs.Count -gt 0) { $subs | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { $tip = $script:ClaudeSubcmdDesc["$sub.$_"] ?? $_ New-ClaudeCompletion $_ -Tooltip $tip } return } # Command with no L2 subcommands → positional enum / path / placeholder. $emitted = $false $posVals = $script:ClaudePositionalEnums[$sub] if ($posVals) { $posVals | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { New-ClaudeCompletion $_ -Tooltip "Positional: $_" $emitted = $true } } if ($script:ClaudePathPositionalL2.Contains($sub) -and $positionalCount -eq 0) { $directoriesOnly = $script:ClaudeDirPositionalL2.Contains($sub) [System.Management.Automation.CompletionCompleters]::CompleteFilename($WordToComplete) | Where-Object { -not $directoriesOnly -or $_.ResultType -eq [System.Management.Automation.CompletionResultType]::ProviderContainer } $emitted = $true } $placeholder = Get-ClaudePositionalPlaceholder -ContextKey $sub -PositionIndex $positionalCount if ($placeholder -and $placeholder -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*')) { New-ClaudeCompletion $placeholder -Tooltip "Positional value for $sub" $emitted = $true } if ([string]::IsNullOrEmpty($WordToComplete)) { Write-ClaudeLongFlagResults -WordToComplete '' -Sub $sub -SubSub $subsub return } if ($emitted) { return } return } # --- 4d. Top level → offer L1 subcommands. --- $script:ClaudeTopCommands | Where-Object { $_ -like ([System.Management.Automation.WildcardPattern]::Escape($WordToComplete) + '*') } | ForEach-Object { $tip = $script:ClaudeCmdDesc[$_] ?? $_ New-ClaudeCompletion $_ -Tooltip $tip } } #endregion #region -- Registration ------------------------------------------------------------------------- Register-ArgumentCompleter -CommandName @('claude', 'claude.exe') -Native -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) Complete-ClaudeNative -CommandName $commandName -ParameterName $parameterName -WordToComplete $wordToComplete -CommandAst $commandAst -FakeBoundParameter $fakeBoundParameter } #endregion |