Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import type { CloudRegion, WizardOptions } from './src/utils/types';
import { runWizard } from './src/run';
import { runEventSetupWizard } from './src/nextjs/event-setup';
import {
runMigrationWizard,
getAvailableMigrationProviders,
type MigrationOptions,
} from './src/migrate';
import {
readEnvironment,
isNonInteractiveEnvironment,
Expand Down Expand Up @@ -126,24 +131,24 @@
});
},
(argv) => {
const finalArgs = {

Check warning on line 134 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe assignment of an `any` value
...argv,
...readEnvironment(),
} as any;

Check warning on line 137 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

let resolvedInstallDir: string;
if (finalArgs.installDir) {

Check warning on line 140 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe member access .installDir on an `any` value
if (path.isAbsolute(finalArgs.installDir)) {

Check warning on line 141 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe member access .installDir on an `any` value

Check warning on line 141 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`
resolvedInstallDir = finalArgs.installDir;

Check warning on line 142 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe member access .installDir on an `any` value

Check warning on line 142 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe assignment of an `any` value
} else {
resolvedInstallDir = path.join(process.cwd(), finalArgs.installDir);

Check warning on line 144 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe member access .installDir on an `any` value

Check warning on line 144 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `string`
}
} else {
resolvedInstallDir = process.cwd();
}

const wizardOptions: WizardOptions = {
debug: finalArgs.debug ?? false,

Check warning on line 151 in bin.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe assignment of an `any` value
installDir: resolvedInstallDir,
cloudRegion: finalArgs.region as CloudRegion | undefined,
default: finalArgs.default ?? false,
Expand All @@ -155,6 +160,62 @@
void runEventSetupWizard(wizardOptions);
},
)
.command(
'migrate',
'Migrate from another analytics provider to PostHog',
(yargs) => {
const availableProviders = getAvailableMigrationProviders();
return yargs
.options({
'install-dir': {
describe:
'Directory to run the migration in\nenv: POSTHOG_WIZARD_INSTALL_DIR',
type: 'string',
},
'force-install': {
default: false,
describe:
'Force install packages even if peer dependency checks fail\nenv: POSTHOG_WIZARD_FORCE_INSTALL',
type: 'boolean',
},
from: {
describe: 'Analytics provider to migrate from',
choices: availableProviders,
default: 'amplitude',
type: 'string',
},
});
},
(argv) => {
const finalArgs = {
...argv,
...readEnvironment(),
} as any;

let resolvedInstallDir: string;
if (finalArgs.installDir) {
if (path.isAbsolute(finalArgs.installDir)) {
resolvedInstallDir = finalArgs.installDir;
} else {
resolvedInstallDir = path.join(process.cwd(), finalArgs.installDir);
}
} else {
resolvedInstallDir = process.cwd();
}

const migrationOptions: MigrationOptions = {
debug: finalArgs.debug ?? false,
installDir: resolvedInstallDir,
cloudRegion: finalArgs.region as CloudRegion | undefined,
default: finalArgs.default ?? false,
signup: finalArgs.signup ?? false,
forceInstall: finalArgs.forceInstall ?? false,
localMcp: finalArgs.localMcp ?? false,
};

void runMigrationWizard(migrationOptions, finalArgs.from as string);
},
)
.command('mcp <command>', 'MCP server management commands', (yargs) => {
return yargs
.command(
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/fixtures/18c11dda2ee4cd5c20c337d23dd61387.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"newContent": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { PostHogProvider } from 'posthog-js/react'\nimport './index.css'\nimport App from './App.tsx'\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <PostHogProvider\n apiKey={import.meta.env.VITE_PUBLIC_POSTHOG_KEY}\n options={{\n api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,\n defaults: '2025-05-24',\n capture_exceptions: true,\n debug: import.meta.env.MODE === 'development',\n }}\n >\n <App />\n </PostHogProvider>\n </StrictMode>,\n)\n"
"newContent": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport posthog from 'posthog-js'\nimport { PostHogProvider } from 'posthog-js/react'\nimport './index.css'\nimport App from './App.tsx'\n\nposthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {\n api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,\n defaults: '2025-05-24',\n capture_exceptions: true,\n debug: import.meta.env.MODE === 'development',\n});\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <PostHogProvider client={posthog}>\n <App />\n </PostHogProvider>\n </StrictMode>,\n)\n"
}
13 changes: 13 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ export enum Integration {
astro = 'astro',
}

export enum MigrationSource {
amplitude = 'amplitude',
}

export function getMigrationSourceDescription(source: MigrationSource): string {
switch (source) {
case MigrationSource.amplitude:
return 'Amplitude';
default:
throw new Error(`Unknown migration source ${source}`);
}
}

export enum FeatureFlagDefinition {
NextV2 = 'wizard-next-v2',
}
Expand Down
79 changes: 79 additions & 0 deletions src/lib/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,82 @@ Here are the files that have not been changed yet:
Below is the current file contents:
{file_content}`,
});

export const migrationFilterFilesPromptTemplate = new PromptTemplate({
inputVariables: [
'documentation',
'file_list',
'source_sdk',
'integration_rules',
],
template: `You are a PostHog migration wizard, a master AI programming assistant that migrates projects from {source_sdk} to PostHog.
Given the following list of file paths from a project, determine which files contain {source_sdk} code that needs to be migrated to PostHog.

- Look for files that import or use {source_sdk} SDK
- Look for files that initialize {source_sdk}
- Look for files that track events with {source_sdk}
- Look for files that identify users with {source_sdk}
- Look for configuration files that may reference {source_sdk}

You should return all files that contain {source_sdk} code that needs to be migrated. Return them in the order you would like to see them processed.

Rules:
- Only return files that actually contain {source_sdk} code or references
- Do not return files that don't use {source_sdk}
- If you are unsure, return the file, since it's better to have more files than less
- Include any configuration or initialization files
{integration_rules}

Migration documentation:
{documentation}

All current files in the repository:

{file_list}`,
});

export const migrationGenerateFileChangesPromptTemplate = new PromptTemplate({
inputVariables: [
'file_content',
'documentation',
'file_path',
'changed_files',
'unchanged_files',
'source_sdk',
'integration_rules',
],
template: `You are a PostHog migration wizard, a master AI programming assistant that migrates projects from {source_sdk} to PostHog.

Your task is to migrate the file from {source_sdk} to PostHog according to the migration documentation.
Do not return a diff — you should return the complete updated file content.

Rules:
- Replace ALL {source_sdk} imports with PostHog imports
- Replace ALL {source_sdk} initialization code with PostHog initialization
- Replace ALL {source_sdk} tracking calls with PostHog equivalents
- Replace ALL {source_sdk} identify calls with PostHog equivalents
- Remove ALL {source_sdk}-specific code that has no PostHog equivalent
- Preserve the existing code formatting and style
- Make sure to remove any unused {source_sdk} imports after migration
- If the file has no {source_sdk} code after review, return it unchanged
{integration_rules}


CONTEXT
---

Migration documentation from {source_sdk} to PostHog:
{documentation}

The file you are updating is:
{file_path}

Here are the changes you have already made to the project:
{changed_files}

Here are the files that have not been changed yet:
{unchanged_files}

Below is the current file contents:
{file_content}`,
});
40 changes: 40 additions & 0 deletions src/migrate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export {
runMigrationWizard,
checkAndOfferMigration,
detectProviderInstallation,
getAllInstalledProviderPackages,
} from './migration-wizard';

export type {
MigrationProviderConfig,
InstalledPackage,
MigrationOptions,
MigrationDocsOptions,
MigrationContext,
MigrationOutroOptions,
} from './types';

export {
getMigrationProvider,
getAvailableMigrationProviders,
migrationProviders,
amplitudeProvider,
} from './providers';

export { AMPLITUDE_PACKAGES } from './providers/amplitude';

import { runMigrationWizard, checkAndOfferMigration } from './migration-wizard';
import type { MigrationOptions } from './types';
import type { WizardOptions } from '../utils/types';

export async function runAmplitudeMigrationWizard(
options: MigrationOptions,
): Promise<void> {
return runMigrationWizard(options, 'amplitude');
}

export async function checkAndOfferAmplitudeMigration(
options: WizardOptions,
): Promise<boolean> {
return checkAndOfferMigration(options, 'amplitude');
}
Loading