From 391f6fefa67dcf2a98463058050c1974e4ce4f8e Mon Sep 17 00:00:00 2001 From: Rob Di Marco Date: Mon, 13 Oct 2025 13:05:11 -0400 Subject: [PATCH 1/3] ATXP-407: Integrate Cloudflare Workers template into atxp create command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for creating projects with the Cloudflare Workers AI agent template alongside the existing Express template. Users can now choose between frameworks using the --framework flag. Changes: - Added 'cloudflare' as a new framework option - Updated Framework type to include 'cloudflare' - Added Cloudflare template configuration pointing to atxp-cloudflare-agent-example - Updated README with cloudflare framework examples - Added framework-specific installation instructions (npm install vs npm run install-all) - Updated tests to validate both express and cloudflare frameworks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 13 +++++++++---- packages/atxp/src/create-project.test.ts | 12 ++++++++---- packages/atxp/src/create-project.ts | 17 ++++++++++++++--- packages/atxp/src/index.ts | 2 +- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e966781..4d1f454 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ This monorepo contains: - `--dir, -d` - Specify demo directory (default: ~/.cache/atxp/demo) ### Create Options -- `--framework, -f` - Specify framework template (default: express) +- `--framework, -f` - Specify framework template (options: express, cloudflare; default: express) - `--git` - Force git initialization - `--no-git` - Skip git initialization @@ -66,12 +66,15 @@ npx atxp demo --refresh ### Create a New Project ```bash -# Create a new project (auto-detects git) +# Create a new Express project (auto-detects git) npx atxp create my-app -# Create with specific framework +# Create with Express framework (default) npx atxp create my-app --framework express +# Create with Cloudflare Workers framework +npx atxp create my-app --framework cloudflare + # Skip git initialization npx atxp create my-app --no-git @@ -84,7 +87,9 @@ npm create atxp my-app # Set up the project cd my-app npm install -npm start +npm start # For Express projects +# or +npm start # For Cloudflare projects (runs vite dev) ``` ## Development diff --git a/packages/atxp/src/create-project.test.ts b/packages/atxp/src/create-project.test.ts index d583a02..9387d60 100644 --- a/packages/atxp/src/create-project.test.ts +++ b/packages/atxp/src/create-project.test.ts @@ -32,13 +32,17 @@ describe('createProject', () => { describe('Framework type', () => { it('should have correct framework types', () => { - const validFramework: Framework = 'express'; - expect(validFramework).toBe('express'); - + const expressFramework: Framework = 'express'; + expect(expressFramework).toBe('express'); + + const cloudflareFramework: Framework = 'cloudflare'; + expect(cloudflareFramework).toBe('cloudflare'); + // Test that the type system prevents invalid frameworks // This is compile-time validation, but we can test the concept - const frameworks = ['express'] as const; + const frameworks = ['express', 'cloudflare'] as const; expect(frameworks).toContain('express'); + expect(frameworks).toContain('cloudflare'); }); }); diff --git a/packages/atxp/src/create-project.ts b/packages/atxp/src/create-project.ts index 4cc4955..97ef90d 100644 --- a/packages/atxp/src/create-project.ts +++ b/packages/atxp/src/create-project.ts @@ -4,7 +4,7 @@ import chalk from 'chalk'; import { spawn } from 'child_process'; import inquirer from 'inquirer'; -export type Framework = 'express'; +export type Framework = 'express' | 'cloudflare'; // Utility function to check if git is available async function isGitAvailable(): Promise { @@ -141,6 +141,10 @@ const TEMPLATES: Record = { express: { url: 'https://github.com/atxp-dev/atxp-express-starter.git', humanText: 'Express (Express.js starter template)' + }, + cloudflare: { + url: 'https://github.com/atxp-dev/atxp-cloudflare-agent-example.git', + humanText: 'Cloudflare (Cloudflare Workers AI agent with ATXP integration)' } // Future frameworks can be added here // vercel: { @@ -266,9 +270,16 @@ export async function createProject(appName: string, framework: Framework, gitOp console.log(chalk.green('\nProject created successfully!')); console.log(chalk.blue('\nNext steps:')); console.log(chalk.white(` cd ${appName}`)); - console.log(chalk.white(' npm run install-all')); + + // Different installation instructions based on framework + if (framework === 'express') { + console.log(chalk.white(' npm run install-all')); + } else { + console.log(chalk.white(' npm install')); + } + console.log(chalk.white(' npm start')); - + // Only show env reminder if there is an .env file that exists if (createdEnvPath && await fs.pathExists(createdEnvPath)) { console.log(chalk.yellow('\nRemember to configure your environment variables in the .env file!')); diff --git a/packages/atxp/src/index.ts b/packages/atxp/src/index.ts index f446d2d..876815c 100644 --- a/packages/atxp/src/index.ts +++ b/packages/atxp/src/index.ts @@ -114,7 +114,7 @@ async function main() { const framework: Framework = createOptions.framework || 'express'; // Validate framework - const validFrameworks: Framework[] = ['express']; + const validFrameworks: Framework[] = ['express', 'cloudflare']; if (createOptions.framework && !validFrameworks.includes(createOptions.framework)) { console.error(`Error: Unknown framework "${createOptions.framework}". Available frameworks: ${validFrameworks.join(', ')}`); process.exit(1); From 3e9da456c81752e0087557d9f3e435f724655515 Mon Sep 17 00:00:00 2001 From: Rob Di Marco Date: Mon, 13 Oct 2025 13:11:05 -0400 Subject: [PATCH 2/3] Fix repo URL --- packages/atxp/src/create-project.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/atxp/src/create-project.ts b/packages/atxp/src/create-project.ts index 97ef90d..6325c7b 100644 --- a/packages/atxp/src/create-project.ts +++ b/packages/atxp/src/create-project.ts @@ -143,8 +143,8 @@ const TEMPLATES: Record = { humanText: 'Express (Express.js starter template)' }, cloudflare: { - url: 'https://github.com/atxp-dev/atxp-cloudflare-agent-example.git', - humanText: 'Cloudflare (Cloudflare Workers AI agent with ATXP integration)' + url: 'https://github.com/atxp-dev/atxp-cloudflare-chat-template', + humanText: 'Cloudflare (Cloudflare Chat agent with ATXP integration)' } // Future frameworks can be added here // vercel: { From 46e3ae7090a7859f2a596f2c3b11beaec0415e1e Mon Sep 17 00:00:00 2001 From: Rob Di Marco Date: Mon, 13 Oct 2025 16:45:45 -0400 Subject: [PATCH 3/3] Generate dev.vars --- packages/atxp/src/create-project.ts | 60 ++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/atxp/src/create-project.ts b/packages/atxp/src/create-project.ts index 6325c7b..a5e6824 100644 --- a/packages/atxp/src/create-project.ts +++ b/packages/atxp/src/create-project.ts @@ -181,6 +181,34 @@ async function findEnvExample(projectPath: string): Promise { return null; } +// Find .dev.vars.example file in project directory or one level of subdirectories +async function findDevVarsExample(projectPath: string): Promise { + // Check root directory first + const rootDevVars = path.join(projectPath, '.dev.vars.example'); + if (await fs.pathExists(rootDevVars)) { + return rootDevVars; + } + + // Check subdirectories (max depth 1) + try { + const entries = await fs.readdir(projectPath, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isDirectory()) { + const subDirDevVars = path.join(projectPath, entry.name, '.dev.vars.example'); + if (await fs.pathExists(subDirDevVars)) { + return subDirDevVars; + } + } + } + } catch { + // If we can't read the directory, just return null + console.warn(chalk.yellow('Could not search for .dev.vars.example files')); + } + + return null; +} + export async function createProject(appName: string, framework: Framework, gitOption?: 'git' | 'no-git'): Promise { try { // Validate app name @@ -224,22 +252,27 @@ export async function createProject(appName: string, framework: Framework, gitOp // Clone template from GitHub await cloneTemplate(framework, projectPath); - // Copy .env file from env.example if it exists and configure it interactively - // Search for env.example in project root and one level of subdirectories + // Copy configuration file from template if it exists and configure it interactively + // Search for .dev.vars.example or env.example in project root and one level of subdirectories + // Prioritize .dev.vars.example (used by Cloudflare) over env.example + const devVarsExamplePath = await findDevVarsExample(projectPath); const envExamplePath = await findEnvExample(projectPath); - let createdEnvPath: string | null = null; + let createdConfigPath: string | null = null; - if (envExamplePath) { - const envDir = path.dirname(envExamplePath); - const envPath = path.join(envDir, '.env'); + const configSource = devVarsExamplePath || envExamplePath; + if (configSource) { + const configDir = path.dirname(configSource); + const isDevVars = configSource === devVarsExamplePath; + const configPath = path.join(configDir, isDevVars ? '.dev.vars' : '.env'); + const configFileName = isDevVars ? '.dev.vars' : '.env'; - await fs.copy(envExamplePath, envPath); - console.log(chalk.green('Environment file created from template')); + await fs.copy(configSource, configPath); + console.log(chalk.green(`${configFileName} file created from template`)); // Configure environment variables interactively - await configureEnvironmentVariables(envPath); + await configureEnvironmentVariables(configPath); - createdEnvPath = envPath; + createdConfigPath = configPath; } // Update package.json with project name @@ -280,9 +313,10 @@ export async function createProject(appName: string, framework: Framework, gitOp console.log(chalk.white(' npm start')); - // Only show env reminder if there is an .env file that exists - if (createdEnvPath && await fs.pathExists(createdEnvPath)) { - console.log(chalk.yellow('\nRemember to configure your environment variables in the .env file!')); + // Only show config file reminder if a config file was created and exists + if (createdConfigPath && await fs.pathExists(createdConfigPath)) { + const configFileName = path.basename(createdConfigPath); + console.log(chalk.yellow(`\nRemember to configure your environment variables in the ${configFileName} file!`)); } } catch (error) {