Skip to content

Clean up bin.ts#489

Draft
gewenyu99 wants to merge 1 commit into
mainfrom
refactor/bin.ts
Draft

Clean up bin.ts#489
gewenyu99 wants to merge 1 commit into
mainfrom
refactor/bin.ts

Conversation

@gewenyu99
Copy link
Copy Markdown
Collaborator

@gewenyu99 gewenyu99 commented May 28, 2026

Problem

We didn't write the Wizard knowing it'll be a permanent fixture or that it'll grow to support more than 1 integration command.

We've been throwing things into bin.ts, but it's very hard to read now.

Time to tackle some of the debt to took on to ship the wizard quickly, now that it'll be here to stay.

Prior art

Mainly looked at Cobra (written in Go, used by Docker, kubectl, gh)

Adopted: Cobra-on-our-own-types.

  • Commands are pure data: { name, description, options, children?, handler? }
  • parents nest children declaratively instead of via a yargs builder callback.

Design

A composable Wizard type, we just register commands in a composable way.

Wizard.use(basicIntegrationCommand)
  .use(mcpCommand)
  .use(provisionCommand)
  .use(...)
  .init();
  • Wizard (src/wizard.ts) — variadic .use(...cmds) chain. Walks the command tree eagerly on registration and throws on any path collision (top-level dupes, positional vs plain, alias clashes, dupe children under a parent). .init() hands the resulting tree to yargs.
  • Command shape — declarative spec at the top of each file, named helpers below. Parents with children get demandCommand(1) automatically; no builder callbacks anywhere.
  • File layout — one command per file. Multi-mode commands (basic-integration, mcp) are folders with index.ts holding the spec and siblings holding the mode runners. programCommands are generated from PROGRAM_REGISTRY so audit/migrate/integrate/etc. stay registry-driven.
  • Runners (src/lib/runners/) — folder of one runner per file (run-wizard, run-wizard-ci), barrel-exported.
  • Tests — handlers invoked directly with constructed argv (fast, no bin.ts reload). Conflict detection has its own dedicated suite.

Shape of command definitions

export const basicIntegrationCommand: WizardCommand = {
  name: ['$0'],
  description: 'Run the PostHog setup wizard',
  children: [provisionCommand],
  options: {
    'force-install': { type: 'boolean', default: false, describe: '...' },
    'install-dir':   { type: 'string',  describe: '...' },
    playground:      { type: 'boolean', default: false, describe: '...' },
    integration:     { type: 'string',  choices: ['nextjs', 'astro', ...] },
    skill:           { type: 'string',  describe: '...' },
    // ...
  },
  // ci, playground, and skill select different run modes — at most one.
  check: (argv) => {
    const modes = (['ci', 'playground', 'skill'] as const).filter((k) => argv[k]);
    if (modes.length > 1) throw new Error(`--${modes.join(', --')} are mutually exclusive`);
    return true;
  },
  handler: (argv) => {
    void (async () => {
      if (argv.ci)                          return (await import('./ci-install')).runCIInstall(argv);
      if (isNonInteractiveEnvironment())    return (await import('./non-interactive')).failNonInteractive();
      if (argv.playground)                  return (await import('./playground')).runPlayground();
      if (argv.skill)                       return (await import('./skill')).runSkillMode(argv);
      (await import('./interactive')).runInteractive(argv);
    })();
  },
};

Parent with subcommand as children:

export const mcpCommand: WizardCommand = {
  name: 'mcp',
  description: 'MCP server management commands',
  children: [mcpAddCommand, mcpRemoveCommand],
};

Test plan

  • New tests
  • Run CI
  • Manually testing all commands

List of tests to do:

- [ ] node dist/bin.js --help
  • node dist/bin.js --version
  • node dist/bin.js mcp --help
  • node dist/bin.js provision --help
  • node dist/bin.js integrate --help
  • node dist/bin.js
  • node dist/bin.js --debug
  • node dist/bin.js --region eu
  • node dist/bin.js --install-dir /tmp/testapp
  • node dist/bin.js --integration nextjs
  • node dist/bin.js --menu
  • node dist/bin.js --force-install
  • node dist/bin.js --signup --email you@example.com
  • node dist/bin.js --playground
  • node dist/bin.js --skill
  • node dist/bin.js --ci --playground
  • node dist/bin.js --ci --skill foo
  • node dist/bin.js --playground --skill foo
  • node dist/bin.js --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js --ci --api-key phx_xxx --install-dir /tmp/testapp --region eu
  • node dist/bin.js --ci --api-key phx_xxx --install-dir /tmp/testapp --integration nextjs --debug
  • node dist/bin.js --ci --signup --email you@example.com --install-dir /tmp/testapp
  • node dist/bin.js --ci --signup --email you@example.com --name "You" --region eu --install-dir /tmp/testapp
  • node dist/bin.js --ci --install-dir /tmp/testapp
  • node dist/bin.js --ci --api-key phx_xxx
  • node dist/bin.js --ci --signup --install-dir /tmp/testapp
  • node dist/bin.js --ci --api-key pha_xxx --install-dir /tmp/testapp
  • node dist/bin.js provision --email you@example.com
  • node dist/bin.js provision --email you@example.com --region eu
  • node dist/bin.js provision --email you@example.com --name "You"
  • node dist/bin.js provision --email you@example.com --json
  • node dist/bin.js provision
  • node dist/bin.js mcp
  • node dist/bin.js mcp add
  • node dist/bin.js mcp add --local
  • node dist/bin.js mcp add --features flags,errors
  • node dist/bin.js mcp add --api-key phx_xxx
  • node dist/bin.js mcp add --local --features flags,errors --api-key phx_xxx
  • node dist/bin.js mcp remove
  • node dist/bin.js mcp remove --local
  • node dist/bin.js integrate --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js audit --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js audit-3000 --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js doctor --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js migrate --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js events-audit --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js revenue --ci --api-key phx_xxx --install-dir /tmp/testapp
  • node dist/bin.js integrate --install-dir /tmp/testapp
  • node dist/bin.js migrate --help
  • node dist/bin.js revenue --help
  • node dist/bin.js --default

@github-actions
Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci misc
  • /wizard-ci revenue

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci misc/quack-quack
  • /wizard-ci revenue/stripe

Results will be posted here when complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant