Manda

The same reference the CLI prints with manda skill. Install it into a harness with one line, or save SKILL.md yourself.

Manda CLI Reference

Manda is the ledger of what customers reported and what they have been told. Reports arrive through the hosted widget on a product's own pages; staff and coding agents work them through this CLI. One command per staff action, JSON on stdout when piped, no runtime dependencies. This skill is the operational reference: assume you have shell access and never need --help.

npx -y @aiaiai-pt/manda-cli@latest <command>   # no install
npm i -g @aiaiai-pt/manda-cli                   # then: manda <command>

Requires Node.js 22 or later.

Conceptual model

Object What it is Lifecycle
Project one product surface (a site, an app); owns reports and topics created by an admin in the web UI
Report (submission) one thing a customer said: bug, feature or other, with a message and an optional reporter email new -> acknowledged -> resolved (and back via reopen)
Update an entry on a report's timeline: an internal note or a customer-facing comment, optionally closing (resolved) or reopening (open) the report append-only
Topic a roadmap item that groups reports by customer intent; has a public view on the portal when published considering -> planned -> in_progress -> available; not_planned from any of the first three
Announcement a public progress note on a topic append-only
Agent token a bearer token minted by an org admin that acts as that admin on the agent lane; every write records the agent name beside the owner's email revocable; dies with the owner

Priority is the topic order on the roadmap, never a column on the report. manda next derives it; nothing on a report says "P1".

Authentication

An org admin mints a token at https://manda.aiaiai.pt/admin/org (section Agents, button ADD AGENT). The secret (mn_agent_...) is shown once. It is its owner: same organisation, same checks, same assignee rules.

export MANDA_TOKEN=mn_agent_...
export MANDA_URL=https://manda.aiaiai.pt   # optional, this is the default
manda whoami                               # email, domain, agent name

Or save it once:

manda auth login --token mn_agent_...      # --url to point at another server
manda auth status                          # prints the credential source and verifies it

Credentials live in ${XDG_CONFIG_HOME:-~/.config}/manda/config.json, mode 0600. Precedence: --url/--token flags, then MANDA_TOKEN/MANDA_URL, then the file. manda skill and manda instructions need no credentials and no server.

Global options and output

Option Effect
--json JSON on stdout even on a TTY
--url <url> server, overrides env and file
--token <token> token, overrides env and file
--help usage, anywhere on the line

Exit codes

Code Meaning
0 Success
1 Usage error: missing or unknown flag, unknown command, stdin used for two flags
2 Auth: 401, 403, or no credentials configured
3 Conflict: 409, the revision you wrote against is stale
4 Not found: 404
5 Server or network error: 400, 415, 429, 5xx, no connection

Branch on the code, not on the message.

The revision rule

Every write (assign, ack, resolve, reopen, link, comment, every topic mutation) reads the resource first to learn its current revision, unless you pass --revision N. On 409 the CLI does not retry: it refetches, prints the current state under error.current, and exits 3. Re-read, decide again, write again. Someone else changed it, and that is information, not noise.

manda report comment always sends a request_id (a random UUID, or --request-id <uuid>), so a retried comment is idempotent.

Commands

Identity and projects

Command Does
manda whoami the admin this token acts for: email, domain, agent.name
manda projects projects in the organisation: id, name
manda skill prints this file
manda watch [--since <cursor>] [--project <id>] [--interval <s>] [--once] change feed, see below
manda instructions prints a short CLAUDE.md / AGENTS.md block describing the loop below
manda auth login --token <t> [--url <u>] saves credentials
manda auth status credential source, server, and a live identity check

Reading the inbox

manda inbox                                   # every report, newest first
manda inbox --project <id> --state new        # filters: --state new|acknowledged|resolved
manda inbox --assignee me                     # --assignee me|unassigned|<email>
manda inbox --q "login"                       # text search; --page N --limit N
manda next                                    # what to work on, in priority order
manda next --project <id>
manda report show <id>                        # the report and its timeline (updates)
manda report screenshot <id> --out shot.png   # the customer's screenshot, when screenshot_key is set

manda next lists open reports you own or nobody owns: owned first, then new before acknowledged, then by the roadmap order of the linked topic (unlinked last), then oldest first. Take the first one. An unowned report you are working on sinks below fresh reports, so assign it to yourself.

JSON shapes: inbox returns { submissions: [...], total }; next returns { items: [...], email } where each item carries assignee_email and topics: [{ title }]; report show returns { submission: { id, type, status, message, revision, ... }, updates: [...] }.

Waking on changes

manda watch                                   # from now on, one event per line, polls every 15 s
manda watch --since "$cursor" --project <id>  # resume from a cursor; --interval <seconds>
manda watch --once --json                     # one poll, then a final {"type":"cursor","cursor":...} line

Events: new reports, report updates (a customer reply carries author_kind: customer), topic updates and announcements, as JSON objects when piped. Without --since it starts from now. The cursor is opaque; keep the last one and pass it back to resume without gaps. A bare YYYY-MM-DD HH:MM:SS (UTC) is also accepted as a starting point.

Working a report

manda report capture --project <id> --type bug|feature|other --message "<text>" \
  [--name "<reporter>"] [--email <reporter email>] [--source-label "<where it came from>"]
manda report assign <id> --to me               # --to <email> | me | none (unassign)
manda report ack <id>                          # new -> acknowledged: review has started
manda report comment <id> --body "<text>"      # internal note (default --audience internal)
manda report comment <id> --audience customer --outcome resolved --body "<what changed, in their words>"
manda report comment <id> --audience customer --outcome open --body "<why it is back open>"
manda report resolve <id> --resolution "<text>"
manda report reopen <id>
manda report link <id> --work-url <url>        # the PR, issue or commit that carries the fix
manda report link <id> --none                  # clear it

--audience customer is the only thing the reporter sees. A report that asked for follow-through gets that comment by mail. Never resolve a report without a customer-facing explanation; resolve alone closes it silently.

Topics: the roadmap

manda topic list [--project <id>]
manda topic show <id>
manda topic create --project <id> --title "<customer intent>" [--description "<text>"]
manda topic transition <id> plan|start|complete|decline|reconsider|reopen [--body "<text>"]
manda topic note <id> --body "<internal note>"
manda topic edit <id> --title "<text>" --description "<text>"
manda topic link-report <id> --report <report id>
manda topic unlink-report <id> --report <report id>
manda topic link-work <id> --tracker github --issue <number> --issue-url <url>
manda topic unlink-work <id> --tracker github --issue <number>
manda topic reorder --project <id> <topic id> <topic id> ...   # the full order, first is top

The topic state machine, the only place a topic's state changes:

Event From To
plan considering planned
start planned in_progress
complete in_progress available
decline considering, planned, in_progress not_planned (--body with the reason is required)
reconsider not_planned considering
reopen available in_progress

An event from a state not in its from column is refused by the server (exit 5 with the message); read manda topic show <id> first when in doubt.

The public portal

manda topic publish <id> [--public-title "<text>"] [--public-summary "<text>"]
manda topic edit-public <id> --public-title "<text>" --public-summary "<text>"
manda topic unpublish <id>
manda topic announce <id> --body "<progress, for customers>"
manda topic unannounce <id> --announcement <announcement id>

Public wording is separate from the internal title and description: write it for the customer, not for the team.

The loop

Use Manda as the coordination layer while working in a codebase, alone or with a person.

  1. manda next --json and take the first item.
  2. manda report show <id> --json; when it has a screenshot_key, manda report screenshot <id> --out shot.png and look at it before reasoning about a layout or widget report. Then manda report assign <id> --to me and manda report ack <id>.
  3. Group it: manda topic list --json to find the topic it belongs to, then manda topic link-report <topic> --report <id>; or create one (manda topic create --project <project> --title "<customer intent>"), transition <topic> plan, then start when you begin.
  4. Do the work in the repository. Put Manda-Report: <id> and Manda-Topic: <topic> as commit trailers, and manda topic link-work <topic> --tracker github --issue <pr number> --issue-url <pr url> once a PR exists. manda report link <id> --work-url <pr url> ties the report to it too.
  5. Tell the customer only when it is true, and "true" means you exercised their path on the deployed version, not the artefact: load the page, call the endpoint, watch it work. A green suite, a merged PR or a file read back are not that. Then manda report comment <id> --audience customer --outcome resolved --body "<what changed, in their words>".
  6. manda topic transition <topic> complete when the change is available to customers; manda topic announce <topic> --body "<progress>" for public progress along the way.

Rules: read before you write (every write checks the revision). Do not reprioritise by editing reports; reorder topics (manda topic reorder) with the person who owns the roadmap. Exit 3 means someone else changed it: re-read and decide again.

Patterns

# Triage everything new in one project
for id in $(manda inbox --project "$P" --state new --json | jq -r '.submissions[].id'); do
  manda report ack "$id"
  manda report comment "$id" --body "Triaged by automation." --audience internal
done

# Group a report under a new topic and plan it
topic=$(manda topic create --project "$P" --title "Widget loses the draft on a network error" --json | jq -r .id)
manda topic link-report "$topic" --report "$id"
manda topic transition "$topic" plan

# Wake on changes: a customer reply on a resolved report reopens it
manda watch --since "$cursor" | while read -r event; do
  if [ "$(jq -r .author_kind <<<"$event")" = customer ]; then
    manda report reopen "$(jq -r .report_id <<<"$event")"
  fi
done

# A long customer-facing comment from a file
manda report comment "$id" --audience customer --outcome resolved --body - < reply.md

# Idempotent retry of a comment after a flaky network
manda report comment "$id" --body "Shipped in 0.4.1." --request-id 2f1e5c3a-0b9d-4b7a-9e2c-1d8f6a4b3c21

# Decide again after a conflict
manda report resolve "$id" --resolution "Fixed" || {
  [ $? -eq 3 ] && manda report show "$id" --json
}

Troubleshooting

Symptom Cause Fix
No credentials configured (exit 2) no MANDA_TOKEN, no config file, no --token set MANDA_TOKEN or manda auth login --token ...
401 or 403 (exit 2) token revoked, owner left the organisation, or the wrong server manda auth status; mint a new token at /admin/org
exit 3 stale revision the error's current field is the fresh state; decide again
exit 4 wrong id, or an object in another organisation manda inbox / manda topic list to find it
Unknown option (exit 1) a flag this command does not take, or a typo the flags above are the whole set; stdin - feeds one flag only
transition refused (exit 5) event not allowed from the current state manda topic show <id>, then the table above
no table, only JSON stdout is a pipe that is the contract; --json makes it explicit

Conventions