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) — mapsIntegrationenum values to configs - •
Integrationenum (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 value - •
docsUrl— PostHog docs URL for manual setup fallback - •
unsupportedVersionDocsUrl— optional fallback for old versions - •
beta— settrueto 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— 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 old - •
getInstalledVersion— async function to get the installed version - •
detect— async function that returnstrueif 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> = {
// ... 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