Skip to content

Latest commit

 

History

History
267 lines (226 loc) · 17.1 KB

cli.md

File metadata and controls

267 lines (226 loc) · 17.1 KB

CLI

Installing the package also installs the lousho command. It scaffolds projects, checks your setup, runs an agent locally, serves it over MCP, runs evals, shows saved traces, builds a deployable artifact and launches the Agent Forge dashboard. Run it with npx lousho <command> inside a project that has the SDK installed.

Command What it does Details
lousho init [dir] Scaffold a project: an agent with an example tool, an offline test, .env.example; installs dependencies and runs git init. Installation
lousho doctor [spec|agent-dir|agent.ts] [--json] [--ping] Check Node, peer packages, provider keys, and optionally a spec file, agent directory or agent module (its provider's key is then required); --ping also checks each configured provider endpoint answers and accepts the key. Prints a fix for every problem. Installation
lousho dev <path> Local dev server for a spec file, an agent directory or a TS agent: chat UI with a session per tab and streamed events, hot reload on save. Below
lousho chat <path> Terminal REPL for a spec file, an agent directory or a TS agent: streams replies, shows tool calls, asks for approvals and questions. Below
lousho acp <path> Serve a spec file, an agent directory or a TS agent to an editor (Zed and other ACP clients) over the Agent Client Protocol on stdio. ACP
lousho add <name> Install a tool, skill, channel, schedule or memory slot from a JSON registry into an agent directory, after showing its permission manifest and checking the code against it. Registry
lousho mcp <spec> Serve the agent as an MCP server (stdio, or HTTP with --http). MCP
lousho eval [globs...] Run *.eval.ts files under vitest; print a summary and write JUnit/JSON reports. Evals
lousho traces [id] List the recent runs saved by fileTraceExporter() (duration, model and tool calls, tokens, cost), or print one run as a span tree. Observability
lousho build --target=<t> --agent=<spec> Build a deployable Node server, Docker image or Cloudflare Worker. Deployment
lousho studio Launch Agent Forge, the visual dashboard, on one local port. Agent Forge

Every command that takes a <spec> reads an agent spec file (.yaml, .yml or .json, see Configuration).

Usage

lousho init [dir] [--provider P] [--template T] [--yes] [--no-install] [--no-git] [--package-manager PM] [--force] [--sdk-path <dir|tgz>]
lousho dev <spec.yaml|spec.json|agent-dir|agent.ts> [--port N] [--host H] [--allowed-hosts a,b] [--no-schedules] [--traces[=dir]]
lousho chat <spec.yaml|spec.json|agent-dir|agent.ts> [--model provider/model] [--session id] [--store sqlite:<file>] [--traces[=dir]]
lousho acp <spec.yaml|spec.json|agent-dir|agent.ts> [--model provider/model]
lousho add <name> [--registry <url-or-path>] [--dir <agent-dir>] [--yes] [--allow <list>] [--overwrite] [--dry-run]    (or --list)
lousho build <agent-dir|spec|agent.ts> --target=<node-server|cloudflare-worker|docker> [--out=<dir>]    (or --agent=<path>)
lousho studio [--port N] [--host H] [--prod|--dev]
lousho mcp <agent.yaml|json|agent-dir|agent.ts> [--http --port N --host H]
lousho doctor [agent.yaml|json|agent-dir|agent.ts] [--json] [--ping]
lousho eval [globs...] [--tag t] [--junit path] [--json path] [--strict] [--judge] [--record | --replay | --drift [--drift-usage]] [--url <base> [--token <bearer>]] [--config vitest.config.ts] [--timeout <ms>]
lousho traces [traceId|prefix] [--dir D] [--limit N] [--json] [--content]
lousho --version

Every command parses its flags the same way (Node's parseArgs, strict): --flag value and --flag=value both work, the last of a repeated flag wins (--tag accumulates), -- ends the flags, and -h / --help (or lousho help <command>) prints the command's usage and flags and exits 0. An unknown flag, a flag without its value (it never takes the next flag as its value) or an extra argument fails with LOUSHO_CONFIG_INVALID and the usage line. A value that starts with - needs the = form: --model=-x. --traces is the one flag whose value is optional: it takes it only in the = form (--traces=./t), so --traces agent.yaml is still the flag followed by the path.

Exit codes. Every command exits 0 on success, 1 when it ran and failed (an agent that cannot load, a build error, lousho eval's gate failure, no matching trace, lousho add refusing an item without --yes), and 2 for a usage error: an unknown command, an unknown flag, a missing or invalid value, an extra argument, or a missing --target or agent path. lousho build prints where it wrote the build (default .lousho/build/<target>) and the command to run there.

.env. The commands that run an agent (dev, chat, acp, mcp), doctor, eval (judge evals need provider keys) and build load ./.env from the working directory when it exists, like node --env-file-if-exists=.env: a variable already set in the environment wins over the file. A UTF-8 byte order mark at the start of the file (which PowerShell 5.1's Set-Content -Encoding utf8 writes) is ignored.

lousho init is the exception: it parses its own flags, and a bad flag or value fails with InitUsageError — its message printed as-is, exit code 2 — not the shared LOUSHO_CONFIG_INVALID path. The only short flags besides -h are -y for --yes, on init and on add.

lousho eval also takes --config <file> to run under your own vitest config instead of the generated one; globs then narrow that config's files as vitest filters.

npm create lousho-agent my-agent runs lousho init with the same arguments. To scaffold against an unreleased build of the SDK, pass --sdk-path (see Installing from a local build).

lousho dev

npx lousho dev agent.yaml                  # a spec file
npx lousho dev ./my-agent                  # an agent directory
npx lousho dev src/agent.ts                # a TypeScript (or JavaScript) module
npx lousho dev agent.yaml --port 4000 --host 0.0.0.0
npx lousho dev ./my-agent --no-schedules   # do not fire the directory's cron schedules

For an agent directory, channels/ are mounted under /channels and schedules/ are started (and both are swapped on reload, the old schedules stopped first); --no-schedules starts none. See Agent directories.

Serves a chat UI on GET /, GET /health, GET /dev/status and the chat endpoints below (1MB body limit). What <path> is follows from the path:

Path Loaded with
.yaml, .yml, .json loadSpec() + specToAgent() (Configuration)
a directory loadAgentDir() (Agent directories)
.ts, .mts, .cts, .js, .mjs, .cjs the module's default export, or its agent export: a SimpleAgent (what createAgent() returns) or a createAgent() options object

Any other extension, a missing path, or a module with no agent export fails with a coded error (LOUSHO_SPEC_UNSUPPORTED_FORMAT, LOUSHO_CONFIG_INVALID) and its fix. A .ts module is imported by the running Node (22.18 or later strips types, so relative imports need their file extension); use .js for code that needs a transpiler.

Chat sessions. The chat UI keeps one session per browser tab (its id is in sessionStorage; "New session" starts another) and shows the streamed turn: text as it arrives, tool calls with their arguments and results, errors, and tokens and cost from run.done. A tool call that needs approval shows Approve / Reject buttons; an ask_question call shows its options as buttons plus a free-text field. The same endpoints work from curl or your own page:

Endpoint What it does
POST /chat { "sessionId", "input" } Runs agent.session({ id: sessionId }).stream(input) and streams the turn as SSE: one data: <AgentEvent JSON> per event (Streaming), then event: done. History is kept per sessionId (1-128 characters of A-Za-z0-9_-).
GET /chat/:sessionId The session's transcript: { sessionId, messages, pending }; pending is { status, approvalId? } while a turn waits on an approval, else null. A turn's messages are saved when it finishes, so while it is paused messages does not hold that turn yet: use pending.
POST /chat/:sessionId/approvals/:id { "approved", "note"? } or { "answer" } Decides the pending approval (agent.approvals.streamResolve()), or answers a question (agent.approvals.streamAnswer()), and streams the continued turn as SSE. It can pause again with another approval.requested. 404 when id is not pending. When the turn's step paused on several calls, each id is decided on its own; a decision that leaves others pending streams run.done with finishReason: 'awaiting-approval', and the last one runs the step.
POST /chat { "message" } Deprecated: no session, no streaming. Returns the agent's ExecutionResult as JSON, with a Deprecation: true header.

--traces (or --traces=<dir>) writes every run to local trace files with fileTraceExporter(), in .lousho/traces unless you name a directory, so npx lousho traces shows what the agent did. It is off by default because the files hold prompt, tool argument and tool result content, and the flag prints the directory it writes to. It applies to spec files, agent directories and modules alike (a module's own exporter is replaced while the flag is set). See Local traces.

Sessions live in an in-process memoryStore() (gone when lousho dev stops), or in the agent's own store when a module's createAgent() options set one. The continuation of an approval is streamed too: the endpoint runs it with agent.approvals.streamResolve() (or streamAnswer() for a question) and sends the continued turn's events — the decided tool call, text deltas, a further pause and run.done — as they happen.

lousho build servers (node-server, docker) serve these same endpoints from the same code, with sessions in a store chosen by LOUSHO_STORE and optional bearer auth: see Deployment: HTTP API.

Hot reload. The agent is rebuilt a moment (100 ms) after a file changes, without restarting the server or dropping the port:

  • a spec: the spec file;
  • a directory: everything under it (instructions.md, the config file, tools/, skills/, subagents/), except node_modules and .git;
  • a module: the file and the local files it imports (relative import / require specifiers, found once at startup; no bundler).

Directory and module files are imported with a ?t=<time> cache-busting query, so an edited tool or agent file is re-evaluated, and so are the local files it imports by relative specifier (a shared ./lib/helper.ts): the query is passed on to them. Packages under node_modules stay cached; restart lousho dev after changing one. The console logs what reloaded. The new agent is built (and ready(), so MCP servers connect) before it replaces the old one, and the old one is then close()d. If the rebuild fails, the last good agent keeps answering, the error is logged and shown in a banner on the chat page (and as error in GET /dev/status); the next good edit clears it. Sessions survive a reload: the store outlives the agent swap, so the next message continues the conversation on the new agent. An approval that was pending on the old agent is not known to the new one (404); start a new session.

Directories and modules are your code and run with your permissions; only run lousho dev on ones you trust.

  • --port - default 3737.
  • --host - default 127.0.0.1 (localhost only). Pass e.g. --host=0.0.0.0 to opt in to LAN access; lousho dev has no auth and shows raw provider errors, so it then prints a warning.
  • --allowed-hosts a,b - extra host names the Host header may carry. To stop DNS rebinding (a web page re-pointing its own domain at your machine), every route but /health and the channels that verify their requests answers 403 (LOUSHO_REQUEST_FORBIDDEN) unless Host is localhost, a 127.x.x.x address or [::1]; off loopback, IP addresses and the --host name are accepted too. The list adds to that rule (a tunnel's name, e.g.); * accepts any host. A channel without verify (httpChannel() with no verify) is checked like the dev routes, so reach it through a tunnel only with its name in the list.
  • POST bodies must be Content-Type: application/json (415 otherwise), and a browser POST from another origin is 403, as on the deployed server (Cross-site requests).

lousho chat

npx lousho chat agent.yaml                          # a spec file
npx lousho chat ./my-agent --model openai/gpt-4o    # an agent directory, on another model
npx lousho chat src/agent.ts --store sqlite:.lousho/chat.db --session support

A terminal REPL: each line you type is one turn of a session. <path> is loaded like lousho dev loads it (spec file, agent directory or .ts/.js module, see lousho dev); a missing path or bad extension fails with the same coded error (LOUSHO_CONFIG_INVALID, LOUSHO_SPEC_UNSUPPORTED_FORMAT), and so does an invalid --session id (LOUSHO_SESSION_ID_INVALID).

> look up cats
[lookup] {"q":"cats"}
  -> found cats
Cats are great.
[usage] 20 in / 6 out tokens, $0.0004
> email the report to sam
Approve send_email({"to":"sam@example.com"})? [y/N] y
  -> sent
Done, the report is on its way.
  • Text is written as the model produces it. Each tool call is one dim line, [tool_name] {args}, followed by -> result (or -> error: ...).
  • A tool call that needs approval asks Approve <tool>(args)? [y/N]: y or yes runs it, anything else rejects it and the model carries on. An ask_question call (askQuestion: true) prints the question with numbered options; answer with the number or with free text.
  • After each turn [usage] shows the tokens and, when every model used has known pricing, the cost (~ marks estimated tokens).
  • Errors from the SDK print with their [LOUSHO_...] code and fix, and the session continues. A provider failure also names its category and HTTP status, e.g. error: User not found. (auth-failure, HTTP 401) for a wrong API key.
  • --traces or --traces=<dir> writes the session's runs to local trace files, as for lousho dev; a line on stderr says where.
Command What it does
/new Start a new session (the old one is kept in the store).
/model <provider/model> Rebuild the agent on another model; the session continues. A failure keeps the current model.
/compact Compact the session's transcript now (old tool results are pruned) and print the before/after size.
/clear Empty the session's transcript; the session id stays.
/history Print the session's transcript.
/quit Leave (Ctrl-D works too).
Option Meaning
--model provider/model Use this model instead of the one the target names. Applies to spec files, agent directories and createAgent() options exports; a module that exports an already built agent keeps its model. The vendor/ model prefix chooses the provider; with OpenRouter use openrouter/<vendor>/<model> (e.g. openrouter/openai/gpt-4o).
--session id Open this session (1-128 characters of A-Za-z0-9_-); default is a new chat-<id>.
--store sqlite:<file> Keep sessions in a SQLite file (SqliteStore), so --session id continues a conversation after a restart. Default: in memory. Pending approvals are not restored after a restart.

The REPL itself is runChatRepl() in src/cli/chatRepl.ts; it takes the input lines, an output stream and an agent factory, so it can be tested with scripted lines and a mockModel agent and no terminal.

lousho build

npx lousho build --target=node-server --agent=agent.yaml        # or docker / cloudflare-worker
npx lousho build ./my-agent --target=node-server                # an agent directory (node-server, docker)
npx lousho build src/agent.ts --target=node-server              # an agent module (node-server, docker)

Writes the artifact to --out (default .lousho/build/<target>) and prints the command to run or deploy it. Building needs tsup (npm install --save-dev tsup). Targets, the HTTP API they serve and the Workers limits are in Deployment.

lousho studio

npx lousho studio            # prints http://127.0.0.1:4750/?token=<per-launch token>
npx lousho studio --port 5000

Starts one local server for the Agent Forge API and UI. Agents and run state are stored under .lousho/ in the current directory. See Agent Forge for the walkthrough.