# @lotics/cli — agent index

The model an agent needs before driving this CLI: which surface answers which question, what the
conventions are, and where the traps are.

| Read | For |
|---|---|
| `lotics --help` | The verb inventory (§ COMMANDS) and global flags. The verb LIST is generated and never stale; the prose beside each verb is hand-written, so where it disagrees with `docs/cli_reference.md`, the reference wins. |
| `lotics tools` · `lotics tools <name>` | The agent tool registry and one tool's full JSON Schema. |
| `lotics docs` · `lotics docs <area>[/<section>]` | Every reference the packages installed beside the project actually ship — `@lotics/app-sdk`, `@lotics/ui` and the document engines each carry their own, and this index only covers THIS package. Discovered by looking, not by a list, so it reports the installed VERSION of each: a doc always describes the code that is really there. Capped at a page: a doc that does not fit hands back its opening and the addresses into it, so the next call is smaller than the last. |
| `lotics scaffold docs` | How to write a `model.json` — the file a workspace is built from. Two forms: `{"from": "<preset-slug>", "variants", "rename", "entities", "rows", "apply"}`, which names a preset by slug and carries only what this business differs by, and the full one, spelled out, for when no preset is the trade: every top-level key, every field type with the config it needs, the row format, the rules, and a worked example. Offline, and not part of `lotics docs`. It also covers the `apply` list — published packages copied in after the model's own tables, each with an optional `bind` onto them — and the `preset` block a published model carries. `lotics scaffold check` then proves the file — offline, except for the one read a `from` file's preset needs — including every branch of a preset merged onto its base; `lotics setup` applies it and refuses a table name the workspace already has, `lotics scaffold apply` adopts that table and adds what is missing. `lotics scaffold export` goes the other way — a workspace printed as one of these files, to edit into another business's model; a starting point, never a source of truth. |
| [docs/building_an_app.md](./docs/building_an_app.md) | The SEQUENCE — scaffold, model, types, queries, workflows, screens, ship — and the deploy-free inner loop. The other references describe contracts; this one is the order they go in and why. Read it once before starting an app. |
| [docs/cli_reference.md](./docs/cli_reference.md) | Per-command contracts, flags, exit codes, and gotchas — the detail `--help` compresses. Read it before hand-building a `set_app_*` payload: several tools REPLACE rather than patch, and a CLI verb already owns the safe assembly. |
| [docs/data_model.md](./docs/data_model.md) | How tables RELATE — one entity per table and the NAME-OVERLAP probe that says when a split has broken, one vocabulary wherever values are copied between tables, a copy boundary that accounts for every source field, provenance as a link rather than a flag, a declared natural key so find-or-create never compares rendered text, and why derived DEPTH costs more than row count. Separate from building_an_app because every workspace starts with tables and many never get an app. The within-table half (one fact, one column) is stated at `create_table` / `update_table`, where you meet it while deciding. |
| [docs/document_templates.md](./docs/document_templates.md) | Generating PDF/Excel/Word/email from reusable templates. |
| [docs/knowledge_docs.md](./docs/knowledge_docs.md) | Authoring the workspace facts an agent can't guess; access-vs-activation; catalog-then-stage retrieval. |
| [docs/migration.md](./docs/migration.md) | What to DO when a release changes the shape of a project this CLI owns. Read once, when something already on disk no longer matches what the CLI writes — currently: an app built from a plan is `app.json` rendered by `@lotics/app-runtime`, not a tree of TSX. |
| [README.md](./README.md) | Install, auth, and worked examples. |

## Two surfaces, and the trap between them

**`lotics tools` is not the CLI's capability list.** There are two disjoint surfaces:

- **Tools** (`lotics tools`, `lotics run <tool>`) — the *agent tool registry*: what an agent, workflow,
  or automation may call. Workspace data, templates, knowledge, admin.
- **Commands** (`lotics --help` § COMMANDS) — the CLI's *own verbs*. Auth and org/workspace scoping,
  file upload/download/preview, and the whole custom-code app loop (scaffold, pull, deploy, codegen,
  dev, and running a bound workflow or agent end to end).

Several capabilities exist **only** as commands and appear nowhere in `lotics tools` — downloading a
file and running a bound app agent are the two that most often get mistaken for missing. Concluding
"the platform can't do X" from the tool list alone is a mistake; check both.

⚠️ **`lotics <subcommand> --help` prints the generic top-level help**, except `lotics report --help`
and a bare `lotics auth`, which print their own. It does not describe the subcommand, so an unhelpful
response there is *not* evidence the subcommand is absent. To find out whether something exists, read
`lotics --help` § COMMANDS — the whole section, not a narrow grep.

## Conventions that hold across every command

- **Scope is resolved per invocation.** `LOTICS_ORG` / `LOTICS_WORKSPACE` (or `LOTICS_API_KEY`) scope a
  single call without changing the active org or a directory pin — the safe way to touch one tenant
  from a shell serving many. Every command that resolves a workspace echoes its target to **stderr**
  (`lotics → <org> / <workspace>`); read it back before trusting a write. `file preview <fil_…>` is
  the one credentialed command with no echo. Resolution precedence is in README § Organizations.
- **A machine with no key can still sign in, and the sign-in never blocks you.** `lotics auth
  login <email>` prints the page a person opens (also mailed) and the code that page must show,
  records the request, and EXITS. They press Confirm whenever they get to it; the next command that
  NEEDS a credential collects the key before doing its own work, so "sign in" costs you one command
  and then re-running what you wanted. Never wrap it in a timeout waiting for a human — `--wait`
  exists if you really want one blocking command, and killing that one is safe too (the request
  survives and the next command claims it). A command run before Confirm exits 1 naming the page and
  code again; once the 15 minutes are up it says to ask again. `lotics setup` falls into the same
  flow by itself when the email it was given already has an account: it stops having created
  nothing, and the SAME command run again carries on.
- **A credential is either a SIGN-IN or an API KEY, and `logout` treats them differently.** A profile
  from `auth login` / `auth signup` acts as the person who confirmed it and is theirs — `lotics auth
  logout` revokes it server-side, and it lapses on its own after 90 idle days (each use pushes that
  out). A profile from `auth api-key` holds a key an ADMIN issued. A key created in Settings carries
  its OWN access — every app and table, or only the ones chosen on the key, so a listing that comes
  back short is the key's reach, not a bug — while a key created FOR a person carries that
  person's access and dies with their membership. Either is routinely also on a server and on
  other machines, so logout only forgets it locally and says so; only an admin revokes it. A profile that states no kind (saved before the field existed) is resolved against the SERVER
  and revoked only if the answer is a sign-in; a bare `--api-key` / `LOTICS_API_KEY` names no
  profile to remove at all. Nothing is ever revoked on a guess — between two, the destructive one is
  wrong. `lotics auth whoami` prints the kind, asking the server when the store cannot say.
- **A key created in Settings never administers the organization, whatever its access.** The verbs
  `docs/cli_reference.md` marks *admin only* split in two under a key: the ones that BUILD inside a workspace
  the key reaches — `scaffold apply`, `library init`, `app upgrade`, `workspace doctor`, `scaffold
  export`, `field rename` — run as before, while managing people, sharing or ownership, creating or
  deleting a workspace, changing workspace settings, setting credit limits, reading the access log
  and publishing a starter or an app's API answer `403` and name the remedy: an admin signed in, so
  `lotics auth login <email>` and run it again. A sign-in acts as that person and is refused none of
  them. Do not retry a `403` with the same credential and do not ask for a wider key — no answer on
  the key's own screen grants this.
- **A 401 names its remedy — act on the hint, do not retry.** "This credential expired / was
  revoked / belongs to a member who is no longer active" carries the one remedy that ends this
  credential: run `lotics auth login <email>` for a sign-in, or ask the admin who issued it for a
  new key. A credential minted before that was recorded carries BOTH, because nothing on the row
  tells them apart — so on a box with no browser, take the second. Only an UNRECOGNIZED key gets
  the generic "Invalid or disabled API key", and that one is generic on purpose, so re-sending it
  teaches nothing. A `reason` rides on the body for a script to branch on, since the code stays
  `unauthorized` for every 401.
- **Large payloads bypass `ARG_MAX`** — `lotics run <tool> @args.json` or piped stdin. A leading `@` is
  unambiguously a file path (JSON args start with `{`).
- **stdout is the payload, stderr is the narration.** Progress, status lines, and the target echo go to
  stderr; the result goes to stdout, so piping stays clean. `--json` switches stdout from the
  agent-readable summary to the full structured object.
- **Every tool is invoked one way — `lotics run <tool>`.** Including the ones that RUN something
  (`run_app_workflow`, `run_app_agent`, `run_app_query`). An `app` command exists only for work no
  tool call can do: scaffold, build, typecheck, serve, or read and push a local file.
- **Exit codes are assertable, and they report the WORK rather than the call.** `lotics run` exits
  non-zero when a `run_app_workflow` or `run_app_agent` result's own envelope carries a failed
  `status` (`error`/`failed`/`cancelled`) — any other tool's top-level status is data and exits 0 —
  so `lotics run … && next-step` cannot walk past a refused run; `workspace doctor` exits non-zero on
  findings. An unrecognized status exits 0 — the list is an allowlist of failure, so a status added
  later never turns a working script red — and a parked run (`awaiting_input`) is not a failure.
- **An app that PUBLISHES an API turns every later manifest write into a release.** `lotics app api
  publish` snapshots what the app's queries, workflows and agents promise to callers outside it — a
  customer's own site or server, which nobody here can redeploy. From then on an additive change
  re-snapshots silently and a breaking one is REFUSED, naming each change;
  `--acknowledge-breaking-api` (on `app deploy`, `app query set`, `app workflow set`,
  `app agent set`, `app upgrade`) is the answer that carries it out and snapshots the break as a new
  contract version. An app that publishes nothing is untouched by any of it.
- **Exposure is per app, all or nothing** — a public share or a key reaches every alias an app
  declares, so what outsiders may call is a second app over the same tables, scaffolded with
  `lotics app create "<name>" --api` ([docs/building_an_app.md](./docs/building_an_app.md) § 9).
- **`--print-created` / `--cleanup` on any call that reports `side_effects`.** The first prints the
  records created plus a paste-ready cleanup plan and what cannot be auto-undone; the second runs
  those deletes (records only — never files, integrations or notifications). Neither is a rollback.
- **Text output is the default and is built for reading**; reach for `--json` only when a field is
  needed programmatically.
- **`lotics report '<json>'` is the channel for what nothing else records.** Reach for it
  when the platform is genuinely missing something (a capability that does not exist — no
  command ran), when a success was wrong (exited 0, wrong effect), or when an error did
  not name the remedy — not for your own mistakes, which the logs already show.
  **It is a frame, not a paragraph** — `{goal, actual, expected?, tried?, wanted?}`, because a log
  reconstructs what you RAN and never what you WANTED, and that gap is the report. `goal` and
  `actual` are required; there is no severity or category to pick. The
  session's commands attach themselves — do not retype them. Run it bare for the full prompt; a
  long one rides `@file.json` or an explicit `-` for stdin (bare NEVER reads stdin).
- **`LOTICS_TELEMETRY=1` correlates a whole session** so the authoring loop's rough edges can be
  found and fixed. Off by default; set it in the shell profile, not per command (each invocation is
  its own process). It sends no arguments, no file contents, and no record data — see README
  § Diagnostics.

## Where this CLI is not the answer

- **Authoring a binding via `app deploy`.** Deploy ships code, queries, and capabilities — it never
  authors bindings. Workflow bodies go through `app workflow set`, agent instructions through
  `app agent set` (both edit a file on disk that `app pull` wrote from the live row). A binding's TYPED
  half — an agent's `tool_names`/`inputs`/`outputs`, a workflow's schemas — is authored by the
  `set_app_*` tools. A manifest alias with no server binding only fails at the app's first call, so
  deploy warns about the mismatch.

  **The four `lotics.*` keys look alike and point in three different directions.** Before editing one,
  know which you are touching — this is the single most expensive thing to get wrong in a manifest:

  | `lotics.<key>` | Owned by | Your edit reaches the app via |
  |---|---|---|
  | `queries`, `capabilities` | the manifest | `app deploy` — re-synced on every one (an absent `capabilities` block turns them all OFF) |
  | `knowledge` | the manifest | nothing, until the app is published as a starter — it declares which docs ship with it |
  | `workflows` | the workflow row's verified contract | `app workflow set`, which type-checks the BODY against your declaration and refuses a schema the body cannot satisfy |
  | `agents` | `inputs`/`outputs`: the manifest. Everything else: the app row | `app agent set`, and `app deploy`, which pushes a diverged `inputs`/`outputs` before it ships. The rest is a mirror `app codegen` re-silvers, changed with `set_app_agent` |

  `agents` is the one that bites, because the two halves of the same block behave differently:
  editing `tool_names` or `knowledge_doc_ids` still retypes `useAgentRun` — green locally, unchanged
  in production, and reverted by the next `app codegen`. An agent's PROSE is not in the manifest at
  all: it lives in `src/agents/<alias>.md` and is pushed by `app agent set`.
- **Editing a local `.xlsx` / `.docx`.** A library call, not a verb: script `@lotics/xlsx` /
  `@lotics/docx` (`lotics docs xlsx` / `lotics docs docx` prints the installed copy's API guide).
  `lotics file preview` draws the result so it can be looked at.
- **OAuth connections.** Attaching a connected account is web-only; the CLI can list them.
- **Anything needing a browser.** `app dev` and `file preview` shell out to a local Chrome.
