# zone command spec DSL

Each service exports an array of spec objects from `src/specs/<service>.js`. The
generator (`src/core/commandSpec.js`) turns each into a real
`zone <service> <group> <name> [args]` command wired to the API engine (auth,
token refresh, context injection, --json/--toon, exit codes are automatic).

## Spec object fields

```js
{
  group: 'invoice',        // command group under the service ('' allowed for a direct leaf)
  name: 'get',             // subcommand; omit for a direct leaf named after `group`
  args: ['id'],            // positional args; use ['name?'] style '[x]' for optional
  method: 'GET',           // GET|POST|PUT|PATCH|DELETE
  path: '/invoices/:id',   // relative to the service base; :arg fills from args, then ctx
  query: [                 // repeatable flags → query params
    ['-s, --status <s>', 'status'],
    ['--page <n>', 'page']
  ],
  body: 'json',            // 'json' adds --data (inline|@file|-), 'form' adds --form, else none
  bodyTemplate: {          // OR build a JSON body declaratively (mode-based APIs)
    mode: 'markAsRead',
    messageId: '$csv1',    // $1..$N positional, $csvN positional split on commas,
    to: '$opt.to'          // $opt.key option value, $opt.key[] option value split on commas
  },
  options: [['-c, --color <hex>', 'desc']], // extra flags used by bodyTemplate/handlers
  desc: 'Get invoice by id',
  stateful: false,         // true → runs a handler instead of a REST call
  handler: 'crm.bulkRead', // handler id (see list); resolved from HANDLERS map
  notes: 'shown in help'   // optional
}
```

## Rules

- Path is RELATIVE to the service base (do not include the base URL).
- `:placeholder` in a path fills from a matching positional arg by name; if not an
  arg, it falls back to stored context (`zone ctx <svc> key=value`). Use ctx for
  org/portal/account ids the user shouldn't retype (e.g. `:accountId`, `:zsoid`).
- Auto-injected context (registry `inject:'query'|'header'`) must NOT be added to
  `query` (e.g. Books/Inventory `organization_id`, Desk `orgId`, Social headers).
- Do NOT add per-service `login/logout/status/token/skill/llm` — zone provides
  those globally. Map any `use <id>` / `config` to `zone ctx <svc>`.
- Every entity should get the full set it supports: list, get, create, update,
  delete, plus service-specific actions (send/void/approve/search/count/...).
- Prefer typed query flags over making the user pass raw params. Add pagination
  flags where the API supports them.
- `--out <file>` is added automatically to every REST command (for downloads).
- For anything that isn't a single REST call (multipart upload, multi-step flow,
  a non-base host, local file IO), set `stateful:true` + a `handler` id and a
  `notes` field describing the exact flow. Do not fake it as a REST row.

Mirror the style of `books.js` / `crm.js` in this folder.
