create-hybrid 1.3.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Hybrid - Typescript Framework for building commerce-connected AI Agents.
1
+ # create-hybrid
2
2
 
3
- An open-source agent framework for building conversational AI agents on XMTP.
3
+ This package is part of the Hybrid monorepo.
4
4
 
5
5
  Hybrid makes it easy for developers to create intelligent agents that can understand natural language, process messages, and respond through XMTP's decentralized messaging protocol.
6
6
 
7
- See [http://hybriddev.com](http://hybrid.dev) for more information.
7
+ See [hybrid.dev](https://hybrid.dev) for more information.
8
8
 
9
9
  ## šŸ“¦ Quickstart
10
10
 
@@ -49,18 +49,10 @@ hybrid register
49
49
 
50
50
  This generates secure wallet and encryption keys for your XMTP agent.
51
51
 
52
- ### 5. Register your wallet with XMTP
53
-
54
- ```bash
55
- hybrid register
56
- ```
57
-
58
- ### 6. Start developing
52
+ ### 5. Start developing
59
53
 
60
54
  ```bash
61
55
  hybrid dev
62
56
  ```
63
57
 
64
- Your agent will start listening for XMTP messages and you're ready to build!
65
-
66
- Go to [https://xmtp.chat/dm/](https://xmtp.chat/dm/) and send a message to your agent.
58
+ Your agent will start listening for XMTP messages and you're ready to build!
package/dist/index.js CHANGED
@@ -140,7 +140,7 @@ async function updateTemplateFiles(projectDir, projectName) {
140
140
  const envContent = `# Required
141
141
  OPENROUTER_API_KEY=your_openrouter_api_key_here
142
142
  XMTP_WALLET_KEY=your_wallet_key_here
143
- XMTP_ENCRYPTION_KEY=your_encryption_key_here
143
+ XMTP_DB_ENCRYPTION_KEY=your_encryption_key_here
144
144
 
145
145
  # Optional
146
146
  XMTP_ENV=dev
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Command } from \"commander\"\nimport degit from \"degit\"\nimport { readFile, readdir, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\ninterface Example {\n\tname: string\n\tdescription: string\n\tpath: string\n\tavailable: boolean\n\tmessage?: string\n}\n\n// Default to main branch, but allow override via REPO env var for CI/testing\nconst DEFAULT_REPO = \"ian/hybrid\"\nconst REPO = process.env.REPO || DEFAULT_REPO\n\nconst EXAMPLES: Example[] = [\n\t{\n\t\tname: \"basic\",\n\t\tdescription: \"Basic XMTP agent with message filtering and AI responses\",\n\t\tpath: \"basic\",\n\t\tavailable: true\n\t},\n\t{\n\t\tname: \"with-ponder\",\n\t\tdescription: \"Agent with Ponder integration for indexing blockchain data\",\n\t\tpath: \"with-ponder\",\n\t\tavailable: false,\n\t\tmessage: \"Coming soon\"\n\t},\n\t{\n\t\tname: \"with-foundry\",\n\t\tdescription:\n\t\t\t\"Agent with Foundry integration for smart contract development\",\n\t\tpath: \"with-foundry\",\n\t\tavailable: false,\n\t\tmessage: \"Coming soon\"\n\t}\n]\n\nfunction replaceTemplateVariables(\n\tcontent: string,\n\tvariables: Record<string, string>\n): string {\n\treturn content.replace(\n\t\t/\\{\\{(\\w+)\\}\\}/g,\n\t\t(match, key) => variables[key] || match\n\t)\n}\n\nasync function updateTemplateFiles(\n\tprojectDir: string,\n\tprojectName: string\n): Promise<void> {\n\tconst variables = { projectName }\n\n\tconst filesToUpdate = [\n\t\tjoin(projectDir, \"package.json\"),\n\t\tjoin(projectDir, \"README.md\"),\n\t\tjoin(projectDir, \"src\", \"agent.ts\")\n\t]\n\n\tfor (const filePath of filesToUpdate) {\n\t\ttry {\n\t\t\tlet content = await readFile(filePath, \"utf-8\")\n\n\t\t\t// First try template variable replacement\n\t\t\tcontent = replaceTemplateVariables(content, variables)\n\n\t\t\t// Special handling for package.json if template variables weren't found\n\t\t\tif (filePath.endsWith(\"package.json\")) {\n\t\t\t\ttry {\n\t\t\t\t\tconst packageJson = JSON.parse(content)\n\t\t\t\t\tlet updated = false\n\n\t\t\t\t\t// If name is still a generic name, replace it\n\t\t\t\t\tif (\n\t\t\t\t\t\tpackageJson.name === \"agent\" ||\n\t\t\t\t\t\tpackageJson.name === \"hybrid-example-basic-agent\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tpackageJson.name = projectName\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensure required scripts exist\n\t\t\t\t\tif (!packageJson.scripts) {\n\t\t\t\t\t\tpackageJson.scripts = {}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add missing scripts and update old scripts to use hybrid CLI\n\t\t\t\t\tconst requiredScripts = {\n\t\t\t\t\t\tclean: \"hybrid clean\",\n\t\t\t\t\t\tdev: \"hybrid dev\",\n\t\t\t\t\t\tbuild: \"hybrid build\",\n\t\t\t\t\t\tstart: \"hybrid start\",\n\t\t\t\t\t\tkeys: \"hybrid keys --write\",\n\t\t\t\t\t\ttest: \"vitest\",\n\t\t\t\t\t\t\"test:watch\": \"vitest --watch\",\n\t\t\t\t\t\t\"test:coverage\": \"vitest --coverage\",\n\t\t\t\t\t\tlint: \"biome lint --write\",\n\t\t\t\t\t\t\"lint:check\": \"biome lint\",\n\t\t\t\t\t\tformat: \"biome format --write\",\n\t\t\t\t\t\t\"format:check\": \"biome format --check\",\n\t\t\t\t\t\ttypecheck: \"tsc --noEmit\"\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const [scriptName, scriptCommand] of Object.entries(\n\t\t\t\t\t\trequiredScripts\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Always update scripts to use the correct hybrid CLI commands\n\t\t\t\t\t\tpackageJson.scripts[scriptName] = scriptCommand\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update dependencies to use independent packages\n\t\t\t\t\tif (packageJson.dependencies) {\n\t\t\t\t\t\tif (packageJson.dependencies.hybrid === \"workspace:*\") {\n\t\t\t\t\t\t\tpackageJson.dependencies.hybrid = \"latest\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@openrouter/ai-sdk-provider\"] ===\n\t\t\t\t\t\t\t\"catalog:ai\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@openrouter/ai-sdk-provider\"] = \"^1.1.2\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (packageJson.dependencies.zod === \"catalog:stack\") {\n\t\t\t\t\t\t\tpackageJson.dependencies.zod = \"^3.23.8\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Remove workspace dependencies\n\t\t\t\t\t\tif (packageJson.dependencies[\"@hybrd/xmtp\"]) {\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@hybrd/xmtp\"] = undefined\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update devDependencies to use independent packages\n\t\t\t\t\tif (packageJson.devDependencies) {\n\t\t\t\t\t\tconst independentDevDeps = {\n\t\t\t\t\t\t\t\"@biomejs/biome\": \"^1.9.4\",\n\t\t\t\t\t\t\t\"@types/node\": \"^22.0.0\",\n\t\t\t\t\t\t\t\"@hybrd/cli\": \"latest\",\n\t\t\t\t\t\t\ttsx: \"^4.20.5\",\n\t\t\t\t\t\t\ttypescript: \"^5.8.3\",\n\t\t\t\t\t\t\tvitest: \"^3.2.4\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Remove workspace dependencies\n\t\t\t\t\t\tpackageJson.devDependencies[\"@config/biome\"] = undefined\n\t\t\t\t\t\tpackageJson.devDependencies[\"@config/tsconfig\"] = undefined\n\n\t\t\t\t\t\t// Add independent dependencies\n\t\t\t\t\t\tfor (const [depName, depVersion] of Object.entries(\n\t\t\t\t\t\t\tindependentDevDeps\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\tpackageJson.devDependencies[depName] = depVersion\n\t\t\t\t\t\t}\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\tif (updated) {\n\t\t\t\t\t\tcontent = `${JSON.stringify(packageJson, null, \"\\t\")}\\n`\n\t\t\t\t\t}\n\t\t\t\t} catch (parseError) {\n\t\t\t\t\tconsole.log(\"āš ļø Could not parse package.json for name update\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Special handling for README.md if template variables weren't found\n\t\t\tif (filePath.endsWith(\"README.md\")) {\n\t\t\t\t// Replace common README title patterns with project name\n\t\t\t\tcontent = content.replace(/^# .*$/m, `# ${projectName}`)\n\t\t\t}\n\n\t\t\tawait writeFile(filePath, content, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconsole.log(\n\t\t\t\t`āš ļø Could not update ${filePath.split(\"/\").pop()}: file not found or error occurred`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Ensure .env file exists - create it if missing\n\tconst envPath = join(projectDir, \".env\")\n\ttry {\n\t\tawait readFile(envPath, \"utf-8\")\n\t} catch {\n\t\t// .env file doesn't exist, create it\n\t\tconst envContent = `# Required\nOPENROUTER_API_KEY=your_openrouter_api_key_here\nXMTP_WALLET_KEY=your_wallet_key_here\nXMTP_ENCRYPTION_KEY=your_encryption_key_here\n\n# Optional\nXMTP_ENV=dev\nPORT=8454`\n\t\tawait writeFile(envPath, envContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created .env template file\")\n\t}\n\n\t// Ensure vitest.config.ts exists - create it if missing\n\tconst vitestConfigPath = join(projectDir, \"vitest.config.ts\")\n\ttry {\n\t\tawait readFile(vitestConfigPath, \"utf-8\")\n\t} catch {\n\t\t// vitest.config.ts doesn't exist, create it\n\t\tconst vitestConfigContent = `import { defineConfig } from \"vitest/config\"\n\nexport default defineConfig({\n\ttest: {\n\t\tenvironment: \"node\",\n\t\tglobals: true,\n\t\tsetupFiles: []\n\t},\n\tresolve: {\n\t\talias: {\n\t\t\t\"@\": \"./src\"\n\t\t}\n\t}\n})`\n\t\tawait writeFile(vitestConfigPath, vitestConfigContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created vitest.config.ts file\")\n\t}\n\n\t// Ensure src/agent.test.ts exists - create it if missing\n\tconst agentTestPath = join(projectDir, \"src\", \"agent.test.ts\")\n\ttry {\n\t\tawait readFile(agentTestPath, \"utf-8\")\n\t} catch {\n\t\t// agent.test.ts doesn't exist, create it\n\t\tconst agentTestContent = `import { describe, expect, it } from \"vitest\"\n\n// Example test file - replace with actual tests for your agent\n\ndescribe(\"Agent\", () => {\n\tit(\"should be defined\", () => {\n\t\t// This is a placeholder test\n\t\t// Add real tests for your agent functionality\n\t\texpect(true).toBe(true)\n\t})\n})`\n\t\tawait writeFile(agentTestPath, agentTestContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created src/agent.test.ts file\")\n\t}\n}\n\nasync function checkDirectoryEmpty(dirPath: string): Promise<boolean> {\n\ttry {\n\t\tconst files = await readdir(dirPath)\n\t\tconst significantFiles = files.filter(\n\t\t\t(file) =>\n\t\t\t\t!file.startsWith(\".\") &&\n\t\t\t\tfile !== \"node_modules\" &&\n\t\t\t\tfile !== \"package-lock.json\" &&\n\t\t\t\tfile !== \"yarn.lock\" &&\n\t\t\t\tfile !== \"pnpm-lock.yaml\"\n\t\t)\n\t\treturn significantFiles.length === 0\n\t} catch {\n\t\t// Directory doesn't exist, so it's \"empty\"\n\t\treturn true\n\t}\n}\n\nasync function createProject(\n\tprojectName: string,\n\texampleName?: string\n): Promise<void> {\n\tconsole.log(\"šŸš€ Creating a new Hybrid project...\")\n\n\t// Validate project name\n\tif (!projectName || projectName.trim() === \"\") {\n\t\tconsole.error(\"āŒ Project name is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst sanitizedName = projectName\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9-]/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\")\n\n\tconst currentDir = process.cwd()\n\tconst projectDir =\n\t\tprojectName === \".\" ? currentDir : join(currentDir, sanitizedName)\n\n\t// Check if directory is empty\n\tconst isEmpty = await checkDirectoryEmpty(projectDir)\n\tif (!isEmpty) {\n\t\tconsole.error(\n\t\t\t`āŒ Directory \"${sanitizedName}\" already exists and is not empty`\n\t\t)\n\t\tconsole.error(\n\t\t\t\"Please choose a different name or remove the existing directory\"\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\t// Select example if not provided\n\tlet selectedExample: Example\n\tif (exampleName) {\n\t\tconst example = EXAMPLES.find((ex) => ex.name === exampleName)\n\t\tif (!example) {\n\t\t\tconsole.error(`āŒ Example \"${exampleName}\" not found`)\n\t\t\tconsole.error(\n\t\t\t\t`Available examples: ${EXAMPLES.map((ex) => ex.name).join(\", \")}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tselectedExample = example\n\t\tconsole.log(`šŸ“‹ Using example: ${selectedExample.name}`)\n\t} else {\n\t\t// Check if we're running in a non-interactive environment (like CI)\n\t\tif (!process.stdin.isTTY) {\n\t\t\tconsole.error(\n\t\t\t\t\"āŒ Example is required in non-interactive mode. Use --example <name>\"\n\t\t\t)\n\t\t\tconsole.error(\n\t\t\t\t`Available examples: ${EXAMPLES.map((ex) => ex.name).join(\", \")}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst { example } = await prompts({\n\t\t\ttype: \"select\",\n\t\t\tname: \"example\",\n\t\t\tmessage: \"Which example would you like to use?\",\n\t\t\tchoices: EXAMPLES.map((ex) => ({\n\t\t\t\ttitle: ex.name,\n\t\t\t\tdescription: ex.available\n\t\t\t\t\t? ex.description\n\t\t\t\t\t: `${ex.description} (${ex.message || \"Coming soon\"})`,\n\t\t\t\tvalue: ex,\n\t\t\t\tdisabled: !ex.available\n\t\t\t})),\n\t\t\tinitial: 0\n\t\t})\n\n\t\tif (!example) {\n\t\t\tconsole.log(\"āŒ No example selected. Exiting...\")\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Check if the selected example is available\n\t\tif (!example.available) {\n\t\t\tconsole.log(\n\t\t\t\t`āŒ Example \"${example.name}\" is not yet available. ${example.message || \"Coming soon\"}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tselectedExample = example\n\t}\n\n\tconsole.log(`šŸ“¦ Cloning ${selectedExample.name} example...`)\n\n\ttry {\n\t\t// For degit, the correct syntax is: repo#branch/subdirectory\n\t\t// But we need to be careful about the path construction\n\t\tlet degitSource: string\n\n\t\tif (REPO.includes(\"#\")) {\n\t\t\t// REPO is in format \"user/repo#branch\"\n\t\t\t// We need to construct: user/repo#branch/examples/basic\n\t\t\tconst [repoWithBranch] = REPO.split(\"/examples/\") // Remove any existing path\n\t\t\tdegitSource = `${repoWithBranch}/examples/${selectedExample.name}`\n\t\t} else {\n\t\t\t// No branch specified, use default format\n\t\t\tdegitSource = `${REPO}/examples/${selectedExample.name}`\n\t\t}\n\n\t\tconsole.log(`šŸ” Degit source: ${degitSource}`)\n\t\tconst emitter = degit(degitSource)\n\t\tawait emitter.clone(projectDir)\n\t\tconsole.log(`āœ… Template cloned to: ${sanitizedName}`)\n\t} catch (error) {\n\t\tconsole.error(\"āŒ Failed to clone template:\", error)\n\t\tprocess.exit(1)\n\t}\n\n\t// Update template variables\n\tconsole.log(\"šŸ”§ Updating template variables...\")\n\ttry {\n\t\tawait updateTemplateFiles(projectDir, sanitizedName)\n\t\tconsole.log(\"āœ… Template variables updated\")\n\t} catch (error) {\n\t\tconsole.error(\"āŒ Failed to update template variables:\", error)\n\t}\n\n\tconsole.log(\"\\nšŸŽ‰ Hybrid project created successfully!\")\n\tconsole.log(`\\nšŸ“‚ Project created in: ${projectDir}`)\n\tconsole.log(\"\\nšŸ“‹ Next steps:\")\n\tif (projectName !== \".\") {\n\t\tconsole.log(`1. cd ${sanitizedName}`)\n\t}\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"2\" : \"1\"}. Install dependencies (npm install, yarn install, or pnpm install)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"3\" : \"2\"}. Get your OpenRouter API key from https://openrouter.ai/keys`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"4\" : \"3\"}. Add your API key to the OPENROUTER_API_KEY in .env`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"5\" : \"4\"}. Set XMTP_ENV in .env (dev or production)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"6\" : \"5\"}. Generate keys: npm run keys (or yarn/pnpm equivalent)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"7\" : \"6\"}. Start development: npm run dev (or yarn/pnpm equivalent)`\n\t)\n\n\tconsole.log(\n\t\t\"\\nšŸ“– For more information, see the README.md file in your project\"\n\t)\n}\n\nexport async function initializeProject(): Promise<void> {\n\tconst program = new Command()\n\n\tprogram\n\t\t.name(\"create-hybrid\")\n\t\t.description(\"Create a new Hybrid XMTP agent project\")\n\t\t.version(\"1.2.3\")\n\t\t.argument(\"[project-name]\", \"Name of the project\")\n\t\t.option(\n\t\t\t\"-e, --example <example>\",\n\t\t\t\"Example to use (basic, with-ponder, with-foundry)\"\n\t\t)\n\t\t.action(async (projectName?: string, options?: { example?: string }) => {\n\t\t\tlet finalProjectName = projectName\n\n\t\t\t// Debug logging for CI troubleshooting\n\t\t\tif (process.env.CI) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`šŸ” Debug: projectName=\"${projectName}\", options.example=\"${options?.example}\"`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// If no project name provided or empty string, prompt for it\n\t\t\tif (!finalProjectName || finalProjectName.trim() === \"\") {\n\t\t\t\t// Check if we're running in a non-interactive environment (like tests)\n\t\t\t\tif (!process.stdin.isTTY) {\n\t\t\t\t\tconsole.error(\"āŒ Project name is required\")\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\n\t\t\t\tconst { name } = await prompts({\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tname: \"name\",\n\t\t\t\t\tmessage: \"What is your project name?\",\n\t\t\t\t\tvalidate: (value: string) => {\n\t\t\t\t\t\tif (!value || !value.trim()) {\n\t\t\t\t\t\t\treturn \"Project name is required\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tif (!name) {\n\t\t\t\t\tconsole.log(\"āŒ Project name is required. Exiting...\")\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\n\t\t\t\tfinalProjectName = name\n\t\t\t}\n\n\t\t\tawait createProject(finalProjectName as string, options?.example)\n\t\t})\n\n\tawait program.parseAsync()\n}\n\nasync function main(): Promise<void> {\n\tconst nodeVersion = process.versions.node\n\tconst [major] = nodeVersion.split(\".\").map(Number)\n\tif (!major || major < 20) {\n\t\tconsole.error(\"Error: Node.js version 20 or higher is required\")\n\t\tprocess.exit(1)\n\t}\n\n\ttry {\n\t\tawait initializeProject()\n\t} catch (error) {\n\t\tconsole.error(\"Failed to initialize project:\", error)\n\t\tconsole.error(\n\t\t\t\"Error details:\",\n\t\t\terror instanceof Error ? error.stack : String(error)\n\t\t)\n\t\tprocess.exit(1)\n\t}\n}\n\nmain().catch((error) => {\n\tconsole.error(\"CLI error:\", error)\n\tconsole.error(\n\t\t\"Error details:\",\n\t\terror instanceof Error ? error.stack : String(error)\n\t)\n\tprocess.exit(1)\n})\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Command } from \"commander\"\nimport degit from \"degit\"\nimport { readFile, readdir, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\ninterface Example {\n\tname: string\n\tdescription: string\n\tpath: string\n\tavailable: boolean\n\tmessage?: string\n}\n\n// Default to main branch, but allow override via REPO env var for CI/testing\nconst DEFAULT_REPO = \"ian/hybrid\"\nconst REPO = process.env.REPO || DEFAULT_REPO\n\nconst EXAMPLES: Example[] = [\n\t{\n\t\tname: \"basic\",\n\t\tdescription: \"Basic XMTP agent with message filtering and AI responses\",\n\t\tpath: \"basic\",\n\t\tavailable: true\n\t},\n\t{\n\t\tname: \"with-ponder\",\n\t\tdescription: \"Agent with Ponder integration for indexing blockchain data\",\n\t\tpath: \"with-ponder\",\n\t\tavailable: false,\n\t\tmessage: \"Coming soon\"\n\t},\n\t{\n\t\tname: \"with-foundry\",\n\t\tdescription:\n\t\t\t\"Agent with Foundry integration for smart contract development\",\n\t\tpath: \"with-foundry\",\n\t\tavailable: false,\n\t\tmessage: \"Coming soon\"\n\t}\n]\n\nfunction replaceTemplateVariables(\n\tcontent: string,\n\tvariables: Record<string, string>\n): string {\n\treturn content.replace(\n\t\t/\\{\\{(\\w+)\\}\\}/g,\n\t\t(match, key) => variables[key] || match\n\t)\n}\n\nasync function updateTemplateFiles(\n\tprojectDir: string,\n\tprojectName: string\n): Promise<void> {\n\tconst variables = { projectName }\n\n\tconst filesToUpdate = [\n\t\tjoin(projectDir, \"package.json\"),\n\t\tjoin(projectDir, \"README.md\"),\n\t\tjoin(projectDir, \"src\", \"agent.ts\")\n\t]\n\n\tfor (const filePath of filesToUpdate) {\n\t\ttry {\n\t\t\tlet content = await readFile(filePath, \"utf-8\")\n\n\t\t\t// First try template variable replacement\n\t\t\tcontent = replaceTemplateVariables(content, variables)\n\n\t\t\t// Special handling for package.json if template variables weren't found\n\t\t\tif (filePath.endsWith(\"package.json\")) {\n\t\t\t\ttry {\n\t\t\t\t\tconst packageJson = JSON.parse(content)\n\t\t\t\t\tlet updated = false\n\n\t\t\t\t\t// If name is still a generic name, replace it\n\t\t\t\t\tif (\n\t\t\t\t\t\tpackageJson.name === \"agent\" ||\n\t\t\t\t\t\tpackageJson.name === \"hybrid-example-basic-agent\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tpackageJson.name = projectName\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensure required scripts exist\n\t\t\t\t\tif (!packageJson.scripts) {\n\t\t\t\t\t\tpackageJson.scripts = {}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add missing scripts and update old scripts to use hybrid CLI\n\t\t\t\t\tconst requiredScripts = {\n\t\t\t\t\t\tclean: \"hybrid clean\",\n\t\t\t\t\t\tdev: \"hybrid dev\",\n\t\t\t\t\t\tbuild: \"hybrid build\",\n\t\t\t\t\t\tstart: \"hybrid start\",\n\t\t\t\t\t\tkeys: \"hybrid keys --write\",\n\t\t\t\t\t\ttest: \"vitest\",\n\t\t\t\t\t\t\"test:watch\": \"vitest --watch\",\n\t\t\t\t\t\t\"test:coverage\": \"vitest --coverage\",\n\t\t\t\t\t\tlint: \"biome lint --write\",\n\t\t\t\t\t\t\"lint:check\": \"biome lint\",\n\t\t\t\t\t\tformat: \"biome format --write\",\n\t\t\t\t\t\t\"format:check\": \"biome format --check\",\n\t\t\t\t\t\ttypecheck: \"tsc --noEmit\"\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const [scriptName, scriptCommand] of Object.entries(\n\t\t\t\t\t\trequiredScripts\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Always update scripts to use the correct hybrid CLI commands\n\t\t\t\t\t\tpackageJson.scripts[scriptName] = scriptCommand\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update dependencies to use independent packages\n\t\t\t\t\tif (packageJson.dependencies) {\n\t\t\t\t\t\tif (packageJson.dependencies.hybrid === \"workspace:*\") {\n\t\t\t\t\t\t\tpackageJson.dependencies.hybrid = \"latest\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@openrouter/ai-sdk-provider\"] ===\n\t\t\t\t\t\t\t\"catalog:ai\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@openrouter/ai-sdk-provider\"] = \"^1.1.2\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (packageJson.dependencies.zod === \"catalog:stack\") {\n\t\t\t\t\t\t\tpackageJson.dependencies.zod = \"^3.23.8\"\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Remove workspace dependencies\n\t\t\t\t\t\tif (packageJson.dependencies[\"@hybrd/xmtp\"]) {\n\t\t\t\t\t\t\tpackageJson.dependencies[\"@hybrd/xmtp\"] = undefined\n\t\t\t\t\t\t\tupdated = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update devDependencies to use independent packages\n\t\t\t\t\tif (packageJson.devDependencies) {\n\t\t\t\t\t\tconst independentDevDeps = {\n\t\t\t\t\t\t\t\"@biomejs/biome\": \"^1.9.4\",\n\t\t\t\t\t\t\t\"@types/node\": \"^22.0.0\",\n\t\t\t\t\t\t\t\"@hybrd/cli\": \"latest\",\n\t\t\t\t\t\t\ttsx: \"^4.20.5\",\n\t\t\t\t\t\t\ttypescript: \"^5.8.3\",\n\t\t\t\t\t\t\tvitest: \"^3.2.4\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Remove workspace dependencies\n\t\t\t\t\t\tpackageJson.devDependencies[\"@config/biome\"] = undefined\n\t\t\t\t\t\tpackageJson.devDependencies[\"@config/tsconfig\"] = undefined\n\n\t\t\t\t\t\t// Add independent dependencies\n\t\t\t\t\t\tfor (const [depName, depVersion] of Object.entries(\n\t\t\t\t\t\t\tindependentDevDeps\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\tpackageJson.devDependencies[depName] = depVersion\n\t\t\t\t\t\t}\n\t\t\t\t\t\tupdated = true\n\t\t\t\t\t}\n\n\t\t\t\t\tif (updated) {\n\t\t\t\t\t\tcontent = `${JSON.stringify(packageJson, null, \"\\t\")}\\n`\n\t\t\t\t\t}\n\t\t\t\t} catch (parseError) {\n\t\t\t\t\tconsole.log(\"āš ļø Could not parse package.json for name update\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Special handling for README.md if template variables weren't found\n\t\t\tif (filePath.endsWith(\"README.md\")) {\n\t\t\t\t// Replace common README title patterns with project name\n\t\t\t\tcontent = content.replace(/^# .*$/m, `# ${projectName}`)\n\t\t\t}\n\n\t\t\tawait writeFile(filePath, content, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconsole.log(\n\t\t\t\t`āš ļø Could not update ${filePath.split(\"/\").pop()}: file not found or error occurred`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Ensure .env file exists - create it if missing\n\tconst envPath = join(projectDir, \".env\")\n\ttry {\n\t\tawait readFile(envPath, \"utf-8\")\n\t} catch {\n\t\t// .env file doesn't exist, create it\n\t\tconst envContent = `# Required\nOPENROUTER_API_KEY=your_openrouter_api_key_here\nXMTP_WALLET_KEY=your_wallet_key_here\nXMTP_DB_ENCRYPTION_KEY=your_encryption_key_here\n\n# Optional\nXMTP_ENV=dev\nPORT=8454`\n\t\tawait writeFile(envPath, envContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created .env template file\")\n\t}\n\n\t// Ensure vitest.config.ts exists - create it if missing\n\tconst vitestConfigPath = join(projectDir, \"vitest.config.ts\")\n\ttry {\n\t\tawait readFile(vitestConfigPath, \"utf-8\")\n\t} catch {\n\t\t// vitest.config.ts doesn't exist, create it\n\t\tconst vitestConfigContent = `import { defineConfig } from \"vitest/config\"\n\nexport default defineConfig({\n\ttest: {\n\t\tenvironment: \"node\",\n\t\tglobals: true,\n\t\tsetupFiles: []\n\t},\n\tresolve: {\n\t\talias: {\n\t\t\t\"@\": \"./src\"\n\t\t}\n\t}\n})`\n\t\tawait writeFile(vitestConfigPath, vitestConfigContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created vitest.config.ts file\")\n\t}\n\n\t// Ensure src/agent.test.ts exists - create it if missing\n\tconst agentTestPath = join(projectDir, \"src\", \"agent.test.ts\")\n\ttry {\n\t\tawait readFile(agentTestPath, \"utf-8\")\n\t} catch {\n\t\t// agent.test.ts doesn't exist, create it\n\t\tconst agentTestContent = `import { describe, expect, it } from \"vitest\"\n\n// Example test file - replace with actual tests for your agent\n\ndescribe(\"Agent\", () => {\n\tit(\"should be defined\", () => {\n\t\t// This is a placeholder test\n\t\t// Add real tests for your agent functionality\n\t\texpect(true).toBe(true)\n\t})\n})`\n\t\tawait writeFile(agentTestPath, agentTestContent, \"utf-8\")\n\t\tconsole.log(\"šŸ“„ Created src/agent.test.ts file\")\n\t}\n}\n\nasync function checkDirectoryEmpty(dirPath: string): Promise<boolean> {\n\ttry {\n\t\tconst files = await readdir(dirPath)\n\t\tconst significantFiles = files.filter(\n\t\t\t(file) =>\n\t\t\t\t!file.startsWith(\".\") &&\n\t\t\t\tfile !== \"node_modules\" &&\n\t\t\t\tfile !== \"package-lock.json\" &&\n\t\t\t\tfile !== \"yarn.lock\" &&\n\t\t\t\tfile !== \"pnpm-lock.yaml\"\n\t\t)\n\t\treturn significantFiles.length === 0\n\t} catch {\n\t\t// Directory doesn't exist, so it's \"empty\"\n\t\treturn true\n\t}\n}\n\nasync function createProject(\n\tprojectName: string,\n\texampleName?: string\n): Promise<void> {\n\tconsole.log(\"šŸš€ Creating a new Hybrid project...\")\n\n\t// Validate project name\n\tif (!projectName || projectName.trim() === \"\") {\n\t\tconsole.error(\"āŒ Project name is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst sanitizedName = projectName\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9-]/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\")\n\n\tconst currentDir = process.cwd()\n\tconst projectDir =\n\t\tprojectName === \".\" ? currentDir : join(currentDir, sanitizedName)\n\n\t// Check if directory is empty\n\tconst isEmpty = await checkDirectoryEmpty(projectDir)\n\tif (!isEmpty) {\n\t\tconsole.error(\n\t\t\t`āŒ Directory \"${sanitizedName}\" already exists and is not empty`\n\t\t)\n\t\tconsole.error(\n\t\t\t\"Please choose a different name or remove the existing directory\"\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\t// Select example if not provided\n\tlet selectedExample: Example\n\tif (exampleName) {\n\t\tconst example = EXAMPLES.find((ex) => ex.name === exampleName)\n\t\tif (!example) {\n\t\t\tconsole.error(`āŒ Example \"${exampleName}\" not found`)\n\t\t\tconsole.error(\n\t\t\t\t`Available examples: ${EXAMPLES.map((ex) => ex.name).join(\", \")}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tselectedExample = example\n\t\tconsole.log(`šŸ“‹ Using example: ${selectedExample.name}`)\n\t} else {\n\t\t// Check if we're running in a non-interactive environment (like CI)\n\t\tif (!process.stdin.isTTY) {\n\t\t\tconsole.error(\n\t\t\t\t\"āŒ Example is required in non-interactive mode. Use --example <name>\"\n\t\t\t)\n\t\t\tconsole.error(\n\t\t\t\t`Available examples: ${EXAMPLES.map((ex) => ex.name).join(\", \")}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst { example } = await prompts({\n\t\t\ttype: \"select\",\n\t\t\tname: \"example\",\n\t\t\tmessage: \"Which example would you like to use?\",\n\t\t\tchoices: EXAMPLES.map((ex) => ({\n\t\t\t\ttitle: ex.name,\n\t\t\t\tdescription: ex.available\n\t\t\t\t\t? ex.description\n\t\t\t\t\t: `${ex.description} (${ex.message || \"Coming soon\"})`,\n\t\t\t\tvalue: ex,\n\t\t\t\tdisabled: !ex.available\n\t\t\t})),\n\t\t\tinitial: 0\n\t\t})\n\n\t\tif (!example) {\n\t\t\tconsole.log(\"āŒ No example selected. Exiting...\")\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Check if the selected example is available\n\t\tif (!example.available) {\n\t\t\tconsole.log(\n\t\t\t\t`āŒ Example \"${example.name}\" is not yet available. ${example.message || \"Coming soon\"}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tselectedExample = example\n\t}\n\n\tconsole.log(`šŸ“¦ Cloning ${selectedExample.name} example...`)\n\n\ttry {\n\t\t// For degit, the correct syntax is: repo#branch/subdirectory\n\t\t// But we need to be careful about the path construction\n\t\tlet degitSource: string\n\n\t\tif (REPO.includes(\"#\")) {\n\t\t\t// REPO is in format \"user/repo#branch\"\n\t\t\t// We need to construct: user/repo#branch/examples/basic\n\t\t\tconst [repoWithBranch] = REPO.split(\"/examples/\") // Remove any existing path\n\t\t\tdegitSource = `${repoWithBranch}/examples/${selectedExample.name}`\n\t\t} else {\n\t\t\t// No branch specified, use default format\n\t\t\tdegitSource = `${REPO}/examples/${selectedExample.name}`\n\t\t}\n\n\t\tconsole.log(`šŸ” Degit source: ${degitSource}`)\n\t\tconst emitter = degit(degitSource)\n\t\tawait emitter.clone(projectDir)\n\t\tconsole.log(`āœ… Template cloned to: ${sanitizedName}`)\n\t} catch (error) {\n\t\tconsole.error(\"āŒ Failed to clone template:\", error)\n\t\tprocess.exit(1)\n\t}\n\n\t// Update template variables\n\tconsole.log(\"šŸ”§ Updating template variables...\")\n\ttry {\n\t\tawait updateTemplateFiles(projectDir, sanitizedName)\n\t\tconsole.log(\"āœ… Template variables updated\")\n\t} catch (error) {\n\t\tconsole.error(\"āŒ Failed to update template variables:\", error)\n\t}\n\n\tconsole.log(\"\\nšŸŽ‰ Hybrid project created successfully!\")\n\tconsole.log(`\\nšŸ“‚ Project created in: ${projectDir}`)\n\tconsole.log(\"\\nšŸ“‹ Next steps:\")\n\tif (projectName !== \".\") {\n\t\tconsole.log(`1. cd ${sanitizedName}`)\n\t}\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"2\" : \"1\"}. Install dependencies (npm install, yarn install, or pnpm install)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"3\" : \"2\"}. Get your OpenRouter API key from https://openrouter.ai/keys`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"4\" : \"3\"}. Add your API key to the OPENROUTER_API_KEY in .env`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"5\" : \"4\"}. Set XMTP_ENV in .env (dev or production)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"6\" : \"5\"}. Generate keys: npm run keys (or yarn/pnpm equivalent)`\n\t)\n\tconsole.log(\n\t\t`${projectName !== \".\" ? \"7\" : \"6\"}. Start development: npm run dev (or yarn/pnpm equivalent)`\n\t)\n\n\tconsole.log(\n\t\t\"\\nšŸ“– For more information, see the README.md file in your project\"\n\t)\n}\n\nexport async function initializeProject(): Promise<void> {\n\tconst program = new Command()\n\n\tprogram\n\t\t.name(\"create-hybrid\")\n\t\t.description(\"Create a new Hybrid XMTP agent project\")\n\t\t.version(\"1.2.3\")\n\t\t.argument(\"[project-name]\", \"Name of the project\")\n\t\t.option(\n\t\t\t\"-e, --example <example>\",\n\t\t\t\"Example to use (basic, with-ponder, with-foundry)\"\n\t\t)\n\t\t.action(async (projectName?: string, options?: { example?: string }) => {\n\t\t\tlet finalProjectName = projectName\n\n\t\t\t// Debug logging for CI troubleshooting\n\t\t\tif (process.env.CI) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`šŸ” Debug: projectName=\"${projectName}\", options.example=\"${options?.example}\"`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// If no project name provided or empty string, prompt for it\n\t\t\tif (!finalProjectName || finalProjectName.trim() === \"\") {\n\t\t\t\t// Check if we're running in a non-interactive environment (like tests)\n\t\t\t\tif (!process.stdin.isTTY) {\n\t\t\t\t\tconsole.error(\"āŒ Project name is required\")\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\n\t\t\t\tconst { name } = await prompts({\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tname: \"name\",\n\t\t\t\t\tmessage: \"What is your project name?\",\n\t\t\t\t\tvalidate: (value: string) => {\n\t\t\t\t\t\tif (!value || !value.trim()) {\n\t\t\t\t\t\t\treturn \"Project name is required\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tif (!name) {\n\t\t\t\t\tconsole.log(\"āŒ Project name is required. Exiting...\")\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\n\t\t\t\tfinalProjectName = name\n\t\t\t}\n\n\t\t\tawait createProject(finalProjectName as string, options?.example)\n\t\t})\n\n\tawait program.parseAsync()\n}\n\nasync function main(): Promise<void> {\n\tconst nodeVersion = process.versions.node\n\tconst [major] = nodeVersion.split(\".\").map(Number)\n\tif (!major || major < 20) {\n\t\tconsole.error(\"Error: Node.js version 20 or higher is required\")\n\t\tprocess.exit(1)\n\t}\n\n\ttry {\n\t\tawait initializeProject()\n\t} catch (error) {\n\t\tconsole.error(\"Failed to initialize project:\", error)\n\t\tconsole.error(\n\t\t\t\"Error details:\",\n\t\t\terror instanceof Error ? error.stack : String(error)\n\t\t)\n\t\tprocess.exit(1)\n\t}\n}\n\nmain().catch((error) => {\n\tconsole.error(\"CLI error:\", error)\n\tconsole.error(\n\t\t\"Error details:\",\n\t\terror instanceof Error ? error.stack : String(error)\n\t)\n\tprocess.exit(1)\n})\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,18 +1,20 @@
1
1
  {
2
2
  "name": "create-hybrid",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "Create a new Hybrid XMTP agent project",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-hybrid": "./dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "src"
11
12
  ],
12
13
  "exports": {
13
14
  ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js"
15
+ "types": "./src/index.ts",
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
16
18
  }
17
19
  },
18
20
  "dependencies": {
@@ -24,8 +26,8 @@
24
26
  "@types/node": "22.8.6",
25
27
  "@types/prompts": "^2.4.9",
26
28
  "tsup": "^8.5.0",
27
- "@config/biome": "0.0.0",
28
- "@config/tsconfig": "0.0.0"
29
+ "@config/tsconfig": "0.0.0",
30
+ "@config/biome": "0.0.0"
29
31
  },
30
32
  "engines": {
31
33
  "node": ">=20"
package/src/index.ts ADDED
@@ -0,0 +1,507 @@
1
+ import { Command } from "commander"
2
+ import degit from "degit"
3
+ import { readFile, readdir, writeFile } from "node:fs/promises"
4
+ import { join } from "node:path"
5
+ import prompts from "prompts"
6
+
7
+ interface Example {
8
+ name: string
9
+ description: string
10
+ path: string
11
+ available: boolean
12
+ message?: string
13
+ }
14
+
15
+ // Default to main branch, but allow override via REPO env var for CI/testing
16
+ const DEFAULT_REPO = "ian/hybrid"
17
+ const REPO = process.env.REPO || DEFAULT_REPO
18
+
19
+ const EXAMPLES: Example[] = [
20
+ {
21
+ name: "basic",
22
+ description: "Basic XMTP agent with message filtering and AI responses",
23
+ path: "basic",
24
+ available: true
25
+ },
26
+ {
27
+ name: "with-ponder",
28
+ description: "Agent with Ponder integration for indexing blockchain data",
29
+ path: "with-ponder",
30
+ available: false,
31
+ message: "Coming soon"
32
+ },
33
+ {
34
+ name: "with-foundry",
35
+ description:
36
+ "Agent with Foundry integration for smart contract development",
37
+ path: "with-foundry",
38
+ available: false,
39
+ message: "Coming soon"
40
+ }
41
+ ]
42
+
43
+ function replaceTemplateVariables(
44
+ content: string,
45
+ variables: Record<string, string>
46
+ ): string {
47
+ return content.replace(
48
+ /\{\{(\w+)\}\}/g,
49
+ (match, key) => variables[key] || match
50
+ )
51
+ }
52
+
53
+ async function updateTemplateFiles(
54
+ projectDir: string,
55
+ projectName: string
56
+ ): Promise<void> {
57
+ const variables = { projectName }
58
+
59
+ const filesToUpdate = [
60
+ join(projectDir, "package.json"),
61
+ join(projectDir, "README.md"),
62
+ join(projectDir, "src", "agent.ts")
63
+ ]
64
+
65
+ for (const filePath of filesToUpdate) {
66
+ try {
67
+ let content = await readFile(filePath, "utf-8")
68
+
69
+ // First try template variable replacement
70
+ content = replaceTemplateVariables(content, variables)
71
+
72
+ // Special handling for package.json if template variables weren't found
73
+ if (filePath.endsWith("package.json")) {
74
+ try {
75
+ const packageJson = JSON.parse(content)
76
+ let updated = false
77
+
78
+ // If name is still a generic name, replace it
79
+ if (
80
+ packageJson.name === "agent" ||
81
+ packageJson.name === "hybrid-example-basic-agent"
82
+ ) {
83
+ packageJson.name = projectName
84
+ updated = true
85
+ }
86
+
87
+ // Ensure required scripts exist
88
+ if (!packageJson.scripts) {
89
+ packageJson.scripts = {}
90
+ }
91
+
92
+ // Add missing scripts and update old scripts to use hybrid CLI
93
+ const requiredScripts = {
94
+ clean: "hybrid clean",
95
+ dev: "hybrid dev",
96
+ build: "hybrid build",
97
+ start: "hybrid start",
98
+ keys: "hybrid keys --write",
99
+ test: "vitest",
100
+ "test:watch": "vitest --watch",
101
+ "test:coverage": "vitest --coverage",
102
+ lint: "biome lint --write",
103
+ "lint:check": "biome lint",
104
+ format: "biome format --write",
105
+ "format:check": "biome format --check",
106
+ typecheck: "tsc --noEmit"
107
+ }
108
+
109
+ for (const [scriptName, scriptCommand] of Object.entries(
110
+ requiredScripts
111
+ )) {
112
+ // Always update scripts to use the correct hybrid CLI commands
113
+ packageJson.scripts[scriptName] = scriptCommand
114
+ updated = true
115
+ }
116
+
117
+ // Update dependencies to use independent packages
118
+ if (packageJson.dependencies) {
119
+ if (packageJson.dependencies.hybrid === "workspace:*") {
120
+ packageJson.dependencies.hybrid = "latest"
121
+ updated = true
122
+ }
123
+ if (
124
+ packageJson.dependencies["@openrouter/ai-sdk-provider"] ===
125
+ "catalog:ai"
126
+ ) {
127
+ packageJson.dependencies["@openrouter/ai-sdk-provider"] = "^1.1.2"
128
+ updated = true
129
+ }
130
+ if (packageJson.dependencies.zod === "catalog:stack") {
131
+ packageJson.dependencies.zod = "^3.23.8"
132
+ updated = true
133
+ }
134
+ // Remove workspace dependencies
135
+ if (packageJson.dependencies["@hybrd/xmtp"]) {
136
+ packageJson.dependencies["@hybrd/xmtp"] = undefined
137
+ updated = true
138
+ }
139
+ }
140
+
141
+ // Update devDependencies to use independent packages
142
+ if (packageJson.devDependencies) {
143
+ const independentDevDeps = {
144
+ "@biomejs/biome": "^1.9.4",
145
+ "@types/node": "^22.0.0",
146
+ "@hybrd/cli": "latest",
147
+ tsx: "^4.20.5",
148
+ typescript: "^5.8.3",
149
+ vitest: "^3.2.4"
150
+ }
151
+
152
+ // Remove workspace dependencies
153
+ packageJson.devDependencies["@config/biome"] = undefined
154
+ packageJson.devDependencies["@config/tsconfig"] = undefined
155
+
156
+ // Add independent dependencies
157
+ for (const [depName, depVersion] of Object.entries(
158
+ independentDevDeps
159
+ )) {
160
+ packageJson.devDependencies[depName] = depVersion
161
+ }
162
+ updated = true
163
+ }
164
+
165
+ if (updated) {
166
+ content = `${JSON.stringify(packageJson, null, "\t")}\n`
167
+ }
168
+ } catch (parseError) {
169
+ console.log("āš ļø Could not parse package.json for name update")
170
+ }
171
+ }
172
+
173
+ // Special handling for README.md if template variables weren't found
174
+ if (filePath.endsWith("README.md")) {
175
+ // Replace common README title patterns with project name
176
+ content = content.replace(/^# .*$/m, `# ${projectName}`)
177
+ }
178
+
179
+ await writeFile(filePath, content, "utf-8")
180
+ } catch (error) {
181
+ console.log(
182
+ `āš ļø Could not update ${filePath.split("/").pop()}: file not found or error occurred`
183
+ )
184
+ }
185
+ }
186
+
187
+ // Ensure .env file exists - create it if missing
188
+ const envPath = join(projectDir, ".env")
189
+ try {
190
+ await readFile(envPath, "utf-8")
191
+ } catch {
192
+ // .env file doesn't exist, create it
193
+ const envContent = `# Required
194
+ OPENROUTER_API_KEY=your_openrouter_api_key_here
195
+ XMTP_WALLET_KEY=your_wallet_key_here
196
+ XMTP_DB_ENCRYPTION_KEY=your_encryption_key_here
197
+
198
+ # Optional
199
+ XMTP_ENV=dev
200
+ PORT=8454`
201
+ await writeFile(envPath, envContent, "utf-8")
202
+ console.log("šŸ“„ Created .env template file")
203
+ }
204
+
205
+ // Ensure vitest.config.ts exists - create it if missing
206
+ const vitestConfigPath = join(projectDir, "vitest.config.ts")
207
+ try {
208
+ await readFile(vitestConfigPath, "utf-8")
209
+ } catch {
210
+ // vitest.config.ts doesn't exist, create it
211
+ const vitestConfigContent = `import { defineConfig } from "vitest/config"
212
+
213
+ export default defineConfig({
214
+ test: {
215
+ environment: "node",
216
+ globals: true,
217
+ setupFiles: []
218
+ },
219
+ resolve: {
220
+ alias: {
221
+ "@": "./src"
222
+ }
223
+ }
224
+ })`
225
+ await writeFile(vitestConfigPath, vitestConfigContent, "utf-8")
226
+ console.log("šŸ“„ Created vitest.config.ts file")
227
+ }
228
+
229
+ // Ensure src/agent.test.ts exists - create it if missing
230
+ const agentTestPath = join(projectDir, "src", "agent.test.ts")
231
+ try {
232
+ await readFile(agentTestPath, "utf-8")
233
+ } catch {
234
+ // agent.test.ts doesn't exist, create it
235
+ const agentTestContent = `import { describe, expect, it } from "vitest"
236
+
237
+ // Example test file - replace with actual tests for your agent
238
+
239
+ describe("Agent", () => {
240
+ it("should be defined", () => {
241
+ // This is a placeholder test
242
+ // Add real tests for your agent functionality
243
+ expect(true).toBe(true)
244
+ })
245
+ })`
246
+ await writeFile(agentTestPath, agentTestContent, "utf-8")
247
+ console.log("šŸ“„ Created src/agent.test.ts file")
248
+ }
249
+ }
250
+
251
+ async function checkDirectoryEmpty(dirPath: string): Promise<boolean> {
252
+ try {
253
+ const files = await readdir(dirPath)
254
+ const significantFiles = files.filter(
255
+ (file) =>
256
+ !file.startsWith(".") &&
257
+ file !== "node_modules" &&
258
+ file !== "package-lock.json" &&
259
+ file !== "yarn.lock" &&
260
+ file !== "pnpm-lock.yaml"
261
+ )
262
+ return significantFiles.length === 0
263
+ } catch {
264
+ // Directory doesn't exist, so it's "empty"
265
+ return true
266
+ }
267
+ }
268
+
269
+ async function createProject(
270
+ projectName: string,
271
+ exampleName?: string
272
+ ): Promise<void> {
273
+ console.log("šŸš€ Creating a new Hybrid project...")
274
+
275
+ // Validate project name
276
+ if (!projectName || projectName.trim() === "") {
277
+ console.error("āŒ Project name is required")
278
+ process.exit(1)
279
+ }
280
+
281
+ const sanitizedName = projectName
282
+ .toLowerCase()
283
+ .replace(/[^a-z0-9-]/g, "-")
284
+ .replace(/-+/g, "-")
285
+ .replace(/^-|-$/g, "")
286
+
287
+ const currentDir = process.cwd()
288
+ const projectDir =
289
+ projectName === "." ? currentDir : join(currentDir, sanitizedName)
290
+
291
+ // Check if directory is empty
292
+ const isEmpty = await checkDirectoryEmpty(projectDir)
293
+ if (!isEmpty) {
294
+ console.error(
295
+ `āŒ Directory "${sanitizedName}" already exists and is not empty`
296
+ )
297
+ console.error(
298
+ "Please choose a different name or remove the existing directory"
299
+ )
300
+ process.exit(1)
301
+ }
302
+
303
+ // Select example if not provided
304
+ let selectedExample: Example
305
+ if (exampleName) {
306
+ const example = EXAMPLES.find((ex) => ex.name === exampleName)
307
+ if (!example) {
308
+ console.error(`āŒ Example "${exampleName}" not found`)
309
+ console.error(
310
+ `Available examples: ${EXAMPLES.map((ex) => ex.name).join(", ")}`
311
+ )
312
+ process.exit(1)
313
+ }
314
+ selectedExample = example
315
+ console.log(`šŸ“‹ Using example: ${selectedExample.name}`)
316
+ } else {
317
+ // Check if we're running in a non-interactive environment (like CI)
318
+ if (!process.stdin.isTTY) {
319
+ console.error(
320
+ "āŒ Example is required in non-interactive mode. Use --example <name>"
321
+ )
322
+ console.error(
323
+ `Available examples: ${EXAMPLES.map((ex) => ex.name).join(", ")}`
324
+ )
325
+ process.exit(1)
326
+ }
327
+
328
+ const { example } = await prompts({
329
+ type: "select",
330
+ name: "example",
331
+ message: "Which example would you like to use?",
332
+ choices: EXAMPLES.map((ex) => ({
333
+ title: ex.name,
334
+ description: ex.available
335
+ ? ex.description
336
+ : `${ex.description} (${ex.message || "Coming soon"})`,
337
+ value: ex,
338
+ disabled: !ex.available
339
+ })),
340
+ initial: 0
341
+ })
342
+
343
+ if (!example) {
344
+ console.log("āŒ No example selected. Exiting...")
345
+ process.exit(1)
346
+ }
347
+
348
+ // Check if the selected example is available
349
+ if (!example.available) {
350
+ console.log(
351
+ `āŒ Example "${example.name}" is not yet available. ${example.message || "Coming soon"}`
352
+ )
353
+ process.exit(1)
354
+ }
355
+
356
+ selectedExample = example
357
+ }
358
+
359
+ console.log(`šŸ“¦ Cloning ${selectedExample.name} example...`)
360
+
361
+ try {
362
+ // For degit, the correct syntax is: repo#branch/subdirectory
363
+ // But we need to be careful about the path construction
364
+ let degitSource: string
365
+
366
+ if (REPO.includes("#")) {
367
+ // REPO is in format "user/repo#branch"
368
+ // We need to construct: user/repo#branch/examples/basic
369
+ const [repoWithBranch] = REPO.split("/examples/") // Remove any existing path
370
+ degitSource = `${repoWithBranch}/examples/${selectedExample.name}`
371
+ } else {
372
+ // No branch specified, use default format
373
+ degitSource = `${REPO}/examples/${selectedExample.name}`
374
+ }
375
+
376
+ console.log(`šŸ” Degit source: ${degitSource}`)
377
+ const emitter = degit(degitSource)
378
+ await emitter.clone(projectDir)
379
+ console.log(`āœ… Template cloned to: ${sanitizedName}`)
380
+ } catch (error) {
381
+ console.error("āŒ Failed to clone template:", error)
382
+ process.exit(1)
383
+ }
384
+
385
+ // Update template variables
386
+ console.log("šŸ”§ Updating template variables...")
387
+ try {
388
+ await updateTemplateFiles(projectDir, sanitizedName)
389
+ console.log("āœ… Template variables updated")
390
+ } catch (error) {
391
+ console.error("āŒ Failed to update template variables:", error)
392
+ }
393
+
394
+ console.log("\nšŸŽ‰ Hybrid project created successfully!")
395
+ console.log(`\nšŸ“‚ Project created in: ${projectDir}`)
396
+ console.log("\nšŸ“‹ Next steps:")
397
+ if (projectName !== ".") {
398
+ console.log(`1. cd ${sanitizedName}`)
399
+ }
400
+ console.log(
401
+ `${projectName !== "." ? "2" : "1"}. Install dependencies (npm install, yarn install, or pnpm install)`
402
+ )
403
+ console.log(
404
+ `${projectName !== "." ? "3" : "2"}. Get your OpenRouter API key from https://openrouter.ai/keys`
405
+ )
406
+ console.log(
407
+ `${projectName !== "." ? "4" : "3"}. Add your API key to the OPENROUTER_API_KEY in .env`
408
+ )
409
+ console.log(
410
+ `${projectName !== "." ? "5" : "4"}. Set XMTP_ENV in .env (dev or production)`
411
+ )
412
+ console.log(
413
+ `${projectName !== "." ? "6" : "5"}. Generate keys: npm run keys (or yarn/pnpm equivalent)`
414
+ )
415
+ console.log(
416
+ `${projectName !== "." ? "7" : "6"}. Start development: npm run dev (or yarn/pnpm equivalent)`
417
+ )
418
+
419
+ console.log(
420
+ "\nšŸ“– For more information, see the README.md file in your project"
421
+ )
422
+ }
423
+
424
+ export async function initializeProject(): Promise<void> {
425
+ const program = new Command()
426
+
427
+ program
428
+ .name("create-hybrid")
429
+ .description("Create a new Hybrid XMTP agent project")
430
+ .version("1.2.3")
431
+ .argument("[project-name]", "Name of the project")
432
+ .option(
433
+ "-e, --example <example>",
434
+ "Example to use (basic, with-ponder, with-foundry)"
435
+ )
436
+ .action(async (projectName?: string, options?: { example?: string }) => {
437
+ let finalProjectName = projectName
438
+
439
+ // Debug logging for CI troubleshooting
440
+ if (process.env.CI) {
441
+ console.log(
442
+ `šŸ” Debug: projectName="${projectName}", options.example="${options?.example}"`
443
+ )
444
+ }
445
+
446
+ // If no project name provided or empty string, prompt for it
447
+ if (!finalProjectName || finalProjectName.trim() === "") {
448
+ // Check if we're running in a non-interactive environment (like tests)
449
+ if (!process.stdin.isTTY) {
450
+ console.error("āŒ Project name is required")
451
+ process.exit(1)
452
+ }
453
+
454
+ const { name } = await prompts({
455
+ type: "text",
456
+ name: "name",
457
+ message: "What is your project name?",
458
+ validate: (value: string) => {
459
+ if (!value || !value.trim()) {
460
+ return "Project name is required"
461
+ }
462
+ return true
463
+ }
464
+ })
465
+
466
+ if (!name) {
467
+ console.log("āŒ Project name is required. Exiting...")
468
+ process.exit(1)
469
+ }
470
+
471
+ finalProjectName = name
472
+ }
473
+
474
+ await createProject(finalProjectName as string, options?.example)
475
+ })
476
+
477
+ await program.parseAsync()
478
+ }
479
+
480
+ async function main(): Promise<void> {
481
+ const nodeVersion = process.versions.node
482
+ const [major] = nodeVersion.split(".").map(Number)
483
+ if (!major || major < 20) {
484
+ console.error("Error: Node.js version 20 or higher is required")
485
+ process.exit(1)
486
+ }
487
+
488
+ try {
489
+ await initializeProject()
490
+ } catch (error) {
491
+ console.error("Failed to initialize project:", error)
492
+ console.error(
493
+ "Error details:",
494
+ error instanceof Error ? error.stack : String(error)
495
+ )
496
+ process.exit(1)
497
+ }
498
+ }
499
+
500
+ main().catch((error) => {
501
+ console.error("CLI error:", error)
502
+ console.error(
503
+ "Error details:",
504
+ error instanceof Error ? error.stack : String(error)
505
+ )
506
+ process.exit(1)
507
+ })
package/src/types.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ declare module "degit" {
2
+ interface DegitEmitter {
3
+ clone(dest: string): Promise<void>
4
+ }
5
+
6
+ function degit(src: string): DegitEmitter
7
+ export = degit
8
+ }