feat(cli): make the agentic-teams stack usable from the terminal

Everything so far was API-only. This adds the CLI surface and proves the whole
vertical works end to end against a real mcpd and a real Gitea.

  mcpctl create gitprovider mysources --url https://mysources.co.uk \
      --secret gitea-token --key TOKEN
  mcpctl repos discover mysources --query mcp --project demo
  mcpctl repos import   mysources --project demo michal/mcpctl
  mcpctl create epic ship-agents --project demo --content "..." \
      --criterion "cli=CLI can drive a task" --criterion "q:feel=reads at a glance"
  mcpctl create epictask foundation --epic ship-agents
  mcpctl create task wire-cli --epictask foundation --title "Wire the CLI"
  mcpctl tasks advance wire-cli in_progress
  mcpctl tasks verdict wire-cli --stage qa_review --commit 3f2ab19 --pass --summary "..."
  mcpctl tasks accept  wire-cli --note "ship it"
  mcpctl tasks history wire-cli

`repos` and `tasks` are their own verb groups rather than more create/edit
flags. Discovery is inherently two-step — look at what is there, then pick — and
accept/reject are the human gate, not field writes.

`get` gained columns for all five resources. The Epic row shows completeness as
"40% (2/5)" and CodeRepo shows SOURCE/FROM, so imported repos say where they
came from at a glance.

BREAKING: `mcpctl get tasks` now lists WORK ITEMS. The inference queue keeps
`inference-task(s)` and gains `itask(s)`; the short `task`/`tasks` aliases move
to work items. This completes the split already made in RBAC (a186d95) — having
the CLI and RBAC disagree about what "tasks" means would be worse than the
rename.

Verified end to end, not just unit tested: mcpd run locally against the test
database, admin bootstrapped, then the full flow driven through the built CLI.
Discovery returned the real repositories from https://mysources.co.uk, import
created the CodeRepo with provenance, re-discovery marked it `imported`, and the
task walked todo → in_progress → building → self_testing → qa_review with a
FAILING QA verdict correctly refusing `done`, then bounced back, passed on a
second commit, and merged through the accept gate. `tasks history` shows both
verdicts and all twelve transitions.

Two bugs the walkthrough caught that unit tests had not: `get repos` and
`get epics` crashed on `padEnd` because Prisma includes hand back `{ name }`
rather than a bare string, and the completions guard test failed because the
resource lists had changed without regenerating.

Local: build clean, workspace 2440 passed, db 128 passed (same 4 pre-existing
Secret failures), lint errors unchanged at the repo's pre-existing 794 (+2
no-console warnings, the same shape every other command registration uses).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4wNHWf7xSwnZCWpJcyv9p
This commit is contained in:
Michal
2026-08-08 17:00:25 +01:00
parent b70300d389
commit 772c25f605
10 changed files with 658 additions and 31 deletions

View File

@@ -5,11 +5,11 @@ _mcpctl() {
local cur prev words cword
_init_completion || return
local commands="status login logout config get describe delete logs create edit apply chat chat-llm patch passwd errors backup approve review skills console cache provider test migrate rotate"
local commands="status login logout config repos tasks get describe delete logs create edit apply chat chat-llm patch passwd errors backup approve review skills console cache provider test migrate rotate"
local project_commands="get describe delete logs create edit attach-server detach-server favourites"
local global_opts="-v --version --daemon-url --direct -p --project -h --help"
local resources="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks all"
local resource_aliases="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks all server srv instance inst secret sec secretbackend sb llm agent personality template tpl project proj user group rbac-definition rbac-binding prompt promptrequest pr serverattachment sa proxymodel pm task tasks inference-task"
local resources="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks epics epictasks tasks repos gitproviders all"
local resource_aliases="servers instances secrets secretbackends llms agents personalities templates projects users groups rbac prompts promptrequests serverattachments proxymodels inference-tasks epics epictasks tasks repos gitproviders all server srv instance inst secret sec secretbackend sb llm agent personality template tpl project proj user group rbac-definition rbac-binding prompt promptrequest pr serverattachment sa proxymodel pm inference-task itask itasks epic epictask et task repo repository repositories gitprovider forge gp"
# Check if --project/-p was given
local has_project=false
@@ -136,6 +136,51 @@ _mcpctl() {
esac
fi
return ;;
repos)
local repos_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$repos_sub" ]]; then
COMPREPLY=($(compgen -W "discover import help" -- "$cur"))
else
case "$repos_sub" in
discover)
COMPREPLY=($(compgen -W "-q --query --project --limit -o --output -h --help" -- "$cur"))
;;
import)
COMPREPLY=($(compgen -W "--project --ssh -h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
tasks)
local tasks_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$tasks_sub" ]]; then
COMPREPLY=($(compgen -W "advance verdict accept reject history help" -- "$cur"))
else
case "$tasks_sub" in
advance)
COMPREPLY=($(compgen -W "--note -h --help" -- "$cur"))
;;
verdict)
COMPREPLY=($(compgen -W "--stage --commit --summary --pass --fail -h --help" -- "$cur"))
;;
accept)
COMPREPLY=($(compgen -W "--note -h --help" -- "$cur"))
;;
reject)
COMPREPLY=($(compgen -W "--note -h --help" -- "$cur"))
;;
history)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "-h --help" -- "$cur"))
;;
esac
fi
return ;;
get)
if [[ -z "$resource_type" ]]; then
COMPREPLY=($(compgen -W "$resources -o --output -p --project -A --all -h --help" -- "$cur"))
@@ -175,9 +220,24 @@ _mcpctl() {
create)
local create_sub=$(_mcpctl_get_subcmd $subcmd_pos)
if [[ -z "$create_sub" ]]; then
COMPREPLY=($(compgen -W "server secret llm agent secretbackend project user group rbac mcptoken prompt skill personality serverattachment promptrequest help" -- "$cur"))
COMPREPLY=($(compgen -W "gitprovider epic epictask task repo server secret llm agent secretbackend project user group rbac mcptoken prompt skill personality serverattachment promptrequest help" -- "$cur"))
else
case "$create_sub" in
gitprovider)
COMPREPLY=($(compgen -W "--url --type --description --secret --key --skip-probe -h --help" -- "$cur"))
;;
epic)
COMPREPLY=($(compgen -W "--project --content --content-file --description --criterion -h --help" -- "$cur"))
;;
epictask)
COMPREPLY=($(compgen -W "--epic --project --description --depends-on -h --help" -- "$cur"))
;;
task)
COMPREPLY=($(compgen -W "--epictask --title --description --risk --repo -h --help" -- "$cur"))
;;
repo)
COMPREPLY=($(compgen -W "--project --url --branch --path --secret --key -h --help" -- "$cur"))
;;
server)
COMPREPLY=($(compgen -W "-d --description --package-name --runtime --docker-image --transport --repository-url --external-url --command --container-port --replicas --env --from-template --env-from-secret --force -h --help" -- "$cur"))
;;