Metadata-Version: 2.5
Name: maiactl
Version: 0.6.2
Summary: Drive the M.AI.A board from a terminal (and from terminal agents).
Requires-Python: >=3.10
Requires-Dist: httpx>=0.28.1
Description-Content-Type: text/markdown

# 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

```bash
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 status --full           # everything: version vs PyPI, accounts, workspaces, projects
maia doctor                  # if anything feels off
maia upgrade                 # newest release, via whatever installed you (uv / pipx / pip)
uv tool upgrade maiactl      # the standard path works too
```

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.

Works on macOS (Apple Silicon and Intel), Linux and Windows — it's a
pure-Python package. On Windows the config lives in `%APPDATA%\maia\config.json`,
colour is enabled in the legacy console automatically, and output never
fails on a character your code page can't show.

`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.

```bash
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:

```bash
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

```bash
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:

```bash
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

```bash
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 history MVP-42           # the ticket's story (alias: maia log)
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.

`maia history` shows who has had the ticket (the chain of custody, with how
and when it changed hands) and a timeline of everything that happened —
created, moved between stages, renamed, handed off, blocked/unblocked,
filed under a story, commented — each with who did it and from which
client. `--json` gives the same as `{custody, timeline}`.

## Writing

```bash
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`. A
screenshot someone dropped in chat becomes the ticket's screenshot with no
download and re-upload. Media that's already on another ticket isn't moved:
the new ticket gets its own copy (its own bytes, a few seconds later), so the
same image can be on any number of tickets and nothing done to the original
later can reach into a ticket.

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

```bash
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:

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

```powershell
Invoke-Expression (maia auth env | Out-String)   # Windows; --shell cmd for cmd.exe
```

## 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;
`maia upgrade --check` exits 1 when a newer release exists.
