Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { hideBin } from 'yargs/helpers';
import { VERSION } from './src/lib/version.js';

const WIZARD_VERSION = VERSION;

Check warning on line 8 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe assignment of an `any` value

const NODE_VERSION_RANGE = '>=18.17.0';

Expand Down Expand Up @@ -361,7 +361,7 @@
const { startPlayground } = await import(
'./src/ui/tui/playground/start-playground.js'
);
(startPlayground as (version: string) => void)(WIZARD_VERSION);

Check warning on line 364 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`
})();
} else if (options.skill) {
// Run a specific skill by ID
Expand Down Expand Up @@ -441,7 +441,7 @@
);

const { Flow } = await import('./src/ui/tui/router.js');
const tui = startTUI(WIZARD_VERSION, Flow.McpAdd);

Check warning on line 444 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`
const session = buildSession({
debug: options.debug,
localMcp: options.local,
Expand Down Expand Up @@ -487,7 +487,7 @@
);

const { Flow } = await import('./src/ui/tui/router.js');
const tui = startTUI(WIZARD_VERSION, Flow.McpRemove);

Check warning on line 490 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`
const session = buildSession({
debug: options.debug,
localMcp: options.local,
Expand Down Expand Up @@ -597,11 +597,17 @@
// ── Skill-based workflow subcommands (derived from registry) ─────────
for (const wfConfig of getSubcommandWorkflows()) {
cli.command(
wfConfig.command!,

Check warning on line 600 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Forbidden non-null assertion
wfConfig.description,
(y) => y.options(skillSubcommandOptions),
(y) =>
y.options({
...skillSubcommandOptions,
...(wfConfig.cliOptions ?? {}),
}),
(argv) => {
const options = { ...argv };
const extras =
wfConfig.mapCliOptions?.(argv as Record<string, unknown>) ?? {};
const options = { ...argv, ...extras };
if (options.ci) {
runWizardCI(wfConfig, options);
} else {
Expand Down Expand Up @@ -642,7 +648,7 @@
);
const { analytics } = await import('./src/utils/analytics.js');

const tui = startTUI(WIZARD_VERSION, config.flowKey as any);

Check warning on line 651 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 651 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `Flow`

Check warning on line 651 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`

const session = buildSession({
debug: options.debug as boolean | undefined,
Expand All @@ -655,7 +661,7 @@
projectId: options.projectId as string | undefined,
email: options.email as string | undefined,
menu: options.menu as boolean | undefined,
integration: options.integration as any,

Check warning on line 664 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 664 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe assignment of an `any` value
benchmark: options.benchmark as boolean | undefined,
yaraReport: options.yaraReport as boolean | undefined,
});
Expand Down
29 changes: 22 additions & 7 deletions src/lib/agent/agent-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,14 +971,29 @@ export async function runAgent(
'//tmp/**',
'//private/tmp',
'//private/tmp/**',
// Package manager stores — allow writes so pnpm/npm can
// install packages without breaking the user's existing setup
'~/Library/pnpm/store/**', // pnpm global store (macOS)
'~/.local/share/pnpm/store/**', // pnpm global store (Linux)
// Package manager stores and toolchain installs — allow writes
// so pnpm/npm/yarn/bun and version managers (corepack, volta)
// can install packages and self-update without breaking the
// user's existing setup.
'~/Library/pnpm/**', // pnpm root (macOS) — store + .tools/ for packageManager pinning
Comment thread
gewenyu99 marked this conversation as resolved.
'~/.local/share/pnpm/**', // pnpm root (Linux)
'~/.pnpm-store/**', // pnpm alternate store
'~/.npm/**', // npm cache
'~/.yarn/**', // yarn classic cache
'~/.yarn/berry/**', // yarn berry cache
'~/.npm/**', // npm cache (covers _npx too)
'~/.yarn/**', // yarn classic + berry cache
'~/.bun/install/**', // bun cache + global installs
'~/.cache/node/corepack/**', // corepack version downloads (Linux/macOS)
'~/Library/Caches/node/corepack/**', // corepack on older macOS layouts
'~/.volta/**', // Volta toolchain (referenced by workbench package.json)
// Python — used by django/flask/fastapi wizards
'~/.cache/pip/**',
'~/Library/Caches/pip/**',
'~/.cache/uv/**',
'~/Library/Caches/uv/**',
'~/.cache/pypoetry/**',
'~/Library/Caches/pypoetry/**',
// Ruby — used by rails wizard
'~/.bundle/**',
'~/.gem/**',
],
},
network: {
Expand Down
58 changes: 58 additions & 0 deletions src/lib/workflows/migration/content/free-tier.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* PostHog free-tier highlights — the numbers a migrating team gets back when
* they consolidate. Sourced from posthog.com/pricing.md.
*/

import { Text } from 'ink';
import { Colors } from '../../../../ui/tui/styles.js';
import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js';

export const FREE_TIER_BLOCK: ContentBlock = {
type: 'lines',
interval: 400,
pause: 9000,
lines: [
<Text bold>{' Free every month, on every product'}</Text>,
<Text> </Text>,
<Text>
<Text color={Colors.accent}>{' 1,000,000 '}</Text>
<Text>events </Text>
<Text dimColor>product analytics</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 1,000,000 '}</Text>
<Text>requests </Text>
<Text dimColor>feature flags + experiments</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 5,000 '}</Text>
<Text>recordings </Text>
<Text dimColor>session replay</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 100,000 '}</Text>
<Text>exceptions </Text>
<Text dimColor>error tracking</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 100,000 '}</Text>
<Text>events </Text>
<Text dimColor>LLM analytics</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 50 GB '}</Text>
<Text>logs </Text>
<Text dimColor>logs</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 1,500 '}</Text>
<Text>responses </Text>
<Text dimColor>surveys</Text>
</Text>,
<Text>
<Text color={Colors.accent}>{' 1,000,000 '}</Text>
<Text>rows </Text>
<Text dimColor>data warehouse</Text>
</Text>,
],
};
258 changes: 258 additions & 0 deletions src/lib/workflows/migration/content/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/**
* Migration learn deck (statsig variant). Statsig is the only `migrate`
* variant today, so this deck plays as-is when the wizard runs
* `migrate --product=statsig`. Three movements:
*
* 1. Welcome and reassure.
* 2. What to expect — the migration is replacement-only, takes a few
* minutes, leaves the build green.
* 3. What's a little different — how flags and experiments work in
* PostHog, presented as right-way guidance rather than gotchas.
*
* FF/experiments guidance paraphrased from PostHog public docs:
* - posthog.com/docs/feature-flags/best-practices
* - posthog.com/docs/feature-flags/common-questions
* - posthog.com/docs/experiments/best-practices
*/

import { Text } from 'ink';
import type { WizardStore } from '../../../../ui/tui/store.js';
import { Colors } from '../../../../ui/tui/styles.js';
import { TextRevealMode } from '../../../../ui/tui/primitives/TextBlock.js';
import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js';
import { StatusPeekTrigger } from '../../../../ui/tui/components/StatusPeekTrigger.js';
import { PRODUCT_SUITE_BLOCK } from '../../posthog-integration/content/product-suite.js';
import { LINE_CHART_BLOCK } from '../../posthog-integration/content/line-chart.js';
import { FUNNEL_BLOCK } from '../../posthog-integration/content/funnel.js';
import { VENDOR_STACK_BLOCK } from './vendor-stack.js';
import { FREE_TIER_BLOCK } from './free-tier.js';
import { PRICING_STRUCTURE_BLOCK } from './pricing-structure.js';

export const getContentBlocks = (store?: WizardStore): ContentBlock[] => [
// ── Welcome ────────────────────────────────────────────────────────────
{
content: 'Hello.',
pause: 3000,
mode: TextRevealMode.Typewriter,
animationInterval: 160,
},

{ content: 'The Wizard is an agent.', pause: 4000 },

{
content:
'As we speak, it’s making a plan to migrate from Statsig to PostHog.',
pause: 6000,
},

{
content: 'PostHog covers the cost of running this agent.',
pause: 4000,
},

{ type: 'clear', pause: 2000 },

{
pause: 5000,
persist: true,
content: <StatusPeekTrigger store={store} />,
},

{
pause: 6000,
persist: true,
content: (
<Text>
Press{' '}
<Text color={Colors.accent} bold>
S
</Text>{' '}
to expand or collapse the status.
</Text>
),
},

{ type: 'clear', pause: 2000 },

// ── What to expect ─────────────────────────────────────────────────────
{ content: 'Here’s what to expect.', pause: 3000 },

{ content: 'The migration takes about ten minutes.', pause: 3000 },

{
content:
'Every Statsig call gets replaced in place with its PostHog equivalent.',
pause: 5500,
},

{
content:
'Nothing new gets added. No extra captures, no surprise instrumentation.',
pause: 5500,
},

{
content:
'The Statsig package gets removed at the end. We’ll run build and lint to clean up after ourselves.',
pause: 6500,
},

{ type: 'clear', pause: 2000 },

// ── What's a little different ─────────────────────────────────────────
{
content: 'A few things work a little differently in PostHog.',
pause: 4500,
},

{
content: (
<Text>
Flags evaluate against a stable user. Call{' '}
<Text bold color={Colors.accent}>
identify()
</Text>{' '}
first, then check the flag.
</Text>
),
pause: 6000,
persist: true,
},

{
content:
'For anything in the first paint, evaluate server-side and bootstrap the values into the client.',
pause: 6500,
},

{
content: (
<Text>
In production, route requests through a reverse proxy to avoid ad
blockers breaking your flags.{'\n'}
<Text dimColor>https://posthog.com/docs/advanced/proxy</Text>
</Text>
),
pause: 6500,
persist: true,
},

{
content:
'When a flag reaches 100% rollout, retire it. Flags are signals, not switches.',
pause: 5500,
},

{
content: (
<Text>
Name flags descriptively. No double negatives. Reflect the return type.{' '}
<Text dimColor>For example </Text>
<Text bold>show-new-checkout</Text>
<Text dimColor>.</Text>
</Text>
),
pause: 6500,
persist: true,
},

{ type: 'clear', pause: 1500 },

// ── Experiments ────────────────────────────────────────────────────────
{
content: (
<Text bold color={Colors.accent}>
Experiments
</Text>
),
pause: 2500,
persist: true,
},

{
content:
'Change one thing per variant. Multiple changes in one variant blur the result.',
pause: 5500,
},

{
content:
'Decide the running time up front. PostHog includes a sample-size and duration calculator in the setup flow.',
pause: 6500,
},

{
content: 'Roll out to 5–10% first. Watch the metrics. Then increase.',
pause: 5000,
},

{
content:
'Exclude users who already completed the flow. They can’t be affected by the test.',
pause: 5500,
},

{ type: 'clear', pause: 1500 },

// ── Close ──────────────────────────────────────────────────────────────
{
content: 'Flags and experiments live alongside the rest of your data.',
pause: 4500,
},

{
content: 'Ship behind a flag, watch replays, check analytics for impact.',
pause: 4500,
},

{ type: 'clear', pause: 1500 },

{
content:
'PostHog also provides every other analytics and AI tool to build your product.',
pause: 4500,
},

PRODUCT_SUITE_BLOCK,

{ type: 'clear', pause: 1500 },

{
content: 'And consolidating onto one platform saves real money.',
pause: 4500,
},

{ content: 'Here’s the math.', pause: 1500 },

VENDOR_STACK_BLOCK,

{ type: 'clear', pause: 1500 },

{
content: 'Pricing is usage-based, with a generous free tier.',
pause: 4000,
},

FREE_TIER_BLOCK,

{ type: 'clear', pause: 1500 },

PRICING_STRUCTURE_BLOCK,

{ type: 'clear', pause: 1500 },

{
content: 'Gain clarity and really understand your users.',
pause: 4000,
},

{ content: 'Use trends to measure growth.', pause: 2500 },

LINE_CHART_BLOCK,

{ type: 'clear', pause: 500 },

{ content: 'Use funnels to reveal bottlenecks.', pause: 2500 },

FUNNEL_BLOCK,
];
Loading
Loading