# FunnelsGrove CLI

Install:

```bash
npm install -g @funnelsgrove/cli
fgrove login
```

Update every global installation visible on `PATH` (including separate NVM and
Homebrew prefixes) with one command:

```bash
fgrove update
```

Use `fgrove update --dry-run` to inspect the installations first. The updater
runs each installation's sibling `npm`, so duplicate global installs cannot
silently remain on different CLI versions.

Create a new hosted funnel from one of three starter templates:

```bash
fgrove whoami
fgrove projects list
fgrove templates list
fgrove templates show one-page-v2
fgrove funnels create --project fitness --template one-page-v2 --name FitBee --idempotency-key <creation-key> --json
fgrove sync down --funnel <returned-funnel-id> --dir ./fitbee
```

Template cards include descriptions, suitable journeys, included screens and a
create command. `--json` works for both list and show. Discovery reads the
offered official catalog through the connected account, preserving its names,
keys and pinned demo/source versions; inspect the demo for the current journey.
Hosted creation requires an existing project; pass `--workspace`
when selecting a different workspace. Template slugs match the app: `default`,
`one-page`, and `one-page-v2`.

`funnels create` uses the same API setup workflow as the app. It waits for a
downloadable draft, while the automatic initial preview builds separately.
`--timeout` defaults to 120 seconds. Progress and recovery identity go to stderr;
stdout contains a tab-separated creation row, or one result object with `--json`.
Retain the creation key and reuse it if the request outcome is uncertain. A
wait timeout does not cancel or delete the hosted funnel. Download the returned
ID with `sync down`; `funnels clone` instead copies an existing funnel.

After downloading, establish the product/audience/offer/brand brief and root
`Design.md` before customization. The Fstack `create-funnel` skill owns that
intake workflow; starter artwork, proof and legal/offer copy need adaptation.
The starter preview includes later local changes only after sync and publish.

Find visual examples in the public Funnel Screens library without an account:

```bash
fgrove references categories
fgrove references list --category 'Health Fitness' --step-type single-choice
fgrove references show <funnel-id> --json
fgrove references steps <funnel-id>
fgrove references step <funnel-id> <screen-id-or-position> --json
```

`show` returns the complete recorded journey; `steps` gives compact ordered
summaries; `step` returns one screen with its image link, visible text, button and
option labels, and a brief visual description when available. Positions start
at 1. Every command supports `--json` and `--site-url` (default:
`https://www.funnelsgrove.com`). These commands use the public reference service
independently of your account API and do not change the active funnel. Content
is automatic observation with uncertainty notes; inspect the actual images before
adapting a design. Unavailable observations are marked explicitly.

Sync a funnel into its own local folder:

```bash
fgrove sync down --funnel claimbee-ios --dir ./claimbee-ios
cd ./claimbee-ios
```

Common workflow:

```bash
fgrove status
git status --short
fgrove github status
fgrove docs
# if GitHub is connected: git push, then fgrove github pull
# if GitHub is not connected:
fgrove sync up --message 'Update funnel copy'
fgrove publish --env preview
```

Inside a synced folder, `fgrove` reads `.funnelsgrove-sync.json` first, so you do not need to run `fgrove use` when switching between local funnel directories. Use `fgrove use` only when you want a global fallback context for commands outside a synced folder.

`fgrove funnels clone` copies the draft and the funnel's offer-set mapping. The
clone reuses the same project-level offer sets and payment profiles; custom-domain
configuration is not copied into the new preview.

`fgrove sync down` refuses to overwrite local changes in an existing synced
folder. `fgrove sync up` sends only changed and deleted paths in one atomic
draft patch. It excludes paths matched by the folder's root `.gitignore` and
always keeps `.claude/settings.local.json` local while allowing shared files
such as `.claude/settings.json`. Disjoint concurrent edits are rebased automatically; if the same
path changed remotely, run `fgrove sync rebase`: it performs a three-way merge
from the recorded base, preserves CLI-managed files, and leaves the working tree
untouched when it finds conflicts. Review the merged diff, rerun `fgrove
validate`, then sync up again.
`sync down --force` is the explicit discard path and writes an automatic source
backup under `.funnelsgrove/rebase-backups/` before replacing hosted paths.

Changed images below `public/` are uploaded directly to asset storage, then the
small immutable asset references are included in the atomic patch. Sync retries
use a deterministic durable idempotency key, so a lost response cannot create a
second draft version. The CLI automatically falls back to candidate sync when
the compatible deployed API lacks the fast procedures or a change set exceeds
patch limits.

`fgrove sync up` is for funnels without GitHub source sync. When GitHub is
connected, commit and push source changes with normal git, then run `fgrove
github pull` to sync GitHub into the hosted draft. Do not run `fgrove sync up`
for the same local diff.

Use `fgrove env pull` from a synced folder to refresh only the ignored local `.env` file after project settings change, without replacing source files. The generated file identifies the current hosted draft through `NEXT_PUBLIC_FUNNEL_VERSION_ID`, so transactional steps such as email capture work during local QA.

Offer sets and payment mappings are runtime configuration. They are resolved remotely for preview and published funnels; `sync down` does not generate or download an offer-set source file.

Create an experiment draft from a strict JSON spec:

```bash
fgrove experiments create --spec experiment.json --dir .
fgrove experiments create --spec experiment.json --dir . --json
fgrove experiments start <experiment-uuid>
fgrove experiments stop <experiment-uuid>
fgrove experiments restart <experiment-uuid> --json
fgrove validate
```

The spec uses a stable `id` and `type: "step"`, `"paywall"`, or `"pricing"` with two to five variants, exactly one control, unique metrics and variant keys, and integer traffic totaling 100. Pricing variants also require `offerSetKey` and must route to the top-level `stepId`. See `docs/funnelsgrove/recipes/add-experiment.md` in a synced funnel for complete valid JSON examples and remediation guidance.

Creation is draft-only: it creates no PostHog flag and activates no traffic. The experiment becomes visible in the UI after the API commits its hosted generated snapshot, and the CLI installs those exact generated bytes locally through recoverable staging. `.funnelsgrove-sync.json` stays local and ignored.

With `--json`, success output contains only `experimentId`, `experimentKey`, `draftVersionId`, and `writtenPaths`, with generated paths in canonical order.

For a non-GitHub funnel, deliver the local source with `fgrove sync up`. For a GitHub-connected funnel, commit and push the generated source with the rest of the change, then run `fgrove github pull`; do not commit `.funnelsgrove-sync.json` or use `fgrove sync up` for that diff.

GitHub sync workflow:

```bash
fgrove github status --funnel claimbee-general
fgrove github connect --funnel claimbee-general --account The-Solid-Grove --repo claimbee-funnel
git push
fgrove github pull --funnel claimbee-general
fgrove publish --funnel claimbee-general --env preview
```

The GitHub commands use the FunnelsGrove API only. `fgrove github pull` pulls
the repository into the hosted draft after you push normal git commits.
`fgrove publish` waits for the current draft to reach GitHub before publishing.
It writes validation, GitHub sync, queue, build, image optimization, and upload
progress to stderr while keeping the final URL/version row alone on stdout.
If the exact job is still pending or running when that wait times out, publish
continues with a warning on stderr and asks you to check the eventual result
with `fgrove github status`. Terminal job failures, missing jobs, and API
failures remain fatal. Use `--no-github-sync` to skip only that CLI pre-publish
wait when the hosted draft is already known to match GitHub; the publish worker
still performs its normal best-effort post-publish GitHub synchronization.
`fgrove github push` still exists for explicit hosted-draft-to-GitHub recovery
work, but it is not the normal path for local source changes. Local `.env*`
files remain CLI-local runtime material from `sync down`; they are not sent to
GitHub sync.

Analytics workflow:

```bash
fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11
fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --date 2026-06-11
fgrove analytics transitions --project claimbee --funnel claimbee-ios --date 2026-06-11
fgrove analytics versions --project claimbee --funnel claimbee-ios
fgrove analytics cohort --project claimbee --date 2026-06-11
```

Each analytics command accepts `--format table|json`, `--out <path>`, `--workspace`, and `--timezone`. Use JSON for agents and automations:

```bash
fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11 --format json --out conversions.json
```

`conversions` downloads conversion totals, primary conversion metrics, full funnel path rows, and step transitions. `funnel-path` focuses on the ordered path report. `transitions` focuses on step-by-step advanced/drop-off counts. `cohort` downloads synced marketing cohort economics for the day. If the requested period has no synced data, or cohort source data is incomplete, the CLI exits non-zero with a human-readable explanation.

`conversions`, `funnel-path`, and `transitions` report one day with `--date`, or a range with `--from` and `--to` (inclusive, totals for the whole range). They report the current Journey Version by default, like Funnel Analytics in the console. Traffic from before the last structural change belongs to an older Journey Version: list the versions with `versions`, then pass `--journey-version <id>`:

```bash
fgrove analytics versions --project claimbee --funnel claimbee-ios
fgrove analytics transitions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14 --journey-version <journey-version-id>
```

`versions` lists each Journey Version newest first with its id, start time, active days, and visitors on those days. Active days are dates in `--timezone` (the API default, America/Los_Angeles, when omitted); the day a version replaced its predecessor belongs to both, and each version counts only its own visitors. An explicit version reports its own metrics and its own step structure. `--journey-version all` counts every version and orders `funnel-path` by the current published flow, so steps that exist only in older versions appear in `transitions` but not in `funnel-path`. JSON reports include `from`, `to`, and a `journeyVersion` object (`id`, `seq`, `label`, `startedAt`, `isCurrent`); `date` is set only for a one-day report. Table titles end with the reported version, for example `Journey 2 (current)`. The `overview` block of `conversions` always covers all Journey Versions.

The package also keeps the longer `funnelsgrove` command as a compatibility alias.

Edit profitability expenses through a local JSON file:

```bash
fgrove expenses pull --project claimbee --file expenses.json
# edit expenses.json; omit id and updatedAt for new expenses
fgrove expenses publish --project claimbee --file expenses.json
```

Publishing creates new rows and updates rows already present in the file. It never
deletes remote expenses that are absent from the file, and it refuses to overwrite
rows changed since the last pull. Pull again to resolve a concurrent edit.

Use `--api-url` or `FUNNELSGROVE_API_URL` for non-production APIs.
Use `--config` or `FUNNELSGROVE_CONFIG` to keep test credentials separate from the default `~/.funnelsgrove/config.json`.

### Add a content locale

Run `fgrove locales add ru --dir ./my-funnel` to copy every default content
object into `src/localization/ru/`, preserving its path relative to `src/`.
Original content stays in place; translated files import its structural type.
Repeat runs preserve translations, add missing exports and repair registrations.
Translate the new content, run `npx tsc --noEmit`, review `?locale=ru` and the live builder Preview language
selector, then sync and publish through the normal workflow. Run `fgrove docs`
for the full localization guide. The funnel must use a runtime release supporting
URL locale selection; this command does not upgrade dependencies.
