# Cloudflare's cf CLI: The Whole API, and a Clock on Wrangler

> 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.

URL: https://blog.bokvi.com/blog/cloudflare-cf-cli/
Published: 2026-09-28
Tags: cloudflare, dev-workflow, ai-agents, infrastructure

**Cloudflare today launched [`cf`](https://blog.cloudflare.com/cloudflare-cf-cli-launch/), 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](https://blog.cloudflare.com/cloudflare-cf-cli-launch/). 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](https://blog.cloudflare.com/cf-cli-local-explorer/) covering "just a small subset" of products, and described it as "just a small piece of the future Wrangler CLI." The [Hacker News thread](https://news.ycombinator.com/item?id=47753689) drew 336 points and 108 comments, from "[Please call it flare](https://news.ycombinator.com/item?id=47754753)" to a plea that the audience "[should not be ai agents. It should be a good experience for humans!](https://news.ycombinator.com/item?id=47754317)" Five months later, `cf` is no longer part of Wrangler. It's Wrangler's replacement, launched during [Birthday Week](https://blog.cloudflare.com/tag/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](https://blog.cloudflare.com/forge-open-source-generation-pipeline/), 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](https://github.com/cloudflare/cf) 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](https://github.com/cloudfoundry/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.

![Two terminal frames stacked: wrangler queues list with a json flag fails with Unknown argument: json and prints a wide boxed table of four queues, then cf queues list is piped into jq and returns two compact JSON lines](https://blog.bokvi.com/_astro/cf-json-vs-wrangler-table.CRzzil3__2uQSBm.webp)

*Top: Wrangler has no JSON flag for queues and falls back to a table. Bottom: cf's output goes straight into jq. Frames from Cloudflare's demo.*

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.

![A cf terminal session registering example.com: two availability checks, a review table with a 1-year term, USD 10.00 due now, auto-renew off and privacy redaction on, then a prompt saying the charge cannot be refunded with a Yes or No choice](https://blog.bokvi.com/_astro/cf-registrar-confirm.CnO-s_1g_Z1Vjy9q.webp)

*cf's hand-written registrar flow: review first, then an explicit yes before a non-refundable charge. Frame from Cloudflare's demo.*

## 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](https://github.com/lucaong/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:

```text
=== 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>"
```

![A terminal running cf cli search with the query look at messages stuck in my queue, returning a JSON array of five commands: cf queues messages peek, pull, ack, purge and bulk-push, each with a short summary](https://blog.bokvi.com/_astro/cf-cli-search.CH34GLNc_Z25YxHR.webp)

*One query, five candidate commands, no help pages. Frame from Cloudflare's demo.*

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](https://github.com/cloudflare/cf/blob/main/packages/cli/telemetry.md) 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:

```ts
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](https://github.com/cloudflare/cloudflare-docs/pull/33735), in a docs pull request opened today and not yet merged, spell out the handoff for `cf dev`, `cf build` and `cf deploy`:

1. If `cf` detects a supported framework, it runs that framework's own command.
2. Otherwise, it uses the Cloudflare Vite plugin if the project declares it.
3. Otherwise, it uses Wrangler 4.136.0 or later.

The [Vite plugin](https://developers.cloudflare.com/changelog/post/2025-04-08-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.

![A terminal running cf build, which detects a Vite project and completes, then cf workers check printing JSON with a 29,081-byte bundle, 9,124 bytes gzipped, a 3.8 ms startup time and a CPU profile file, then cf deploy with a message uploading launchpad-api to a workers.dev URL](https://blog.bokvi.com/_astro/cf-build-check-deploy.OMtufUfU_jAw44.webp)

*A Vite Worker built, profiled and deployed in three commands. Frame from Cloudflare's demo.*

## I ran cf migrate on this blog

This blog is an Astro static site. It deploys as a [Workers static assets](https://developers.cloudflare.com/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:

```text
$ 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`:

```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."

```ts
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:

```text
$ 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"](https://github.com/cloudflare/cf#divergences-from-wrangler) section and the draft docs are refreshingly direct about the gaps. As of `1.0.0-beta.5`:

| 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](https://www.npmjs.com/package/cf) 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](https://github.com/cloudflare/workers-sdk/pull/15412), Wrangler v5 becomes a stub that shows migration docs to humans and tells detected coding agents to "remove Wrangler, install `cf`." In [the other](https://github.com/cloudflare/workers-sdk/pull/15413), 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 init` if you're fine running a beta. If not, use Wrangler with the Vite plugin, the setup that `cf migrate` converts most cleanly.
- **Existing Worker on the Vite plugin:** run `cf migrate --dry-run` on 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.json` so the final major doesn't reach your CI by surprise. Put a line in each repo's `AGENTS.md` or `CLAUDE.md` saying 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.