Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2fd897b
fix(gateway): show the server's reason when the mint refuses a run
gewenyu99 Sep 6, 2026
dc5bbf1
feat(gateway): stop falling back to the legacy gateway
brandonleung Sep 8, 2026
d207ca9
fix(gateway): declare that this build reads a refusal, and clean the …
brandonleung Sep 8, 2026
472936f
fix(gateway): keep the actionable copy on the two stock-detail statuses
brandonleung Sep 8, 2026
3c8d34d
fix(gateway): show the server's refusal reason on every status
brandonleung Sep 8, 2026
2aa1b63
docs(gateway): describe the capability flag, not the posture it replaced
brandonleung Sep 8, 2026
1a360e9
chore: remove old gw readiness check
edwinyjlim Sep 8, 2026
6fe5dbf
chore: remove old gw readiness check (#1226)
edwinyjlim Sep 8, 2026
fb391dc
Merge branch 'brandon/wizard-w1-1-drop-legacy-posture' of github.com:…
edwinyjlim Sep 8, 2026
c524adf
remove
edwinyjlim Sep 8, 2026
f6e1f89
docs(gateway): say why the refusal detail is sanitized
brandonleung Sep 8, 2026
3733976
feat(gateway): code and report a refused mint
brandonleung Sep 8, 2026
b7eb462
fix(gateway): read the mint refusal outcome from the DRF body code
brandonleung Sep 8, 2026
eb9e605
fix(gateway): stop an empty code shadowing a usable outcome
brandonleung Sep 8, 2026
657c112
feat(gateway): re-mint once and resume on a 401 from an aged bearer
brandonleung Sep 8, 2026
e69fc48
add aws context mill fallback to anthropic
edwinyjlim Sep 8, 2026
388cd7d
fix(gateway): decide a re-mint on pi's diagnostic code, not its error…
brandonleung Sep 8, 2026
6723957
feat(ci): accept a wizard-app gateway token under --ci
brandonleung Sep 8, 2026
09c3902
fix: use gateway-owned file trust guidance
gewenyu99 Sep 9, 2026
d80dcf6
fix: route security triage through gateway policy
gewenyu99 Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The PostHog wizard helps you quickly add PostHog to your project using AI.
To use the wizard, you can run it directly using:

```bash
npx @posthog/wizard
npx @posthog/wizard@latest
```

Currently the wizard can be used for over 16+ frameworks for frontend, backend, and mobile applications. If you have other integrations you would like the wizard to
Expand All @@ -43,10 +43,10 @@ Protocol) servers:

```bash
# Install PostHog MCP server to supported clients
npx @posthog/wizard mcp add
npx @posthog/wizard@latest mcp add

# Remove PostHog MCP server from supported clients
npx @posthog/wizard mcp remove
npx @posthog/wizard@latest mcp remove
```

## Wizard programs
Expand All @@ -58,7 +58,7 @@ The wizard's commands are grouped into **programs** — self-contained agentic j
Running the wizard with no arguments installs PostHog into your project. It detects your framework, wires up initialization, instruments a starter set of events, and walks you through a first dashboard:

```bash
npx @posthog/wizard
npx @posthog/wizard@latest
```

Powered by the `posthog-integration` program. Most other programs below build on it (they declare `requires: ['posthog-integration']`) and will offer to run it first if PostHog isn't already set up.
Expand All @@ -68,7 +68,7 @@ Powered by the `posthog-integration` program. Most other programs below build on
Autonomously sets up PostHog self-driving end-to-end. It connects GitHub, enables Session Replay and Error Tracking, wires up signal sources, and configures a Signals scout troop that watches your project for you.

```bash
npx @posthog/wizard self-driving
npx @posthog/wizard@latest self-driving
```

If PostHog isn't already installed, the wizard runs the default integration first (composed run) before starting the self-driving setup.
Expand All @@ -81,16 +81,16 @@ audit (the default); pass a subcommand to run a specific one:

```bash
# Runs the events audit (the default) — no subcommand needed
npx @posthog/wizard audit
npx @posthog/wizard@latest audit

# Or run a specific audit directly
npx @posthog/wizard audit events # event capture quality + cost (default)
npx @posthog/wizard audit all # comprehensive audit across every area
npx @posthog/wizard audit autocapture # autocapture setup + cost
npx @posthog/wizard audit feature-flags # feature flag usage + cost
npx @posthog/wizard audit identify # your $identify implementation
npx @posthog/wizard audit session-replay # session replay setup
npx @posthog/wizard audit web-analytics # web analytics setup
npx @posthog/wizard@latest audit events # event capture quality + cost (default)
npx @posthog/wizard@latest audit all # comprehensive audit across every area
npx @posthog/wizard@latest audit autocapture # autocapture setup + cost
npx @posthog/wizard@latest audit feature-flags # feature flag usage + cost
npx @posthog/wizard@latest audit identify # your $identify implementation
npx @posthog/wizard@latest audit session-replay # session replay setup
npx @posthog/wizard@latest audit web-analytics # web analytics setup
```

Most audit subcommands resolve at runtime from the published skill registry, so
Expand All @@ -108,7 +108,7 @@ new audits appear without a wizard release (`web-analytics` is wizard-native).
Wire up an existing PostHog + Stripe project for revenue analytics:

```bash
npx @posthog/wizard revenue-analytics
npx @posthog/wizard@latest revenue-analytics
```

Requires PostHog and Stripe SDKs already installed. Supports `--ci` with the
Expand All @@ -120,7 +120,7 @@ Detect data sources your project already uses (Postgres, MySQL, MongoDB,
Snowflake, BigQuery, Stripe, …) and connect them to PostHog's data warehouse:

```bash
npx @posthog/wizard warehouse
npx @posthog/wizard@latest warehouse
```

The wizard scans your dependencies and `.env` key names (never the values) to
Expand All @@ -132,7 +132,7 @@ OAuth sources open the PostHog app's new-source flow in your browser.
Upload JavaScript source maps to PostHog error tracking so stack traces are symbolicated back to your original code:

```bash
npx @posthog/wizard upload-source-maps
npx @posthog/wizard@latest upload-source-maps
```

### Run skill
Expand All @@ -141,8 +141,8 @@ Run any context-mill skill directly by name, even if it isn't exposed as its own
command:

```bash
npx @posthog/wizard skill list # list every available skill
npx @posthog/wizard skill <skill-name> # run one by name
npx @posthog/wizard@latest skill list # list every available skill
npx @posthog/wizard@latest skill <skill-name> # run one by name
```

## Wizard ownership
Expand Down Expand Up @@ -181,7 +181,7 @@ account, uses the returned personal API key to run the normal CI install,
and wires PostHog into the project at `--install-dir`:

```bash
npx @posthog/wizard --ci --signup \
npx @posthog/wizard@latest --ci --signup \
--email you@example.com \
--install-dir .
```
Expand All @@ -197,10 +197,10 @@ PostHog yourself — use the `provision` subcommand, which emits a structured

```bash
# Human-readable (when stdout is a TTY)
npx @posthog/wizard provision --email user@example.com --region us
npx @posthog/wizard@latest provision --email user@example.com --region us

# Machine-readable — auto when stdout is piped, or force with --json
npx @posthog/wizard provision --email user@example.com --region eu --json
npx @posthog/wizard@latest provision --email user@example.com --region eu --json
```

Success prints the full `ProvisioningResult` (`projectApiKey`, `host`,
Expand Down Expand Up @@ -235,13 +235,13 @@ The following CLI arguments are available:
> gateway doesn't yet grant the scopes the wizard needs to personal API keys
> for most users, so non-interactive `--ci` runs fail at the gateway. The flag
> is disabled in the published package and exits with an error — run the wizard
> in an interactive terminal instead (`npx @posthog/wizard`). The notes below
> in an interactive terminal instead (`npx @posthog/wizard@latest`). The notes below
> describe CI mode as it works in development builds.

Run the wizard non-interactive executions with `--ci`:

```bash
npx @posthog/wizard --ci --api-key $POSTHOG_PERSONAL_API_KEY --install-dir .
npx @posthog/wizard@latest --ci --api-key $POSTHOG_PERSONAL_API_KEY --install-dir .
```

When running in CI mode (`--ci`):
Expand Down Expand Up @@ -614,7 +614,7 @@ point is `evaluateWizardReadiness()`, which returns one of three values:
| --- | --- |
| `types.ts` | Enums, interfaces (`ServiceHealthStatus`, `AllServicesHealth`, etc.) |
| `statuspage.ts` | Statuspage.io v2 API helpers + checks for Anthropic, PostHog, GitHub, npm, Cloudflare |
| `endpoints.ts` | Direct endpoint checks for LLM Gateway (`/_liveness`), MCP (`/`), and the skills origins (`skill-menu.json` on GitHub Releases + the AWS mirror) |
| `endpoints.ts` | Direct endpoint checks for MCP (`/`) and the skills origins (`skill-menu.json` on GitHub Releases + the AWS mirror) |
| `readiness.ts` | `checkAllExternalServices`, `evaluateWizardReadiness`, readiness config |
| `index.ts` | Barrel re-export |
| `testme.md` | Test running instructions and endpoint reference |
Expand All @@ -632,7 +632,7 @@ two arrays:
### Current defaults

```ts
downBlocksRun: ['anthropic', 'npmOverall', 'llmGateway', 'mcp', 'skillsOrigin'],
downBlocksRun: ['anthropic', 'npmOverall', 'mcp', 'skillsOrigin'],
degradedBlocksRun: ['anthropic'],
```

Expand Down
4 changes: 3 additions & 1 deletion docs/error-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ as an API: backends may branch on it.

New codes follow the pattern `PHW_<GROUP>_<NAME>` (see `ERROR_CODE_PATTERN` in
`codes.ts`). Groups are lowercase module prefixes (`cli`, `args`, `auth`, `env`,
`detect`, `skill`, `agent`, `settings`, `internal`).
`detect`, `skill`, `agent`, `settings`, `gateway`, `internal`).

## How codes propagate

Expand Down Expand Up @@ -107,6 +107,8 @@ screen, debug log) keep the full detail.
| `PHW_AGENT_ORCHESTRATOR_SINK_INVARIANT` | agent | orchestrator plan violates sink coverage invariant | no |
| `PHW_SETTINGS_UNFIXABLE_CONFLICT` | settings | Claude settings conflict that cannot be auto-neutralized (managed/unwritable) | no |
| `PHW_INTERNAL_UNHANDLED` | internal | catch-all: an unexpected error escaped the pipeline | yes |
| `PHW_GATEWAY_MINT_REFUSED` | gateway | the gateway-token mint refused this run (blocked, throttled, unlisted program, rollout off); the server's reason is shown | no |
| `PHW_GATEWAY_MINT_FAILED` | gateway | the gateway-token mint could not be reached or answered unusably | yes |

Retry advice is guidance for automated hosts (sandbox re-run policies), not a
guarantee.
Expand Down
7 changes: 5 additions & 2 deletions scripts/smoke-test-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# wizard-workbench, and run in CI mode.
#
# Prerequisites:
# - POSTHOG_PERSONAL_API_KEY env var (or in .env)
# - POSTHOG_PERSONAL_API_KEY env var (or in .env): a personal API key (phx_)
# or a wizard-app OAuth access token (pha_), which is what the CI bot uses.
# The env name stays the same for either so the secret rotates in place.
# - A wizard-workbench repo checked out (for the test app), pointed to by:
# - WIZARD_WORKBENCH_ROOT=/path/to/wizard-workbench
# or
Expand All @@ -15,8 +17,9 @@
# ./scripts/smoke-test-ci.sh basic-integration/next-js/15-pages-router-saas
#
# Examples:
# # With API key inline:
# # With the key inline (a phx_ personal key or a pha_ wizard-app token):
# POSTHOG_PERSONAL_API_KEY=phx_your_key_here ./scripts/smoke-test-ci.sh
# POSTHOG_PERSONAL_API_KEY=pha_wizard_app_token ./scripts/smoke-test-ci.sh
#
# # With project ID override:
# POSTHOG_PERSONAL_API_KEY=phx_your_key_here POSTHOG_PROJECT_ID=12345 ./scripts/smoke-test-ci.sh
Expand Down
30 changes: 30 additions & 0 deletions src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,36 @@ describe('CLI argument parsing', () => {

expect(mockStreamAttach).not.toHaveBeenCalled();
});

// The CI bot authenticates with a wizard-app pha_ token, the same
// credential headless takes. Either key reaches buildSession untouched and
// neither draws the unexpected-prefix warning.
test.each(['phx_ci_key', 'pha_ci_bot_token'])(
'accepts %s without a prefix warning',
async (apiKey) => {
const log = vi
.spyOn(console, 'log')
.mockImplementation(() => undefined);
try {
await runCLI([
'--ci',
'--api-key',
apiKey,
'--install-dir',
'/tmp/test',
]);

expect(process.exit).not.toHaveBeenCalledWith(1);
expect(getLastBuildSessionArgs().apiKey).toBe(apiKey);
const lines = log.mock.calls.map((c) => c.map(String).join(' '));
expect(lines.some((l) => l.includes('does not start with'))).toBe(
false,
);
} finally {
log.mockRestore();
}
},
);
});

// The experimental headless flag is the published-build sibling of --ci: it
Expand Down
18 changes: 18 additions & 0 deletions src/__tests__/wizard-abort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
runCleanups,
} from '@utils/wizard-abort';
import { analytics } from '@utils/analytics';
import { ErrorCodes } from '@lib/errors';
import { getUI } from '../ui';

vi.mock('../utils/analytics');
Expand Down Expand Up @@ -132,6 +133,23 @@ describe('wizardAbort', () => {
});
});

it('resolves the code from a coded WizardError when the caller passes none', async () => {
// A mint refusal reaches wizardAbort as the error alone; its code must
// still land on the captured exception.
const error = new WizardError(
'refused',
{ status: 403 },
ErrorCodes.GatewayMintRefused,
);

await expect(wizardAbort({ error })).rejects.toThrow('process.exit called');

expect(mockAnalytics.captureException).toHaveBeenCalledWith(error, {
status: 403,
error_code: ErrorCodes.GatewayMintRefused,
});
});

it('runs registered cleanup functions before analytics and display', async () => {
const callOrder: string[] = [];

Expand Down
35 changes: 17 additions & 18 deletions src/commands/basic-integration/__tests__/ci-install.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { keyPrefixWarning } from '../ci-install';

/**
* `keyPrefixWarning` is the one behavioral fork between `--ci` and headless
* mode: headless accepts a `pha_` OAuth access token as first-class, CI does
* not. Everything else about the two modes is shared.
* `--ci` and headless accept the same credentials: a personal API key and a
* wizard-app OAuth access token (the CI bot's). Only unknown prefixes warn.
*/
describe('keyPrefixWarning', () => {
describe.each([false, true])('headless=%s', (headless) => {
test('a personal API key (phx_) is always accepted', () => {
expect(keyPrefixWarning('phx_abc', headless)).toBeNull();
});
test('a personal API key (phx_) is accepted', () => {
expect(keyPrefixWarning('phx_abc')).toBeNull();
});

test('no key returns no warning', () => {
expect(keyPrefixWarning(undefined, headless)).toBeNull();
});
test('a wizard-app OAuth access token (pha_) is accepted', () => {
// The CI bot authenticates the mint with one of these; a warning here
// would name the sanctioned credential as a mistake on every CI run.
expect(keyPrefixWarning('pha_abc')).toBeNull();
});

test('a project/client key (phc_) always warns', () => {
expect(keyPrefixWarning('phc_abc', headless)).toMatch(/phc_/);
});
test('no key returns no warning', () => {
expect(keyPrefixWarning(undefined)).toBeNull();
});

test('headless accepts a pha_ OAuth access token without warning', () => {
expect(keyPrefixWarning('pha_abc', true)).toBeNull();
test('a project/client key (phc_) warns and names both accepted kinds', () => {
expect(keyPrefixWarning('phc_abc')).toMatch(/phc_/);
expect(keyPrefixWarning('phc_abc')).toMatch(/"phx_" or "pha_"/);
});

test('CI mode warns on a pha_ OAuth access token', () => {
const warning = keyPrefixWarning('pha_abc', false);
expect(warning).toMatch(/OAuth access token/);
test('an unknown prefix warns', () => {
expect(keyPrefixWarning('sk-abc')).toMatch(/does not start with/);
});
});
Loading
Loading