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

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 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 the one-day 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 day has no synced data, or cohort source data is incomplete, the CLI exits non-zero with a human-readable explanation.

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.
