Skip to content

CLI reference

The fastagent CLI reference: init, info, dev, chat, invoke, tool, start, login, models, add, fire, schedule, and deploy commands with flags.

The fastagent CLI is the file-first workflow for creating, inspecting, serving, and operating an agent.

fastagent <command> [args] [options]

Most commands take an optional workspace directory (the agent is there, or in its ./fastagent/). When omitted, the current directory is used.

Commands

Command Purpose
init [dir] Scaffold a runnable agent.
info [dir] Inspect what an agent assembles into without serving.
models [search] List model specs.
login [provider] Store provider credentials in the project-level <agent dir>/.secrets/auth.json (override: --auth-path / FASTAGENT_AUTH_PATH, dir: FASTAGENT_SECRETS_DIR).
dev [dir] Serve locally with watch/reload.
chat [dir] Open the same assembled agent in pi’s interactive TUI.
invoke <message> [dir] Run one agent turn and exit.
fire <name> [dir] Run one schedule’s turn immediately (authoring loop).
schedule history <name> [dir] Print the run audit for a schedule (or wake).
schedule list [dir] [--json] Everything that will fire: static schedules (next instant) + pending wake-ups.
schedule cancel <id> [dir] Remove a pending wake-up (operator kill switch).
tool <name> <json> [dir] Run one discovered tool directly.
`add github telegram
add skill <source> [dir] Vendor an Agent Skills skill into skills/.
deploy docker [dir] Generate fastagent.compose.yml + the portable Dockerfile/.dockerignore for local Docker: one agent service, loopback port, /data state volume, and exact env-var names. --tunnel --run starts app+tunnel, reads the ephemeral URL, and auto-registers Telegram, locally onboarded Slack, and Feishu/Lark webhooks. Existing files stay authoritative unless --force; durable ingress/proxy/DNS/TLS remain operator-owned.
deploy fly [dir] Generate Fly.io artifacts (fly.toml/Dockerfile/.dockerignore, autostop=suspend, state→volume) and print a flyctl runbook + webhook step. --run drives flyctl to completion (idempotent, resumable; carries your local credential; needs flyctl). --stop (stop instead of suspend), --no-scale-to-zero (keep one machine up), --force (overwrite artifacts).
deploy railway [dir] Generate Railway artifacts (railway.json with healthcheckPath=/health, plus the shared Dockerfile/.dockerignore) and print a railway runbook: init a project, create a service (railway add --service), attach a /data volume, set the state root + secrets as variables (railway variables set, before the first deploy), railway up, then mint a domain (railway domain) and register the webhook. Scale-to-zero (App Sleeping) is a dashboard-only step the runbook states. --run drives the railway CLI to completion on an UNLINKED dir (auth → init/add/volume → variables → railway up → mint domain → telegram webhook; carries your local credential; needs the railway CLI); a dir already linked to a project is refused unless --into-linked (provision into it) — a routine redeploy is just railway up. --force overwrites artifacts.
deploy agentcore [dir] Generate AWS Bedrock AgentCore artifacts (agentcore.template.yaml — one CloudFormation stack: Runtime + forwarder Lambda/Function URL for webhooks + EventBridge rules for schedules and wake alarms — plus lambda/forwarder.js and the shared Dockerfile/.dockerignore) and print an aws/docker runbook. State rides platform SessionStorage at /mnt/state (tied to the Runtime resource). --run drives it end to end: identity/region/docker gates, ECR, local docker buildx arm64 push, cloudformation deploy (secrets via a 0600 params file), stack outputs, webhook registration, then stops the ingress session so the new image serves immediately. Long-connection channels are unsupported (switch to webhook mode); a kept generated template that no longer matches the definition gates --run until --force.
logs agentcore [dir] Discover the deployed stack’s CloudWatch log group and run aws logs tail. Defaults to the Runtime’s application stdout/stderr; --source forwarder selects the separate Lambda ingress logs. --since <duration> sets the history window and --follow keeps polling. Read-only; does not change FASTAGENT_LOG_LEVEL.
start [dir] Serve without watch.

fastagent init

fastagent init [dir] [--minimal] [--no-install] [--agent-dir <name>|--flat]

Creates a self-iterating agent — it can edit its own definition (persona.md and skills are re-read every turn). A fresh agent has persona.md (the agent’s identity: how to improve yourself), a writing-great-skills example skill (from mattpocock/skills — the guide to authoring skills), a fetch-url example code tool, config, .secrets/.env.example, and .gitignore. No AGENTS.md is scaffolded (it is project context, not identity); an existing one is kept untouched. Everything is written offline; by default it also writes package.json and runs npm install. Ignore hygiene is two scaffolded files: the agent .gitignore (node_modules/, .state/, a stray .env) and .secrets/.gitignore (everything but .env.example). fastagent writes both once, at init — no later command reads, rewrites or verifies them, so they are yours. They are separate on purpose: the root one is the file you will edit, and a nested .gitignore outranks it, so the credentials stay protected either way.

Where the files land — no detection and no prompt. By default the WHOLE agent — definition, config, .secrets/, .state/ — goes into ./fastagent/; the directory around it gets zero writes and becomes the agent’s WORKSPACE when you point fastagent there. The agent self-contains its package.json, so the workspace’s manifest and lockfile are never touched. init refuses when the target already holds a fastagent.config.* (already an agent) or any other content.

--agent-dir <name> names that directory anything you like: the name never decides what IS an agent — a fastagent.config.* does, so ./bot/ and ./fastagent/ are equally agents. The name carries weight in exactly one place, and only among agents already identified: when a workspace holds several and nothing selects, the one named fastagent answers (see FASTAGENT_AGENT below). It must be a single directory name (a path would put the agent where fastagent’s one-level lookup could not find it).

--agent-dir . (spelled --flat) puts the identical shape in the directory itself — for a standalone agent repo or a monorepo package, where the agent’s tools operate on its own definition. Adopting a directory is the point, so every file that already exists is kept (reported, never overwritten); only a fastagent.config.* refuses, because that means it is already an agent.

What a served agent’s workspace turns out to be is not decided here: it is whatever directory you later point fastagent at (see dev/start below). init’s one placement duty is to refuse a target the lookup could never SELECT — an agent already resolving AT dir, which wins over anything inside it and would hide the new one. A second agent BESIDE an existing one is fine and supported: several agents can share one workspace (an engineer’s, a PM’s, a content owner’s, all driving the same repository), and FASTAGENT_AGENT=<name> picks between them — the one named fastagent answers by default. init prints the note when a workspace crosses into that shape. The config is a DECLARATION, not configuration: its contents may be export default {} (a model can come from --model), but a directory has to SAY it is an agent — the job package.json and Cargo.toml do for their tools. A directory holding nothing else is already a complete agent.

Options:

Option Meaning
--minimal persona.md + the example skill + config only — no code tool, package.json, or install.
--no-install Scaffold everything but skip npm install.
--agent-dir <name> Name the agent directory (default fastagent; . = dir itself).
--flat Alias for --agent-dir . — the directory IS the agent; existing files are kept.

fastagent info

fastagent info [dir] [--json] [--model provider/modelId] [--auth-path file] [--sessions-dir dir]

Prints the assembled surface without starting a server:

  • model source,
  • config path,
  • persona presence and context files (AGENTS.md),
  • skills and their diagnostics,
  • the complete coding-tool set (read/grep/find/ls/bash/edit/write), plus authored tools and collisions,
  • channel files,
  • schedules (name + next fire instant; a broken schedule file is reported here, not first at dev),
  • whether self-scheduling (selfSchedule) is on,
  • session directory.

info is read-only: it does not create sessions or modify .state//.secrets/.

fastagent models

fastagent models [search]

Lists available model specs in provider/modelId form. Pass a search string to filter.

Use a listed spec with --model, FASTAGENT_MODEL, or fastagent.config.*.

fastagent login

fastagent login [provider] [--auth-path file] [--no-input]

Authenticates a model provider and stores credentials in the project-level <agent dir>/.secrets/auth.json (dir override: FASTAGENT_SECRETS_DIR; file override: --auth-path / FASTAGENT_AUTH_PATH; run it OUTSIDE any agent — no ./fastagent/ in the current directory — to write the global ~/.fastagent/.secrets/auth.json, which it announces on stderr). Inside an agent but not at its root (fastagent/tools/), it refuses and tells you where to cd. There is no implicit fallback between the project and global files — the default is project-level for isolation (different agents can use different accounts) and fail-visibly (a missing credential surfaces instead of being masked by a machine-global one absent on a fresh box). So cd into your agent before logging in. FastAgent uses its own credential file, separate from pi’s CLI state.

An API-key login is verified immediately with one minimal request (OAuth needs no check — completing the flow proves the credential): a definitive rejection (HTTP 401) removes the bad key and prompts for it again on the spot (cancel to stop), so a mistyped key is corrected at login time instead of failing at the first invoke; an inconclusive failure (network, quota, permissions) keeps the key and prints the provider’s message.

The directory holding auth.json is managed as a secrets directory, whichever knob named it: every credential write (including an OAuth refresh mid-run) re-applies 0700 to it, and where the process cannot chmod it the write fails instead of leaving the credential readable. Point --auth-path / FASTAGENT_AUTH_PATH inside a directory this process owns, not a shared one others need to read.

Running several agents off one account on your dev machine? Point them all at the one global file: set FASTAGENT_AUTH_PATH=~/.fastagent/.secrets/auth.json (a .env entry, a shell env var, or --auth-path), or just login from outside any agent. A leading ~ is expanded to your home dir in --auth-path and FASTAGENT_AUTH_PATH (shell variables like $HOME are not — use ~ or an absolute path). Sharing one file is safe — a single cross-process lock serializes OAuth refresh, so concurrent instances always read the latest token. (What is not safe is copying the file around: two files over one grant each rotate the single-use refresh token and break the other.)

fastagent dev

fastagent dev [dir] [--port N] [--bind addr] [--model provider/modelId] [--auth-path file] [--no-watch] [--tunnel] [--no-input]

Assembles the agent and serves it locally. persona.md/AGENTS.md/skills/ are re-read every turn (edits go live next turn, no restart); a supervisor restarts the worker on edits to the code inputs — tools/, channels/, schedules/, fastagent.config.*, package.json, .secrets/.env.

With no model set and a terminal attached, dev first shows the full model catalog — models whose provider already has credentials are listed first and annotated with the source (e.g. ready — OPENAI_API_KEY); picking one that needs auth runs the login flow inline — then writes the choice back to the config (same for start / invoke / fire / chat / deploy). Pass --model or set FASTAGENT_MODEL to skip the prompt.

Options:

Option Meaning
--port N Override http.port / default 8787.
--model spec Override model selection.
--auth-path file Override the credential file (default <agent dir>/.secrets/auth.json).
--no-watch Serve once without the watch supervisor.
--tunnel Open a Cloudflare quick tunnel for webhook testing.
--no-input Never prompt (CI/scripts) — e.g. the first-run model pick becomes an actionable error instead of a question.

Model precedence:

--model > FASTAGENT_MODEL > fastagent.config.* model

fastagent attach

fastagent attach <session> [dir] [--url url --token token]

Attach to a session served by a running dev/start with sessionControl: true in the config: stream its live events (text, tool activity, run boundaries), steer the active run by typing a line — or, with no run active, start one (the line becomes a new prompt over the remote data plane) — /abort to stop a run, /commands to list the names the agent defines (skills; name one in a normal message — this composer does not expand /name), Ctrl+C to detach. A run YOU started from attach is driven by attach’s own connection, so detaching cancels it (channel-started runs are unaffected). Discovers the local endpoint from <stateRoot>/control.json; --url/--token reach a remote serve. Speaks the same wire protocol a Web panel or desktop app uses (connectSessionControl).

fastagent chat

fastagent chat [dir] [--model provider/modelId] [--auth-path file]

Opens the same assembled agent in pi’s interactive TUI. This is useful for trying the agent before serving it through channels.

Auth is fastagent’s, same as every other command: --auth-path > FASTAGENT_AUTH_PATH > the agent auth.json. Log in with fastagent login (or pi’s /login inside the TUI, which writes to the same file). With no model set, chat runs the same first-run picker as the serving commands (credential-annotated catalog, inline login) and writes the choice back to the config.

fastagent invoke

fastagent invoke <message> [dir] [--model provider/modelId] [--auth-path file] [--no-input]

Runs one turn through the same agent assembly and exits:

  • answer text streams to stdout,
  • tool and diagnostic lines go to stderr,
  • a failed terminal event exits non-zero.

Use this for smoke tests and scripts.

fastagent fire

fastagent fire <name> [dir] [--model provider/modelId] [--auth-path file] [--no-input]

Runs ONE schedule’s turn immediately — the authoring loop for schedules (like invoke is for a prompt). Fires schedules/<name>.ts now, without waiting for its cron, using the schedule’s stable session, so you see exactly what the served scheduler would do:

  • answer text streams to stdout, tool/diagnostic lines to stderr, a failed turn exits non-zero (like invoke),
  • no name → usage on stderr, exit 2; an unknown schedule name → exit 1 with the available names,
  • it does not advance the schedule’s fire state — a test run never makes the running scheduler skip the real next run.

A schedules/<name>.ts file default-exports defineSchedule({ cron, tz?, prompt }); the scheduler fires the agent on that cron when you dev/start. Output is the agent’s tools’ job — the scheduler only fires and logs. See the API reference.

fastagent schedule history

fastagent schedule history <name> [dir] [--json]

Prints the run audit for one schedule — or wake for the agent’s self-scheduled wake-ups: when each run fired, its outcome (completed / failed / deferred), duration, and a preview of the reply or error. The answer to “did last night’s run silently fail?”. Read-only (reads <state root>/schedule/runs.jsonl, written by the serving scheduler); --json prints the full records, including the complete reply text.

fastagent schedule list [dir] shows everything that will fire, from BOTH producers: the static schedules/ files (name + next cron instant) and the agent’s pending self-scheduled wake-ups (id, next fire, one-shot/cron, session, prompt), with --json for the machine-readable form; fastagent schedule cancel <id> [dir] removes one wake-up: the operator’s kill switch for a runaway recurring wake (the agent’s own is the unwake tool, which is session-scoped).

fastagent tool

fastagent tool <name> '<json-args>' [dir]

Runs one discovered or configured tool directly, without a model or server. The call receives its workspace cwd but no session manager; a session-dependent tool reports that requirement itself.

Example:

fastagent tool fetch-url '{"url":"https://example.com"}'

fastagent add github|telegram|slack|feishu|lark

fastagent add github [dir]
fastagent add telegram [dir]
fastagent add slack [dir]      # create/install an internal app; --no-onboard scaffolds only
fastagent add feishu [dir]   # 飞书 (open.feishu.cn) — also CREATES the app (scan-to-create; credentials → .secrets/.env)
fastagent add lark [dir]     # Lark intl — opens console + collects/validates credentials

Creates a channels/<kind>.ts file with adapter glue and appends env placeholders to .secrets/.env.example when possible. The channel’s GENERATED secrets (telegram’s TELEGRAM_SECRET_TOKEN, github’s GITHUB_WEBHOOK_SECRET — random strings the user contributes nothing to) are written to .secrets/.env, leaving only genuinely-manual values (e.g. TELEGRAM_BOT_TOKEN from BotFather) as next steps — they are covered by the .secrets/.gitignore written at init. Everything (glue, companion tool, secrets) lands in the agent dir (./fastagent/) — the same place dev/start discover channels. The channel file is written once and is yours after that; a companion tool (tools/slack-send.ts, tools/telegram-send.ts, …) is the package’s and is rewritten on every add, so after upgrading the package, re-run add <kind> (--no-onboard skips the app prompts) to refresh it.

An enabled channels/*.ts|*.js|*.mjs file must load successfully or dev / start fails. To intentionally disable one, rename it to e.g. channels/telegram.ts.disabled; channel files, not config, are the enable/disable source of truth.

Slack scaffolds channels/slack.ts plus tools/slack-send.ts; --group-behavior context|mentions selects the created app’s manifest scopes/events (the runtime has one behavior; it hears what the app is allowed to), defaulting to context-aware context; choose mentions explicitly for least privilege. By default it opens Slack’s App Configuration Token page, creates a new internal app with agent_view, native Agent streaming, and suggested prompts through apps.manifest.create, installs it through OAuth, and writes the Bot User OAuth Token + the Signing Secret to .secrets/.env. The configuration refresh token stays owner-readable under <state root>/channels/slack/ and is used locally by dev --tunnel / deploy --run to update the Events API URL; it never travels to the host. --no-onboard preserves the manual scaffold-only path. --replace-config skips the menu and directly replaces the local App Configuration token pair — the repair when automatic Request URL updates fail because the tokens expired or were revoked. It works only on the machine that onboarded the app (the pair lives in its local state); other machines set the Request URL manually in the Slack console.

See:

fastagent add skill

fastagent add skill <source> [dir] [--update]

Vendors an Agent Skills skill into skills/<name>/ in the agent dir (./fastagent/skills/ in the default placement). Sources can be:

  • a GitHub-style ref,
  • a local path,
  • a bare name from local global skill directories.

Use --update to overwrite an existing vendored skill. Review the result with git diff before deploying.

fastagent start

fastagent start [dir] [--port N] [--bind addr] [--model provider/modelId] [--sessions-dir dir] [--auth-path file] [--tunnel] [--no-input]

Runs the agent in production posture: no watch, same assembly as dev.

Port precedence:

--port > PORT > fastagent.config.* http.port > 8787

Bind precedence (same for dev):

--bind > fastagent.config.* http.host > all interfaces

Session directory precedence:

FASTAGENT_STATE_DIR      > <agent dir>/.state       (mutable machine state)
FASTAGENT_SECRETS_DIR    > <agent dir>/.secrets        (.env + auth.json)
--sessions-dir > FASTAGENT_SESSIONS_DIR > <state root>/sessions

FASTAGENT_AGENT — which agent, when a workspace holds several. Not a path knob: it is an input to every command, because several agents can share one workspace (an engineer’s, a PM’s and a content owner’s agent all driving the same repository). It ASSERTS the agent by directory name — a directory without one by that name refuses rather than serving a different agent — and with nothing set, the agent named fastagent (the init default) answers. Scope it per repository (an .envrc) or per command (FASTAGENT_AGENT=pm fastagent dev); deploy bakes the selected agent into the image, so the container never re-picks. Selection lives in your environment on purpose: it is per-person, and a file committed to the shared workspace could not express “mine”.

For deployments, point the state root (sessions, channel state) and the secrets dir (the seeded/rotated auth.json) at durable storage:

FASTAGENT_STATE_DIR=/data/.state FASTAGENT_SECRETS_DIR=/data/.secrets fastagent start

Global options

Flags belong to their command and come after it: fastagent info --json, not fastagent --json info. (Earlier releases accepted flags anywhere on the line; that form now fails with unknown option, exit 2 — move the flag after the command/subcommand.)

Only two options are global:

Option Meaning
-h, --help Print help. Works per command too: fastagent deploy --help, fastagent help deploy.
-v, --version Print the package version.

Recurring per-command options (same meaning everywhere they appear):

Option Commands Meaning
--bind <addr> dev, start Bind address — an IP literal, or localhost (read as 127.0.0.1). Default: all interfaces (containers need it); 127.0.0.1 keeps the port, /control/* included, off the LAN. Prefer this flag over http.host for a local-only bind — config travels into a deployed image, where deploy gates it. See Bind address.
--no-input dev, start, invoke, fire, login, deploy Never prompt; missing information becomes an error with the flag to pass (deploy plan mode only warns on a missing model — --run gates).
--model <provider/modelId> assembly commands Model override (--model > FASTAGENT_MODEL > config).
--auth-path <file> assembly commands, login Credentials file override.
--json info, schedule history, schedule list Machine-readable output.

Exit codes

Code Meaning
0 Success (including help/version displays).
1 Runtime failure — a failed turn, a broken definition, a deploy gate, an unknown tool/schedule name, invalid runtime configuration (e.g. a bad PORT env).
2 Usage error — unknown command/flag, missing/empty/invalid arguments, conflicting flags. A mistyped command suggests the nearest one (fastagent depoly → “Did you mean deploy?”).
Navigation

Type to search…

↑↓ navigate↵ selectEsc close