Skip to content
Open
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
21 changes: 20 additions & 1 deletion src/lib/agent-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ import {
registerCleanup,
} from '../utils/wizard-abort';
import { formatScanReport, writeScanReport } from './yara-hooks';
import {
detectCloudflareTarget,
fetchCloudflareReference,
} from './cloudflare-detection';

/**
* Build a WizardOptions bag from a WizardSession (for code that still expects WizardOptions).
Expand Down Expand Up @@ -210,6 +214,15 @@ export async function runAgentWizard(
analytics.setTag(key, value);
});

// Detect Cloudflare Workers target and fetch runtime reference if needed
const isCloudflare = await detectCloudflareTarget(session.installDir);
let cloudflareReference: string | null = null;
if (isCloudflare) {
logToFile('[agent-runner] Cloudflare Workers target detected');
analytics.setTag('cloudflare', 'true');
cloudflareReference = await fetchCloudflareReference(skillsBaseUrl);
}

const integrationPrompt = buildIntegrationPrompt(
config,
{
Expand All @@ -220,6 +233,7 @@ export async function runAgentWizard(
projectId,
},
frameworkContext,
cloudflareReference,
);

// Initialize and run agent
Expand Down Expand Up @@ -407,6 +421,7 @@ function buildIntegrationPrompt(
projectId: number;
},
frameworkContext: Record<string, unknown>,
cloudflareReference?: string | null,
): string {
const additionalLines = config.prompts.getAdditionalContextLines
? config.prompts.getAdditionalContextLines(frameworkContext)
Expand All @@ -417,6 +432,10 @@ function buildIntegrationPrompt(
? '\n' + additionalLines.map((line) => `- ${line}`).join('\n')
: '';

const runtimeOverrides = cloudflareReference
? `\n\n---\n\n${cloudflareReference}`
: '';

return `You have access to the PostHog MCP server which provides skills to integrate PostHog into this ${
config.metadata.name
} project.
Expand Down Expand Up @@ -459,7 +478,7 @@ STEP 5: Set up environment variables for PostHog using the wizard-tools MCP serv
- Reference these environment variables in the code files you create instead of hardcoding the public token and host.

Important: Use the detect_package_manager tool (from the wizard-tools MCP server) to determine which package manager the project uses. Do not manually search for lockfiles or config files. Always install packages as a background task. Don't await completion; proceed with other work immediately after starting the installation. You must read a file immediately before attempting to write it, even if you have previously read it; failure to do so will cause a tool failure.

${runtimeOverrides}

`;
}
90 changes: 90 additions & 0 deletions src/lib/cloudflare-detection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import fg from 'fast-glob';
import fs from 'fs';
import path from 'path';
import { logToFile } from '../utils/debug';

/**
* Detect whether the project targets Cloudflare Workers.
*
* Checks for:
* 1. wrangler.toml / wrangler.jsonc / wrangler.json in project root
* 2. Cloudflare adapter packages in dependencies (@react-router/cloudflare,
Comment on lines +10 to +11
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wanna add tests in src/lib/__tests__ that validates this script against these two Cloudflare criteria?

* @astrojs/cloudflare, @sveltejs/adapter-cloudflare, etc.)
*/
export async function detectCloudflareTarget(
installDir: string,
): Promise<boolean> {
// Check for wrangler config files
const wranglerFiles = await fg('wrangler.@(toml|jsonc|json)', {
cwd: installDir,
dot: true,
});
if (wranglerFiles.length > 0) {
logToFile(
`[cloudflare-detection] detected via wrangler config: ${wranglerFiles[0]}`,
);
return true;
}

// Check for Cloudflare adapter/platform packages in deps
try {
const packageJsonPath = path.join(installDir, 'package.json');
const content = fs.readFileSync(packageJsonPath, 'utf-8');
const packageJson = JSON.parse(content);
Comment on lines +31 to +33
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can reuse the tryGetPackageJson util function

import { tryGetPackageJson } from '../utils/setup-utils';
const packageJson = await tryGetPackageJson({ installDir });

const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};

const cloudflarePackages = Object.keys(allDeps).filter(
(dep) =>
dep === '@react-router/cloudflare' ||
dep === '@astrojs/cloudflare' ||
dep === '@sveltejs/adapter-cloudflare' ||
dep === '@sveltejs/adapter-cloudflare-workers' ||
dep === '@cloudflare/workers-types' ||
dep === 'wrangler',
);

if (cloudflarePackages.length > 0) {
logToFile(
`[cloudflare-detection] detected via packages: ${cloudflarePackages.join(
', ',
)}`,
);
return true;
}
} catch {
// package.json not found or invalid
}

return false;
}

/**
* Fetch the Cloudflare Workers reference from the skills server.
* Returns the markdown content, or null on failure.
*/
export async function fetchCloudflareReference(
skillsBaseUrl: string,
): Promise<string | null> {
try {
const url = `${skillsBaseUrl}/cloudflare-workers.md`;
logToFile(`[cloudflare-detection] fetching reference from ${url}`);
const resp = await fetch(url);
if (resp.ok) {
const text = await resp.text();
logToFile(
`[cloudflare-detection] loaded reference (${text.length} chars)`,
);
return text;
}
logToFile(
`[cloudflare-detection] reference fetch failed: HTTP ${resp.status}`,
);
return null;
} catch (err: any) {
logToFile(`[cloudflare-detection] reference fetch error: ${err.message}`);
return null;
}
}
Loading