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

@@ -186,7 +186,9 @@ async function extractTree(): Promise<CmdInfo> {
const CANONICAL_RESOURCES = [
'servers', 'instances', 'secrets', 'secretbackends', 'llms', 'agents', 'personalities', 'templates', 'projects',
'users', 'groups', 'rbac', 'prompts', 'promptrequests',
'serverattachments', 'proxymodels', 'inference-tasks', 'all',
'serverattachments', 'proxymodels', 'inference-tasks',
// Agentic teams
'epics', 'epictasks', 'tasks', 'repos', 'gitproviders', 'all',
];
const ALIAS_ENTRIES: [string, string][] = [
@@ -206,10 +208,18 @@ const ALIAS_ENTRIES: [string, string][] = [
['promptrequest', 'promptrequests'], ['promptrequests', 'promptrequests'], ['pr', 'promptrequests'],
['serverattachment', 'serverattachments'], ['serverattachments', 'serverattachments'], ['sa', 'serverattachments'],
['proxymodel', 'proxymodels'], ['proxymodels', 'proxymodels'], ['pm', 'proxymodels'],
// v5: inference-task queue. Short forms (`task`, `tasks`) are what
// operators actually type — `mcpctl get tasks`.
['task', 'inference-tasks'], ['tasks', 'inference-tasks'],
// v5 shipped the inference queue under the short `task`/`tasks` aliases.
// Agentic-teams work items own those now (see shared.ts); the queue keeps
// only its explicit spellings plus an `itask` shorthand.
['inference-task', 'inference-tasks'], ['inference-tasks', 'inference-tasks'],
['itask', 'inference-tasks'], ['itasks', 'inference-tasks'],
// Agentic teams
['epic', 'epics'], ['epics', 'epics'],
['epictask', 'epictasks'], ['epictasks', 'epictasks'], ['et', 'epictasks'],
['task', 'tasks'], ['tasks', 'tasks'],
['repo', 'repos'], ['repos', 'repos'], ['repository', 'repos'], ['repositories', 'repos'],
['gitprovider', 'gitproviders'], ['gitproviders', 'gitproviders'],
['forge', 'gitproviders'], ['gp', 'gitproviders'],
['all', 'all'],
];