Add a new framework integration to the PostHog wizard. Use when adding support for a new language or framework (e.g. Ruby on Rails, Go, Angular). Covers creating the agent config, detection logic, registry entry, and enum/label additions.
Every framework integration is a single FrameworkConfig object. The wizard has no switch statements or per-framework routing — everything is data-driven through:
FrameworkConfig (src/lib/framework-config.ts) — the interface each framework implementsFRAMEWORK_REGISTRY (src/lib/registry.ts) — maps Integration enum values to configsIntegration enum (src/lib/constants.ts) — enum order determines detection priority and menu display orderThe universal runner (src/lib/agent-runner.ts) handles all shared behavior: debug logging, version checking, welcome message, beta notices, AI consent, credential flow, agent execution, error handling, and outro messaging.
In src/lib/constants.ts, add the new value to Integration. Enum order matters — it controls both the detection priority (first match wins) and the display order in the CLI select menu. The display label comes from metadata.name in your FrameworkConfig.
export enum Integration {
// ... existing entries
rails = 'rails', // insert at the desired detection/display position
}
Create src/<framework>/<framework>-wizard-agent.ts. This file exports:
FrameworkConfig<TContext> object (the full integration definition)runAgentWizard(CONFIG, options)Define a context type for any data gathered before the agent runs, then pass it as the generic parameter. Use type (not interface) so it satisfies the Record<string, unknown> constraint:
type RailsContext = {
projectType?: RailsProjectType;
gemfilePath?: string;
};
export const RAILS_AGENT_CONFIG: FrameworkConfig<RailsContext> = {
// All context-consuming callbacks (getTags, getOutroChanges, etc.)
// are now fully typed — no `any` casts needed.
};
Use an existing config as a template. The config has these sections:
metadataname — display name (e.g. "Ruby on Rails")integration — the enum valuedocsUrl — PostHog docs URL for manual setup fallbackunsupportedVersionDocsUrl — optional fallback for old versionsbeta — set true to show a [BETA] notice before runninggatherContext — optional async function to detect project-specific context (e.g. router type, project variant)detectionpackageName — the package to check (e.g. 'rails')packageDisplayName — human-readable name for error messagesusesPackageJson — set false for non-JS frameworks (Python, PHP, Ruby, etc.)getVersion — extract version from package.json (return undefined if usesPackageJson: false)getVersionBucket — optional function to bucket versions for analytics (e.g. '7.x')minimumVersion — optional minimum version string; runner auto-checks and bails if too oldgetInstalledVersion — async function to get the installed versiondetect — async function that returns true if this framework is present in the projectenvironmentuploadToHosting — whether to offer uploading env vars to hosting providersgetEnvVars — returns the env var names and values for this frameworkanalyticsgetTags — returns analytics tags from gathered contextpromptsprojectTypeDetection — text describing how to confirm the project typepackageInstallation — text describing package manager conventionsgetAdditionalContextLines — optional function returning extra prompt lines from contextuisuccessMessage, estimatedDurationMinutesgetOutroChanges — returns "what the agent did" bulletsgetOutroNextSteps — returns "next steps" bulletsIn src/lib/registry.ts, import the config and add it:
import { NEW_AGENT_CONFIG } from '../<framework>/<framework>-wizard-agent';
export const FRAMEWORK_REGISTRY: Record<Integration, FrameworkConfig> = {
// ... existing entries
[Integration.newFramework]: NEW_AGENT_CONFIG,
};
If the framework needs project type detection, version extraction, or other complex logic, create src/<framework>/utils.ts with the relevant functions. Keep this separate from the agent config to maintain testability.
package.json for the framework package using hasPackageInstalled and tryGetPackageJson from src/utils/clack-utils.ts and src/utils/package-json.tsrequirements*.txt, pyproject.toml, setup.py, Pipfile and check contentscomposer.json or framework-specific files (e.g. artisan for Laravel)Gemfile or Gemfile.lock for the framework gemAfter adding a framework:
pnpm build # Must compile with no errors
pnpm test # All tests must pass
pnpm fix # No new lint errors (warnings are OK)
Good examples to study:
src/nextjs/nextjs-wizard-agent.ts — package.json detection, context gathering (router type)src/django/django-wizard-agent.ts — filesystem detection, usesPackageJson: falsesrc/laravel/laravel-wizard-agent.ts — composer.json detection, multiple detection strategies