# The Analytiics CLI

```sh
npx analytiics start
```

That is setup. It signs you in, creates the project, issues a server write key
and writes it to `.env`, scaffolds `analytics.yaml`, and installs the
`instrument` agent skill — then tells you the two things it cannot do for
you: add the tracker snippet, and ask your agent to instrument the code.

Every step is idempotent, so running it again is a status check rather than a
second setup. A project is its site's domain; it is read from `homepage` in
`package.json`, or you pass it: `npx analytiics start example.com`.

## The individual commands

```sh
npx analytiics login             # authorize this machine
npx analytiics link example.com  # create/attach the project, write a key
npx analytiics init example.com  # scaffold analytics.yaml
npx analytiics codegen           # write analytics.gen.ts, and push the manifest
npx analytiics push              # send analytics.yaml to your dashboard
npx analytiics validate          # check the manifest
npx analytiics check             # compare the manifest against the code
npx analytiics whoami            # who you are signed in as
npx analytiics upgrade maker     # open checkout, and wait for the payment
```

## Upgrading without leaving the terminal

```sh
npx analytiics upgrade studio
```

It reads the plan you are on, asks the server for a checkout link, opens it,
and then waits — polling until the plan actually changes.

The waiting is the point. This command cannot know whether you paid: it never
sees the card, and the page you type it into is Stripe's. What it does instead
is keep asking the server, which finds out from Stripe directly. So a
`Done — you're on Studio` is a fact rather than an assumption, and stopping
early is not a failure — if you finished paying after it gave up, the plan is
live anyway. `--no-wait` prints the link and exits, for scripts.

An agent can run the whole thing. What it structurally cannot do is finish it:
the one irreversible step happens on a page a human is looking at.

## Pushing the manifest

Your dashboard draws a card per declared event, ranks its enum properties, and
groups events by tag. It can only do that from a manifest it has been given:
`analytics.yaml` lives in your repository and the dashboard renders on ours.

`codegen` pushes it for you — regenerating the typed SDK is the moment the
manifest changed — and `--no-push` skips that. `push` does it on its own,
for anyone who never runs codegen: a Python or Ruby backend has a tracking plan
and no TypeScript to generate.

The file stays the source of truth. Nothing edits it from the dashboard, and
what we keep is a copy — to change what your charts show, change the file and
push again.

A push that cannot happen never fails `codegen`. Offline or signed out, it
says which manifest your dashboard is still showing and carries on.

## Keeping the manifest and the code in step

`validate` reads the manifest on its own. `check` reads it against the
repository, and is the one to run in CI:

```sh
npx analytiics check             # warn, and exit 0
npx analytiics check --strict    # exit 1 on anything unambiguously wrong
npx analytiics check src apps    # only search these paths
```

It reports four kinds of drift:

- **Sent but not declared.** Code emits an event the manifest has never heard
  of, so nothing derives a chart for it. Add it, or stop sending it.
- **Declared but never sent.** An event with no call site draws an empty chart,
  which reads as *nobody did this* rather than *nobody wired this*.
- **Properties that diverge.** A call passing a property the event does not
  declare, or omitting a required one.
- **A stale `analytics.gen.ts`.** The generated module records the manifest it
  came from, so one built before the last edit is caught even when it still
  compiles.

Anything ambiguous is reported as nothing at all. A call whose properties are a
variable or a spread cannot be read at rest, and a false alarm would cost the
credibility of every warning after it — so the check stays quiet rather than
guessing. It is a text scan, not a compiler, so it also runs in a repository
whose build is broken.

**`--strict` never fails on an unused event.** Wiring a call site is a
decision a person has to make, and an unfinished tracking plan is not a reason
to block a release.

A call in a test is a call site like any other, which is usually what you want
— an event is declared, and something sends it. Where it isn't, name the paths
to search: `analytiics check src`.

### In a pull request

```yaml
# .github/workflows/analytics.yml
name: analytics
on: pull_request
jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npx analytiics check
```

Start there — warnings on every pull request, failing nothing. Once the list is
empty, add `--strict` and it stays empty. No sign-in and no network: the
manifest and the code are both in the checkout.

## Signing in

Sign-in is the OAuth device flow: the CLI prints a short code and a URL, you
approve it in a browser you are already signed into, and the CLI receives a
session token. The CLI never sees a password.

The write key is **refused rather than written** when `.env` is not covered by
`.gitignore`, because a key committed to a repository is a key you have to
rotate.

## Agent skills

```sh
npx analytiics skills list
npx analytiics skills install instrument
```

Skills install into `.claude/skills/`. The `instrument` skill teaches a
coding agent to audit a codebase, propose a tracking plan, and wire up the typed
calls — including the parts that are easy to get wrong, like revenue events
being server-side only. Skills ship inside the CLI package, so a skill can never
disagree with the CLI that generated the code it describes.
