Skip to main content

maia

The M.AI.A board from a terminal, for people and for the coding agents they run there.

It talks to the same public HTTP API the web app uses, so it inherits the server's rules rather than restating them: any active project member can move, edit, assign or reassign any ticket, and observers are read-only.

Install

uv tool install maiactl      # from PyPI (the package is maiactl; the command is maia)
maia login                   # opens your browser, then asks which workspace/project
maia                         # where am I, what next
maia doctor                  # if anything feels off

Bare maia is a status card (offline): who you are, which workspace and project, and what to try. maia doctor checks version, server, token, workspace and project, and says how to fix each. The server check probes the API itself (an unauthenticated /api/v1/auth/me must answer JSON), not /health, which the web host answers on the backend's behalf.

login runs the same brokered PKCE flow the desktop app uses: it opens Google in your real browser (always showing the account chooser, so a profile's current session is never reused silently) and polls until the backend releases a token, then finishes setting you up: if you're in several workspaces it asks which, and if that workspace has several projects it asks which of those. One of each is picked for you without a question. In a script or an agent shell (no TTY) there's no prompt; pass --tenant and then maia use <code>. The token is written to ~/.config/maia/config.json with mode 0600.

Several accounts at once

Like gh, you can be signed into more than one account and switch between them. An account is (email, server), so the same person on prod and on a local backend are two accounts — which is the point.

maia login                       # adds an account; never evicts an existing one
maia auth list                   # all of them, active one marked with *
maia auth switch priya           # by email, or any unique fragment
maia auth logout --account priya # sign out of just that one
maia auth logout --all

Each account remembers its own workspace and project, so switching restores where you were. For a single command, -a (--as) acts as another account without switching, and nothing that command does is saved:

maia -a priya@cydratech.com ls --mine
maia ls --mine -a priya          # flags may come before or after the command

-a can't be combined with login, logout or auth switch; those manage saved accounts, so name the account directly (logout --account priya).

Workspaces

maia workspaces              # the ones this account belongs to
maia workspace "Cydra Tech"  # switch

Switching workspace clears the selected project, because a project belongs to exactly one workspace — keeping it would point at something the account can no longer see. Pick a new one with maia projects and maia use.

It is a normal app session token, not a scoped API key, so it carries your full access and expires on the server's schedule. Treat the file as a credential. maia logout removes it.

For CI or a throwaway shell, skip the file entirely:

export MAIA_TOKEN=...        MAIA_TENANT_ID=...
export MAIA_PROJECT=MVP      MAIA_API_URL=https://maia.cydratech.com

Anything set this way applies to the current run only and is never written to disk: a CI job can't persist its credentials, and MAIA_PROJECT=X maia use Y won't change your saved project. Commands that would normally save say (not saved: …) on stderr. MAIA_ACCOUNT=priya selects a saved account for one run; a name that matches nothing is an error, not a silent fallback. With MAIA_TOKEN set, logout and auth switch refuse, since there is no saved session in play.

Reading

maia ls                       # active tickets, doing first (alias: maia tickets)
maia status                   # same as bare `maia`
maia ls --mine --status doing
maia ls --stage "In QA"
maia ls -s payment            # title/description search
maia show MVP-42              # detail + comments
maia show MVP-42 --history
maia board                    # stages and card counts
maia members                  # who's on it, and their roles
maia stories
maia files MVP-42 --download ./tmp
maia media                    # the project's shared media (chat + tickets), by handle
maia media -s "invoice"       # search Maia's descriptions and filenames
maia media --download image-42 --to ./tmp

Every photo, video or PDF shared anywhere in the project has a handle like image-42, the same one you see in chat. maia media lists them with Maia's one-line description of each.

Tickets are addressed by key (MVP-42), bare number (42), or a title fragment. A fragment matching more than one ticket is an error, never a guess.

Writing

maia new "Payment webhook retries" -d "Stripe retries land as duplicates." \
  --assign priya --stage "In Progress" --story "Billing"

git log -1 --format=%B | maia new "Ship the retry fix" -d -

maia mv MVP-42 doing          # a status…
maia mv MVP-42 "In QA"        # …or a stage by name
maia assign MVP-42 priya
maia story MVP-42 "Billing"   # file under an existing story
maia comment MVP-42 "Deployed to staging." --mention priya
maia comment MVP-42 - < notes.md
maia attach MVP-42 screenshot.png trace.pdf
maia attach MVP-42 --media image-42      # put EXISTING project media on the ticket
maia new "Webhook fix" -d "see the screenshot" --media image-42
maia drop MVP-42 --reason "superseded by MVP-51"   # project admins only

--media <handle> works on new, attach, comment and edit: the same asset is reused, not copied, so a screenshot someone dropped in chat becomes the ticket's screenshot with no download and re-upload. An asset already pinned to another ticket or comment stays where it is; the CLI says so.

Every command takes --json for programmatic use, and -p CODE to act on a project other than the selected one for that command. Both may come before or after the subcommand. Progress notes ("uploaded x.png") go to stderr, so --json stdout is always a single parseable document.

What it deliberately doesn't do

  • No stage (column) create, rename or delete. Board structure is admin-curated in the app.
  • No story create, edit or delete. Same reason. You can file a ticket into an existing story with maia story.
  • No ticket delete. The API has none. maia drop sets the reversible dropped status, which is what "scrap that" means on this board; maia mv <key> next puts it back. Dropping is project-admin only from the CLI (the app lets any member), and maia mv <key> dropped is held to the same rule.

For agents

maia agent-guide                     # a cheat-sheet written for an LLM
maia agent-guide --write CLAUDE.md   # drop it into the repo (idempotent; re-run to refresh)

The guide tells the agent to use --json, never to run login itself, how tickets are addressed, the exit codes, and which rules the CLI enforces. With --json on, errors are JSON on stderr too ({"error", "hint", "exit_code"}), so failures parse as cleanly as successes.

An agent on your machine shares your saved login. For a CI job or a shell on another box, hand it your identity for one run:

eval "$(maia auth env)"        # exports MAIA_API_URL, MAIA_TOKEN, MAIA_TENANT_ID, MAIA_PROJECT
MAIA_TOKEN=$(maia auth token)  # or just the token

Exit codes

code meaning
0 fine
1 generic failure
2 not authenticated, or nothing selected
3 forbidden (e.g. you're an observer)
4 not found (a ticket, member, story, stage, project or workspace)
5 conflict; refetch and retry

maia doctor exits 1 when any check fails, so a script can gate on it.

Metadata

Release files for maiactl 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for maiactl 0.5.2
File Size Uploaded
maiactl-0.5.2.tar.gz 84.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for maiactl 0.5.2
File Interpreter ABI Platform
maiactl-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 132.8 kB

Release files / maiactl-0.5.2.tar.gz

Download URL maiactl-0.5.2.tar.gz
Size 84.8 kB
Tags Source
SHA-256 checksum
How to use checksums
9d6771cbfce25c7f34d51db2f1506a3d96b927c34b1f1e3cf66ddb1c0838b1b5
BLAKE2b-256 checksum
How to use checksums
60e525ea02876f215fb97ddfd9f44ceb915efc81e51e70d2a2b68062f11b5fda
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.5

Release files / maiactl-0.5.2-py3-none-any.whl

Download URL maiactl-0.5.2-py3-none-any.whl
Size 48.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c93a349e0749e4a2bc97668bb6f480bef4b7aebbcbcdba5c26ef60a7017f513f
BLAKE2b-256 checksum
How to use checksums
66555334b629b389f3dfe85b1cac2e93c96a3814f2800d565fd7a5607e2e64ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.5

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page