Merge pull request 'feat: context-aware completions with dynamic resource names' (#28) from feat/completions-project-scope-dynamic into main
This commit is contained in:
@@ -3,12 +3,65 @@ _mcpctl() {
|
|||||||
_init_completion || return
|
_init_completion || return
|
||||||
|
|
||||||
local commands="status login logout config get describe delete logs create edit apply backup restore help"
|
local commands="status login logout config get describe delete logs create edit apply backup restore help"
|
||||||
local global_opts="-v --version --daemon-url --direct -h --help"
|
local project_commands="attach-server detach-server get describe delete logs create edit help"
|
||||||
|
local global_opts="-v --version --daemon-url --direct --project -h --help"
|
||||||
local resources="servers instances secrets templates projects users groups rbac"
|
local resources="servers instances secrets templates projects users groups rbac"
|
||||||
|
|
||||||
case "${words[1]}" in
|
# Check if --project was given
|
||||||
|
local has_project=false
|
||||||
|
local i
|
||||||
|
for ((i=1; i < cword; i++)); do
|
||||||
|
if [[ "${words[i]}" == "--project" ]]; then
|
||||||
|
has_project=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Find the first subcommand (skip --project and its argument, skip flags)
|
||||||
|
local subcmd=""
|
||||||
|
local subcmd_pos=0
|
||||||
|
for ((i=1; i < cword; i++)); do
|
||||||
|
if [[ "${words[i]}" == "--project" || "${words[i]}" == "--daemon-url" ]]; then
|
||||||
|
((i++)) # skip the argument
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "${words[i]}" != -* ]]; then
|
||||||
|
subcmd="${words[i]}"
|
||||||
|
subcmd_pos=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Find the resource type after get/describe/delete/edit
|
||||||
|
local resource_type=""
|
||||||
|
if [[ -n "$subcmd_pos" ]] && [[ $subcmd_pos -gt 0 ]]; then
|
||||||
|
for ((i=subcmd_pos+1; i < cword; i++)); do
|
||||||
|
if [[ "${words[i]}" != -* ]] && [[ " $resources " == *" ${words[i]} "* ]]; then
|
||||||
|
resource_type="${words[i]}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If completing the --project value
|
||||||
|
if [[ "$prev" == "--project" ]]; then
|
||||||
|
local names
|
||||||
|
names=$(mcpctl get projects -o json 2>/dev/null | grep -oP '"name":\s*"\K[^"]+')
|
||||||
|
COMPREPLY=($(compgen -W "$names" -- "$cur"))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch resource names dynamically
|
||||||
|
_mcpctl_resource_names() {
|
||||||
|
local rt="$1"
|
||||||
|
if [[ -n "$rt" ]]; then
|
||||||
|
mcpctl get "$rt" -o json 2>/dev/null | grep -oP '"name":\s*"\K[^"]+'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$subcmd" in
|
||||||
config)
|
config)
|
||||||
if [[ $cword -eq 2 ]]; then
|
if [[ $((cword - subcmd_pos)) -eq 1 ]]; then
|
||||||
COMPREPLY=($(compgen -W "view set path reset claude-generate impersonate help" -- "$cur"))
|
COMPREPLY=($(compgen -W "view set path reset claude-generate impersonate help" -- "$cur"))
|
||||||
fi
|
fi
|
||||||
return ;;
|
return ;;
|
||||||
@@ -20,35 +73,29 @@ _mcpctl() {
|
|||||||
return ;;
|
return ;;
|
||||||
logout)
|
logout)
|
||||||
return ;;
|
return ;;
|
||||||
get)
|
get|describe|delete)
|
||||||
if [[ $cword -eq 2 ]]; then
|
if [[ -z "$resource_type" ]]; then
|
||||||
COMPREPLY=($(compgen -W "$resources" -- "$cur"))
|
COMPREPLY=($(compgen -W "$resources" -- "$cur"))
|
||||||
else
|
else
|
||||||
COMPREPLY=($(compgen -W "-o --output -h --help" -- "$cur"))
|
local names
|
||||||
fi
|
names=$(_mcpctl_resource_names "$resource_type")
|
||||||
return ;;
|
COMPREPLY=($(compgen -W "$names -o --output -h --help" -- "$cur"))
|
||||||
describe)
|
|
||||||
if [[ $cword -eq 2 ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "$resources" -- "$cur"))
|
|
||||||
else
|
|
||||||
COMPREPLY=($(compgen -W "-o --output --show-values -h --help" -- "$cur"))
|
|
||||||
fi
|
|
||||||
return ;;
|
|
||||||
delete)
|
|
||||||
if [[ $cword -eq 2 ]]; then
|
|
||||||
COMPREPLY=($(compgen -W "$resources" -- "$cur"))
|
|
||||||
fi
|
fi
|
||||||
return ;;
|
return ;;
|
||||||
edit)
|
edit)
|
||||||
if [[ $cword -eq 2 ]]; then
|
if [[ -z "$resource_type" ]]; then
|
||||||
COMPREPLY=($(compgen -W "servers projects" -- "$cur"))
|
COMPREPLY=($(compgen -W "servers projects" -- "$cur"))
|
||||||
|
else
|
||||||
|
local names
|
||||||
|
names=$(_mcpctl_resource_names "$resource_type")
|
||||||
|
COMPREPLY=($(compgen -W "$names -h --help" -- "$cur"))
|
||||||
fi
|
fi
|
||||||
return ;;
|
return ;;
|
||||||
logs)
|
logs)
|
||||||
COMPREPLY=($(compgen -W "--tail --since -f --follow -h --help" -- "$cur"))
|
COMPREPLY=($(compgen -W "--tail --since -f --follow -h --help" -- "$cur"))
|
||||||
return ;;
|
return ;;
|
||||||
create)
|
create)
|
||||||
if [[ $cword -eq 2 ]]; then
|
if [[ $((cword - subcmd_pos)) -eq 1 ]]; then
|
||||||
COMPREPLY=($(compgen -W "server secret project user group rbac help" -- "$cur"))
|
COMPREPLY=($(compgen -W "server secret project user group rbac help" -- "$cur"))
|
||||||
fi
|
fi
|
||||||
return ;;
|
return ;;
|
||||||
@@ -61,14 +108,24 @@ _mcpctl() {
|
|||||||
restore)
|
restore)
|
||||||
COMPREPLY=($(compgen -W "-i --input -p --password -c --conflict -h --help" -- "$cur"))
|
COMPREPLY=($(compgen -W "-i --input -p --password -c --conflict -h --help" -- "$cur"))
|
||||||
return ;;
|
return ;;
|
||||||
|
attach-server|detach-server)
|
||||||
|
local names
|
||||||
|
names=$(_mcpctl_resource_names "servers")
|
||||||
|
COMPREPLY=($(compgen -W "$names" -- "$cur"))
|
||||||
|
return ;;
|
||||||
help)
|
help)
|
||||||
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
||||||
return ;;
|
return ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [[ $cword -eq 1 ]]; then
|
# No subcommand yet — offer commands based on context
|
||||||
|
if [[ -z "$subcmd" ]]; then
|
||||||
|
if $has_project; then
|
||||||
|
COMPREPLY=($(compgen -W "$project_commands $global_opts" -- "$cur"))
|
||||||
|
else
|
||||||
COMPREPLY=($(compgen -W "$commands $global_opts" -- "$cur"))
|
COMPREPLY=($(compgen -W "$commands $global_opts" -- "$cur"))
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
complete -F _mcpctl mcpctl
|
complete -F _mcpctl mcpctl
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# mcpctl fish completions
|
# mcpctl fish completions
|
||||||
|
|
||||||
set -l commands status login logout config get describe delete logs create edit apply backup restore help
|
set -l commands status login logout config get describe delete logs create edit apply backup restore help
|
||||||
|
set -l project_commands attach-server detach-server get describe delete logs create edit help
|
||||||
|
|
||||||
# Disable file completions by default
|
# Disable file completions by default
|
||||||
complete -c mcpctl -f
|
complete -c mcpctl -f
|
||||||
@@ -9,30 +10,112 @@ complete -c mcpctl -f
|
|||||||
complete -c mcpctl -s v -l version -d 'Show version'
|
complete -c mcpctl -s v -l version -d 'Show version'
|
||||||
complete -c mcpctl -l daemon-url -d 'mcplocal daemon URL' -x
|
complete -c mcpctl -l daemon-url -d 'mcplocal daemon URL' -x
|
||||||
complete -c mcpctl -l direct -d 'Bypass mcplocal, connect directly to mcpd'
|
complete -c mcpctl -l direct -d 'Bypass mcplocal, connect directly to mcpd'
|
||||||
|
complete -c mcpctl -l project -d 'Target project context' -x
|
||||||
complete -c mcpctl -s h -l help -d 'Show help'
|
complete -c mcpctl -s h -l help -d 'Show help'
|
||||||
|
|
||||||
# Top-level commands
|
# Helper: check if --project was given
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a status -d 'Show status and connectivity'
|
function __mcpctl_has_project
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a login -d 'Authenticate with mcpd'
|
set -l tokens (commandline -opc)
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a logout -d 'Log out'
|
for i in (seq (count $tokens))
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a config -d 'Manage configuration'
|
if test "$tokens[$i]" = "--project"
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a get -d 'List resources'
|
return 0
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a describe -d 'Show resource details'
|
end
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a delete -d 'Delete a resource'
|
end
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a logs -d 'Get instance logs'
|
return 1
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a create -d 'Create a resource'
|
end
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a edit -d 'Edit a resource'
|
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a apply -d 'Apply configuration from file'
|
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a backup -d 'Backup configuration'
|
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a restore -d 'Restore from backup'
|
|
||||||
complete -c mcpctl -n "not __fish_seen_subcommand_from $commands" -a help -d 'Show help'
|
|
||||||
|
|
||||||
# Resource types for get/describe/delete/edit
|
# Helper: check if a resource type has been selected after get/describe/delete/edit
|
||||||
set -l resources servers instances secrets templates projects users groups rbac
|
set -l resources servers instances secrets templates projects users groups rbac
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from get describe delete" -a "$resources" -d 'Resource type'
|
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from edit" -a 'servers projects' -d 'Resource type'
|
|
||||||
|
|
||||||
# get/describe/delete options
|
function __mcpctl_needs_resource_type
|
||||||
|
set -l tokens (commandline -opc)
|
||||||
|
set -l found_cmd false
|
||||||
|
for tok in $tokens
|
||||||
|
if $found_cmd
|
||||||
|
# Check if next token after get/describe/delete/edit is a resource type
|
||||||
|
if contains -- $tok servers instances secrets templates projects users groups rbac
|
||||||
|
return 1 # resource type already present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if contains -- $tok get describe delete edit
|
||||||
|
set found_cmd true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if $found_cmd
|
||||||
|
return 0 # command found but no resource type yet
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function __mcpctl_get_resource_type
|
||||||
|
set -l tokens (commandline -opc)
|
||||||
|
set -l found_cmd false
|
||||||
|
for tok in $tokens
|
||||||
|
if $found_cmd
|
||||||
|
if contains -- $tok servers instances secrets templates projects users groups rbac
|
||||||
|
echo $tok
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if contains -- $tok get describe delete edit
|
||||||
|
set found_cmd true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch resource names dynamically from the API
|
||||||
|
function __mcpctl_resource_names
|
||||||
|
set -l resource (__mcpctl_get_resource_type)
|
||||||
|
if test -z "$resource"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
# Use mcpctl to fetch names (quick JSON parse with string manipulation)
|
||||||
|
mcpctl get $resource -o json 2>/dev/null | string match -rg '"name":\s*"([^"]+)"'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch project names for --project value
|
||||||
|
function __mcpctl_project_names
|
||||||
|
mcpctl get projects -o json 2>/dev/null | string match -rg '"name":\s*"([^"]+)"'
|
||||||
|
end
|
||||||
|
|
||||||
|
# --project value completion
|
||||||
|
complete -c mcpctl -l project -xa '(__mcpctl_project_names)'
|
||||||
|
|
||||||
|
# Top-level commands (without --project)
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a status -d 'Show status and connectivity'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a login -d 'Authenticate with mcpd'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a logout -d 'Log out'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a config -d 'Manage configuration'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a get -d 'List resources'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a describe -d 'Show resource details'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a delete -d 'Delete a resource'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a logs -d 'Get instance logs'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a create -d 'Create a resource'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a edit -d 'Edit a resource'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a apply -d 'Apply configuration from file'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a backup -d 'Backup configuration'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a restore -d 'Restore from backup'
|
||||||
|
complete -c mcpctl -n "not __mcpctl_has_project; and not __fish_seen_subcommand_from $commands" -a help -d 'Show help'
|
||||||
|
|
||||||
|
# Project-scoped commands (with --project)
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a attach-server -d 'Attach a server to the project'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a detach-server -d 'Detach a server from the project'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a get -d 'List resources (scoped to project)'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a describe -d 'Show resource details'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a delete -d 'Delete a resource'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a logs -d 'Get instance logs'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a create -d 'Create a resource'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a edit -d 'Edit a resource'
|
||||||
|
complete -c mcpctl -n "__mcpctl_has_project; and not __fish_seen_subcommand_from $project_commands" -a help -d 'Show help'
|
||||||
|
|
||||||
|
# Resource types — only when resource type not yet selected
|
||||||
|
complete -c mcpctl -n "__fish_seen_subcommand_from get describe delete; and __mcpctl_needs_resource_type" -a "$resources" -d 'Resource type'
|
||||||
|
complete -c mcpctl -n "__fish_seen_subcommand_from edit; and __mcpctl_needs_resource_type" -a 'servers projects' -d 'Resource type'
|
||||||
|
|
||||||
|
# Resource names — after resource type is selected
|
||||||
|
complete -c mcpctl -n "__fish_seen_subcommand_from get describe delete edit; and not __mcpctl_needs_resource_type" -a '(__mcpctl_resource_names)' -d 'Resource name'
|
||||||
|
|
||||||
|
# get/describe options
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from get" -s o -l output -d 'Output format' -xa 'table json yaml'
|
complete -c mcpctl -n "__fish_seen_subcommand_from get" -s o -l output -d 'Output format' -xa 'table json yaml'
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from describe" -s o -l output -d 'Output format' -xa 'detail json yaml'
|
complete -c mcpctl -n "__fish_seen_subcommand_from describe" -s o -l output -d 'Output format' -xa 'detail json yaml'
|
||||||
complete -c mcpctl -n "__fish_seen_subcommand_from describe" -l show-values -d 'Show secret values'
|
complete -c mcpctl -n "__fish_seen_subcommand_from describe" -l show-values -d 'Show secret values'
|
||||||
|
|||||||
@@ -148,8 +148,8 @@ export function createProgram(): Command {
|
|||||||
log: (...args: string[]) => console.log(...args),
|
log: (...args: string[]) => console.log(...args),
|
||||||
getProject: () => program.opts().project as string | undefined,
|
getProject: () => program.opts().project as string | undefined,
|
||||||
};
|
};
|
||||||
program.addCommand(createAttachServerCommand(projectOpsDeps));
|
program.addCommand(createAttachServerCommand(projectOpsDeps), { hidden: true });
|
||||||
program.addCommand(createDetachServerCommand(projectOpsDeps));
|
program.addCommand(createDetachServerCommand(projectOpsDeps), { hidden: true });
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user