# Command Reference

This is a compact reference for agent use. Prefer the workspace-first flow in `llms/SKILL.md`; this file is only for command lookup.

## Global

```bash
lumo --help
lumo <command> --help
lumo --format json <command>
lumo --debug <command>
```

Use `--format json` when another tool will consume command output. Do not inspect `.lumo/state`; use commands below or WORKING YAML.

## Setup

```bash
lumo auth login
lumo auth status
lumo auth logout
lumo tenants list
lumo init <tenant-id>
```

Tenant selection is workspace-scoped through `lumo init <tenant-id>`. There is no tenant switch command.

## CLI Updates

```bash
lumo version                 # build version / commit / date
lumo update                  # update to the latest release (binary + skills)
lumo update --check          # report whether a newer version exists, without installing
```

`lumo` checks for a newer release after each command and prints a one-line notice to stderr when one is available (`⬆  lumo vX.Y.Z disponível …`). `lumo update` substitui o binário em execução, atualiza as skills dos agentes detectados (Claude/Codex) e remove cópias antigas do PATH. Se o diretório do binário exigir privilégio, o comando **para e instrui** a ação exata (ex.: `sudo lumo update`) e sai com **exit 20**; erros de rede/checksum/IO saem com **exit 21**. Disable the notice with `LUMO_NO_UPDATE_NOTIFIER=1`.

## Workspace Inspection

```bash
lumo status
lumo list <kind> [--search TEXT] [--desk ID] [--limit N] [--offset N]
lumo list <kind> --remote
lumo columns table:<id>
lumo lint [files...]
lumo log
lumo diff <kind>:<id> [--include-remote] [--field path]
```

Valid list kinds: `flow`, `table`, `app`, `credential`, `dw-desk`, `bi-desk`, `desk` (legacy alias for `bi-desk`), `schedule`, `agent`, `variables`.

`lumo list` reads the initialized workspace by default. Use `--remote` only for explicit live-server discovery.

## Sync

```bash
lumo fetch [<kind>:<id>...] [--full]
lumo pull [<kind>:<id>...] [--discard-local] [--prune]
lumo push [<kind>:<id>...] [--force-ours --i-know-what-im-doing]
lumo new <kind> [--name NAME] [--variant VARIANT]
lumo rm <kind>:<id>
lumo backup [--exclude-state] [--output FILE]
```

`fetch` refreshes the local sync base and does not overwrite WORKING YAML. `pull` refreshes and updates WORKING. `push` sends WORKING YAML to the server after drift checks.

When a resource is deleted server-side, `fetch` drops its STATE but keeps the YAML on disk so a mistaken delete stays recoverable. `lumo status` reports those files as **Orphaned**, separately from **Untracked** — an orphan is a resource the server no longer has, so pushing it or re-running `new` re-creates something that was deleted on purpose. `lumo pull --prune` drops all of them in one pass; naming a resource explicitly drops just that one. This is the cleanup after any bulk server-side delete, `lumo app publish` of many clones included: publish removes the clone resources, and without `--prune` their YAML stays behind and the next reader of the workspace counts resources that no longer exist.

## Resource References

Most commands accept refs in the form:

```text
flow:<id>
table:<id>
app:<id>
credential:<id>
dw-desk:<id>
bi-desk:<id>
desk:<id>           # legacy; resolves to bi-desk:<id>
schedule:<id>
agent:<id>
variables
```

Many commands also accept a WORKING YAML path.

## Flows

```bash
lumo flow clone flow:<id>
lumo flow publish flow:<id> --desk <dw-desk-id>   # target is a data desk; discover via `lumo list dw-desk`
lumo flow run flow:<id> --context Total|Incremental|Temporal [--wait]
lumo flow nodes flow:<id> [--type TYPE]
lumo flow run-node flow:<id> --node <internalId> [--wait]
lumo flow sync-table flow:<id> [--node <internalId>] [--name <table-name>] [--force]
lumo flow preview-node flow:<id> --node <internalId>
lumo flow export-node flow:<id> --node <internalId>
lumo flow status flow:<id>
lumo flow logs flow:<id>
lumo flow cancel --execution-id <id>
```

Schema-changing table work flows through `flow sync-table`, which creates the DW table from the InsertDatawarehouse node's source schema (first run) or re-derives its columns (later runs), then auto-pushes the flow. Re-deriving an EXISTING table prompts for confirmation because it drops loaded rows; `--force` (or `--yes`/`-y`) skips the prompt and is required when the caller has no attended terminal on stdin (`--no-input`, `--format json`, or a script/CI invocation).

## Tables

```bash
lumo table preview table:<id> [--limit N]
lumo table truncate table:<id>
lumo columns table:<id>
```

**Editing a table.** Physical columns are a cosmetic **overlay**: an object keyed by `"COLUMN_NAME (DataType)"` (read-only identity — the type is shown inline) whose values carry only editable cosmetics: `label`, `mask`, `defaultBehavior`, `human_description`. Schema (name/type/order/keys) is owned by the originating flow and is read-only — change it by cloning the FLOW (`lumo flow clone` → edit InsertDW → `lumo flow publish`), never the table. Cosmetic edits work in place via `lumo push` even on a **published** table. `expressions` (calculated columns) are a fully-editable list on draft tables; **update-only** on published (no add/delete). `lumo table clone` is not supported (run it to see why). Legacy table YAML (columns as a list) is still accepted and auto-migrates to the overlay on the next sync.

## Apps

```bash
lumo info app:<id> --full
lumo app clone app:<id>
lumo app publish app:<id> --desk <bi-desk-id> [--new] [--force]   # target is an application desk; discover via `lumo list bi-desk`; a predicted REPLACE prompts for confirmation unless --force (required non-interactively)
lumo app change-table app:<id> --from-table <id> --to-table <id> [--dry-run] [--force]   # re-point app to a compatible table by NAME match; --dry-run previews rewire/dangle; refuses if used columns are missing unless --force
lumo app dashboard-preview app:<id>
lumo app query app:<id> --fields <columnId:AGG>[,<columnId:AGG>...] [--filter F] [--order-by O] [--limit N]
lumo app export app:<id> --image-format png|pdf|svg [--output FILE]
lumo app export-widget app:<id> --dashboard <id> --widget <id> [--output FILE]
lumo app open app:<id>
```

Run `lumo info app:<id> --full` before editing dashboards/widgets. It is the semantic report for column IDs, expressions, relationships, indicators, dashboards, and widgets.

## Credentials

```bash
lumo credential test credential:<id>
lumo credential query credential:<id> --sql "SELECT 1"
lumo credential schema credential:<id> [--schema NAME]
lumo credential transfer credential:<id> --target-agent <agent-id>
```

Persistent credential configuration lives in `credentials/*.yaml` and is changed with YAML plus `lumo push`.

## Schedules

```bash
lumo schedule run schedule:<id>
lumo schedule pause schedule:<id>
lumo schedule resume schedule:<id>
lumo schedule logs schedule:<id>
```

Persistent schedule configuration lives in `schedules/*.yaml`.

## Agents

```bash
lumo agent restart agent:<id>
lumo agent migrate --flows <ids> --to-agent <id>
lumo agent events agent:<id> [--window 48h] [--since S] [--until U] [--status MISSED,LATE,ERROR,CRASHED] [--recovered] [--acknowledged] [--all] [--format table|ndjson|tsv|json]
lumo agent health agent:<id> [--range 1h|6h|24h|7d|30d] [--full] [--mac MAC] [--format table|json]
```

Persistent agent configuration lives in `agents/*.yaml`. `events` (backend
`/agent/:id/events`) and `health` (backend `/agent/:id/health`, or
`/agent/:id/metrics` when `--full`/`--range 7d|30d`) are read-only
investigation tools that work **even while the agent is offline**. `events`
defaults to the open view (48h, not acknowledged/recovered); `health`
defaults to the 24h pulse (uptime %, memory, jobs, queue, 48-cell timeline).
`lumo agent logs` (agent-side `.jsonl` segments) is a later increment.

## Variables

```bash
lumo variables list [--remote]
lumo variables get <key> [--remote]
lumo variables set KEY=value
lumo variables set KEY --from-stdin
lumo variables unset KEY
lumo list variables [--remote]
lumo push variables
lumo diff variables --include-remote
```

Variables live at `variables/tenant.yaml`. `list` and `get` read WORKING YAML by default; `--remote` queries the live server.

## Desks

Two kinds, stored in separate backend tables:

- `dw-desk` — data desks. Hold flows + DW tables. YAML in `dw-desks/*.yaml`. Target of `lumo flow publish` / `lumo table publish`.
- `bi-desk` — application desks. Hold BI apps + dashboards. YAML in `bi-desks/*.yaml`. Target of `lumo app publish`.

```bash
lumo new dw-desk --name "Operação"
lumo new bi-desk --name "Projetos"
lumo list dw-desk
lumo list bi-desk
lumo info dw-desk:<id>
lumo info bi-desk:<id>
lumo rm dw-desk:<id> [--cascade]
lumo rm bi-desk:<id> [--cascade]
```

Legacy aliases (`lumo list desk`, `desks/*.yaml`, `desk:<id>`) still work and resolve to `bi-desk` for compatibility; prefer the typed forms. Use workspace YAML plus `lumo push` for persistent changes to a desk's metadata.

## Exit Codes

| Exit | Meaning | First step |
|---:|---|---|
| 0 | success | — |
| 1 | local diff present | `lumo diff <r>` to review; merge or pull |
| 2 | usage error | Read `lumo <cmd> --help` |
| 3 | drift detected | `lumo diff <r>` then choose pull / merge / `--force-ours` |
| 4 | local/server conflict | Manual merge; both sides changed |
| 5 | resource not found | Check `kind:id`; maybe `lumo fetch` |
| 6 | auth/access problem | `lumo auth login`; if access changed, check `lumo tenants list` and re-run `lumo init <tenant-id>` in the right workspace |
| 7 | network/timeout — **retryable** | Re-run the same command. If it keeps failing, check connectivity to the Horus BI server |
| 8 | lint/schema failed | Read the path; fix the YAML |
| 9 | server validation failed | Read the message; common: `FLOW_READONLY` (clone first), `SCHEDULE_UNPUBLISHED_FLOW` (publish flows), `SCHEDULE_MIXED_AGENTS` (one agent per schedule) |
| 10 | server 5xx | Transient. Retry once; if it persists, stop and report — do not loop |
| 11 | workspace invalid / lock contention | Another `lumo` process holds the workspace lock. Wait for it to finish; if you are sure the holder is dead, `lumo workspace unlock` |
| 12 | state missing for resource | `lumo fetch <r>` first |
| 13 | `rm` blocked by dependents | Re-run with `--cascade` to delete dependents, or remove them first |
| 14 | precondition (If-Match) failed | `lumo fetch`, re-edit, push again |
| 15 | partial apply — server ignored ≥ 1 field | Workspace is **already self-healed** by the post-save GET; read the field diff in stdout, fix the root cause (often edit flow node, not table), then re-push. No extra `lumo fetch` needed. |
| 20 | `lumo update` needs privilege | Run the action the command prints (e.g. `sudo lumo update`) |
| 21 | `lumo update` failed (network/checksum/IO/swap) | Re-run; if it persists, check connectivity to the storage host |

**Safe to retry**: read commands (`status`, `info`, `list`, `columns`, `diff`, `log`, `fetch`).
**NOT safe to blind-retry**: imperatives (`new`, `push`, `clone`, `publish`, `run`, `rm`, `truncate`). State may have moved — handle, don't loop.

## Drift Decision Tree

After `lumo push`:

- **exit 0** → all green.
- **exit 3 (drift)** → server moved. Run `lumo diff <r>`, then choose:
  - `lumo pull --discard-local <r>` to accept server state
  - merge YAML manually and retry push
  - `push --force-ours --i-know-what-im-doing` to overwrite server (rare; use with caution)
- **exit 4 (conflict)** → local + server both moved. Merge by hand.
- **exit 14 (precondition)** → `If-Match` lost race. `lumo fetch`, re-edit, push again.
- **exit 15 (partial)** → server reverted ≥ 1 field. Read the field diff in stdout; fix root cause.

Do not loop on drift with retries. Resolve the divergence first.

## Troubleshooting

- **`flow run-node --wait` fails or `credential test` returns "agente não respondeu"** → `lumo agent restart agent:<id>` first. The agent's WebSocket pool gets stale state between worker reconnects; restart cycles it cleanly. If two restarts don't help, the problem is below the WebSocket layer (worker process or pod).
- **IDE bookmarks break after `flow publish`** → The CLI renames the file post-publish. Check `note: <ref> renamed (server changed nome): <old> -> <new>` in the output.
- **Push returns `422 TABLE_COLUMN_MISMATCH`** → A column key in your table YAML doesn't match the server (unknown `COLUMN_NAME`, or the `(DataType)` in the key disagrees). The key is read-only identity; schema is flow-owned. Don't change the key — to change a column's name/type/schema, clone the FLOW (`lumo flow clone` → edit InsertDW → `lumo flow publish`).
- **Push returns `422 TABLE_EXPRESSION_READONLY_PUBLISHED`** → On a published table, calculated `expressions` are update-only: you can edit an existing one but not add or delete. Do that on a draft (clone the flow) and republish.
- **Push on a published app refuses naming a field (`exit 9` / `409 APP_READONLY_PUBLISHED`)** → The gate is per-field, not per-app. `nome`, `descricao`, `icon`, `color`, `facts` and `filtros` go in place; anything else (`tables`, `expressions`, `relationships`, `dashboards`, `config`, `tags`) needs `lumo app clone` → edit → `lumo app publish`. The message names the blocking field and the ones that would have passed. If it names a field you did not edit, the server moved — that surfaces as `drift:` instead, and the fix is `lumo pull` or `--force-ours`.
