Instructions for all agents (and humans) working in this repo. This is the
single source of truth; CLAUDE.md just points here. User-facing
docs: https://posthog.com/docs/ai-engineering/ai-wizard
The PostHog wizard (npx @posthog/wizard) is a CLI that adds PostHog to a user's project using an AI agent. It authenticates the user, detects their framework, runs an agent that integrates the SDK and instruments events, and walks the user through their first dashboard. All from the terminal.
This codebase follows a specific design discipline: product knowledge never enters infrastructure code. The runner pipeline, the TUI store, the detection loop, and the prompt assembler are machinery. They don't know what PostHog is. They don't know what a framework is. They execute a pipeline driven by typed configuration surfaces.
Each domain has a dedicated boundary:
- Frameworks →
FrameworkConfiginsrc/frameworks/<name>/ - Integration knowledge → markdown skills in the context-mill repo
- Security policy → YARA-X rules in the warlock sibling repo. The wizard wires the scanner via PostToolUse/PreToolUse hooks (
src/lib/yara-hooks.ts); the rule content itself lives in warlock. To disable scanning in the field without a release, see the kill-switch runbook:docs/runbooks/warlock-kill-switch.md. ONLY USE THIS IF ABSOLUTELY NECESSARY. - Programs → step arrays in
src/lib/programs/ - TUI → screen components and primitives in
src/ui/tui/
Adding a new concern means finding the narrowest existing surface, not adding logic to the runner. The wizard is small (~20K lines) because boundaries prevent damage from propagating between concerns.
Read .claude/skills/wizard-development/SKILL.md first. It covers the design discipline, a decision framework for new extensions, and warning signs that a change is drifting off-pattern. Two reference files extend it:
references/ARCHITECTURE.md— pipeline anatomy, data flow, security boundaries, screen resolutionreferences/ANTI-PATTERNS.md— concrete failure modes with alternatives
Five skills live under .claude/skills/. Read wizard-development first for any structural change; then load the relevant procedural skill:
| Skill | When to use |
|---|---|
wizard-development |
Before any structural change. Design principles + decision framework. |
adding-framework-support |
Adding a new framework integration (e.g. Ruby on Rails, Go, Angular). |
adding-skill-program |
Adding a new skill-based program (e.g. a new product feature setup). |
ink-tui |
Building or modifying TUI screens, layouts, and primitives. |
exploring-the-wizard |
Running/driving/exploring the wizard headlessly (read_state/perform_action, TUI snapshots). |
The CLI was overhauled to a smaller, extensible command surface. Use the new command names. Old names mostly no longer exist — only some are kept as aliases.
| Old command | New command | Status |
|---|---|---|
wizard integrate |
wizard (default flow) |
command removed |
wizard events-audit |
wizard audit events |
moved into audit family |
wizard audit (single) |
wizard audit <subcommand> |
now a family — see Audit subcommands |
wizard audit-3000 |
removed | retired |
wizard revenue |
wizard revenue-analytics |
renamed (old revenue removed) |
wizard upload-sourcemaps |
wizard upload-source-maps |
renamed; upload-sourcemaps kept as alias |
audit is the only family with skill-backed subcommands today:
| Subcommand | What it audits |
|---|---|
wizard audit events |
event capture quality + cost (default leaf) |
wizard audit all |
comprehensive audit across every area |
wizard audit autocapture |
autocapture setup + cost |
wizard audit feature-flags |
feature flag usage + cost |
wizard audit identify |
$identify implementation |
wizard audit session-replay |
session replay setup |
wizard audit web-analytics |
web analytics setup (wizard-native, not a skill) |
A skill and a command are the same machinery — a context-mill skill becomes a
command when its cli: block sets role: command. So wizard audit events
is the audit-events skill, just promoted. wizard skill <skill-name>
(skill.ts) runs a skill that wasn't promoted.
Two surfaces, one mechanism. So wizard audit <subcommand> is choosing an audit
area — it is not asking for a skill name, despite wizard audit --help
labelling the positional [skill] (a wizard-internal name we left as-is). Don't
confuse it with the top-level wizard skill command.
- Registration:
bin.ts— the.use()chain wires each command. - Command shape:
src/commands/command.ts— theCommandinterface every command implements. - Flat native commands (e.g.
revenue-analytics,upload-source-maps) are built withnativeCommandFactory(src/commands/factories/native-command-factory.ts). - Family commands (e.g.
audit) resolve subcommands at runtime against thecliEntriesinskill-menu.json. Logic lives insrc/lib/programs/dispatch-family.ts. Adding a skill-backed subcommand is a context-mill release, not a wizard change.
- A command is the word a user types (
audit,revenue-analytics). - A program is the internal business logic (
posthog-integration,revenue-analytics-setup) that a command invokes, and that other programs depend on viarequires: [...]. posthog-integrationis a program id, not a command. It powers the default flow and is a dependency of most other programs. Do not treat it as a CLI command or reference it in CI as one.
Give the Command.name an array of [newName, ...legacyNames]. yargs treats the
extra entries as aliases. See
src/commands/upload-sourcemaps.ts. Reserve
aliases for names that external callers (users' scripts) may still use — when the
only caller is one we control, update the caller instead.
pnpm install # Install dependencies
pnpm try --install-dir=<path> # Run the wizard locally against a test project
pnpm build # Compile TypeScript
pnpm test # Unit tests (builds first)
pnpm test:watch # Unit tests in watch mode
pnpm test:e2e # End-to-end tests
pnpm lint # Prettier + ESLint checks
pnpm fix # Auto-fix lint issues
pnpm dev # Build, link globally, watch for changesAfter any change, verify with:
pnpm build && pnpm test && pnpm fixFour things can independently point at a local server — the wizard binary,
context-mill (:8765), the MCP server (:8787), and PostHog (:8010). One
flag per service (--local-context-mill, --local-mcp, --local-posthog),
plus --local-dev for all three. They're dev-build-only; published builds
reject them.
Note wizard mcp add --local is not one of these — it writes a
posthog-local entry into your editor's MCP config, and is unrelated to where
a wizard run points. Full catalog: docs/local-dev.md.
- TypeScript everywhere. Use
type(notinterface) for framework context types so they satisfyRecord<string, unknown>. - All UI calls go through
getUI()(returnsWizardUIinterface). Never import the store directly from business logic. - Session mutations go through explicit store setters that call
emitChange(). Never mutatesessiondirectly — nanostore holds a shallow copy. - The router resolves the active screen from session state. No imperative
navigation (
goTo,navigate,push) anywhere. - Never write secrets to source code or hardcode API keys. Use the
wizard-toolsMCP server (check_env_keys/set_env_values) for.envfile operations. - Feedback / issues: wizard@posthog.com or GitHub Issues.
- context-mill — builds and publishes the markdown skills the wizard agent uses for framework-specific integration knowledge. Skills are decoupled from the wizard release cycle so docs and integration patterns can update independently.
- wizard-workbench — the
development and testing environment. Houses framework test apps (Next.js, React Router, Django, Flask, Laravel, SvelteKit, Swift, TanStack, FastAPI) with no PostHog installed, plus an
mprocs-driven local dev stack that runs context-mill + MCP + the wizard together with hot reload. Use this to develop and test wizard changes against real projects. - warlock — the security scanner engine for PostHog's agentic flows. Bundles YARA-X rules for prompt injection, exfiltration, destructive operations, supply chain attacks, hardcoded secrets, and PII. Engine-only: it returns matches with category/severity/action metadata; the wizard decides how to respond. New security rules belong in warlock, not in the wizard.