@wildfire/rec-cli
v0.1.0
Published
CLI for managing REC+ sites (templates over FTPS, content over the API), built for coding agents
Readme
rec-cli
A small CLI for working on a REC+ site from the terminal: templates over FTPS, pages and other content over the REST API. It's built so coding agents (Claude Code, Codex and others) can drive it from its --help output, and it's just as usable by hand.
How it works
You edit template files locally, but everything else happens on the REC+ site itself: it renders the pages and compiles the SCSS, so there's no local server or build step. rec-cli push uploads your changes over FTPS and they take effect as soon as they're on the site. Pushing to the site's active template changes what visitors see straight away, which is why live sites are normally changed through a fork (see below). Pages and other content live in the site's database and are changed through its API with rec-cli api.
Quick start
Needs Node.js 22.12 or newer.
npm install -g @wildfire/rec-clirec-cli upgrade updates it later.
Then make a folder for the site and set it up:
mkdir my-site && cd my-site
rec-cli initrec-cli init asks for the site URL, an API account (create one in the site's admin under API Accounts, with the content and templates scopes) and the two FTP accounts (listed under FTP Access). It writes them to .env, makes the folder a git repository if it isn't one, adds .env, .rec-cli/, *-base/ and .playwright-cli/ to .gitignore, checks the connections, writes an AGENTS.md with instructions for coding agents (plus a CLAUDE.md that includes it), and offers to download the site's templates. Re-run it any time; Enter keeps the current values.
A typical change, by hand
On a live site, work in a fork of the active template so visitors don't see changes until you publish them:
rec-cli templates # which template is active? (say it's "mytheme")
rec-cli fork mytheme-header # new template extending it; prints its preview URL
rec-cli pull # brings down the new (nearly empty) mytheme-header folder
mkdir -p mytheme-header/html/sections
cp mytheme/html/sections/header.html.twig mytheme-header/html/sections/ # override just this file
# ...edit mytheme-header/html/sections/header.html.twig...
rec-cli push mytheme-header --dry-run # check what will be uploaded
rec-cli push mytheme-headerIf mytheme doesn't have its own copy of the file, it's inherited from further up the chain, so copy it from there instead (e.g. responsive-base/html/sections/header.html.twig); rec-cli templates shows what each template extends.
Open the preview URL from rec-cli fork (or rec-cli templates --json) to see the site with your fork; only that browser session sees it. When it's ready, flatten the fork in the site's admin under Templates: that copies its files into the parent template and makes them live. Run rec-cli bump-cache after CSS or JS changes so browsers fetch the new files.
On a site that isn't live yet, you can skip the fork and edit and push the active template directly.
Working with an agent
Start your agent in the site folder. It reads AGENTS.md (Claude Code reads it through CLAUDE.md), which points it at the site's own docs and the rec-cli commands. Then describe the task, for example:
Read AGENTS.md and the site docs it links to. Then update the header in our template to match the attached mockup, at desktop (1400px) and mobile (390px) widths. Pull first, push your changes as you go, check each change on the site with playwright-cli, and bump the cache when CSS changes are done. This site is live, so fork the active template and work in the fork. Don't commit until I've reviewed it.
Add anything you learn about the site to the "Site notes" section of AGENTS.md, so later sessions start with it.
Commands
rec-cli init # set up .env, .gitignore and AGENTS.md for a site
rec-cli pull # download every template folder (only new or changed files)
rec-cli push rivendell # upload a template folder or file (only new or changed files)
rec-cli push rivendell --dry-run # see what would change first
rec-cli userfiles push userfiles/images/design # upload design assets (never downloads)
rec-cli templates # list template folders, active/locked status
rec-cli bump-cache # bump the asset cache buster after css/js changes
rec-cli fork winter-2026 # new template extending the active one (asks first)
rec-cli ftp views ls rivendell/html # single FTPS commands: ls, get, put, rm, mkdir
rec-cli ftp userfiles put ./hero.jpg images/design/hero.jpg
rec-cli api GET /pages # any /api/v1 endpoint, prints the JSON response
rec-cli api POST /pages -d @page.json
rec-cli api DELETE /pages/12 # sent as POST ?force_method=DELETE
rec-cli upgrade # update rec-cli itself to the latest versionrec-cli <command> --help shows each command's arguments, options and an example. API endpoints are documented on each site at /admin/docs/api.md.
Global options: --json (machine-readable output), --verbose (log requests and transfers to stderr), --yes (skip confirmations; without a terminal, confirmations fail instead of waiting).
Exit codes: 0 success, 1 failed operation or API error, 2 usage or config error.
How pull and push decide what to copy
After every sync, rec-cli records each file's size and modified time in .rec-cli/state.json (gitignored), so the next run can tell which side changed since then:
- only the side being copied from changed: the file is copied;
- only the other side changed: it's left alone and listed (push won't overwrite a colleague's change on the site; pull won't overwrite your unpushed edit);
- both changed: the contents are compared, and if they differ the file is left alone on both sides and listed as a conflict.
Files with no record yet (the first sync, or a new clone) fall back to comparing times: copied when the source is newer, compared by content otherwise. --force copies everything regardless, and --dry-run shows what would happen. After a transfer the local file takes the site's modified time. Site times are never changed, only moved on by real uploads, as REC+'s compiled template cache only rebuilds when a file gets newer.
It's still not version control: it only tracks what changed since the last sync, and a change that keeps a file the same size within the same second as a sync can be missed. Keep the site folder in git and commit before pulling, so git diff shows anything a pull brings in.
push --delete removes site files you don't have locally (after confirming). pull never deletes local files. .git, *.md, .env*, .playwright-cli, node_modules and similar are never transferred. Base templates (*-base) are pulled for reference but can't be pushed.
Config
Each site gets its own folder with a .env (see .env.example). rec-cli finds the nearest .env walking up from the current folder, so it works from any subfolder, and real environment variables override it.
Development
To work on rec-cli from source:
git clone https://github.com/WildfireInternet/rec-cli.git
cd rec-cli
npm install
npm link # puts your local `rec-cli` on your PATH
npm testThe offline tests run bin/rec-cli against a fake API server. To also run the live tests, add a .env.test (gitignored, same keys as .env.example) for a dev site you can safely write to: they only read from the API, and their FTP writes stay in throwaway rec-cli-test-* folders that are removed afterwards. Node doesn't use the OS certificate store by default, so for a local dev site with a locally trusted certificate run rec-cli with NODE_OPTIONS=--use-system-ca (the live tests set this for you).
Commands live in lib/commands/, one file each, exporting { name, description, args, options, example, help, destructive, run(ctx) }; add new ones to lib/commands/index.js. The starter AGENTS.md that rec-cli init writes is templates/AGENTS.md.
License
MIT, see LICENSE.
