adding-framework-support · diff
v1.0 to v2.0
76 added, 111 removed. Audit A to B.
---
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.
+ description:
+ Add or extend language and framework support in the PostHog wizard, including
+ detection, framework configuration, registration, and matching context-mill
+ content.
+ compatibility:
+ Designed for coding agents working on the PostHog wizard codebase.
metadata:
author: posthog
- version: "1.0"
+ version: '2.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:
-
- 1. **`FrameworkConfig`** (`src/lib/framework-config.ts`) — the interface each framework implements
- 2. **`FRAMEWORK_REGISTRY`** (`src/lib/registry.ts`) — maps `Integration` enum values to configs
- 3. **`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`.
-
- ```ts
- 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:
-
- ```ts
- 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` — 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
+ Read [wizard-development](../wizard-development/SKILL.md) for the shared design
+ policy and gateway contract. New agent work uses Pi and prefers the
+ orchestrator; adding a framework extends the integration program without
+ creating its own runner or changing existing routing defaults.
- #### `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
+ ## Extend the framework configuration
- #### `ui`
- - `successMessage`, `estimatedDurationMinutes`
- - `getOutroChanges` — returns "what the agent did" bullets
- - `getOutroNextSteps` — returns "next steps" bullets
+ Start with [FrameworkConfig](../../../src/lib/framework-config.ts) and a nearby
+ example under [src/frameworks](../../../src/frameworks/). Framework-specific
+ detection, context, environment conventions, and UI metadata belong here.
+ Integration instructions and examples belong in context-mill.
- ### 3. Register in the framework registry
+ 1. Add the integration to [Integration](../../../src/lib/constants.ts). Its
+ order controls first-match detection and the framework picker. Keep specific
+ frameworks before language fallbacks and generic Node last; preserve the
+ overlap rules in the
+ [detection checks](../../../src/lib/detection/__tests__/framework.test.ts).
+ 2. Add the config under `src/frameworks/<name>/<name>-wizard-agent.ts`. Use a
+ `type` for framework context so it satisfies `Record<string, unknown>`.
+ Export the config; the integration program already supplies execution.
+ 3. Import the config into [FRAMEWORK_REGISTRY](../../../src/lib/registry.ts).
+ The display label comes from `metadata.name`.
- In `src/lib/registry.ts`, import the config and add it:
+ Read the current interface for the complete required fields. In particular,
+ `detection.detectPackageManager` is required: reuse an adapter from
+ [package-manager detection](../../../src/lib/detection/package-manager.ts). Use
+ `metadata.setup.questions` for unresolved project variants; `gatherContext`
+ collects framework context. Optional notices and extra MCP servers also belong
+ in metadata.
- ```ts
- import { NEW_AGENT_CONFIG } from '../<framework>/<framework>-wizard-agent';
+ Use `usesPackageJson: false` for frameworks without a package.json dependency.
+ Their required `getVersion` callback can return `undefined`. Minimum-version
+ checking requires both `minimumVersion` and `getInstalledVersion`; unknown
+ versions pass. [Context detection](../../../src/lib/detection/context.ts)
+ returns unsupported-version data for the integration UI rather than aborting
+ itself.
- export const FRAMEWORK_REGISTRY: Record<Integration, FrameworkConfig> = {
- // ... existing entries
- [Integration.newFramework]: NEW_AGENT_CONFIG,
- };
- ```
+ ## Detection and examples
- ### 4. Create framework utilities (if needed)
+ | Starting point | Pattern to reuse |
+ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
+ | [Next.js](../../../src/frameworks/nextjs/) | `hasDeclaredDependency` from `utils/package-json`, `tryGetPackageJson` from `utils/setup-utils`, and router setup questions |
+ | [Django](../../../src/frameworks/django/) | Python project files, context gathering, and Python package-manager detection |
+ | [Laravel](../../../src/frameworks/laravel/) | Composer and framework-specific filesystem signals |
+ | [Rails](../../../src/frameworks/rails/) | Gemfile detection and Ruby conventions |
- 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.
+ Use [bounded filesystem helpers](../../../src/utils/bounded-fs.ts) for project
+ scans and reads. They bound traversal and skip dependency/build directories; add
+ framework-specific exclusions with `extraIgnore`. Keep complex parsers and
+ detectors beside the config so they can be checked independently.
- ## Detection Guidelines
+ ## Complete the content side
- - 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
+ Ensure [context-mill](https://github.com/PostHog/context-mill) supplies the
+ matching integration reference and task-skill variants for the framework. A
+ registry entry alone does not provide integration knowledge. The orchestrator
+ resolves framework variants from the skill menu and rejects missing task
+ variants; see the
+ [orchestrator runner](../../../src/lib/agent/runner/sequence/orchestrator/orchestrator-runner.ts).
- ## Verification
+ Keep project-specific facts in configuration and reusable integration guidance
+ in that content. Model IDs, reasoning efforts, and gateway-required system
+ prompts follow the cross-repo contract in
+ [wizard-development](../wizard-development/SKILL.md); a framework config cannot
+ enable a new gateway model.
- After adding a framework:
+ ## Verify the changed behavior
- ```bash
- pnpm build # Must compile with no errors
- pnpm test # All tests must pass
- pnpm fix # No new lint errors (warnings are OK)
- ```
+ Check detection against the target framework and the closest overlapping
+ framework/fallback. Reuse the existing detection checks; add a focused case only
+ for behavior they do not cover. Confirm package-manager selection and matching
+ content-mill variants. For an end-to-end run, use a disposable test app and the
+ [exploration guide](../exploring-the-wizard/SKILL.md).
- ## Reference Configs
+ For prompt, environment-upload, or outro changes, inspect the current
+ [integration program](../../../src/lib/programs/posthog-integration/) and the
+ selected sequence. Some fields remain in the interface without a current
+ consumer: `getOutroNextSteps` is not used by the integration outro. Linear
+ post-run/outro hooks are not shared by the orchestrator; see the
+ [program guide](../adding-skill-program/SKILL.md).
- 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
+ Use the proportionate validation guidance in
+ [wizard-development](../wizard-development/SKILL.md). Documentation-only changes
+ need source/link checks, not an agent run.