Why a CLI
The editor is great. Some jobs still belong in a terminal.
Fifty pages to update, a review before it goes live, a repeatable deploy. Clicking through a GUI is the slow way to do all three.
ElasticFunnels CLI
Meet ef, the ElasticFunnels CLI. Pull your pages, components, backend scripts, and assets into a folder, edit them in your own tools, and push them back. Built for Git, CI/CD, Claude Code, Codex, and humans who live in a terminal.
npm i -g @elasticfunnels/cli ยท Requires Node.js 18 or newer
$ npm i -g @elasticfunnels/cli $ ef init # bind this folder to a brand $ ef pull # pages, components, scripts, assets ✓ Pulled 214 pages, 32 components, 9 scripts, 140 assets $ ef push pages/pricing.ef --direct ✓ Published pricing.ef (revision 8f2a1c)
Why a CLI
Fifty pages to update, a review before it goes live, a repeatable deploy. Clicking through a GUI is the slow way to do all three.
Changing one line across forty landing pages by hand is an afternoon. A loop over ef push is a coffee.
Edits in a builder go straight to the page. With files on disk you branch, open a PR, and merge to publish.
Claude Code and Codex do not use a mouse. They need a command that speaks JSON and returns a clean exit code.
Three commands to live
No dashboard tour. The folder you bind is interchangeable with the VS Code extension.
npm i -g @elasticfunnels/cli and run ef --help. One binary, two dependencies, Node 18 or newer.
ef init stores your brand and API key in .ef/, then adds it to .gitignore so a key never lands in a commit.
ef pull brings everything down. Edit in any tool. ef push sends it back, with a 409 guard if the server moved on.
What ef does
One tool for pages, components, backend scripts, assets, products, and variables.
For scripts and agents
The CLI was designed for Claude Code, Codex, CI runners, and humans, roughly in that order of how picky they are about output.
--json on every command. Data on stdout, logs on stderr, so pipes stay clean.
Stable exit codes: 0 success, 3 auth, 4 conflict, 5 network, 7 not found. Scripts branch on results.
ef push --dry-run prints the plan without touching the server or your disk.
Optimistic concurrency: a stale push returns 409 and exits 4 instead of clobbering newer work.
ef diff shows local-vs-server drift before you push a thing.
$ ef list pages --json | jq '.[] | {id, slug}' { "id": 10633, "slug": "changelog" } { "id": 10650, "slug": "platform/split-testing" } $ ef push --all --dry-run plan: 3 create, 11 update, 0 delete $ ef push pages/about.ef # server moved on ! conflict (409) pull first, then retry $ echo $? 4
CI/CD
Keep your pages in Git. Wire one step into GitHub Actions and every merge to main publishes. Roll back by reverting a commit.
Authenticate with EF_API_KEY, no interactive login on the runner.
Preview branches with --draft, publish main with --direct.
Fast on big brands: pulls and pushes run with bounded concurrency.
name: Deploy funnels on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm i -g @elasticfunnels/cli - run: ef push --all --direct env: EF_API_KEY: ${{ secrets.EF_API_KEY }}
Same files as the extension
The CLI writes the exact efmeta headers the VS Code extension does, into the same folder shape. Prefer the editor some days and the terminal others.
Identical elasticfunnels/ tree: pages, components, scripts, assets, variables.
A .ef file saved by either tool round-trips byte-for-byte.
Optional .ef syntax highlighting for Cursor, VS Code, and VSCodium via ef install-highlighter.
your-funnel/ โโ .ef/ # config + API key (git-ignored) โโ elasticfunnels/ โ โโ pages/ โ โโ components/ โ โโ scripts/ โ โโ assets/ โ โโ variables.json โโ .gitignore
Keep going
Every published version of @elasticfunnels/cli, newest first.
Command reference, auth model, drift detection, and CI recipes.
Read the docs โInstall @elasticfunnels/cli globally. Built-in TypeScript types.
Published for transparency and security review. Open an issue any time.
Browse the repo โStart Building
Create a free brand, run ef init, and pull it into your terminal in under two minutes.