| name | adding-framework-support |
| description | 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. |
| compatibility | Designed for Claude Code working on the PostHog wizard codebase. |
| metadata | {"author":"posthog","version":"1.0"} |
Adding Framework Support
Architecture Overview
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 implements
FRAMEWORK_REGISTRY (src/lib/registry.ts) — maps Integration enum values to configs
Integration enum (src/lib/constants.ts) — enum order determines detection priority and menu display order
The 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.
Steps to Add a New Framework
1. Add to the Integration enum and labels
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 {
rails = 'rails',
}
2. Create the agent config file
Create src/<framework>/<framework>-wizard-agent.ts. This file exports:
- A context type for framework-specific data
- A
FrameworkConfig<TContext> object (the full integration definition)
- A thin runner function that just calls
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> = {
};
Use an existing config as a template. The config has these sections:
metadata
name — display name (e.g. "Ruby on Rails")
integration — the enum value
docsUrl — PostHog docs URL for manual setup fallback
unsupportedVersionDocsUrl — optional fallback for old versions
beta — set true to show a [BETA] notice before running
gatherContext — optional async function to detect project-specific context (e.g. router type, project variant)
detection
packageName — the package to check (e.g. 'rails')
packageDisplayName — human-readable name for error messages
usesPackageJson — 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 old
getInstalledVersion — async function to get the installed version
detect — async function that returns true if this framework is present in the project
environment
uploadToHosting — whether to offer uploading env vars to hosting providers
getEnvVars — returns the env var names and values for this framework
analytics
getTags — returns analytics tags from gathered context
prompts
projectTypeDetection — text describing how to confirm the project type
packageInstallation — text describing package manager conventions
getAdditionalContextLines — optional function returning extra prompt lines from context
ui
successMessage, estimatedDurationMinutes
getOutroChanges — returns "what the agent did" bullets
getOutroNextSteps — returns "next steps" bullets
3. Register in the framework registry
In 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> = {
[Integration.newFramework]: NEW_AGENT_CONFIG,
};
4. Create framework utilities (if needed)
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.
Detection Guidelines
- For JS/TS frameworks: check
package.json for the framework package using hasPackageInstalled and tryGetPackageJson from src/utils/clack-utils.ts and src/utils/package-json.ts
- For Python frameworks: glob for
requirements*.txt, pyproject.toml, setup.py, Pipfile and check contents
- For PHP frameworks: check
composer.json or framework-specific files (e.g. artisan for Laravel)
- For Ruby frameworks: check
Gemfile or Gemfile.lock for the framework gem
- Always ignore virtual environment and dependency directories in globs
Verification
After adding a framework:
pnpm build
pnpm test
pnpm fix
Reference Configs
Good examples to study:
- JS framework:
src/nextjs/nextjs-wizard-agent.ts — package.json detection, context gathering (router type)
- Python framework:
src/django/django-wizard-agent.ts — filesystem detection, usesPackageJson: false
- PHP framework:
src/laravel/laravel-wizard-agent.ts — composer.json detection, multiple detection strategies