Cloudflare's cf CLI: The Whole API, and a Clock on Wrangler
TL;DR
Cloudflare launched cf on September 28, 2026: an open-beta CLI generated from its OpenAPI schema by the newly open-sourced Forge pipeline, covering about 3,000 API operations against Wrangler's roughly 280. It prints JSON by default, finds commands with a local cf cli search, configures Workers in a TypeScript cloudflare.config.ts and builds through Vite by default. When the beta ends, Cloudflare will ship a final major Wrangler release that points users to cf, then maintain Wrangler for 18 more months. Running cf migrate on this Astro static blog produced the new config files, but cf build then failed, so the blog stays on Wrangler for now.
Cloudflare today launched cf, a new command-line tool that covers the entire Cloudflare API: more than 3,000 operations, where Wrangler has about 280. It’s built for coding agents first, and it starts a clock on Wrangler. When the open beta ends, Cloudflare will ship one last major version of Wrangler that sends you to cf, then maintain Wrangler for 18 months. This blog deploys with Wrangler, so I installed the beta, dug through the package and ran cf migrate on a copy of the repo. Here’s what’s new, what worked, what broke, and what it means if you use Wrangler today.
Half of Wrangler’s users are now agents
Cloudflare’s case for a new CLI starts with a number. In March 2026, coding agents accounted for a quarter of Wrangler usage, up from single digits a year earlier. Last week they hit 48%, according to the launch post. Agents also use it harder than people do: almost twice as many distinct commands a day, and almost four times as likely to run six or more.
Wrangler wasn’t built for that. Each product team added its own commands over the years, and it shows: d1 info, hyperdrive get and workflows describe do the same job with three different verbs. Many commands print Unicode tables and have no --json flag. And about 280 command paths cover only a slice of a platform with thousands of API operations.
Cloudflare first showed cf in April, during Agents Week, as a technical preview covering “just a small subset” of products, and described it as “just a small piece of the future Wrangler CLI.” The Hacker News thread drew 336 points and 108 comments, from “Please call it flare” to a plea that the audience “should not be ai agents. It should be a good experience for humans!” Five months later, cf is no longer part of Wrangler. It’s Wrangler’s replacement, launched during Birthday Week.
Forge generates the commands, which is how there are 3,000 of them
Nobody hand-writes 3,000 commands. cf is generated by Forge, Cloudflare’s API generation pipeline, which it open-sourced today under Apache 2.0. Every Cloudflare API already has an OpenAPI schema. Teams annotate it a little further, and Forge turns it into a TypeScript SDK and then into CLI commands. The Forge post counts “over 3,500 operations” in the API. Over the next few months Forge will also power Cloudflare’s API docs and SDKs. The post names TypeScript, Rust, Python, Go, PHP and Terraform, then adds: “Especially Terraform.”
The beta I installed, 1.0.0-beta.5, ships its command tree as a 5 MB JSON file: 2,936 generated commands plus 49 hand-written ones. The repository README counts 2,991 leaf commands across 163 namespaces. The generator shows through in the top-level help: of the 80 commands it lists, 31 have no description beyond their own name. cf dns is described as “dns”, cf kv as “kv”, and cf workers as “workers”.
The hand-written commands are where the workflows live: auth, build, dev, deploy, migrate, D1 migrations, Container image builds, and a wrapper around cloudflared for Tunnels and Access. The package installs the same binary as both cf and cloudflare, which helps if you also have the Cloud Foundry CLI, whose binary has been called cf for years. It’s moving fast: three betas shipped today alone.
JSON by default is the right call
When agents drive Wrangler, they append --json to every command and pipe the output through jq, except where a command doesn’t support --json and they’re left parsing a box-drawn table. cf flips the default. API commands print JSON, which Cloudflare says is pretty-printed for humans and condensed for agents.

Where a human should stay in the loop, cf still gives you a proper interface. cf registrar registrations create shows a review screen and asks for an explicit yes before charging a non-refundable domain registration. Agents skip the prompt with --force, which is exactly the flag your agent’s permission rules should catch.

cf cli search: describe the task, get the command
With 3,000 commands, --help stops being a map. cf cli search takes a plain-English description and returns the five best matches as JSON. It runs locally: the package bundles a MiniSearch index over that 5 MB command file, so the search itself needs no model and no network. Every --help I ran started with a block addressed to agents, not people:
=== STOP: AGENT COMMAND DISCOVERY ===
AGENTS: Do not explore commands by chaining nested --help calls.
Your first port of call and the best way to discover commands is:
AGENTS: Keep cf cli search queries anonymous; describe the action and resource type only.
Never include names, email addresses, domains, account or resource IDs, tokens, or other identifying values.
cf cli search "<describe the task you want to accomplish>"

My own queries mostly landed. “purge the cache for one url” put cf cache purge first. “add a dns record” returned five cf dns records commands, although edit and update ranked above create. “tail logs of a worker” returned Pages deployment tails and audit logs. That’s not the search failing: cf has no Worker tail command yet.
The “keep queries anonymous” line is there for a reason. cf’s telemetry policy sends each cf cli search query as you typed it. That’s the only free text it collects. The rest is command paths, flag names with free-form values redacted, durations, error codes, OS and Node versions, a random device ID, the name of the coding agent running cf, and a device-specific hash of that agent’s session ID. Telemetry is on by default, and cf ignores Wrangler’s opt-out. If you turned it off for Wrangler, do it again with cf cli telemetry disable, or set DO_NOT_TRACK=1.
cloudflare.config.ts replaces wrangler.jsonc
Configuration moves to TypeScript. Here is a trimmed version of the launch post’s examples:
import { bindings, defineConfig, triggers } from "cf/config";
import * as entrypoint from "./index.js" with { type: "cf-worker" };
export default defineConfig(({ mode }) => ({
worker: {
name: "example-worker",
entrypoint,
compatibilityDate: "2026-09-27",
env: {
API_TOKEN: bindings.secret(),
DATABASE: bindings.d1({ name: `example-${mode}-database` }),
UPLOADS: bindings.r2({ name: `example-${mode}-uploads` }),
},
triggers: [triggers.scheduled({ schedule: "0 * * * *" })],
},
}));
Two things change from Wrangler. First, environments. Wrangler’s [env.staging] blocks, copied and tweaked per environment, are gone along with the --env flag. Instead, one function switches on mode, which you pass as cf deploy --mode staging. Cloudflare says some of its internal Wrangler configs, which had grown past 5,000 lines, shrank by 40% once rewritten this way. Second, the types. bindings.* and triggers.* give your editor, and any agent with an LSP plugin, autocomplete and type errors for every binding. Wrangler’s TOML had no accessible schema, and its JSONC schema, in Cloudflare’s words, was one “agents rarely used.”
Cloudflare wants the file to grow beyond Workers: “Soon you will be able to configure entire policies, set up zones, configure DNS and more.” That’s Terraform territory, and Forge will generate Cloudflare’s Terraform provider too, so I’m curious where they’ll draw the line between the two.
Vite is the default, but cf doesn’t build anything itself
cf has no bundler or dev server of its own. The draft docs, in a docs pull request opened today and not yet merged, spell out the handoff for cf dev, cf build and cf deploy:
- If
cfdetects a supported framework, it runs that framework’s own command. - Otherwise, it uses the Cloudflare Vite plugin if the project declares it.
- Otherwise, it uses Wrangler 4.136.0 or later.
The Vite plugin has been generally available since April 2025, and Cloudflare now recommends it for every Worker. cf uses its 2.0 beta, which unlike the current 1.x no longer needs Wrangler installed. Workers that still bundle with esbuild through Wrangler, and Rust and Python Workers, keep going through Wrangler.
On the happy path, a Vite Worker needs three commands. cf workers check takes over from wrangler check startup: it profiles the Worker’s startup locally and reports bundle size and startup time as JSON before you deploy.

I ran cf migrate on this blog
This blog is an Astro static site. It deploys as a Workers static assets project from a ten-line wrangler.jsonc, with Wrangler pinned in package.json. Its end-to-end tests run against wrangler dev, because that’s the only local server that applies our _headers file, Content Security Policy included. With no Worker code and no bindings, it should be the easy case.
On a fresh clone:
$ cf migrate
Using the Wrangler bundler because @cloudflare/vite-plugin is not declared. Pass --bundler vite to override.
Updated 4 file(s):
├─ cloudflare.config.ts
├─ wrangler.config.ts
├─ package.json
└─ package-lock.json
✔ Migration complete.
cf migrate won’t run on a dirty git tree unless you pass --force, and it installs cf as a dev dependency. Then it splits the config in two. Platform settings go to cloudflare.config.ts:
import { defineConfig } from "cf/config";
export default defineConfig({
worker: {
name: "bokvi-blog",
compatibilityDate: "2026-02-22",
workersDev: false,
previewUrls: true,
assets: {
notFoundHandling: "404-page",
},
},
});
Build settings go to wrangler.config.ts, which imports from wrangler/experimental-config. That import needs a local Wrangler install: my first attempt, before npm ci, stopped on exactly that. The draft docs warn the file “can change during the beta.”
import { defineWranglerConfig } from "wrangler/experimental-config";
export default defineWranglerConfig({
types: {
generate: false,
},
assetsDirectory: "./dist",
});
The migration left wrangler.jsonc where it was. cf ignores it once cloudflare.config.ts exists, and Wrangler never reads cloudflare.config.ts, so the two tools can share a repo until you delete the old file. So far, so tidy. Then I ran the build:
$ cf build
├ Build
│ Delegating to npx astro build
...
[build] 82 page(s) built in 1.26s
[build] Complete!
┌ Error
│ Build Output Specification: no root config found at
│ <project>/.cloudflare/output/v0/config.json.
└
cf spotted Astro and handed the build to astro build rather than Wrangler. Astro built all 82 pages into dist/. But a static Astro build without Cloudflare’s adapter doesn’t write the Build Output that cf deploys from, so cf build and cf deploy --dry-run both stop there. cf dev has the same problem: it starts astro dev, which serves the site without a single header from _headers. No CSP, no Cross-Origin-Opener-Policy. Meanwhile, npx wrangler deploy --dry-run in the same migrated checkout read 359 files from dist/ and was happy.
Even with that fixed, cf wouldn’t build this blog the way it’s built today. The draft docs say cf doesn’t run your package.json scripts. Our build script generates Open Graph images, builds a Pagefind search index and checks the CSP hashes, and cf build would skip all of it unless I chained it in by hand. I didn’t try adding Astro’s Cloudflare adapter, which would change how the whole site builds, just to swap deploy tools.
So this blog stays on Wrangler, which is what Cloudflare tells agents to do anyway. The launch post includes a ready-made prompt for your global CLAUDE.md or AGENTS.md. It tells agents to use cf for new projects, and for projects that already have a Wrangler config, to “Keep using Wrangler in those projects unless asked to migrate.”
What cf can’t do yet
The README’s “Divergences from Wrangler” section and the draft docs are refreshingly direct about the gaps. As of 1.0.0-beta.5:
Scroll horizontally if needed.
| In Wrangler | In cf today |
|---|---|
wrangler tail | No Worker log streaming. The docs say to run npx wrangler tail. |
wrangler secret put | Can’t set a single secret yet. |
wrangler rollback | No dedicated Worker rollback command. |
--env staging | --mode staging, read by cloudflare.config.ts. |
wrangler login | cf auth login, with its own credentials and a device-code flow by default. Your Wrangler login doesn’t carry over. |
list commands that page for you | One API page per call. You pass the cursor yourself. |
--local | Covers common KV, D1 and R2 data operations, not resource creation. |
cf migrate also doesn’t convert Workflow bindings or Containers configuration, cloudflare.config.ts can’t be loaded under Bun, and cf needs Node 22 or later. Even the package name is new to Cloudflare. The first three versions of cf on npm are a configuration-file loader another developer published in 2013. Cloudflare’s first version went up the day of the April preview.
What this means for Wrangler
Nothing breaks today. Wrangler 4.143.0 shipped half an hour before the launch post, and you can ignore cf entirely and keep deploying. But the clock is set. When the open beta ends (no date yet), Cloudflare will release a final major version of Wrangler that “directs you and your agent to use cf,” and maintain Wrangler for 18 months from then. Even if the beta ended tomorrow, that’s maintenance until spring 2028.
Two experimental draft pull requests from August show what that last major version could look like. In one, Wrangler v5 becomes a stub that shows migration docs to humans and tells detected coding agents to “remove Wrangler, install cf.” In the other, it forwards every command to npx cf. Neither is merged, but the direction is clear.
For existing projects, the detail that matters most is that Wrangler as a build engine will outlive Wrangler as a CLI. cf hands builds to Wrangler for esbuild Workers, Rust and Python Workers, and anything cf migrate leaves on the Wrangler bundler, like this blog. The command you type changes first. The code that bundles your Worker changes later, and only if you move to Vite.
Why a new name instead of Wrangler 5? Cloudflare’s argument is that models learned Wrangler from years of docs and blog posts, so changing how it behaves would fight their training. A new tool with its own name, plus instructions injected through --help and AGENTS.md, is less confusing for an agent than two versions of a tool it thinks it already knows. I buy it. The prompt block Cloudflare wants in your global agent instructions even carries a version, v20260928, so expect it to change as the beta does.
Here’s what I’d do today:
- New Worker: start with
cf initif you’re fine running a beta. If not, use Wrangler with the Vite plugin, the setup thatcf migrateconverts most cleanly. - Existing Worker on the Vite plugin: run
cf migrate --dry-runon a branch and see what it wants to change. - Static sites, framework builds, esbuild Workers, or anything that relies on
wrangler tail, single secrets or--env: wait for the gaps to close. - Everyone: pin Wrangler’s version in
package.jsonso the final major doesn’t reach your CI by surprise. Put a line in each repo’sAGENTS.mdorCLAUDE.mdsaying which CLI the project uses. Agents following Cloudflare’s suggested global prompt pick a CLI from whichever config file they find, and a half-migrated repo has both.
FAQ
What is the Cloudflare cf CLI?
cf is Cloudflare's new command-line tool, released as an open beta on September 28, 2026 and installed with npm i -g cf. Its commands are generated from Cloudflare's OpenAPI schema, so it covers the whole API, about 3,000 operations, where Wrangler covered roughly 280. It prints JSON by default, has a cf cli search command for finding commands by intent, and configures Workers in a TypeScript file called cloudflare.config.ts.
Is Wrangler being deprecated?
Not yet, but it has an end date. When the cf open beta ends, Cloudflare will release a final major version of Wrangler that directs users and agents to cf, and will maintain Wrangler for 18 months after that. No end date for the beta has been announced. cf itself still hands builds to Wrangler for Workers bundled with esbuild and for Rust and Python Workers.
How do I migrate a Worker from Wrangler to cf?
Run cf migrate in a clean git checkout of the project. It converts wrangler.jsonc, wrangler.json or wrangler.toml into cloudflare.config.ts and adds cf as a dev dependency. Projects that already use the Cloudflare Vite plugin move to Vite; the rest keep Wrangler as the bundler, with build settings in a generated wrangler.config.ts. Try cf migrate --dry-run first. It does not convert Workflow bindings or Containers configuration.
What can Wrangler do that cf can't yet?
In 1.0.0-beta.5, cf has no equivalent of wrangler tail for live Worker logs, cannot set a single secret the way wrangler secret put does, has no dedicated Worker rollback command, drops service environments (--env) in favour of --mode, and returns one page of results from list commands. It also keeps its own login rather than reusing Wrangler's.
Does the cf CLI collect telemetry?
Yes, by default. cf sends command paths, flag names with free-form values redacted, durations, error codes, OS and Node versions, a random device ID, the name of the coding agent running it and the text of cf cli search queries. It does not read Wrangler's telemetry setting. Turn it off with cf cli telemetry disable or by setting DO_NOT_TRACK=1.