Metadata-Version: 2.5
Name: maiactl
Version: 0.1.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.

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

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

Every command takes `--json` for programmatic use, and `-p CODE` to act on
a project other than the selected one.

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

## 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 |
| 5 | conflict; refetch and retry |
