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 implementsFRAMEWORK_REGISTRY(src/lib/registry.ts) — mapsIntegrationenum values to configsIntegrationenum (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 {
// ... existing entries
rails = 'rails', // insert at the desired detection/display position
}
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> = {
// 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:
metadata
name— display name (e.g. "Ruby on Rails")integration— the enum valuedocsUrl— PostHog docs URL for manual setup fallbackunsupportedVersionDocsUrl— optional fallback for old versionsbeta— settrueto show a[BETA]notice before runninggatherContext— 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 messagesusesPackageJson— setfalsefor non-JS frameworks (Python, PHP, Ruby, etc.)getVersion— extract version from package.json (returnundefinedifusesPackageJson: 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 returnstrueif this framework is present in the project
environment
uploadToHosting— whether to offer uploading env vars to hosting providersgetEnvVars— 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 typepackageInstallation— text describing package manager conventionsgetAdditionalContextLines— optional function returning extra prompt lines from context
ui
successMessage,estimatedDurationMinutesgetOutroChanges— returns "what the agent did" bulletsgetOutroNextSteps— 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> = {
// ... existing entries
[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.jsonfor the framework package usinghasPackageInstalledandtryGetPackageJsonfromsrc/utils/clack-utils.tsandsrc/utils/package-json.ts - For Python frameworks: glob for
requirements*.txt,pyproject.toml,setup.py,Pipfileand check contents - For PHP frameworks: check
composer.jsonor framework-specific files (e.g.artisanfor Laravel) - For Ruby frameworks: check
GemfileorGemfile.lockfor the framework gem - Always ignore virtual environment and dependency directories in globs
Verification
After 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)
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