analytiics
v0.9.0
Published
Analytiics CLI: set up analytics, scaffold a tracking manifest, and compile it into a typed SDK.
Maintainers
Readme
analytiics
Turns analytics.yaml — a tracking manifest checked into your repo — into a
typed SDK. Instrumentation becomes "call the right typed function" instead of
"remember the right event name."
npx analytiics startThat is the whole of 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.
Sign-in is the device flow — a short code, a URL, and a browser you are already
signed into. 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 repo is a key you have to rotate.
The individual commands
start is those steps in order; each is also its own command.
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 asThe drift check
A file is only the source of truth if something notices when the code stops agreeing with it.
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 pathscheck reads the manifest, finds every call site in the repo, and reports four
kinds of drift: an event sent but never declared, an event declared but never
sent, a call whose props diverge from the declaration, and an analytics.gen.ts
built from a manifest that has since moved on.
Anything ambiguous is reported as nothing at all. Props that 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 and not a compiler, so it runs in a repo whose build is broken, and it needs neither sign-in nor network: the manifest and the code are both in the checkout.
It warns by default and that default is the point — a check that fails a build
the first time it runs gets deleted. Put it on pull requests, let it describe
the drift for a few weeks, then turn on --strict once the list is empty.
# .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--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.
Where it isn't, name the paths to search: analytiics check src.
Pushing the manifest
Your dashboard draws a card per declared event, breaks down enum props, 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, because regenerating the typed SDK is the moment
the manifest changed. --no-push skips that, and 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 the server keeps 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 — a generated
file is correct whether or not the network was reachable while it was written.
Agent skills
npx analytiics skills list
npx analytiics skills install instrument
npx analytiics skills install exploreInstalls into .claude/skills/. The instrument skill teaches an agent to
audit a codebase, propose a tracking plan, and wire the typed calls — including
the parts that are easy to get wrong, like revenue events being server-side
only. Skills ship inside this package, so one can never disagree with the CLI
that generated the code it describes.
The explore skill reads the owner-only analytics API using your CLI
session — traffic, breakdowns, custom events, funnels, People and journeys —
and prefers the MCP tools below when the client has them. It can trace an
identified customer's retained history or an anonymous visitor's current UTC
day. Search uses your application's exact opaque user ID.
MCP server
npx analytiics mcp install # registers the server in this repo's .mcp.json
npx analytiics mcp # what the client runs: tools over stdioThe same owner API, as tools an agent can call: list_projects,
get_metrics, get_breakdown, list_events, get_funnel, list_people,
get_journey and get_manifest. Every one of them reads; none can change a
project. They answer with the numbers the dashboard shows, under the same
query budget.
The server runs on your machine, started by the agent's client, and reads the
session analytiics login wrote to your home directory. Nothing is copied
into a client config, so the .mcp.json it writes is safe to commit: it names
a command, and each teammate's own login supplies their credentials. Claude
Code reads .mcp.json on its own; any other client that speaks stdio can run
npx analytiics mcp. A self-hosted ANALYTIICS_HOST travels with the entry.
Ships inside this package, like the skills, so the tools can never disagree
with the CLI that installed them. The explore skill says how to read what
they return.
The manifest
version: 1
project: thiings
events:
purchase:
description: Customer bought an icon pack
tags: [conversion]
revenue: true
props:
type:
type: string
enum: [personal_indie, studio_agency, credits]Unknown keys are rejected rather than ignored — a silently dropped field means
a silently missing chart. Validation also catches what a schema can't express:
funnel steps referencing events you never declared, enum on a non-string
prop, and event names that would collide once camelCased.
The generated SDK
import { createTrack } from "./analytics.gen.js";
import { createClient, toAnalytiicsClient } from "@analytiics/sdk-node";
// Inside a server request handler:
const client = createClient({ site, writeKey });
const track = createTrack(toAnalytiicsClient(client, { userId }));
track.purchase(
{ type: "personal_indie" },
{ userId, revenueCents: 4900, currency: "USD" },
);revenue: true makes the amount part of the signature, so recording a
conversion while losing the money is a compile error rather than a silent zero.
enum props become union types. Descriptions become doc comments.
In the browser the generated calls find the tracker on window.analytiics with no
setup. On a server, create typed helpers for each request:
import { createClient, toAnalytiicsClient } from "@analytiics/sdk-node";
import { createTrack } from "./analytics.gen.js";
const track = createTrack(toAnalytiicsClient(createClient({ site, writeKey }), { userId }));Generated calls are fail-open: a missing client is a no-op, never a thrown
error in someone's checkout flow. Keep request identities in createTrack helpers;
the legacy configure function changes a shared browser convenience client.
Options
--manifest <path> default: analytics.yaml
--out <path> default: analytics.gen.ts
--env <path> where `link` writes the key (default: .env)
--strict `check`: exit non-zero on drift, instead of warningANALYTIICS_HOST points the CLI at a different server (default
https://www.analytiics.co). Credentials live in ~/.analytiics/config.json,
mode 0600, keyed by host — so being signed in to the hosted service and a
self-hosted instance at once is the normal case rather than a conflict.
codegen is idempotent — an unchanged manifest prints "already up to date" and
leaves the file's mtime alone, so it's safe to run on every build. Its push is
idempotent too: the same manifest sent twice replaces itself with itself.
