Authorized GraphQL CLI for Takt — full GraphQL surface from the shell, stdlib-only core
Project description
taktcli
Authorized GraphQL CLI for Takt — the full Takt GraphQL surface from the shell, for agents and humans. Everything the MCP server does, and more.
The core is stdlib-only (urllib + argparse + json): a thin
argv → GraphQL-over-HTTP client. Because arguments arrive as argv strings, it
structurally sidesteps the MCP client-serialization bug class (arrays-as-JSON-
strings, double-encoded results, the empty-string-drops-all-args bug) — an argv
string simply can't be double-JSON-encoded.
The package and console-script are both named taktcli (bare takt is taken on
PyPI).
Install
pip install taktcli # stdlib-only core
pip install 'taktcli[watch]' # + websockets, enables `taktcli watch`
pip install -e cli/ # from a monorepo checkout
Maestro/harness images ship pip install 'taktcli[watch]' — taktcli watch is
the supported replacement for the retired tools/wait_for_task.py.
Quickstart
taktcli login # device-auth, stores ~/.takt/credentials.json
taktcli whoami # nickname + claims + active credential source
taktcli board --project takt # show the board
taktcli claim takt-42 # READY → RUNNING, prints leaseId
taktcli run takt-42 # heartbeat the lease
taktcli done takt-42 # mark DONE
taktcli --help (or taktcli help) lists every command; taktcli <noun> --help
lists a group's verbs.
Auth
Token resolution, simplest-wins:
TAKT_TOKENenv → used verbatim. This is what harness containers are stuffed with; env always wins over any stored credential.- Else
~/.takt/credentials.json— plain JSON{ "token": "<jwt>", "url": "..." }, mode0600, written bytaktcli login. - Else error:
No Takt credentials found. Run: taktcli login(exit3).
URL resolution mirrors it: TAKT_URL env → url in the credentials file →
default https://api.takt.sh/graphql. The watch WS endpoint is derived by
swapping the scheme (https → wss).
taktcli login runs the OAuth 2.0 device-authorization grant: it prints a
verification URL + user code, polls until you approve in a browser, then persists
the token. logout removes the stored credential; whoami prints who you are
and which credential source is active.
taktcli login # default endpoint
taktcli login --url http://localhost:8454/graphql # override the endpoint
taktcli logout
taktcli whoami
Auth is the same JWT + claims used by MCP and GraphQL — taktcli adds no
privilege surface. There is no claims/permission verb anywhere; admin ops are
reachable via gql only if the caller's token already holds the admin claim.
Long-lived bot tokens stay minted out-of-band (tools/jwt_cli.py) and dropped
into TAKT_TOKEN; login is for humans and dev machines.
Passthrough — reaches 100% of GraphQL
taktcli gql + taktcli schema are a complete superset of every MCP tool: any
GraphQL op MCP never wrapped is reachable here.
taktcli gql -q 'query { me { nickname claims } }'
taktcli gql -f query.graphql --var id=takt-42 --field-file description=desc.md
taktcli schema --sdl # SDL introspection (--json for JSON)
Big markdown flows through --field-file NAME=PATH (NAME=- = stdin) — this
sidesteps shell quoting and the empty-string-args bug class.
Shared I/O flags
--var KEY=VALUE(repeatable; value parsed as JSON when valid, else string)--vars JSON/--vars-file PATH(-= stdin) — bulk variables--field-file NAME=PATH(NAME=-= stdin) — inject a file's contents as a variable; the canonical path for large markdown--raw— full{data, errors, extensions}envelope (default is pretty.data)--compact— single-line JSON
Variable precedence (lowest → highest):
--vars-file < --vars < --var < --field-file.
Command surface
The CLI uses a positional namespace — taktcli <noun> <verb> [args] — so the
entity is always the first token. This fixes the MCP create_wiki (workspace) vs
wiki_create (page) collision at the CLI layer. Run taktcli <noun> --help for a
group's verbs.
Every MCP tool maps to a verb:
| MCP tool | taktcli verb |
|---|---|
get_task |
get <id> / task get <id> |
list_tasks |
task list |
search_tasks |
task search <q> |
get_board |
board |
create_task |
task create |
update_task |
task update <id> |
update_status |
status <id> <STATUS> (or run / done / fail) |
claim_task |
claim <id> |
release_claim |
release <id> |
force_release_claim |
gql (admin claim required) |
transition_task |
task transition <id> <action> |
get_dependencies |
task deps <id> |
add_comment |
task comment <id> |
get_task_comments |
task comments <id> |
attach_result |
task result <id> |
get_task_results |
task results <id> |
delete_result |
task result-delete <id> |
wiki_read |
wiki read <path> |
wiki_search / wiki_semantic_search |
wiki search <q> (--semantic) |
wiki_grep |
wiki grep <pattern> |
wiki_create |
wiki create <path> |
wiki_update |
wiki update <path> |
wiki_edit |
wiki edit <path> |
wiki_move |
wiki move <path> <new-path> |
wiki_verify |
wiki verify <path> |
wiki_delete |
wiki delete <path> |
wiki_bulk_update_status |
wiki bulk-status <folder> <status> |
find_related_wiki_pages |
wiki related <task-id> |
list_wiki_feedback |
wiki feedback list |
resolve_wiki_feedback |
wiki feedback resolve <id> |
wiki_list |
workspace list |
create_wiki |
workspace create |
get_project_wiki |
workspace get <project> |
request_upload_url_and_headers + confirm_upload |
file upload <path> (composite) |
get_file_url |
file url <id> |
list_files |
file list |
attach_file_to_task |
file attach <id> <task-id> |
detach_file_from_task |
file detach <id> <task-id> |
attach_file_to_wiki |
file attach-wiki <id> <path> |
update_label / delete_label |
label update <name> / label delete <name> |
| (none — CLI-only) | label create |
list_notifications |
notif list |
mark_notification_read |
notif read <id> |
mark_all_notifications_read |
notif read-all |
unread_notification_count |
notif count |
| (none — CLI-only) | project {list,get,create,rename,member-add,member-remove} |
taskStatusChanged / taskChanged / resultAdded / commentAdded |
watch (needs [watch] extra) |
Notes:
label createis CLI-only (the MCP server never wrappedcreateLabel, but the GraphQL mutation exists).label update/deleteare keyed by name and resolve the id client-side via thelabelsquery.- The
wiki/workspaceverbs take a global--wiki SLUG(default$TAKT_WIKI) and stream big markdown through--content-file/--old-file/--new-file(-= stdin). project renameis name-only — slug changes are blocked on the backend.
Output & exit codes
Default output is pretty JSON of the unwrapped .data payload. --compact =
single-line; --raw = full {data, errors, extensions} envelope; watch =
NDJSON (one event per line, flushed per event).
| Code | Meaning |
|---|---|
0 |
success |
1 |
GraphQL error (server reached; errors[] non-empty) |
2 |
transport error (DNS/TCP/TLS/HTTP non-2xx/timeout/WS drop) |
3 |
config or usage error (no token, bad URL, unknown verb, missing flag) |
extensions.code (e.g. LEASE_EXPIRED, ALREADY_CLAIMED) is preserved in error
output so loops can branch on it.
Task monitoring — taktcli watch
Replaces the retired tools/wait_for_task.py. Streams subscription events as
NDJSON and reconnects with exponential backoff on drops (e.g. deploys). Needs the
[watch] extra.
taktcli watch --project takt --timeout 3600 # status changes, any task
taktcli watch --task takt-42 --task takt-43 # specific tasks
taktcli watch --project takt --event resultAdded # other subscriptions
--event defaults to taskStatusChanged. Unlike the old one-shot script,
watch is a continuous stream — read NDJSON lines as they arrive and act on the
first matching one; no restart-per-event. Tasks already READY when the watcher
starts are not detected — check taktcli board --project takt first.
MCP coexistence
taktcli and the MCP server run side-by-side over the same GraphQL API,
authenticating identically (JWT, same claims). Use either or both in one session.
The MCP freeze/removal timeline lives in PRD 002 §8.
Documentation
- Usage reference:
takt/context/taktcliin the Takt wiki. - Design & rationale:
takt/dev/prd-002-taktcli(PRD 002). - Contract placement:
takt/contract§4.3 — the CLI as a first-class integration interface alongside GraphQL + MCP.
Release
Push a cli-v* tag (e.g. cli-v0.1.0) — .github/workflows/cli.yaml builds
cli/ and OIDC trusted-publishes to PyPI. The tag must match the version in
cli/pyproject.toml (CI asserts this). The CLI has its own CI job alongside
backend/plugin/frontend (npm run test:cli).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file taktcli-0.4.0.tar.gz.
File metadata
- Download URL: taktcli-0.4.0.tar.gz
- Upload date:
- Size: 66.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5c78ca160c209c19fabe95e9f7313c6d8e24b7c1927381ef9a2d9d1ac1b7d34
|
|
| MD5 |
5e6fd2873630cad532bbfc6267a16d8c
|
|
| BLAKE2b-256 |
97afc13fd20801f53b5bf34605b2420fed0fe81616babd30bb521c376be317e5
|
Provenance
The following attestation bundles were made for taktcli-0.4.0.tar.gz:
Publisher:
cli.yaml on muzhig/takt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
taktcli-0.4.0.tar.gz -
Subject digest:
c5c78ca160c209c19fabe95e9f7313c6d8e24b7c1927381ef9a2d9d1ac1b7d34 - Sigstore transparency entry: 2191758092
- Sigstore integration time:
-
Permalink:
muzhig/takt@aa55f8a1ab73530f72c6d3f81fb32be223bd0d99 -
Branch / Tag:
refs/tags/cli-v0.4.0 - Owner: https://github.com/muzhig
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cli.yaml@aa55f8a1ab73530f72c6d3f81fb32be223bd0d99 -
Trigger Event:
push
-
Statement type:
File details
Details for the file taktcli-0.4.0-py3-none-any.whl.
File metadata
- Download URL: taktcli-0.4.0-py3-none-any.whl
- Upload date:
- Size: 50.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f52666ed878eadeb31eb79eb06d671ccb8bf900e9d4c906c7a3137a7ae944b53
|
|
| MD5 |
9d4607a7837e0e09fcf2a40ce9e6aba5
|
|
| BLAKE2b-256 |
97ba9607e2dc047af6a0a27e1e11e05cb9a97a58c9f5673c58dcf349788b2a93
|
Provenance
The following attestation bundles were made for taktcli-0.4.0-py3-none-any.whl:
Publisher:
cli.yaml on muzhig/takt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
taktcli-0.4.0-py3-none-any.whl -
Subject digest:
f52666ed878eadeb31eb79eb06d671ccb8bf900e9d4c906c7a3137a7ae944b53 - Sigstore transparency entry: 2191758128
- Sigstore integration time:
-
Permalink:
muzhig/takt@aa55f8a1ab73530f72c6d3f81fb32be223bd0d99 -
Branch / Tag:
refs/tags/cli-v0.4.0 - Owner: https://github.com/muzhig
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cli.yaml@aa55f8a1ab73530f72c6d3f81fb32be223bd0d99 -
Trigger Event:
push
-
Statement type: