From a9ffeecdc66a242e13d014fb75486efcb970b6c0 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 24 May 2026 17:40:41 -0400 Subject: [PATCH 1/5] Rebase, fix issues with agent sandbox allow list --- bin.ts | 10 +- src/lib/agent/agent-interface.ts | 29 +++- .../content/feature-flags-experiments.tsx | 16 +++ .../workflows/migration/content/free-tier.tsx | 58 ++++++++ .../workflows/migration/content/funnel.tsx | 101 +++++++++++++ src/lib/workflows/migration/content/index.tsx | 32 +++++ .../migration/content/line-chart.tsx | 65 +++++++++ .../workflows/migration/content/migration.tsx | 136 ++++++++++++++++++ .../migration/content/pricing-structure.tsx | 41 ++++++ .../migration/content/product-suite.tsx | 70 +++++++++ .../migration/content/vendor-stack.tsx | 47 ++++++ src/lib/workflows/migration/index.ts | 68 +++++++++ src/lib/workflows/migration/steps.ts | 36 +++++ src/lib/workflows/workflow-registry.ts | 2 + src/lib/workflows/workflow-step.ts | 14 ++ src/ui/tui/components/StatusPeekTrigger.tsx | 1 + src/ui/tui/flows.ts | 2 + src/ui/tui/playground/demos/RunScreenDemo.tsx | 6 +- src/ui/tui/screen-registry.tsx | 2 + src/ui/tui/screens/MigrationIntroScreen.tsx | 43 ++++++ 20 files changed, 768 insertions(+), 11 deletions(-) create mode 100644 src/lib/workflows/migration/content/feature-flags-experiments.tsx create mode 100644 src/lib/workflows/migration/content/free-tier.tsx create mode 100644 src/lib/workflows/migration/content/funnel.tsx create mode 100644 src/lib/workflows/migration/content/index.tsx create mode 100644 src/lib/workflows/migration/content/line-chart.tsx create mode 100644 src/lib/workflows/migration/content/migration.tsx create mode 100644 src/lib/workflows/migration/content/pricing-structure.tsx create mode 100644 src/lib/workflows/migration/content/product-suite.tsx create mode 100644 src/lib/workflows/migration/content/vendor-stack.tsx create mode 100644 src/lib/workflows/migration/index.ts create mode 100644 src/lib/workflows/migration/steps.ts create mode 100644 src/ui/tui/screens/MigrationIntroScreen.tsx diff --git a/bin.ts b/bin.ts index 4a5eaacf..9da65731 100644 --- a/bin.ts +++ b/bin.ts @@ -599,9 +599,15 @@ for (const wfConfig of getSubcommandWorkflows()) { cli.command( wfConfig.command!, wfConfig.description, - (y) => y.options(skillSubcommandOptions), + (y) => + y.options({ + ...skillSubcommandOptions, + ...(wfConfig.cliOptions ?? {}), + }), (argv) => { - const options = { ...argv }; + const extras = + wfConfig.mapCliOptions?.(argv as Record) ?? {}; + const options = { ...argv, ...extras }; if (options.ci) { runWizardCI(wfConfig, options); } else { diff --git a/src/lib/agent/agent-interface.ts b/src/lib/agent/agent-interface.ts index 2144f49d..d354be0b 100644 --- a/src/lib/agent/agent-interface.ts +++ b/src/lib/agent/agent-interface.ts @@ -954,14 +954,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 + '~/.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: { diff --git a/src/lib/workflows/migration/content/feature-flags-experiments.tsx b/src/lib/workflows/migration/content/feature-flags-experiments.tsx new file mode 100644 index 00000000..6bbe809a --- /dev/null +++ b/src/lib/workflows/migration/content/feature-flags-experiments.tsx @@ -0,0 +1,16 @@ +/** + * Feature flags + experiments learn material. Plays alongside the generic + * migration deck when the source product is a feature-flag / experimentation + * tool (e.g. Statsig). Placeholder content for now. + * + * Examples of what could go here: how PostHog represents flags vs configs vs + * experiments, how flags wire into analytics and replays, how rollouts work, + * how multivariate flags carry JSON payloads. + */ + +import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js'; + +export const FEATURE_FLAGS_EXPERIMENTS_BLOCKS: ContentBlock[] = [ + { content: 'Feature flags and experiments in PostHog.', pause: 3000 }, + // TODO: replace with real FF/experiments narrative. +]; diff --git a/src/lib/workflows/migration/content/free-tier.tsx b/src/lib/workflows/migration/content/free-tier.tsx new file mode 100644 index 00000000..50801ca2 --- /dev/null +++ b/src/lib/workflows/migration/content/free-tier.tsx @@ -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: [ + {' Free every month, on every product'}, + , + + {' 1,000,000 '} + events + product analytics + , + + {' 1,000,000 '} + requests + feature flags + experiments + , + + {' 5,000 '} + recordings + session replay + , + + {' 100,000 '} + exceptions + error tracking + , + + {' 100,000 '} + events + LLM analytics + , + + {' 50 GB '} + logs + logs + , + + {' 1,500 '} + responses + surveys + , + + {' 1,000,000 '} + rows + data warehouse + , + ], +}; diff --git a/src/lib/workflows/migration/content/funnel.tsx b/src/lib/workflows/migration/content/funnel.tsx new file mode 100644 index 00000000..0d8340e3 --- /dev/null +++ b/src/lib/workflows/migration/content/funnel.tsx @@ -0,0 +1,101 @@ +/** + * ASCII funnel — illustrates a conversion funnel. Same shape as the + * integration script's variant; kept separate so the migration narrative + * can evolve independently. + */ + +import { Text } from 'ink'; +import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js'; + +export const FUNNEL_BLOCK: ContentBlock = { + type: 'lines', + interval: 200, + pause: 8000, + lines: [ + {' Funnel · ride conversion'}, + , + + {' '} + 1 + {' app_launched'} + {' '} + + 100.00% + + , + {' ██████████████████████████████'}, + {' → 1,200 users'}, + , + + {' '} + 2 + {' ride_requested'} + {' '} + {'avg 2m 30s'} + {' '} + + 72.00% + + , + + {' '} + {'██████████████████████'} + {'░░░░░░░░░'} + , + + {' '} + → 864 users + {' '} + + {' 336 (28%)'} + , + , + + {' '} + 3 + {' ride_accepted'} + {' '} + {'avg 5m 12s'} + {' '} + + 51.00% + + , + + {' '} + {'██████████████████'} + {'░░░░░░░░░░░░░'} + , + + {' '} + → 612 users + {' '} + + {' 252 (29%)'} + , + , + + {' '} + 4 + {' ride_started'} + {' '} + {'avg 1m 45s'} + {' '} + + 38.00% + + , + + {' '} + {'█████████████'} + {'░░░░░░░░░░░░░░░░░░'} + , + + {' '} + → 456 users + {' '} + + {' 156 (25%)'} + , + ], +}; diff --git a/src/lib/workflows/migration/content/index.tsx b/src/lib/workflows/migration/content/index.tsx new file mode 100644 index 00000000..84fbf48f --- /dev/null +++ b/src/lib/workflows/migration/content/index.tsx @@ -0,0 +1,32 @@ +/** + * Migration learn-deck composer. Picks topic decks to play alongside the + * generic migration deck based on the command + product combo the operator + * invoked. + * + * The active variant is read from `session.skillId`, which the wizard sets + * from `migrate --product=` (`mapCliOptions` in migration/index.ts). + * Variants whose source SDK is a feature flag / experimentation tool play + * the FF/experiments deck. Add more topic decks to TOPIC_DECKS_BY_PRODUCT + * as new variants ship. + */ + +import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js'; +import type { WizardStore } from '../../../../ui/tui/store.js'; +import { getMigrationBlocks } from './migration.js'; +import { FEATURE_FLAGS_EXPERIMENTS_BLOCKS } from './feature-flags-experiments.js'; + +const TOPIC_DECKS_BY_PRODUCT: Record = { + statsig: FEATURE_FLAGS_EXPERIMENTS_BLOCKS, +}; + +function productFromSkillId(skillId: string | null): string | null { + const prefix = 'migrate-'; + if (!skillId || !skillId.startsWith(prefix)) return null; + return skillId.slice(prefix.length); +} + +export const getContentBlocks = (store?: WizardStore): ContentBlock[] => { + const product = productFromSkillId(store?.session.skillId ?? null); + const topicDeck = product ? TOPIC_DECKS_BY_PRODUCT[product] ?? [] : []; + return [...getMigrationBlocks(store), ...topicDeck]; +}; diff --git a/src/lib/workflows/migration/content/line-chart.tsx b/src/lib/workflows/migration/content/line-chart.tsx new file mode 100644 index 00000000..ebcd1c07 --- /dev/null +++ b/src/lib/workflows/migration/content/line-chart.tsx @@ -0,0 +1,65 @@ +/** + * ASCII line chart — illustrates a Trends insight. Same shape as the + * integration script's variant; kept separate so the migration narrative + * can evolve independently. + */ + +import { Text } from 'ink'; +import type { ContentBlock } from '../../../../ui/tui/primitives/content-types.js'; + +export const LINE_CHART_BLOCK: ContentBlock = { + type: 'lines', + interval: 300, + pause: 6000, + lines: [ + {' Trends · user signups (monthly)'}, + , + + {' 10k ┤'} + {' '} + {'╭──'} + {' 9,575'} + , + + {' │'} + {' '} + {'╭╯'} + , + + {' 7.5k ┤'} + {' '} + {'╭╯'} + , + + {' │'} + {' '} + {'╭─╯'} + , + + {' 5k ┤'} + {' '} + {'╭─╯'} + , + + {' │'} + {' '} + {'╭──╯'} + , + + {' 2.5k ┤'} + {' '} + {'╭───╯'} + , + + {' │'} + {' '} + {'╭──────╯'} + , + + {' 0 ┤'} + {'──────╯'} + , + {' └┬─────┬─────┬─────┬─────┬──'}, + {' May Aug Nov Feb May'}, + ], +}; diff --git a/src/lib/workflows/migration/content/migration.tsx b/src/lib/workflows/migration/content/migration.tsx new file mode 100644 index 00000000..c279b45a --- /dev/null +++ b/src/lib/workflows/migration/content/migration.tsx @@ -0,0 +1,136 @@ +/** + * Generic migration learn material. Plays for every `migrate` run regardless + * of source product. The narrative focuses on multi-vendor consolidation: + * cost comparison, free-tier breakdown, pricing model, then familiar analyses + * (trends + funnels) the team already had. + */ + +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 { VENDOR_STACK_BLOCK } from './vendor-stack.js'; +import { FREE_TIER_BLOCK } from './free-tier.js'; +import { PRICING_STRUCTURE_BLOCK } from './pricing-structure.js'; +import { PRODUCT_SUITE_BLOCK } from './product-suite.js'; +import { LINE_CHART_BLOCK } from './line-chart.js'; +import { FUNNEL_BLOCK } from './funnel.js'; + +/** + * Human-readable display name for each `--product=` variant of the + * `migrate` command. The intro line reads "migrate from