Metadata-Version: 2.5
Name: maiactl
Version: 0.3.0
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 --from ./cli maiactl    # from a checkout
maia login                               # opens your browser
maia projects
maia use MVP
```

`login` runs the same brokered PKCE flow the desktop app uses: it opens
Google in your real browser and polls until the backend releases a token.
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
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
```

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

```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 drop MVP-42 --reason "superseded by MVP-51"   # project admins only
```

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

Point your terminal agent at `maia --help` and the per-command
`maia <command> --help`; both are written to be read once and remembered.
Two habits worth putting in a project's `CLAUDE.md`:

- Prefer `--json` when parsing, plain output when showing a human.
- Narrow before listing. `maia ls` caps at 40 rows by default; on a big
  board use `--mine`, `--stage`, or `-s` rather than `--limit 0`.

## 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 |
