CLI reference¶
A complete, generated reference for every af command and flag, produced from the Cobra command tree. This page is regenerated by scripts/gen-docs.sh and verified in CI, so it never drifts from the binary — do not edit it by hand.
Run af <command> --help for the same information at the terminal. For a narrative, task-oriented tour of the CLI see the CLI guide.
Commands¶
af— Agent Factory - Manage multiple AI agents like Claude Code, Aider, Codex, Gemini, and Amp.af accounts— Manage per-session agent credential directoriesaf accounts add— Register a credential directory for an agent accountaf accounts list— List registered agent accountsaf accounts login— Log in to an agent account by running the agent's own login flowaf agent-server— Run a headless single-workspace backend (not the web UI — that is 'af daemon')af api— Show the daemon-hosted HTTP/JSON API catalogaf bug-report— Bundle logs, versions, tasks, and redacted state for a bug reportaf completion— Generate the autocompletion script for the specified shellaf completion bash— Generate the autocompletion script for bashaf completion fish— Generate the autocompletion script for fishaf completion powershell— Generate the autocompletion script for powershellaf completion zsh— Generate the autocompletion script for zshaf config— Read global or project-effective config and write global configaf config get— Print one global or project-effective config valueaf config list— Print global or project-effective config valuesaf config migrate— Rewrite deprecated config keys to their current spellingaf config set— Set one global config keyaf config unset— Clear a config override or migrated global settingaf config validate— Check that the global config parses and validatesaf daemon— Manage the background daemon: serves the web UI and schedules tasksaf daemon adopt— Hand a detached daemon back to the installed autostart unitaf daemon install— Register the daemon to start automatically at loginaf daemon restart— Restart the running daemon without stopping live sessionsaf daemon status— Report daemon liveness, config freshness, and supervisionaf daemon uninstall— Remove the daemon autostart unitaf debug— Print debug information like config pathsaf doctor— Diagnose setup, daemon health, and leaked session resourcesaf keys— Show the effective TUI key bindings (defaults plus [keys] rebinds)af projects— Manage projects and durable registrationsaf projects add— Add a project: register a repo by path with a stable local identityaf projects delete— Delete a project, archiving its restorable sessionsaf projects list— List registered projectsaf projects rebind— Rebind a registered project after its checkout movesaf quota— Show usage-limit status for each agent CLIaf reset— Factory-reset Agent Factory: remove AF sessions, tasks, project registrations, worktrees, and state (keeps repos and config)af sessions— Manage sessionsaf sessions archive— Finish with a session by archiving it for later restoreaf sessions attach— Attach to a session's terminalaf sessions backends— List the runtimes this project can create sessions onaf sessions create— Create a new sessionaf sessions get— Get a session by titleaf sessions handoff— Continue a session under a different agent, in placeaf sessions kill— Permanently delete a session and af-owned resourcesaf sessions list— List sessions in the current projectaf sessions preview— Preview a session's terminal contentaf sessions restore— Restore an archived, lost, or dead sessionaf sessions retry-limit— Retry a usage-limit resume or inspected handoffaf sessions send-prompt— Send a prompt to a session (or broadcast to all with --all)af sessions tab-create— Spawn a shell, process, web, or VS Code tab in a sessionaf sessions tab-delete— Delete a single tab from a sessionaf sessions tab-rename— Rename a tab of a sessionaf sessions tab-reorder— Move a tab within a session's tab orderaf sessions tabs— Manage a session's tabs (create/delete/rename/reorder)af sessions tabs create— Spawn a shell, process, web, or VS Code tab in a sessionaf sessions tabs delete— Delete a single tab from a sessionaf sessions tabs rename— Rename a tab of a sessionaf sessions tabs reorder— Move a tab within a session's tab orderaf sessions watch— Wait for idle, or a fleet stop-state change or disappearanceaf sessions whoami— Identify the current Agent Factory sessionaf tasks— Manage tasksaf tasks add— Add a new task bound to the current projectaf tasks get— Get a task in the current project by IDaf tasks list— List tasks in the current projectaf tasks remove— Remove a task in the current projectaf tasks restart— Restart an enabled watch task without process overlapaf tasks show— Show one task's schedule health and audit trailaf tasks trigger— Trigger a task in the current project to run immediatelyaf tasks update— Update a task in the current projectaf token— Manage the daemon's bearer token for the direct-TCP APIaf token rotate— Replace the bearer token with a fresh one and print itaf token show— Print the bearer token (generating it if absent)af upgrade— Upgrade agent-factory to the latest release on the configured channelaf version— Print the version number of agent-factory
af¶
Agent Factory - Manage multiple AI agents like Claude Code, Aider, Codex, Gemini, and Amp.
Run 'af' with no arguments to open the TUI. The subcommands below drive the
same daemon non-interactively (af sessions, af tasks emit JSON).
The daemon also serves the web UI — the same sessions in a browser — at http://localhost:8443, which needs no token by default. See 'af daemon --help' for the web UI, the listener and its auth, and autostart; to drive a daemon on another machine, start with the remote guide: https://sachiniyer.github.io/agent-factory/remote-http-auth/
Subcommands
af accounts— Manage per-session agent credential directoriesaf agent-server— Run a headless single-workspace backend (not the web UI — that is 'af daemon')af api— Show the daemon-hosted HTTP/JSON API catalogaf bug-report— Bundle logs, versions, tasks, and redacted state for a bug reportaf completion— Generate the autocompletion script for the specified shellaf config— Read global or project-effective config and write global configaf daemon— Manage the background daemon: serves the web UI and schedules tasksaf debug— Print debug information like config pathsaf doctor— Diagnose setup, daemon health, and leaked session resourcesaf keys— Show the effective TUI key bindings (defaults plus [keys] rebinds)af projects— Manage projects and durable registrationsaf quota— Show usage-limit status for each agent CLIaf reset— Factory-reset Agent Factory: remove AF sessions, tasks, project registrations, worktrees, and state (keeps repos and config)af sessions— Manage sessionsaf tasks— Manage tasksaf token— Manage the daemon's bearer token for the direct-TCP APIaf upgrade— Upgrade agent-factory to the latest release on the configured channelaf version— Print the version number of agent-factory
Flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
-p, --program |
string |
Program to run in new sessions (one of: claude, codex, aider, gemini, amp, opencode, devin) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
-v, --version |
version for af |
af accounts¶
Manage per-session agent credential directories
Prepare the credential directories a session can later be scoped to.
An account is one of an agent's logged-in identities, held as a directory the agent CLI treats as its home. af never reads, stores, or forwards the credential itself — it decides which directory a session sees, and the agent's own login flow puts the material there.
add creates that directory and prints its path · login runs the agent's own login flow against it · list shows what is registered.
af accounts login codex work
That one command registers the account if needed, starts codex's own login in a tmux session scoped to it, hands you the terminal for the device-code step, and afterwards reports whether the account holds a credential — read from the agent's own credential file, by checking that it exists.
Every login af runs is browser-free. The pane is on the daemon's host, which is usually headless and remote, so the flow that fits is the device code: the CLI prints a URL and a code, you sign in from whatever device you are holding, and the CLI polls. af selects it per agent — see af accounts login --help.
You can still do it by hand, and af runs exactly these:
CODEX_HOME=$(af accounts add codex work) codex login --device-auth CLAUDE_CONFIG_DIR=$(af accounts add claude work) BROWSER=true claude auth login GEMINI_CLI_HOME=$(af accounts add gemini work) NO_BROWSER=true gemini
Those variables do not all have the same shape, and mixing them up is the easy mistake. CODEX_HOME and CLAUDE_CONFIG_DIR name the config directory itself. GEMINI_CLI_HOME is a HOME-like root: gemini appends .gemini/ to it, so the account directory af prints holds the credential at
Select an account for a session with:
af sessions create --account work
That both injects the account's directory and removes every other identity-bearing variable for the agent. The removal is what makes the selection real: an ambient ANTHROPIC_API_KEY or OPENAI_API_KEY outranks the config directory, so without it a session would authenticate as whoever that key belongs to while every visible signal reported the selected account.
Account-scoped sessions require the local or docker backend, and tmux 3.2 or newer. af refuses rather than falling back, because a fallback would run on the ambient account while reporting the one you asked for.
ssh, sandbox and hook refuse by design, not because the work is pending. docker bind-MOUNTS the directory, so account writes land in your real account. An account is a writable agent home, so the agent writes refreshed authentication back into it. For ssh, sandbox and hook, af cannot establish that those writes come back, so a rotated token can be lost. If your provider rotates refresh tokens, losing it also invalidates the copy on this machine — so a feature meant to NARROW where an identity is used could break it.
By default af never switches accounts on its own. With limit_auto_resume enabled, an explicit limit_account_candidates list may opt an unpinned local session into switching after a usage limit. af skips registered candidates with a current limit observation, says which identity changed in the session, and waits normally when none is usable. Docker account-scoped creates remain supported, but automatic Docker replacement is disabled until af can durably identify and reap a crash-surviving container and freeze its complete provision plan. An explicit --account is a pin that automatic switching never overrides.
Subcommands
af accounts add— Register a credential directory for an agent accountaf accounts list— List registered agent accountsaf accounts login— Log in to an agent account by running the agent's own login flow
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af accounts add¶
Register a credential directory for an agent account
Create the credential directory for an account and print its path.
This makes a place; it does not log in. Run the agent's own login flow against the printed directory to put credentials there.
Registration is idempotent — running it again on an existing account reports the same directory and preserves existing settings. Missing non-credential runtime settings may be seeded; notices explaining them are printed to stderr.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Output the {data,error} JSON envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af accounts list¶
List registered agent accounts
List the registered accounts, for one agent or for every agent that supports account scoping.
An agent absent from this list is one whose credential relocation af has not verified, not one that is merely unconfigured — af reports unsupported rather than accepting a selection that would silently do nothing.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Output the {data,error} JSON envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af accounts login¶
Log in to an agent account by running the agent's own login flow
Run the agent's own login command against an account's credential directory.
af registers the account if it does not exist yet, asks the daemon to start the agent's login flow in a tmux session scoped to that account, and hands you the terminal so you can complete the device-code step. When the flow ends, af reports whether the account holds a credential — read from the agent's own credential file, by checking that it exists, never by reading it.
af accounts login codex work af accounts login claude personal
af never reads, stores, or forwards the credential. It sets one variable and runs the agent's own flow:
claude → claude auth login · codex → codex login --device-auth · gemini → gemini
The login is browser-free. The pane runs on the daemon's host, which is usually headless and remote, so a browser-callback sign-in there opens a browser nobody is sitting in front of, or waits for a redirect to that host's own localhost that your machine cannot reach. Every flow above prints a URL and waits for a code you paste back, and af selects that path per agent:
codex codex login --device-auth prints a verification URL and a device code gemini the pane sets NO_BROWSER=true · gemini prints the URL and prompts "Enter the authorization code: " claude claude has no such flag, so the pane sets BROWSER=true — a no-op opener the CLI honours — and claude prints "If the browser didn't open, visit:" with the URL, then "Paste code here if prompted > "
Sign in on whatever device you are actually holding, then paste the code into the pane.
The login pane gets exactly the environment an account-scoped session gets: the account's directory injected, and every other identity-bearing variable for that agent REMOVED. The removal is what makes the login land in the account you asked for — an ambient ANTHROPIC_API_KEY or OPENAI_API_KEY outranks the config directory, so without it the CLI could report success against that key's identity while the account directory stayed empty.
It removes identity, not environment: proxies, private CA roots and your git and SSH configuration are passed through as always. If the login needs something else — DISPLAY or BROWSER, on a host with a browser to open — add it to session_env_passthrough. Naming BROWSER or NO_BROWSER there also overrides the browser-free defaults above; your value wins.
The flow runs on the daemon's host, where the credential directory is. With --no-attach af prints the tmux session to attach to instead of taking your terminal.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Output the {data,error} JSON envelope | |
--no-attach |
Start the login flow and print how to attach instead of taking this terminal |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af agent-server¶
Run a headless single-workspace backend (not the web UI — that is 'af daemon')
Run a headless agent-server for exactly one session's workspace, served over the same REST + WebSocket protocol the daemon speaks, behind a plain-HTTP listener that requires a bearer token on every request.
This does not start the web UI, and serves no frontend at all — opening its port in a browser returns a 404 saying so. If you want the browser app, run the daemon — any 'af' command starts it — and open http://localhost:8443. The web UI is bundled into the daemon and served from its network.listen_addr; agent-server is only the headless per-workspace backend that a daemon drives, and it exists to be consumed by a daemon rather than opened by a person.
This is the process that runs inside a docker container or on an ssh remote (#1592 Phase 4): a remote daemon dials the authed URL it exposes and drives the workspace exactly as it drives a local in-process session. Run it directly only to host one workspace as a backend for a daemon on another machine.
The listener always requires the token and serves plain HTTP (no TLS) — reach it over a private network or a tunnel (the docker/ssh runtimes forward a loopback port). Its token is mandatory for every peer, whatever the global network.require_token key says: that key governs only the daemon's own web listener. On startup it prints one JSON line to stdout carrying the bound address and the bearer token. On SIGINT/SIGTERM it tears the workspace down (kills tmux, removes the worktree) — durability of in-progress work is the driving daemon's job (push the branch before shutdown), not this server's.
Flags
| Flag | Type | Description |
|---|---|---|
--listen |
string |
HTTP TCP bind address (host:port); :0 lets the kernel pick a free port (default 127.0.0.1:0) |
--program |
string |
Agent program to run (default: the configured default_program) |
--program-resolved |
Treat --program as an already-resolved runtime command | |
--repo |
string |
Repository path the workspace runs against (default: current directory) |
--session-env |
stringSlice |
Additional exact environment variable name an agent may inherit (repeatable) |
--title |
string |
Session title for the workspace (required) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af api¶
Show the daemon-hosted HTTP/JSON API catalog
Print the HTTP/JSON API the daemon exposes over a local Unix socket.
The daemon serves a small JSON API — a 1:1 mirror of the session and task operations the CLI performs — on a 0600 Unix socket (owner-only; there is no TCP port and no token). This command lists the resolved socket path and every endpoint with a ready-to-run curl example.
It is read-only and never contacts or starts the daemon; it just prints the catalog. Use --json for a machine-readable form wrapped in the shared {data,error} envelope. Full reference: docs/http-api.md.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the catalog as JSON wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af bug-report¶
Bundle logs, versions, tasks, and redacted state for a bug report
Collect a single, shareable diagnostics bundle for triage:
- the daemon log tail (bounded to the last ~2MiB / 5000 lines)
- versions: af, Go, OS/arch, and the daemon's version snapshot
- the configured tasks (redacted)
- the session state from instances.json (redacted)
- the daemon health snapshot (same no-spawn probe as af daemon status)
- the global config file, if any (redacted)
By default the redacted bundle is written to a single text file
(~/af-bug-report-
The draft body carries a bounded, redacted summary of the key diagnostics (versions, daemon status, counts, and the newest log lines) so the report is useful even as filed; the complete bundle is too large for a URL, so it stays on disk for you to attach. The draft is never submitted for you: review it, drag the bundle file onto the issue, and click Submit yourself. If neither gh nor a browser opener is available, the command falls back to just writing the bundle file and printing where it is so you can attach it to an issue by hand.
Use -o/--output
REDACTION IS BEST-EFFORT. Free-text and secret-bearing fields (session titles, session prompts, task prompts, tab and session commands, tab names, account labels, remote metadata) are dropped. A registered account label is replaced by the same marker everywhere it appears — the session records, and the daemon log and config sections that name it as text — so the report is no less private through its log than through its records; if the accounts registry cannot be read, the bundle says so in its collection errors instead of looking redacted when it is not. Every directory the bundle names is replaced by the role it plays ([repo:N], [worktree:N], [af-home], ~) rather than by its own name, and your username by [user]; and known credential shapes are scrubbed wherever they appear. Perfect redaction is impossible — open the file and review it before sharing it publicly.
Use --json to emit the structured manifest (wrapped in the shared {data,error} envelope) to stdout instead of writing a file or opening a draft.
Flags
| Flag | Type | Description |
|---|---|---|
--file |
Only write the bundle file (to ~/af-bug-report- |
|
--json |
Emit the structured manifest to stdout (in the {data,error} envelope) instead of writing a file or opening a draft | |
-o, --output |
string |
Write the bundle to this path and skip opening a GitHub draft (implies --file) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af completion¶
Generate the autocompletion script for the specified shell
Generate the autocompletion script for af for the specified shell. See each sub-command's help for details on how to use the generated script.
Subcommands
af completion bash— Generate the autocompletion script for bashaf completion fish— Generate the autocompletion script for fishaf completion powershell— Generate the autocompletion script for powershellaf completion zsh— Generate the autocompletion script for zsh
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af completion bash¶
Generate the autocompletion script for bash
Generate the autocompletion script for the bash shell.
This script depends on the 'bash-completion' package. If it is not installed already, you can install it via your OS's package manager.
To load completions in your current shell session:
source <(af completion bash)
To load completions for every new session, execute once:
Linux:¶
af completion bash > /etc/bash_completion.d/af
macOS:¶
af completion bash > $(brew --prefix)/etc/bash_completion.d/af
You will need to start a new shell for this setup to take effect.
Flags
| Flag | Type | Description |
|---|---|---|
--no-descriptions |
disable completion descriptions |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af completion fish¶
Generate the autocompletion script for fish
Generate the autocompletion script for the fish shell.
To load completions in your current shell session:
af completion fish | source
To load completions for every new session, execute once:
af completion fish > ~/.config/fish/completions/af.fish
You will need to start a new shell for this setup to take effect.
Flags
| Flag | Type | Description |
|---|---|---|
--no-descriptions |
disable completion descriptions |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af completion powershell¶
Generate the autocompletion script for powershell
Generate the autocompletion script for powershell.
To load completions in your current shell session:
af completion powershell | Out-String | Invoke-Expression
To load completions for every new session, add the output of the above command to your powershell profile.
Flags
| Flag | Type | Description |
|---|---|---|
--no-descriptions |
disable completion descriptions |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af completion zsh¶
Generate the autocompletion script for zsh
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions in your current shell session:
source <(af completion zsh)
To load completions for every new session, execute once:
Linux:¶
af completion zsh > "${fpath[1]}/_af"
macOS:¶
af completion zsh > $(brew --prefix)/share/zsh/site-functions/_af
You will need to start a new shell for this setup to take effect.
Flags
| Flag | Type | Description |
|---|---|---|
--no-descriptions |
disable completion descriptions |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config¶
Read global or project-effective config and write global config
Read and write keys in the global config (~/.agent-factory/config.toml).
"get"/"list" print the effective config for the current repository, including
its checked-in and personal per-project layers. Pass --repo
"set"/"unset" write config in place so all comments and ordering are preserved.
Without --project, set changes one settable global key and unset clears one
migrated grouped/flat alias pair. With --project
Subcommands
af config get— Print one global or project-effective config valueaf config list— Print global or project-effective config valuesaf config migrate— Rewrite deprecated config keys to their current spellingaf config set— Set one global config keyaf config unset— Clear a config override or migrated global settingaf config validate— Check that the global config parses and validates
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config get¶
Print one global or project-effective config value
Print the effective value of one config key (e.g. default_program, auto_update, update_channel). By default the current repository's legacy, checked-in, and personal layers participate; outside git, the command falls back to global defaults. Run "af config list" to see every key. Scalar values print bare; composite values (program_overrides, root_agents, limit_patterns, keys) print as JSON.
Use --repo
Local-only: it answers about the machine it runs on, so --daemon-url/AF_DAEMON_URL is refused rather than ignored. Run it on the daemon host to ask about that host.
Flags
| Flag | Type | Description |
|---|---|---|
--explain |
Show every source candidate and why it did or did not supply the value | |
--json |
Emit the value(s) as JSON wrapped in the {data,error} envelope | |
--repo |
string |
Resolve config for this project instead of the current repository |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config list¶
Print global or project-effective config values
Print every effective config key. By default the current repository's
legacy, checked-in, and personal layers participate; outside git, the command
falls back to global defaults. Pass --repo
Local-only: it answers about the machine it runs on, so --daemon-url/AF_DAEMON_URL is refused rather than ignored. Run it on the daemon host to ask about that host.
Flags
| Flag | Type | Description |
|---|---|---|
--explain |
Show every source candidate and why it did or did not supply each value | |
--json |
Emit the value(s) as JSON wrapped in the {data,error} envelope | |
--repo |
string |
Resolve config for this project instead of the current repository |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config migrate¶
Rewrite deprecated config keys to their current spelling
Rewrite the deprecated keys in the global config to their current spelling, in place, and print the diff. This is the command the deprecated-key warnings name.
It changes spelling, never meaning. A value written on one line is carried over exactly as its own bytes, quoting and all; a value spread over several lines (an array, typically) is re-encoded compactly rather than relocated as raw text, so its formatting can change even though its contents do not. Either way the rewritten file is re-parsed before anything is saved, and a rewrite that would change even one effective value is refused rather than written. The readers of the old spellings stay, so an older config keeps loading and nothing about your running configuration changes.
One thing to know before you DOWNGRADE. The grouped spellings have only been read since 2026-08-14 (#3354); an af older than that does not know them and falls back to the built-in default for a migrated key. For most keys that default is the conservative one (strict host-key checking, no credential mount). Two cases are not, both because the listener defaults to a LIVE 127.0.0.1:8443: migrating network.require_token = true loses the token an older binary could read (network.require_loopback_token only matters alongside it, being inert on its own), and migrating an empty network.listen_addr hides the fact that the web server was turned OFF, so an older binary starts one. Migrate compares what such a binary saw before and after, and says so explicitly when a migration costs you either. Restore the backup before such a downgrade.
The previous file is kept beside it as config.toml.bak (an existing backup is never overwritten; the copy is numbered instead). A legacy config.json is converted to config.toml on the way in, exactly as any af start would convert it.
Running it twice is safe: the second run finds nothing to migrate.
Two things it will not do:
A key written in BOTH spellings with different values is refused, naming the key. af has a documented winner at load time — the grouped value — but no migration should make that tie-break permanent on your behalf.
root_agents is reported and left exactly where it is. Its successor is a registered project's personal [root_agent], so migrating it would mean registering projects for you: durable state outside this file. The keys that can move still move.
Local-only: it rewrites the file on the machine it runs on, so --daemon-url/AF_DAEMON_URL is refused rather than ignored. Run it on the daemon host to migrate that host.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the migration result wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config set¶
Set one global config key
Write one key into the global config.toml, editing only that value in place — preserving every unrelated comment, blank line, section header, and key ordering (the file is not regenerated). Every global config key is settable. Scalar values use their ordinary text form; tables and non-comma lists use compact JSON. Values are validated with the same rules the config loader uses before anything is written, so set can never leave a config that fails to load.
Settable keys:
default_program agent enum (claude, codex, aider, gemini, amp, opencode, devin)
program_overrides compact JSON object of agent-to-command entries
program_overrides.
Legacy CLI aliases listen_addr, preview_listen_addr, require_token, require_loopback_token, cors_allowed_origins, docker_mount_agent_credentials, ssh_host_key_verification, and sandbox_ssh remain accepted and edit the same canonical grouped values.
Structured values must be shell-quoted so the JSON remains one argument. A write uses the same apply-on-save path as the TUI and web config panes (#2480). Most keys apply to the running daemon immediately; each successful set prints its exact effect notice.
With --project
Examples: af config set default_program codex af config set auto_update false af config set appearance dark af config set session_env_passthrough '["HTTP_PROXY","NO_PROXY"]' af config set root_agent.enabled true --project . af config set root_agent.program "codex --profile work" --project . af config set keys '{"quit":"Q"}' af config set program_overrides.claude "/usr/local/bin/claude --verbose" af config set default_program codex --project ~/work/myrepo af config set default_accounts.codex work --project ~/work/myrepo af config unset default_program --project ~/work/myrepo
With --daemon-url/AF_DAEMON_URL naming a remote daemon, the global write is sent to THAT daemon's admission-gated write — the same one the web config form posts to — and the success line names the daemon it landed on. It is never silently applied to this machine instead: a daemon too old to serve the route is refused, not written around. --project is the exception and stays local-only, because it writes a registered project's machine-local override file, which no remote daemon owns.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the value(s) as JSON wrapped in the {data,error} envelope | |
--project |
string |
Write to this project's machine-local config instead of the global config (a prj_ id or a repository path) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config unset¶
Clear a config override or migrated global setting
Remove one key's personal override for a project so the value falls back to the lower layers again (built-in < global < in-repo). Clearing an override is deliberately different from setting a value equal to the lower layer, which is still a present, winning override.
With --project, unset targets a project's machine-local config (a prj_ id from 'af projects list', or a path inside a registered repository). Without --project, it clears one migrated global backend setting: docker.mount_agent_credentials, ssh.host_key_verification, or sandbox.ssh. Their legacy flat CLI names are accepted aliases. Global unset removes both on-disk spellings together, so a conflicting legacy value cannot silently reappear. Every path edits only the target setting, preserves unknown keys and comments, and is a clean no-op when there is nothing to clear.
With --daemon-url/AF_DAEMON_URL naming a remote daemon, the global form is sent to THAT daemon's admission-gated write, like 'af config set'; a daemon too old to serve the route is refused rather than written around, so a remote unset never quietly clears a key on this machine instead. --project stays local-only — the override file it clears is this machine's.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the value(s) as JSON wrapped in the {data,error} envelope | |
--project |
string |
The project whose override to clear (a prj_ id or a repository path) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af config validate¶
Check that the global config parses and validates
Read the global config (~/.agent-factory/config.toml) exactly as af and the daemon do at startup and report whether it loads. It writes nothing and materializes nothing — a read-only check.
This is the companion to a raw hand-edit. "af config set" validates every scalar and structured key before it writes and so cannot leave a broken file. A manual edit bypasses that protection: exit 0 means no config defect was found, while a non-zero exit names what must be fixed before the next launch.
Local-only: it checks the config on the machine it runs on, so --daemon-url/AF_DAEMON_URL is refused rather than ignored. Run it on the daemon host to check that host.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the value(s) as JSON wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon¶
Manage the background daemon: serves the web UI and schedules tasks
The agent-factory daemon runs task cron schedules in-process, supervises watch-task scripts, monitors sessions, and serves the bundled web UI.
The web UI is part of the daemon — there is no separate web command — so it is served whenever the daemon is running. Running af starts one: the TUI reads session state through the daemon and spawns it if none is up, so simply opening af serves the web UI. Any enabled task starts one too. Only standalone commands that never talk to the daemon (such as 'af config list') leave it down.
With af running, open:
http://localhost:8443
It needs no token by default, so the page connects as soon as it loads. Set network.listen_addr to change the address (or to "" to turn the web server off). network.require_token = true demands a bearer token ('af token show') from network peers; on the default loopback listener same-host callers stay exempt, so the UI keeps opening with no login on this machine. Add network.require_loopback_token = true to require the token from localhost as well. Note that 'af agent-server' does not serve the web UI: it is the headless per-workspace backend a daemon drives on a remote machine.
Clients reach the daemon over a local Unix socket by default. To drive one from another machine, either ssh to that host and run 'af' there, or give network.listen_addr a routable address and point a client at it with the persistent --daemon-url and --token flags. A routable listener is allowed with the token off, but af warns once at daemon start: with network.require_token = false anyone who can reach the address drives your agents, so set network.require_token = true unless you trust the network. That listener speaks plain HTTP either way, so put it behind a reverse proxy or a private network (Tailscale/VPN) if you need TLS. Full guide: https://sachiniyer.github.io/agent-factory/remote-http-auth/
Install the daemon as a user-level autostart unit (systemd user service on Linux, launchd agent on macOS) so tasks keep firing and the web UI stays up after reboots, even when af is never opened:
af daemon install
Subcommands
af daemon adopt— Hand a detached daemon back to the installed autostart unitaf daemon install— Register the daemon to start automatically at loginaf daemon restart— Restart the running daemon without stopping live sessionsaf daemon status— Report daemon liveness, config freshness, and supervisionaf daemon uninstall— Remove the daemon autostart unit
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon adopt¶
Hand a detached daemon back to the installed autostart unit
Reclaim supervision of the background daemon for the installed autostart unit.
When a daemon is running detached from its unit — an ad-hoc child a live af spawned, which the service manager can no longer restart — adopt stops that daemon and starts the installed unit in its place, then verifies the unit now owns the process answering the control socket. Live sessions keep running in tmux; the new supervised daemon re-adopts persisted session state on startup, exactly as 'af daemon restart' does.
It needs an installed unit that serves this home ('af daemon install'); with no such unit there is nothing to adopt the daemon into. If the installed unit already owns the running daemon, adopt reports that and changes nothing.
Flags
| Flag | Type | Description |
|---|---|---|
--force |
displace the running daemon even if af cannot confirm it is unsupervised (you assert it is yours to replace) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon install¶
Register the daemon to start automatically at login
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon restart¶
Restart the running daemon without stopping live sessions
Restart the background daemon if one is running. Live sessions keep running in tmux; the new daemon re-adopts persisted session state on startup. If no daemon is running, this command exits successfully without starting one.
Flags
| Flag | Type | Description |
|---|---|---|
--quiet |
Suppress output when no daemon is running |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon status¶
Report daemon liveness, config freshness, and supervision
Print a read-only snapshot of the background daemon: whether it is responding on the control socket, the control and HTTP socket paths (and whether their files are present), the recorded and responding pids, whether the installed autostart unit owns that responding process, whether the config on disk matches what the daemon booted with, and whether the running binary was replaced.
It never contacts a paused daemon in a way that spawns one and never starts the daemon. Service-manager inspection is bounded and read-only. Use --json for a machine-readable form wrapped in the shared {data,error} envelope.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the status as JSON wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af daemon uninstall¶
Remove the daemon autostart unit
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af debug¶
Print debug information like config paths
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af doctor¶
Diagnose setup, daemon health, and leaked session resources
Diagnose the local agent-factory environment.
For first-run setup checks, use:
af doctor --setup
The setup profile checks the prerequisites needed to create the first local session: AF home writability, config materialization and parsing, git and the current repo, git identity, tmux, configured agent commands, state/log storage, daemon health, and remote-hook setup when this repo configures it.
Without --setup, doctor runs the full maintenance sweep for problems that accumulate silently on a machine running agent-factory:
- orphaned processes spawned by sessions that no longer exist
- processes that escaped a live session's pane, or peg a CPU core for hours
- af_ tmux sessions with no backing session record
- abandoned agent-factory homes under the temp dir (leaked by tests/debug runs)
- af daemons running a binary no install owns — one under the temp dir, or one whose file is gone from disk. A temp-dir binary is debris a test or debug run left behind; --fix stops it once it is old enough that no test run can still own it, and never when it serves this home or when which home it serves cannot be read. A missing binary is reported, not stopped: af upgrade replaces it in place, so every healthy daemon looks that way until it restarts
- temp directories holding nothing but a daemon socket nobody answers on — the residue an abandoned daemon's bind left behind. --fix removes one with os.Remove rather than a recursive delete, so a directory that has gained anything since the scan fails instead of being swept up with it
- directories af's own test harness left under the temp dir when a test run ended before its cleanup (af-test-home-, af-test-user-home-, af-tmux-pkg-, af-tmux-, ...). --fix removes one only when it holds nothing but that run's leftover harness content, has not changed for a week, and no live process has a file open in it, names it, or works inside it — entry by entry with os.Remove, never a recursive delete. Anything else in one is reported, and a tmux server still answering in one is named rather than stopped
- daemon health: control socket, autostart unit, pid file, binary freshness
- client/daemon version skew, and the ways a stale daemon survives an upgrade: a second daemon on this home, an autostart unit launching a different af binary than yours, several af installs at different versions, sockets left behind with no daemon answering, and an autostart unit that is installed but not actually supervising anything
- remote-hook setup for the current repo: config completeness and launch_cmd/delete_cmd script presence/executability (skipped cleanly when no remote backend is configured)
- pinned remote host-key directories under hook-hosts/ that no session owns. Hook names are one namespace for the whole machine, so this one spans every project rather than the current repo, and it runs whether or not this repo configures a remote backend. --fix removes a directory only on proof that no session owns it — live, archived, mid-kill and awaiting-teardown sessions all count — and removes nothing at all when any part of that inventory cannot be read
The version-skew check exists because a skewed daemon fails quietly: it keeps
answering while rejecting fields a newer client sends, which surfaces as
"unknown field
Use --json to emit each check as {name, section, status, detail, remedy, actionable} in the shared {data,error} envelope for scripting. Branch on "actionable", not on the status or whether --fix supports the row. Actionable means doctor established a specific unhealthy condition and named a correction that must happen before the run is healthy. Some exact corrections are manual and remain actionable even though --fix does not perform them.
UNKNOWN observations stay visible as advisory warnings with inspection guidance, but they are not actionable: "inspect it and decide" is not a finding that the run is unhealthy. A CI step or health probe should fail on the command exit code: 1 means unresolved actionable issues remain or a check did not finish looking. Advisory warnings alone do not fail the run.
A check that stops early — for example, the temp-home sweep hits a candidate budget on a machine with a very large temp dir — now exits 1 even if it found no unhealthy condition. The summary line ends with "INCOMPLETE" naming the checks that gave up, and summary.incomplete lists them in --json. The exit code already covers incomplete checks, so a plain-exit-code probe needs no extra JSON check. To distinguish the exit-1 cases, read summary.unresolved and summary.incomplete under data in the JSON envelope: exit status -eq 1 with unresolved == 0 means incomplete; unresolved > 0 means actionable issues remain (and summary.incomplete may also be non-empty).
High-volume findings are summarized by default so the actionable problem is visible first — process findings, abandoned temp homes, dead-socket directories, and test-harness directories, all of which run to hundreds or thousands on a busy machine. Use --verbose to show each item behind those summaries. A summary from a check that did not finish reads "at least N … a lower bound" in its own row, so its count is never mistaken for the total.
Read-only by default. With --fix, applies the safe remediations — killing orphans whose ancestry markers prove they came from a dead af session, removing stale temp homes, stopping daemons proven to be running a temp-dir binary, removing directories holding nothing but a dead daemon socket, and removing the test-harness directories described above — logging each action. Ambiguous cases are always reported rather than acted on, and remain advisory unless another check establishes a specific unhealthy condition.
Exits 1 when unresolved actionable issues remain or summary.incomplete is non-empty. Exits 0 when no actionable issues remain and no checks are incomplete (advisory warnings may still be present).
Flags
| Flag | Type | Description |
|---|---|---|
--fix |
apply safe remediations (kill verified orphans and leaked daemons, remove stale temp homes and dead-socket dirs) | |
--json |
emit each check as JSON in the {data,error} envelope | |
--setup |
run the first-run setup profile (prerequisites, config, agent commands) | |
--verbose |
show per-process doctor findings instead of collapsed summaries |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af keys¶
Show the effective TUI key bindings (defaults plus [keys] rebinds)
Show every TUI action with its effective key binding: the built-in default, or the rebind from the [keys] table in config.toml (#1026). Fixed bindings — structural keys config cannot touch — are listed last. Contextual pane actions such as pane_prev/pane_next are included; their default arrow keys apply only while a workspace pane has focus.
Every default key removed by a user rebind is named in SOURCE with its taker. The affected row keeps any remaining keys; when none remain, it shows an em dash instead. JSON appends the same key/taker pairs in suppressed_by.
Key values use config spellings you can paste into [keys]. With --json, bindings are wrapped in {data,error}; keys/default keep those spellings.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Wrap output in the {data,error} JSON envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af projects¶
Manage projects and durable registrations
Subcommands
af projects add— Add a project: register a repo by path with a stable local identityaf projects delete— Delete a project, archiving its restorable sessionsaf projects list— List registered projectsaf projects rebind— Rebind a registered project after its checkout moves
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af projects add¶
Add a project: register a repo by path with a stable local identity
Add a project by registering a git checkout with a stable, machine-local identity, so it appears as an (initially sessionless) project you can create sessions into.
The path may be relative (including '.'), absolute, or start with ~. A relative path or '~' is resolved against YOUR shell's working directory before the request is sent — so 'af projects add .' registers the repo you are standing in. The daemon then walks to the checkout's canonical main-repo root and validates it is a git repository (an actionable error otherwise). Any directory inside a checkout resolves to that root. Registration is idempotent: adding a known checkout is a no-op success that returns its existing identity.
The returned project id survives an explicit rebind after the checkout moves.
Two clones remain separate projects. Identity is anchored in an AF-home-scoped
agent-factory/checkout-id-
'register' is a deprecated alias for 'add'.
Aliases: register
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af projects delete¶
Delete a project, archiving its restorable sessions
Delete a project for a git repository and remove its live sessions.
Every regular worktree session is archived (its tmux is torn down and its worktree moved to the archive dir, but its branch and uncommitted changes are preserved). The always-on root agent (if any) is stopped and its root-agent opt-in removed. In-place sessions (the root agent, 'af sessions create --here') are torn down instead of archived — their cleanup never touches your working tree or branch.
The durable project registration, if any, is removed so the project leaves the project list. Restoring an archived session makes its repository active again, but does not restore the durable registration or root-agent opt-in.
Your real git repository is never touched. To undo a mis-click, restore any archived session with 'af sessions restore
[repo] is a path inside the repository to delete (default: the current repo). Deleting an unknown project is a clean no-op; deleting a registered project with no live sessions still removes its registration. Prints how many sessions were archived.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af projects list¶
List registered projects
List durable machine-local project bindings.
path_exists reports only whether the last-known path is present. It does not claim that a new checkout at a reused path has the registered identity.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af projects rebind¶
Rebind a registered project after its checkout moves
Rebind a stable project id to a new checkout path.
The project id is preserved. Rebinding refuses to take a path already owned by another registered project.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af quota¶
Show usage-limit status for each agent CLI
Show what af knows about each agent CLI's usage limits.
Two different things, kept apart on purpose:
QUOTA what the provider reports about the account's ceiling. af has no quota API for any supported agent today, so every row reads "not reported". That is af declining to guess, not a ceiling of zero.
OBSERVED what af has seen in its OWN sessions — a session parked at a usage wall, and the reset time recorded with it. Real signal even where the provider exposes nothing.
Read-only: it reads local session records and starts nothing.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af reset¶
Factory-reset Agent Factory: remove AF sessions, tasks, project registrations, worktrees, and state (keeps repos and config)
Factory-reset Agent Factory.
Removes every AF-created resource — all sessions (live and archived), all scheduled cron/watch tasks, registered-project bindings and their reachable checkout identity markers for this AF home, all AF worktrees, the AF session branches AF created, and all stored state — returning AF to a clean slate.
Stops every af daemon running for this AF home — the managed one and any orphan left behind by an upgrade or a source build — and removes the daemon sockets, so a stale daemon or socket cannot serve the next af you start. Only daemons owned by you AND using this AGENT_FACTORY_HOME are stopped, and the autostart unit is only paused when it serves this AGENT_FACTORY_HOME; a daemon or unit for a different AF home is never touched.
KEEPS your real git repositories (working tree, .git, and your own branches), and KEEPS the daemon configuration (config.toml: network.listen_addr, defaults, root_agents, update_channel, and per-repo config). After the wipe the supervised daemon restarts with empty session/task state and the same config; root_agents in config re-register, which is intended.
This is IRREVERSIBLE. You will be asked to type WIPE to confirm. Pass --yes (or --force) to skip the prompt for scripted use; when stdin is not a terminal the prompt is skipped automatically so existing scripted callers do not hang.
Flags
| Flag | Type | Description |
|---|---|---|
--force |
Alias for --yes: skip the typed WIPE confirmation | |
-y, --yes |
Skip the typed WIPE confirmation (for non-interactive/scripted use) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions¶
Manage sessions
Subcommands
af sessions archive— Finish with a session by archiving it for later restoreaf sessions attach— Attach to a session's terminalaf sessions backends— List the runtimes this project can create sessions onaf sessions create— Create a new sessionaf sessions get— Get a session by titleaf sessions handoff— Continue a session under a different agent, in placeaf sessions kill— Permanently delete a session and af-owned resourcesaf sessions list— List sessions in the current projectaf sessions preview— Preview a session's terminal contentaf sessions restore— Restore an archived, lost, or dead sessionaf sessions retry-limit— Retry a usage-limit resume or inspected handoffaf sessions send-prompt— Send a prompt to a session (or broadcast to all with --all)af sessions tab-create— Spawn a shell, process, web, or VS Code tab in a sessionaf sessions tab-delete— Delete a single tab from a sessionaf sessions tab-rename— Rename a tab of a sessionaf sessions tab-reorder— Move a tab within a session's tab orderaf sessions tabs— Manage a session's tabs (create/delete/rename/reorder)af sessions watch— Wait for idle, or a fleet stop-state change or disappearanceaf sessions whoami— Identify the current Agent Factory session
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions archive¶
Finish with a session by archiving it for later restore
Archive keeps a session restorable. Locally, stop its terminals
and move its owned git worktree to the global archive directory
(
Archive is refused while any enabled task targets the session. The error names every blocking task; disable or retarget them, then archive again. Agent Factory never silently restores the target or disables its automation as a side effect. While a session is archiving or archived, task writes that would newly enable it as a target are likewise refused until the session is restored.
With --self, archive the current session (resolved via whoami) instead of a named one — use it from inside a session when your work is done. --self and a
Sandboxes publish work before removal; restore recreates them from the published branch. In-place (--here) sessions cannot be archived because af does not own their worktree. Local archives print the relocated worktree path on success.
Flags
| Flag | Type | Description |
|---|---|---|
--self |
Archive the current session (resolved via whoami); use from inside a session when your work is done |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions attach¶
Attach to a session's terminal
Attach to a running session's tmux terminal. Detach with the configured detach key (default: Ctrl-w).
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions backends¶
List the runtimes this project can create sessions on
Report which backends 'af sessions create --backend' accepts for this project, whether each one is usable as the project is configured right now, and which backend a create with no --backend resolves to.
--backend names the enum, but knowing that "docker" is spelled correctly is not the same as knowing this project can use it. The daemon checks each backend's preconditions against the project's config and answers one of three things:
available every precondition that can be checked was checked and passed unavailable a precondition FAILED — a create would fail; "reason" says what to fix unknown the preconditions could NOT be evaluated (e.g. the project's config would not parse) — neither yes nor no is honest, so "reason" says what stopped the check
"unknown" is deliberately not folded into either of the other two: reporting an unchecked backend as available is a promise nobody verified.
"default" is the backend a create with no --backend resolves to here. It is EMPTY when the project's 'backend' config key names something unrecognized — such a create fails rather than quietly running local, and "default_reason" names the offending value.
The reasons are the same text a create prints when it refuses, because both come from the same precondition checks.
Example: af sessions backends af sessions backends --repo ~/src/myproject
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions create¶
Create a new session
Create a new session running an agent in its own git worktree.
With --prompt, unconfirmed or incomplete delivery adds a warning field to the JSON session result. Outside --json mode, it also prints the warning on stderr. The session remains created; inspect its pane before retrying, since an unverified prompt may already have run. Creation still exits successfully and does not automatically resend.
With --here (alias --in-place) the session instead attaches to the repo's existing working tree at its current branch: no worktree or branch is created, the agent runs in the repo root, and killing the session never removes the working tree or branch. Requires running inside a git repository (or --repo pointing at one).
Flags
| Flag | Type | Description |
|---|---|---|
--account |
string |
Credential account name to run the agent as (register it with af accounts add; defaults to this project's default_accounts entry for the agent, else the ambient identity) |
--backend |
string |
Runtime to run the session on (one of: local, docker, ssh, sandbox, hook; defaults to the repo's backend config, or local). docker runs the session in a container (set docker.image in the repo config); ssh runs it on a remote host (set ssh.host in the repo config). Run "af sessions backends" for which of these this project can actually use, and why not |
--here |
Run in the repo's existing working tree at its current branch (no new worktree/branch; kill preserves both) | |
--in-place |
Alias for --here | |
--name |
string |
Session title (alternative to positional |
--program |
string |
Program to run (one of: claude, codex, aider, gemini, amp, opencode, devin; defaults to config default) |
--prompt |
string |
Initial prompt to send |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions get¶
Get a session by title
Titles are unique within a project, not across projects, so the same name can exist in several repos. The title resolves inside the repo given by --repo, or the current directory's repo when --repo is omitted.
With no repo context, a title held by exactly one session still resolves; one held by sessions in several projects is ambiguous and reports an error naming those projects instead of guessing between them.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions handoff¶
Continue a session under a different agent, in place
Hand a session's work over to another agent or account without losing it.
Use --account to choose a registered account. Omit --to to keep the same agent and stored prompt, or combine both flags to change agent and account. A manual handoff moves an explicit account pin; automatic rotation still respects it. Targets with current usage-limit evidence are refused.
An account belongs to one agent, so a scoped session that changes agents must name the incoming agent's account with --account — unless the target has no account support at all, which drops the scope instead and reports it on from_account. What decides capability is the command the target resolves to, not the enum: program_overrides can make aider launch codex (a codex account is then required) or codex launch something unscopable (the scope is dropped). A resolved command af cannot classify as an agent at all — a wrapper like "npx codex" may launch an account-capable agent underneath — refuses rather than drop the pin on an unproven answer. The drop is one-way: handing back to an account-capable agent later does not restore it, so name the account again with --account. Dropping the scope restarts only the agent pane, so a session with shell, process, or VS Code sibling tabs is refused until those tabs are closed — they would keep running under the dropped account's environment.
The session keeps its identity, its git worktree, and its branch — only the agent process changes. A different agent starts a fresh conversation and is given a mission brief: the session's goal, and what is already on the branch.
A same-agent account handoff (--account alone, or --to naming the current agent) keeps the conversation for claude and codex: af copies the transcript into the new account's home and resumes it. If that copy cannot be made, the new account starts a fresh conversation, and its brief says why.
This is the answer to an agent that has stopped and cannot continue — most often one blocked at its provider's usage limit, where the alternative is waiting for the window to reset (see 'af sessions list' for a [limit] badge, and docs/usage-limits.md for the waiting path).
Agent conversations are not portable between providers: after a cross-agent handoff the incoming agent cannot read what its predecessor was thinking, only the working tree and the git history. The brief points it at both. Because of that, a handoff is recorded — the swap and the branch tip at the moment it happened — so a reviewer reading the resulting diff can tell which agent wrote which part.
Local-worktree sessions only: swapping the agent inside a remote/docker/ssh sandbox is a different lifecycle and is not supported yet.
Examples: af sessions handoff fix-auth --account personal af sessions handoff fix-auth --to claude af sessions handoff fix-auth --to gemini --brief "finish the retry test, skip the docs"
Flags
| Flag | Type | Description |
|---|---|---|
--account |
string |
Registered target account; omit --to to keep the same agent |
--brief |
string |
Mission for the incoming agent, replacing the session's stored prompt |
--to |
string |
Agent to hand the session off to (one of claude, codex, aider, gemini, amp, opencode, devin) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions kill¶
Permanently delete a session and af-owned resources
Permanently delete the session record and stop its terminals. Remove only worktrees and branches owned by af; user-owned resources stay.
For normal "done with this session" cleanup, prefer: af sessions archive
Deletion is permanent. Uncommitted or unmerged work in af-owned resources may be lost. Archive instead to keep the session restorable. --force is accepted but has no effect (kept for backward compatibility).
Flags
| Flag | Type | Description |
|---|---|---|
--force |
Deprecated no-op, accepted for compatibility: kill always destroys the session (use 'af sessions archive' to keep it restorable) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions list¶
List sessions in the current project
List sessions in the current project.
Scope follows the shared project-context contract: --repo names a project, otherwise the current directory's project is used, and --all spans every project. Run from outside a git repository with no --repo, there is no project context and every project's sessions are listed. Lifecycle, age, and limit filters compose and are applied by the daemon before transfer. With no filter flags, the complete list and its existing order are unchanged.
Every row names its own integers. liveness_name is the one --status matches, and it round-trips exactly; status_name only names the legacy status integer and is not a filter value. tabs[].kind_name uses the tab_kinds[].kind words.
Flags
| Flag | Type | Description |
|---|---|---|
--all |
List sessions across every project instead of only the current one | |
--limit |
int |
Return at most N sessions after filtering (must be greater than 0 when set; omitted is unbounded) (default 0) |
--live |
Exclude archived sessions | |
--max-age |
duration |
Only list sessions created within this duration (for example 24h) (default 0s) |
--status |
stringArray |
Filter by lifecycle status; repeat for more than one (running, ready, lost, dead, archived, limit-reached). Matches each row's liveness_name; status_name names the legacy status integer and is not a filter value |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions preview¶
Preview a session's terminal content
Titles are unique within a project, not across projects, so the same name can exist in several repos. The title resolves inside the repo given by --repo, or the current directory's repo when --repo is omitted.
With no repo context, a title held by exactly one session still resolves; one held by sessions in several projects is ambiguous and reports an error naming those projects instead of guessing between them.
By default this captures the session's AGENT tab (slot 0), visible screen only. Address another tab with --tab-name (the tab name "af sessions tab-create" printed, as reported by "af sessions get" — not the TUI's "Agent"/"Terminal" label), --tab-id (the stable id, for scripts that must not follow a reused name), or --tab (the 0-based slot). They resolve in that precedence — id, then name, then slot — the same order every tab verb uses. An id or name that does not resolve is an error, never a silent fall back to a slot: that would capture whatever tab had shifted into it.
--full returns the entire scrollback instead of the visible screen. The default capture is the VISIBLE SCREEN, so for a pane that has scrolled it omits whatever is above — the output says so ("partial" in the JSON, a note on stderr) and names how many lines were left out, because a partial capture that looks complete is how a working session gets read as a wedged one (#3169).
--plain strips ANSI escape sequences from the captured content, for callers that parse it rather than render it.
Flags
| Flag | Type | Description |
|---|---|---|
--full |
Capture the entire scrollback instead of the visible screen | |
--plain |
Strip ANSI escape sequences from the captured content | |
--tab |
int |
Tab slot to capture, 0-based as the tab bar reads left to right (slot 0 is the agent tab) (default 0) |
--tab-id |
string |
Stable id of the tab to capture (#1738); wins over --tab-name and --tab |
--tab-name |
string |
Name of the tab to capture, as reported by "af sessions get" (not the TUI's "Agent"/"Terminal" label); wins over --tab |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions restore¶
Restore an archived, lost, or dead session
Restore a session that is currently archived, lost, or dead.
Archived sessions are moved back next to the repository, re-registered, re-spawned, and marked running. Lost/dead sessions are recovered in place, rebuilding a missing worktree when possible and resuming the recorded agent conversation when required.
Fails if the session is not restorable, or if its origin repository is gone. The restored worktree path is printed on success.
For a sandbox session (docker/ssh/hook) whose sandbox still ANSWERS but whose agent is gone, restore first pushes the sandbox's work to origin, because recovery re-clones from there and anything unpushed would be destroyed. If that push fails, or if af cannot tell whether the sandbox is gone or merely unreachable, the restore REFUSES and the session stays recoverable.
--force-reap replaces the sandbox anyway, without the push. It discards whatever that sandbox has not pushed, so it is for a sandbox you know is expendable. It applies to this one session and this one command; there is no global equivalent.
Flags
| Flag | Type | Description |
|---|---|---|
--force-reap |
replace a reachable sandbox WITHOUT pushing its work first (discards anything it has not pushed) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions retry-limit¶
Retry a usage-limit resume or inspected handoff
Retry a session parked at a provider usage-limit wall, or explicitly retry a handoff whose mission delivery could not be confirmed.
The daemon runs the same recovery action as the TUI's c key and the web's Retry button: it re-spawns an exited agent when necessary, re-delivers the pending prompt (or "continue" for an interactive session with no stored prompt), and clears the limit state after delivery succeeds.
Before retrying an unconfirmed handoff, inspect its pane: the first submission may already have landed, and this command is the operator's explicit decision to send the pending mission again. The command fails when neither recovery obligation exists. Use 'af sessions list' to find sessions carrying the [limit] badge; the TUI and web expose Retry handoff for an unconfirmed handoff.
Example: af sessions retry-limit fix-auth
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions send-prompt¶
Send a prompt to a session (or broadcast to all with --all)
Send a prompt to an existing session. The session must already exist unless --create is used.
If the session does not exist, use --create to automatically create it first, or use 'af sessions create --name
For one target, the JSON acknowledgement includes status: delivered, not-delivered, sent-unverified, or could-not-confirm. sent-unverified means tmux accepted the paste and Enter while a readable pane did not render exact content proof; could-not-confirm means the pane observer itself was unavailable. Neither status claims delivery, and the command adds no second confirmation wait.
With --all, broadcast a single prompt to every live session in scope:
af sessions send-prompt --all "<prompt>"
Broadcast scope defaults to the current repo (honoring --repo). Pass --all-repos to broadcast across every repo. The reserved root session is excluded unless --include-root is given. Delivery is best-effort per session: unreachable (Lost, Dead) and Archived sessions are skipped and reported, and one failure never aborts the rest. The command prints a JSON summary whose delivery observations are delivered / not-delivered / sent-unverified / could-not-confirm, alongside failed and skipped targets. It exits 0 even when some sessions fail, so scripts can inspect per-session results.
Flags
| Flag | Type | Description |
|---|---|---|
--all |
Broadcast the prompt to every live session in scope (current repo by default; excludes the reserved root session) | |
--all-repos |
With --all, broadcast across every repo instead of only the current/--repo one | |
--create |
Auto-create the session if it doesn't exist | |
--include-root |
With --all, also deliver to the reserved root session (excluded by default) | |
--program |
string |
Program to run when creating a new session (one of: claude, codex, aider, gemini, amp, opencode, devin; defaults to config default) |
--prompt |
string |
The prompt to send (alias for the positional |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tab-create¶
Spawn a shell, process, web, or VS Code tab in a session
Create a new tab in an existing session.
Shell tab (--kind shell): starts the user's $SHELL in the session's git worktree. This is the same Terminal tab the TUI and web UI open; "shell" is its canonical kind and name, while "Terminal" is only the label those UIs display.
Process tab (default): runs --command in the session's git worktree (e.g. a data explorer TUI or a test watcher). If --name is omitted, a name is derived from the command's basename. The command runs once, at creation, and af never runs it again. If it exits non-zero immediately (a mistyped command, for example), tab-create fails with the exit status and the command's last output, and no tab is added. Across a daemon/af restart, af reattaches to the pane: a running command keeps running, and a finished one keeps its output and records its exit status in the tab's "exit" field. A process tab whose tmux session is gone entirely restores inert.
In an account-scoped session, af stops a running process tab it did not start under the session's account, once, at restart; an account swap stops every running process tab. Neither runs the command again, and the tab's exit.stopped_by says why ("account-scope" or "account-swap"). A finished process tab with nothing left running is kept as it is.
Web tab (--kind web): a URL/iframe tab with NO process — an agent injects a live
browser view into the user's screen. Point it at a local dev server with --port
The tab persists and reconnects across a daemon/af restart like every other tab — for a process tab "reconnect" means reattach-only, never re-running the command (see above).
--name sets a process, web, or VS Code tab's name — the handle every other tab verb addresses it by. A shell tab does not accept --name: its canonical name is "shell" (auto-suffixed on collision), while the TUI renders the presentation-only label "Terminal". Other names are sanitized before use: characters outside [A-Za-z0-9_-] become "-". The name is then made unique within the session (auto-suffixed -2, -3, …). So the name you pass is not always the name you get — the resolved tab name is printed on success, and that is the one the other tab verbs address.
For remote sessions, only external HTTPS web tabs are admitted. That kind is metadata-only: it needs no PTY and spawns no process in the missing daemon-side worktree. Shell, process, and VS Code tabs still require local runtime/worktree resources, while loopback web targets still need a relay through the off-box agent and plain HTTP cannot be framed by an HTTPS Agent Factory UI.
Flags
| Flag | Type | Description |
|---|---|---|
--command |
string |
Command to run in a process tab (required when --kind is empty) |
--kind |
string |
Tab kind: "shell" for a $SHELL terminal, empty for a process command, "web" for a URL/iframe, or "vscode" for VS Code |
--name |
string |
Tab name — sanitized to [A-Za-z0-9_-] and auto-suffixed on collision (not valid with --kind shell) |
--port |
int |
Web tab convenience for --url http://localhost:0) |
--url |
string |
Web tab target URL (with --kind web): a localhost dev-server address or an external https URL |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tab-delete¶
Delete a single tab from a session
Delete the named tab from an existing session — the counterpart of tab-create.
The tab is removed from the daemon's session state and its tmux window is killed. The removal is persistent: the daemon will not respawn the tab, and it does not return on a daemon/af restart. The name to pass is the tab name tab-create printed, as reported by "af sessions get" — not the label the TUI tab bar shows, which is a fixed "Agent"/"Terminal" for agent and shell tabs. A miss lists the tabs that exist, with those labels, so a wrong name is a next step rather than a dead end.
The agent tab can't be deleted — use "af sessions kill" to tear down the whole session. Deleting a tab or session that doesn't exist is an error, not a silent success. On remote sessions, admitted metadata-only web tabs can be deleted; PTY-backed kinds are never admitted there.
Flags
| Flag | Type | Description |
|---|---|---|
--name |
string |
Name of the tab to delete (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tab-rename¶
Rename a tab of a session
Rename an existing tab — the fix for a name you have to live with all day, typically one an agent picked when it created the tab.
Only web, process and VS Code tabs can be renamed: those are the tabs that display their name. The agent tab always shows "Agent" and shell tabs always show "Terminal" on every surface, so renaming one would change the handle you address it by without changing anything you can see — confusing rather than useful, so it is refused.
--new-name follows the same rules as tab-create's --name: characters outside [A-Za-z0-9_-] become "-", and the name is made unique within the session (auto-suffixed -2, -3, …). A name that sanitizes away to nothing is an error rather than a silent fall back to a default. The resolved name is printed on success — that is what the tab is actually called, and what the other tab verbs now address it by.
The rename persists across a daemon/af restart and does not disturb the tab's running process. On remote sessions, admitted metadata-only web tabs can be renamed; PTY-backed kinds are never admitted there. Archived sessions still have to be restored first.
Flags
| Flag | Type | Description |
|---|---|---|
--name |
string |
Name of the tab to rename (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
--new-name |
string |
New name for the tab (required; sanitized and auto-suffixed on collision) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tab-reorder¶
Move a tab within a session's tab order
Move a tab to a different slot, so the tab order is yours rather than whatever order the tabs happened to be created in.
--index is the destination slot, 0-based, counting the tab bar left to right, and read as the tab's FINAL position: moving a tab to --index 3 of a 4-tab session puts it last.
Slot 0 is reserved for the agent tab: the agent tab can't be moved, and no tab can be moved in front of it. That is structural, not cosmetic — the agent tab is identified by its position throughout a session's lifecycle.
The new order persists across a daemon/af restart and does not disturb any tab's running process. On remote sessions, admitted metadata-only web tabs can be reordered; PTY-backed kinds are never admitted there. Archived sessions still have to be restored first.
Flags
| Flag | Type | Description |
|---|---|---|
--index |
int |
Destination slot, 0-based, as the tab bar reads left to right (required; slot 0 is the agent tab and can't be targeted) (default 0) |
--name |
string |
Name of the tab to move (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tabs¶
Manage a session's tabs (create/delete/rename/reorder)
Noun-subcommand aliases for the tab-create/tab-delete/tab-rename/tab-reorder verbs.
"sessions tabs create" is identical to "sessions tab-create", and the same holds for delete, rename and reorder; the hyphen verbs remain supported for existing scripts. To list a session's tabs, use "sessions get
Subcommands
af sessions tabs create— Spawn a shell, process, web, or VS Code tab in a sessionaf sessions tabs delete— Delete a single tab from a sessionaf sessions tabs rename— Rename a tab of a sessionaf sessions tabs reorder— Move a tab within a session's tab order
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tabs create¶
Spawn a shell, process, web, or VS Code tab in a session
Alias for "sessions tab-create". See "af sessions tab-create --help" for details.
Flags
| Flag | Type | Description |
|---|---|---|
--command |
string |
Command to run in a process tab (required when --kind is empty) |
--kind |
string |
Tab kind: "shell" for a $SHELL terminal, empty for a process command, "web" for a URL/iframe, or "vscode" for VS Code |
--name |
string |
Tab name — sanitized to [A-Za-z0-9_-] and auto-suffixed on collision (not valid with --kind shell) |
--port |
int |
Web tab convenience for --url http://localhost:0) |
--url |
string |
Web tab target URL (with --kind web): a localhost dev-server address or an external https URL |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tabs delete¶
Delete a single tab from a session
Alias for "sessions tab-delete". See "af sessions tab-delete --help" for details.
Flags
| Flag | Type | Description |
|---|---|---|
--name |
string |
Name of the tab to delete (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tabs rename¶
Rename a tab of a session
Alias for "sessions tab-rename". See "af sessions tab-rename --help" for details.
Flags
| Flag | Type | Description |
|---|---|---|
--name |
string |
Name of the tab to rename (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
--new-name |
string |
New name for the tab (required; sanitized and auto-suffixed on collision) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions tabs reorder¶
Move a tab within a session's tab order
Alias for "sessions tab-reorder". See "af sessions tab-reorder --help" for details.
Flags
| Flag | Type | Description |
|---|---|---|
--index |
int |
Destination slot, 0-based, as the tab bar reads left to right (required; slot 0 is the agent tab and can't be targeted) (default 0) |
--name |
string |
Name of the tab to move (required; the tab's name, not the TUI's "Agent"/"Terminal" label) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions watch¶
Wait for idle, or a fleet stop-state change or disappearance
Watch a session and exit 0 when its agent is idle and awaiting input. Idle does not mean the work is complete.
Polls the daemon's snapshot (the same read path as 'af sessions get') every --interval (default 2s). Exits non-zero if the session reaches a terminal state it can't leave on its own (lost, dead, or archived), if it disappears (killed), or if --timeout elapses first (default 30m). A session that is still working, a usage-limit block that auto-resumes, or a create/archive/restore in progress all keep the watch waiting.
By default prints a concise line on transition; with --json emits the final session record. Honors --repo to scope the title lookup to one repository.
With NO title (or --all) it watches every session in scope and returns on the first stop-state change or disappearance, printing which session and why. It is edge-triggered: the first poll establishes a baseline and reports nothing, so a session that was already idle before you called does not fire. Pass --include-current to report those too, for a driver that wants a starting snapshot; that snapshot omits archived sessions, which are inert by construction and cannot change on their own (a session archived WHILE you watch is still reported).
Each reported session carries a reason a driver can act on: idle, usage-limited (af resumes it automatically — do not prompt it), lost, dead, archived, killed, gone, or unknown. "idle" covers both "finished its work" and "waiting on input": af records both as the same state and does not distinguish them, so neither does this. A session af cannot classify reports "unknown" and is never reported as idle, because an idle report tells a driver to act.
Flags
| Flag | Type | Description |
|---|---|---|
--all |
Watch every session in scope and return when the first one changes state (the default when no title is given) | |
--include-current |
Fleet form: also report sessions that are already stopped at the first poll, instead of only transitions | |
--interval |
duration |
How often to poll the session's status (default 2s) |
--timeout |
duration |
Give up and exit non-zero if the session is not idle within this window (0 = wait forever) (default 30m0s) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af sessions whoami¶
Identify the current Agent Factory session
Returns the session info for the current tmux session by matching the tmux session name against stored sessions.
Requires TMUX and TMUX_PANE. Queries that pane on the inherited tmux socket. When tmux supports session environment stamping (3.2+), AF_SESSION must match the pane; otherwise inherited markers are ignored. Missing context or a stamped identity mismatch is an error.
Identity is not scoped: you are the session you are, in whatever project it belongs to. --repo therefore acts as an assertion — it checks that the resolved session really is in that project, and errors if it is not.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks¶
Manage tasks
Subcommands
af tasks add— Add a new task bound to the current projectaf tasks get— Get a task in the current project by IDaf tasks list— List tasks in the current projectaf tasks remove— Remove a task in the current projectaf tasks restart— Restart an enabled watch task without process overlapaf tasks show— Show one task's schedule health and audit trailaf tasks trigger— Trigger a task in the current project to run immediatelyaf tasks update— Update a task in the current project
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks add¶
Add a new task bound to the current project
Add a new task bound to the current project.
A task is bound to exactly one project, and every run's worktree is created inside it. The binding comes from --repo when given, otherwise from the current directory's git repository (a linked worktree resolves to its main repository). The resolved project is echoed back as project_path so the binding is visible at creation rather than inferred later.
Outside a git repository, --repo is required — the binding is never guessed. A current directory that resolves to a clone inside af's own home is refused as a stray checkout (#1891); pass --repo to name the intended project.
With --daemon-url/AF_DAEMON_URL set, the task is added to that daemon and --repo is required, naming a path on the DAEMON's host: it is sent as typed and resolved there, since a path on this machine says nothing about the daemon's filesystem. The success line names the daemon URL beside the path. An omitted --program is left to the daemon's own default_program rather than resolved from this machine's config.
Flags
| Flag | Type | Description |
|---|---|---|
--cron |
string |
Cron expression (exactly one of --cron / --watch-cmd) |
--max-concurrent-runs |
int |
Cap how many sessions this watch task may have in flight at once; excess events are queued in order instead of spawning runs, subject to the durable queue's retention limits (0 = unlimited; --watch-cmd tasks without --target-session only) (default 0) |
--name |
string |
Task name (required) |
--on-complete |
string |
What happens to the session a run spawns once it finishes: keep (default, leaves it in place), archive (restorable, but retains its whole worktree), or kill (reclaims the worktree and prunes the session's branch). Not for --target-session tasks, whose session is meant to be reused |
--program |
string |
Program to run (one of: claude, codex, aider, gemini, amp, opencode, devin; defaults to config default) |
--prompt |
string |
Prompt to send (required for --cron tasks; --watch-cmd tasks default to the emitted line, with {{line}} substituted when present) |
--target-session |
string |
Deliver the prompt into this session (auto-created if missing); empty creates a new session per run |
--watch-cmd |
string |
Long-running watch command; each stdout line triggers the task (exactly one of --cron / --watch-cmd) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks get¶
Get a task in the current project by ID
Get a task in the current project by ID.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Inspecting another project's task requires naming it with --repo. Outside a git repository there is no project context and the id resolves globally.
With --daemon-url/AF_DAEMON_URL set, the task is looked up on that daemon and never in this machine's store. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks list¶
List tasks in the current project
List tasks in the current project.
Scope follows the shared project-context contract: --repo names a project, otherwise the current directory's project is used, and --all spans every project. Run from outside a git repository with no --repo, there is no project context and every project's tasks are listed.
This default changed in #1893: af tasks list inside a repository used to list every project's tasks. Pass --all for the old behavior.
With --daemon-url/AF_DAEMON_URL set, the list comes from that daemon and never from this machine's task store — a daemon that cannot be reached is an error, not a fall back to local rows. There is no project context against a remote daemon: your current directory names a repository here, which says nothing about the daemon's projects, so every project's tasks are listed and --repo is refused rather than silently matching nothing.
Flags
| Flag | Type | Description |
|---|---|---|
--all |
List tasks across every project instead of only the current one |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks remove¶
Remove a task in the current project
Remove a task in the current project.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Removing another project's task requires naming it with --repo. Outside a git repository there is no project context and the id resolves globally.
With --daemon-url/AF_DAEMON_URL set, the task is looked up on that daemon and never in this machine's store. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks restart¶
Restart an enabled watch task without process overlap
Restart an enabled watch task in the current project. The command waits for the old process tree to exit before starting one replacement, so an edited script is re-read without double-emitting events.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Outside a git repository there is no project context and the id resolves globally.
With --daemon-url/AF_DAEMON_URL set, the task is looked up on that daemon and never in this machine's store. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks show¶
Show one task's schedule health and audit trail
Show one task in the current project: its trigger, whether the running daemon has it armed, when it will next fire, whether it has missed scheduled runs, and the bounded audit trail of who created, updated, enabled, or disabled it.
Overdue is derived, never stored: a cron task is overdue when it has gone more than one period (or five minutes, whichever is larger) past its most recent scheduled occurrence, measured from the latest of its last run, its last enable, and its creation — so a task paused and switched back on does not report the occurrences it missed while it was off. Watch tasks have no schedule and are never overdue — their arming state is the signal.
"Enabled but not armed" means the task is enabled on disk and the running daemon is not holding it: it will not fire until that is fixed. When nothing has reported on it — no daemon running, or one still starting up — the arming state is reported as unknown rather than guessed.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Outside a git repository there is no project context and the id resolves globally.
Pass --json for the same record af tasks get returns, in the {data,error} envelope.
With --daemon-url/AF_DAEMON_URL set, the task is read from that daemon, a Daemon row names it beside the project path, and the schedule verdict is the one the DAEMON derived — a cron expression is evaluated in the scheduler's timezone, and re-deriving it here would answer in this terminal's. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks trigger¶
Trigger a task in the current project to run immediately
Trigger a task in the current project to run immediately.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Triggering another project's task requires naming it with --repo. Outside a git repository there is no project context and the id resolves globally.
With --daemon-url/AF_DAEMON_URL set, the task is looked up on that daemon and never in this machine's store. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused.
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af tasks update¶
Update a task in the current project
Update a task in the current project.
The task must belong to the resolved project: --repo when given, otherwise the current directory's project. Updating another project's task requires naming it with --repo. Outside a git repository there is no project context and the id resolves globally.
--repo scopes which task may be updated; it never re-binds one. Pass --project-path to move that task to another existing git repository. The new path becomes the task's working directory and project binding.
With --daemon-url/AF_DAEMON_URL set, the patch is applied on that daemon and never to this machine's store. There is no project context against a remote daemon, so the id resolves across its projects and --repo is refused; --project-path names a path on the DAEMON's host and is sent as typed for it to resolve.
Flags
| Flag | Type | Description |
|---|---|---|
--cron |
string |
New cron expression (clears watch-cmd) |
--enabled |
string |
Enable or disable the task (true/false) |
--max-concurrent-runs |
int |
New in-flight session cap for this watch task; pass 0 to revert to unlimited (default 0) |
--name |
string |
New task name |
--on-complete |
string |
New spawned-session lifecycle: keep, archive, or kill; pass keep to revert to leaving sessions in place |
--program |
string |
New program to run (one of: claude, codex, aider, gemini, amp, opencode, devin; leave unset to keep the current one) |
--project-path |
string |
Move the task to this git repository (distinct from --repo, which scopes its current project) |
--prompt |
string |
New prompt |
--prompt-file |
string |
Read the new prompt verbatim from a local file (mutually exclusive with --prompt) |
--target-session |
string |
New target session; pass an empty value to revert to a new session per run |
--watch-cmd |
string |
New watch command (clears cron) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--json |
Wrap output in the {data,error} JSON envelope (default: bare payload) | |
--repo |
string |
Path to the project's git repository (default: the current directory's project) |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af token¶
Manage the daemon's bearer token for the direct-TCP API
Manage the bearer token that authenticates the daemon's direct-TCP HTTP API.
The token grants full access under the single-owner auth model. It is only used by the TCP listener (enabled with the network.listen_addr config key); the local unix socket stays unauthenticated (its 0600 filesystem perms are the local auth). The token is stored in the af home (~/.agent-factory) with 0600 permissions.
The listener serves plain HTTP — af terminates no TLS of its own. The token travels over the connection, so expose the listener only behind a reverse proxy (nginx/caddy) or on a private network (Tailscale/VPN/SSH tunnel).
Subcommands
af token rotate— Replace the bearer token with a fresh one and print itaf token show— Print the bearer token (generating it if absent)
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af token rotate¶
Replace the bearer token with a fresh one and print it
Generate a new bearer token, persist it (overwriting the old one), and print it.
Rotation takes effect for new connections immediately — the auth gate re-reads the token file per request — while any in-flight streams keep running until they reconnect.
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the result as JSON wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af token show¶
Print the bearer token (generating it if absent)
Print the daemon's bearer token.
It is generated on first access if it does not yet exist, so this is safe to run before the TCP listener is ever enabled. Present it to a remote daemon with the --token flag (or the AF_DAEMON_TOKEN env var).
Flags
| Flag | Type | Description |
|---|---|---|
--json |
Emit the result as JSON wrapped in the {data,error} envelope |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af upgrade¶
Upgrade agent-factory to the latest release on the configured channel
Upgrade agent-factory to the newest release on the configured update channel (stable by default, or preview via the update_channel config key).
You rarely need this: af auto-updates on launch by default, at most once every 6 hours, and re-launches you into the new version. Disable that with auto_update = false in your config to pin the installed version — af upgrade keeps working either way.
A manual upgrade never downgrades: if the channel's latest release is older than the running binary — which happens when you switch from the preview channel back to stable — the upgrade is a no-op with an explanation. Pass --allow-downgrade to install the older release anyway.
A manual upgrade also refuses to install a build this machine has already rolled back, so this repair path does not silently reinstall the bytes the rollback just recovered from. Publishing a corrected build is the normal fix; pass --allow-rejected to install the rejected build anyway.
The same refusal protects launch auto-update and the daemon's transactional upgrade. Neither unattended path can override it; only af upgrade offers --allow-rejected because a human is present to make that choice.
af upgrade restarts the running daemon after the swap, and always has: the daemon keeps executing the old code until something restarts it, so a fix that does not reach it is not really installed. Live sessions survive — they run in tmux and the new daemon re-adopts them. Pass --no-restart to leave the daemon on the old binary until you restart it yourself with 'af daemon restart'.
Flags
| Flag | Type | Description |
|---|---|---|
--allow-downgrade |
Install the channel's latest release even if it is older than the current binary (e.g. switching from preview back to stable) | |
--allow-rejected |
Install even if this exact build was rolled back on this machine (it failed validation here before) | |
--ignore-active-upgrade |
Install even while a daemon upgrade transaction is in progress (this can leave that upgrade unable to roll back) | |
--no-restart |
Leave the running daemon alone (af upgrade restarts it by default so the new binary takes effect) |
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |
af version¶
Print the version number of agent-factory
Global flags
| Flag | Type | Description |
|---|---|---|
--daemon-url |
string |
Target a REMOTE daemon at this http:// or ws:// URL instead of the local unix socket (env: AF_DAEMON_URL). The daemon is HTTP-only; terminate TLS at your own proxy if needed. |
--token |
string |
Bearer token for a remote daemon set with --daemon-url (env: AF_DAEMON_TOKEN). Get it with 'af token show' on the daemon host. |