kelta CLI — Agent Guide

How an AI agent should drive the CLI: discovery, auth precedence, output contract, exit codes and gotchas.

CLI for the Kelta platform (multi-tenant, metadata-driven). Everything here is machine-friendly by design: JSON output when piped, stable error codes, a discoverable command catalog.

Discovery#

  • kelta manifest — full machine-readable catalog: every command with a JSON Schema of its input, dangerous/auth flags, positionals, options. Start here.
  • kelta manifest --group records — one group only.
  • kelta <group> --help / kelta <group> <command> --help — human help, ending with that command’s examples (also in the manifest as examples).

Auth & profiles#

  • Credentials live in profiles (~/.kelta/). Resolution precedence: flags > env (KELTA_PROFILE, KELTA_URL, KELTA_TENANT, KELTA_TOKEN) > profile file.
  • For CI/agents, env-only works with no profile at all: KELTA_URL=https://api.kelta.io KELTA_TENANT=acme KELTA_TOKEN=klt_… kelta collections list
  • kelta auth status --output json → {"authenticated": true|false, …}.
  • Missing/expired auth → exit 3 with {"error":{"code":"AUTH_REQUIRED",…}}.

Output contract#

  • Global --output table|json|yaml|csv|ndjson. Piped output defaults to JSON — never parse the table format, it has no stability guarantee.
  • JSON:API responses are flattened to { "id", ...attributes, "<toOneRel>": id }. --raw gives the unflattened envelope (links, meta, relationships).
  • --quiet prints ids only, one per line.
  • stdout = data ONLY. Diagnostics and progress always go to stderr.

Errors & exit codes#

  • Non-table modes emit ONE line of JSON on stderr: {"error":{"code":"VALIDATION_FAILED","status":400,"detail":"…","source":{"pointer":"/data/attributes/name"},"meta":{"requestId":"…"}},"errors":[…]}
  • error is errors[0] flattened for convenience; errors is the full JSON:API array — read it when a request can fail on more than one field (e.g. records create with two invalid attributes) or when you need every entry’s source.pointer/meta, not just the first.
  • code is the platform’s stable UPPER_SNAKE_CASE contract — branch on it, never on detail. meta.requestId correlates with server logs; reference errors additionally carry meta.targetCollection.
  • Exit codes: 0 ok · 1 API error · 2 usage/confirmation · 3 auth · 4 not found · 5 conflict/rate-limit.
  • Argument errors — a missing required argument, an unknown option or an unknown command — are INVALID_ARGUMENTS, exit 2, in the same envelope: {"error":{"code":"INVALID_ARGUMENTS","detail":"unknown option '--colour'"}}. --help and --version exit 0.

Destructive commands#

  • Anything destructive (delete/remove/reset/bulk/…) requires --yes when stdin is not a TTY; without it you get exit 2 and {"error":{"code":"CONFIRMATION_REQUIRED"}}. kelta manifest marks these with "dangerous": true.

Query grammar (list commands)#

  • --filter field=value (op defaults to eq) or --filter field.op=value; repeatable, ANDed. Ops: eq, neq, gt, gte, lt, lte, isnull, contains, starts, ends, icontains, istarts, iends, ieq, in (also aliased as any).
  • --sort -createdAt,name (leading - = descending)
  • --fields a,b (sparse) · --include rel1,rel2 · --page N --size N (max 200)
  • --all auto-paginates (client cap 10k, warning on stderr when hit).

Escape hatch#

  • kelta api GET '/api/collections?page[size]=5' — any endpoint, profile auth + tenant prefix applied automatically, response verbatim. --data takes any JSON (inline, @file, or - for stdin); --header Name:value repeatable. Every non-GET method is dangerous by spec (needs --yes off-TTY, DELETE included) — without it, exit 2 and {"error":{"code":"CONFIRMATION_REQUIRED"}}, unchanged. kelta manifest advertises this via "dangerous": true.
  • On a non-2xx response, kelta api surfaces the full error envelope above (all of errors[], not just the first) and exits per the status table.

Canonical examples#

kelta collections list --output json | jq '.[].name'
kelta records list invoices --filter status=open --filter amount.gte=100 --sort -createdAt
kelta records create invoices --data '{"amount": 120, "status": "open"}'
kelta records update invoices <id> --data @patch.json --yes
kelta fields add invoices --name due_date --type date --required
kelta validation-rules create invoices --name positive \
  --formula 'amount <= 0' --message 'Amount must be positive'   # TRUE = reject
kelta flows execute <flowId> --input '{"key":"value"}' --wait   # flows read $.input.key
kelta records bulk --data @ops.json --yes    # {"atomic:operations":[…]}, all-or-nothing
kelta records search "acme" --limit 5
kelta api GET '/api/governor-limits'
kelta menus get main --tree                  # nests ui-menu-items by parentId, client-side
kelta dashboards get <id> --components       # widgets sorted by rowPosition/columnPosition
kelta pages publish /home                    # resolves ui-pages by path, sets published=true

Gotchas#

  • Flow input is double-wrapped by the platform: pass --input '{"k":"v"}', the flow reads $.input.k.
  • Validation rules are ERROR conditions: the formula evaluating TRUE rejects the write.
  • A rule can test related records through a ROLLUP_SUMMARY field it names: the rollup is computed from the child rows at write time, and a value sent in the body is ignored. E.g. a COUNT rollup openTasks (filter {"status":"open"}) plus AND(state = "closed", openTasks > 0).
  • A just-created collection can briefly 404 on other pods (cache refresh); the CLI retries name lookups, but chained scripts should tolerate one retry.
  • Page size clamps at 200 server-side; the CLI rejects larger values up front.
  • No color is ever emitted; NO_COLOR needs no special handling.

Generated from the CLI command registry — view the source and run npm run gen:docs in kelta-web to update.