fx
⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢰⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⣠⣶⣿⣿⣷⣶⡶⣶⣶⣆⠀⠀⠀⣴⣶⣶⠆
⠀⠀⠀⠉⢹⣿⣿⠉⠉⠀⠘⢿⣿⣧⣀⣾⣿⡿⠃⠀ Tiny, open, embeddable, native coding agent.
⠀⠀⠀⠀⣼⣿⡏⠀⠀⠀⠀⠀⠻⣿⣿⣿⠟⠀⠀⠀
⠀⠀⠀⢀⣿⣿⠃⠀⠀⠀⠀⢠⣦⠘⢿⣿⣷⡀⠀⠀ curl -fsSL https://fx.sh/setup.sh | bash
⠀⠀⠀⣸⣿⡟⠀⠀⠀⠀⣰⣿⣿⠗⠀⠻⣿⣿⣄⠀
⠀⠀⠀⣿⣿⠇⠀⠀⠀⠾⠿⠿⠋⠀⠀⠀⠘⠿⠿⠦ ⚠ Status: Experimental. Use at your own risk.
⠀⣸⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣿⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
fx is a coding agent CLI written in Zig: a small native binary that is open source (Apache-2.0), model-agnostic, and embeddable as a harness in larger systems. Its interface stays closer to a Unix shell than an IDE in the terminal.
- Any model: Vercel AI Gateway, ChatGPT or Grok subscriptions, or your own OpenAI-compatible endpoint such as Ollama or OpenRouter
- Any interface: interactive shell, one-shot
fx askfor scripts, or embedded through libfx and ACP - Shell-like output: inline rendering that preserves your terminal scrollback
- Extensible: skills, MCP servers, and subagents
Install
curl -fsSL https://fx.sh/setup.sh | bash
Get started
Sign in with one of:
fx login: Vercel AI Gatewayfx login codex: ChatGPT subscription (OpenAI Codex OAuth)fx login grok: Grok subscription (xAI OAuth)fx setup: AI Gateway API key
fx loads Grok models from your subscription's live catalog, so new supported models appear without a static model list. Public xAI metadata enriches image support but does not filter subscription models.
Then start the interactive shell from a project:
cd your_project
fx
Or make a one-shot request:
fx ask "explain the changes in this repository"
Inside the shell, run /help to browse interactive commands.
In tmux, use your usual prefix bindings to switch sessions or enter copy mode. fx preserves those tmux views while resizing, including when the switcher zooms a split pane.
Documentation
Visit fx.sh/docs for the full manual: sessions, models, custom model connections, permissions, configuration, skills, MCP, subagents, embedding, and the complete CLI and slash command references. Agents can read any page as Markdown by appending .md to its URL, or fetch llms-full.txt for everything in one file.
Custom model connections
Add named connections for any OpenAI Chat Completions endpoint, including local servers such as Ollama and gateways such as OpenRouter, in ~/.fx/settings.json, then select one for the profile or a single invocation:
fx provider local
FX_PROVIDER=openrouter FX_MODEL=openai/gpt-4.1 fx ask "review this change"
See Custom model connections for connection JSON, model metadata, and behavior details.
Ultrafast mode
Ultrafast mode is off by default. It requests OpenAI's higher-cost Gateway service tier with openai.serviceTier: "ultrafast" for models whose Gateway metadata advertises Ultra eligibility. ultrafast_requested in fx status --json and /status reports the request, not a guarantee that a provider served the tier.
Set a profile default in ~/.fx/settings.json:
{
"provider": "gateway",
"models": { "gateway": "openai/gpt-6-astra" },
"ultrafast_mode": true
}
Use it explicitly in an interactive session, a one-shot request, or ACP:
fx --ultrafast
fx ask --ultrafast "review this change"
fx acp --ultrafast
Use /ultrafast on, /ultrafast off, or /ultrafast status in the shell. The Settings menu includes an Ultra mode row. FX_ULTRAFAST=1 and --ultrafast are process-local opt-ins and are not persisted. FX_ULTRAFAST=0, --no-ultrafast, and /ultrafast off explicitly disable it. A resumed session keeps its saved request unless a higher-precedence explicit disable applies.
Ultra mode is available only through the Vercel AI Gateway's OpenAI service tier. Gateway metadata currently marks Astra eligible. fx does not select Ultra automatically, and switching models clears an existing Ultra request. Subagents inherit the parent turn's request; an explicit parent disable and capability checks override an existing child preference. Background side calls, including titles, reviews, and compaction, do not use Ultra mode.
Gateway provider routing
When the active model goes through the Vercel AI Gateway, one model is often served by several providers (for example Anthropic directly, AWS Bedrock, or Google Vertex). fx can tell the gateway which providers to use, in what order:
// ~/.fx/settings.json
{
"provider_order": ["bedrock", "anthropic"], // try Bedrock first, then Anthropic
"provider_strict": false // true restricts requests to only these providers
}
Both keys also work in a committed project .fx.json, and per launch:
fx --provider-order azure,openai --provider-strict
fx ask --provider-order bedrock "review this change"
FX_PROVIDER_ORDER=vertex FX_PROVIDER_STRICT=1 fx
Slugs are the gateway's provider identifiers (letters, digits, dashes, for example anthropic, bedrock, vertexAnthropic), listed on the models page. An empty provider_order in a higher-precedence layer clears a list set by a lower one. Routing applies to gateway requests only; custom model connections ignore it.
Themes
fx ships with fx-dark and fx-light and follows your terminal's light or dark mode. Pin a variant with FX_THEME=light or FX_THEME=dark, or drop a VS Code format theme at ~/.fx/themes/<name>.json and select it with the theme setting or FX_THEME=<name> per launch. Without an explicitly selected theme, diff markers and edit counts stay monochrome; selecting any theme adds its diff marker colors. See Configuration for all environment variables.
Context compaction
When a conversation fills the model's context, fx compacts it so the work can continue. The newest few turns stay unchanged. Every compacted turn keeps your messages and the assistant's final reply word for word. The conversation's own model adds a short note on what the assistant did in between, and a line for each tool call: fx writes what the call was from the call itself, like shell zig build test (failed, exit 1, 3120 bytes), and the model adds why it was used and what it showed. The model also keeps numbered entries for your rules, quoted word for word, and for facts, decisions, status and open questions, plus a list of the skills and MCP tools used. Entries are never rewritten: a later entry can say it replaces an earlier one. At the next compaction, the one before it is saved whole with an ID like L2, and in its place the agent sees a short summary the model writes of all earlier compactions, plus their rules, status and open entries still in force, word for word. The turns of earlier compactions leave the agent's view however many compactions a session has; only those kept entries grow with it. In a session that is not saved, nothing can be stored, so earlier compactions stay in view. fx checks every new note and entry, and marks without removing one that names no source, quotes words you did not write, states a path, number, version or quoted text found in none of the compacted turns and tool calls, names an ID that does not exist, or calls a failed tool call a success; turns the model skipped, or a missing summary of earlier compactions, are asked for once more. Only when the compacted conversation would leave too little room to continue are its longest texts shortened to their start and end, each naming the saved turn that keeps it whole. Every compacted turn is saved word for word with an ID like M3, every tool call with its input and output as the model saw them, plus the handle of any full output saved separately, with an ID like T12, and every earlier compaction with an ID like L2. The agent can search them by text or open one by ID with read_tool_result; a search also says how many saved records hold all of its words, and which came first and last.
Automatic compaction asks the model right after the conversation, exactly as the agent was about to send it and with the same settings, so the provider can reuse what it has cached. When that request does not fit or fails, and when you run /compact to compact now, fx writes the turns out in a separate request at the model's lowest reasoning; turns too large for one such request go oldest first, in as many requests as it takes. If a separate request fails or comes back empty on AI Gateway, fx retries it once with a model from another provider.
Automatic compaction starts when a request reaches 80 percent of the model's usable input. Set auto_compact_percent in ~/.fx/settings.json to any value from 10 to 80, or FX_AUTO_COMPACT_PERCENT for a single launch:
// ~/.fx/settings.json
{ "auto_compact_percent": 60 }
Embed fx
fx builds as a native binary or WebAssembly. Applications embedding fx can provide network transport, session storage, configuration, permission handling, and terminal I/O.
| Surface | Use |
|---|---|
fx acp |
Connect the native agent to editors and other Agent Client Protocol clients. |
createFxAgent() |
Embed the agent core in a JavaScript host with fx-core.wasm. |
createFxTerminal() |
Embed the interactive terminal with fx-term.wasm. |
ACP clients can keep their MCP tools loaded on every turn, steer a running turn, supply a session system prompt, serve MCP servers over the ACP connection, and choose each session's workspace. See ACP embedding.
The SDK is published to npm as libfx. See the WebAssembly SDK and the runnable Node.js, browser, Next.js, and Nuxt examples. The WebAssembly SDK is experimental.
Connect your Slack account
Run /mcp add slack in an fx session, or fx mcp add slack from your terminal.
The command saves Slack's MCP URL and the public fx Client ID to your profile,
opens the fx.sh authorization flow, and connects Slack after you consent. Keep
fx running while you authorize in a browser on the same computer. In an fx
session, Slack's tools become available without a restart. The Servers tab
in /mcp also offers Add Slack with the s key.
You don't need to edit ~/.fx/mcp.json or run fx slack install to connect your
personal account. Workspace app approval may still be required. fx reports
Slack connected. You can now use Slack. after the connection succeeds.
Running the command again uses an existing working connection or starts
missing authorization. It restores a missing fx Client ID and preserves other
servers, timeouts, and explicit scope overrides. A conflicting Slack endpoint,
Client ID, or authentication configuration stops setup with guidance instead of
being overwritten. Use /mcp auth slack --open to reauthorize an existing
configuration. Removing and re-adding the fx preset restores its configuration;
it does not revoke credentials. Use /mcp logout slack to sign out.
Slack workspace installation
Run fx slack install to install the fx bot in the configured Vercel Slack
workspace. Keep the command running and authorize Slack in a browser on the same
computer. The HTTPS callback at fx.sh returns the authorization to the CLI;
PKCE state and the verifier stay in memory. The companion web bridge must be
deployed and configured first.
After the CLI saves the installation, the browser returns to an fx.sh confirmation page. You can close that tab or refresh it after the command exits.
fx slack status --json reports local installation metadata without tokens.
Plain-text output omits Slack IDs and shows expiration as a readable UTC date
and time. JSON output retains the IDs and Unix timestamps for scripts.
fx slack refresh rotates the local bot credentials when needed. Credentials
live in the owner-only file ~/.fx/slack/installation.json; no hosted database
or background refresh service is created. An expired refresh token requires
installation again. This workspace operation is separate from each employee's
MCP user authorization. Employees connect their own account with
/mcp add slack in an fx session (or fx mcp add slack from a terminal).
For https://mcp.slack.com/mcp, the CLI recognizes the fx app by its public
Client ID and uses the HTTPS callback for personal login. Changing that Client
ID requires a CLI update. OAuth uses the canonical form of Slack's advertised
resource, https://mcp.slack.com/, while the MCP transport remains at
https://mcp.slack.com/mcp. First login and reauthorization request the full shared
user_scopes list from fx.sh. If local scopes are configured, they must include
every shared scope; extra local scopes are not requested. A narrower or explicitly
empty list stops authorization before opening the browser, leaving the configuration
and stored credentials unchanged. Remove the override only if you want to authorize
the full shared scope set. Per-user read-only subsets are not supported for the fx app. Saved scopes,
Slack's advertised capabilities, and scope challenges cannot expand this
request. The shared list contains nine personal scopes configured for fx and
advertised by Slack MCP; changing it requires a deliberate configuration update
and any necessary Slack approval. This does not revoke
permissions on previously issued tokens or change token refresh behavior. It
opens an ephemeral loopback listener instead of the configured callback_port,
keeps PKCE and personal tokens in the CLI, and shows “Slack connected” after
saving to the existing MCP credential store. Other MCP providers and different
Slack app Client IDs retain their direct callback behavior without contacting
fx.sh. Fx app authorization requires fx.sh to be available; an unavailable
metadata endpoint returns SlackBridgeUnavailable. Deploy the web
personal-authorization routes and scope metadata before releasing this CLI.
Missing or invalid shared scopes stop authorization rather than falling back
to Slack's broader capabilities. Keep the registered
localhost callback for older clients until they have upgraded. Slack workspace
approval requirements still apply to personal authorization.
Bot installation does not establish whether Slack will display a hoverable “Sent using @fx” attribution; that requires a live message test.
Build from source
Building fx requires Zig 0.16.0+:
git clone https://github.com/vercel-labs/fx.git
cd fx
zig build -Doptimize=ReleaseSafe
./zig-out/bin/fx
Run the test suite with zig build test. See CONTRIBUTING.md for development and contribution guidelines.
Security
Report security vulnerabilities through the contact page instead of a public issue.
License
Apache-2.0. Third-party licenses and attributions are listed in THIRD_PARTY_NOTICES.md.
Credits
Interface sounds by cuelume.
