@openagentmarket/create-agent 1.0.1 → 1.2.0

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.
Files changed (3) hide show
  1. package/dist/index.js +444 -98
  2. package/package.json +4 -3
  3. package/src/index.ts +467 -98
package/dist/index.js CHANGED
@@ -2,70 +2,402 @@
2
2
  import fs from 'fs-extra';
3
3
  import path from 'node:path';
4
4
  import prompts from 'prompts';
5
- import { fileURLToPath } from 'node:url';
6
5
  import kleur from 'kleur';
7
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ import { Wallet } from 'ethers';
8
7
  async function main() {
9
- console.log(kleur.bold().cyan('\nšŸš€ Create OpenAgent Project\n'));
8
+ console.log(kleur.bold().cyan('\nšŸš€ OpenAgent Market — Project Scaffolder\n'));
9
+ // Step 1: Choose role
10
+ const { role } = await prompts({
11
+ type: 'select',
12
+ name: 'role',
13
+ message: 'What do you want to do?',
14
+ choices: [
15
+ { title: 'šŸ”Ø Worker — Build an agent that handles tasks', value: 'worker' },
16
+ { title: 'šŸ’¼ Hirer — Hire agents to do work for you', value: 'hirer' }
17
+ ]
18
+ });
19
+ if (!role) {
20
+ console.log(kleur.red('Cancelled.'));
21
+ process.exit(1);
22
+ }
23
+ if (role === 'hirer') {
24
+ await scaffoldHirer();
25
+ }
26
+ else {
27
+ await scaffoldWorker();
28
+ }
29
+ }
30
+ // ── Hirer Flow (zero-config) ─────────────────────────────────────────────────
31
+ async function scaffoldHirer() {
32
+ const { folderName } = await prompts({
33
+ type: 'text',
34
+ name: 'folderName',
35
+ message: 'Folder name:',
36
+ initial: 'my-hirer'
37
+ });
38
+ if (!folderName) {
39
+ console.log(kleur.red('Cancelled.'));
40
+ process.exit(1);
41
+ }
42
+ const dirName = folderName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
43
+ const targetDir = path.join(process.cwd(), dirName);
44
+ if (fs.existsSync(targetDir)) {
45
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
46
+ process.exit(1);
47
+ }
48
+ console.log(`\nCreating hirer project in ${kleur.green(targetDir)}...\n`);
49
+ fs.mkdirSync(targetDir, { recursive: true });
50
+ // Auto-generate wallet
51
+ const wallet = Wallet.createRandom();
52
+ const mnemonic = wallet.mnemonic.phrase;
53
+ // 1. package.json
54
+ const packageJson = {
55
+ name: dirName,
56
+ version: "1.0.0",
57
+ description: "Hire agents on OpenAgent Market",
58
+ type: "module",
59
+ scripts: {
60
+ start: "tsx index.ts",
61
+ dev: "tsx watch index.ts"
62
+ },
63
+ dependencies: {
64
+ "@openagentmarket/nodejs": "^1.0.0",
65
+ ethers: "^6.10.0",
66
+ dotenv: "^16.4.0"
67
+ },
68
+ devDependencies: {
69
+ tsx: "^4.7.0",
70
+ typescript: "^5.3.3",
71
+ "@types/node": "^20.11.0"
72
+ }
73
+ };
74
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
75
+ // 2. tsconfig.json
76
+ writeSharedTsConfig(targetDir);
77
+ // 3. .env (with auto-generated mnemonic)
78
+ fs.writeFileSync(path.join(targetDir, '.env'), `MNEMONIC="${mnemonic}"\n`);
79
+ // 4. .gitignore
80
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
81
+ // 5. index.ts — persistent XMTP chat CLI
82
+ const indexTs = [
83
+ `import { OpenAgentClient } from '@openagentmarket/nodejs';`,
84
+ `import * as readline from 'node:readline';`,
85
+ `import 'dotenv/config';`,
86
+ ``,
87
+ `const DISCOVER_URL = "https://openagent.market/discover?protocol=openagentmarket";`,
88
+ ``,
89
+ `async function main() {`,
90
+ ` const client = await OpenAgentClient.create({`,
91
+ ` mnemonic: process.env.MNEMONIC,`,
92
+ ` env: "production"`,
93
+ ` });`,
94
+ ``,
95
+ ` console.log("");`,
96
+ ` console.log("āœ… Connected to XMTP");`,
97
+ ` console.log(" Wallet: " + client.getAddress());`,
98
+ ` console.log("");`,
99
+ ` printHelp();`,
100
+ ``,
101
+ ` // ── Start REPL ──`,
102
+ ` const rl = readline.createInterface({`,
103
+ ` input: process.stdin,`,
104
+ ` output: process.stdout,`,
105
+ ` prompt: "> "`,
106
+ ` });`,
107
+ ``,
108
+ ` // ── Background message listener ──`,
109
+ ` await client.streamAllMessages((sender, content, convId) => {`,
110
+ ` if (!content || !content.trim()) return;`,
111
+ ` process.stdout.write("\\r\\x1b[K");`,
112
+ ` console.log("šŸ“Ø [" + sender.slice(0, 10) + "...] " + content);`,
113
+ ` rl.prompt(true);`,
114
+ ` });`,
115
+ ``,
116
+ ` rl.prompt();`,
117
+ ``,
118
+ ` rl.on("line", async (line: string) => {`,
119
+ ` const input = line.trim();`,
120
+ ` if (!input) { rl.prompt(); return; }`,
121
+ ``,
122
+ ` try {`,
123
+ ` if (input === "/help") {`,
124
+ ` printHelp();`,
125
+ ` }`,
126
+ ` else if (input === "/discover") {`,
127
+ ` await discoverAgents();`,
128
+ ` }`,
129
+ ` else if (input.startsWith("/chat ")) {`,
130
+ ` const parts = input.slice(6).trim().split(" ");`,
131
+ ` const address = parts[0];`,
132
+ ` const message = parts.slice(1).join(" ");`,
133
+ ` if (!address || !message) {`,
134
+ ` console.log("Usage: /chat <agent-address> <message>");`,
135
+ ` } else {`,
136
+ ` console.log("šŸ“¤ Sending to " + address + "...");`,
137
+ ` const reply = await client.chat(address, message);`,
138
+ ` if (reply.success) {`,
139
+ ` console.log("šŸ“© Reply:", reply.result ?? reply.raw ?? "(empty)");`,
140
+ ` } else {`,
141
+ ` console.log("āŒ Error:", reply.error ?? "Unknown error");`,
142
+ ` }`,
143
+ ` }`,
144
+ ` }`,
145
+ ` else if (input.startsWith("/task ")) {`,
146
+ ` const parts = input.slice(6).trim().split(" ");`,
147
+ ` const address = parts[0];`,
148
+ ` const method = parts[1];`,
149
+ ` const paramsStr = parts.slice(2).join(" ") || "{}";`,
150
+ ` if (!address || !method) {`,
151
+ ` console.log("Usage: /task <agent-address> <method> [json-params]");`,
152
+ ` } else {`,
153
+ ` let params: any = {};`,
154
+ ` try { params = JSON.parse(paramsStr); } catch { params = { input: paramsStr }; }`,
155
+ ` console.log("šŸ“¤ Sending task '" + method + "' to " + address + "...");`,
156
+ ` const result = await client.sendTask(address, method, params);`,
157
+ ` if (result.paymentRequired) {`,
158
+ ` console.log("šŸ’° Payment required:", result.paymentRequired);`,
159
+ ` } else if (result.success) {`,
160
+ ` console.log("āœ… Result:", JSON.stringify(result.result, null, 2));`,
161
+ ` } else {`,
162
+ ` console.log("āŒ Error:", result.error);`,
163
+ ` }`,
164
+ ` }`,
165
+ ` }`,
166
+ ` else if (input === "/quit" || input === "/exit") {`,
167
+ ` console.log("Bye! šŸ‘‹");`,
168
+ ` process.exit(0);`,
169
+ ` }`,
170
+ ` else {`,
171
+ ` console.log("Unknown command. Type /help for available commands.");`,
172
+ ` }`,
173
+ ` } catch (err: any) {`,
174
+ ` console.error("āŒ Error:", err.message || err);`,
175
+ ` }`,
176
+ ``,
177
+ ` rl.prompt();`,
178
+ ` });`,
179
+ ``,
180
+ ` rl.on("close", () => {`,
181
+ ` console.log("\\nBye! šŸ‘‹");`,
182
+ ` process.exit(0);`,
183
+ ` });`,
184
+ `}`,
185
+ ``,
186
+ `function printHelp() {`,
187
+ ` console.log("šŸ“– Commands:");`,
188
+ ` console.log(" /discover — Browse available agents");`,
189
+ ` console.log(" /chat <agent-address> <message> — Send a chat message");`,
190
+ ` console.log(" /task <agent-address> <method> [params] — Send a task (params as JSON)");`,
191
+ ` console.log(" /help — Show this help");`,
192
+ ` console.log(" /quit — Exit");`,
193
+ `}`,
194
+ ``,
195
+ `async function discoverAgents() {`,
196
+ ` console.log("šŸ” Fetching agents from OpenAgent Market...\\n");`,
197
+ ` try {`,
198
+ ` const res = await fetch(DISCOVER_URL);`,
199
+ ` const data = await res.json() as any;`,
200
+ ` if (!data.success || !data.items?.length) {`,
201
+ ` console.log("No agents found.");`,
202
+ ` return;`,
203
+ ` }`,
204
+ ` for (const item of data.items) {`,
205
+ ` const reg = item.registrationFile || {};`,
206
+ ` const name = reg.name || "Unknown";`,
207
+ ` const desc = reg.description || "";`,
208
+ ` // Find xmtpAddress from metadata array`,
209
+ ` const meta = item.metadata || [];`,
210
+ ` let addr = item.owner || "?";`,
211
+ ` const walletMeta = meta.find((m: any) => m.key === "agentWallet");`,
212
+ ` if (walletMeta) addr = walletMeta.value;`,
213
+ ` const agentId = item.agentId || "";`,
214
+ ` console.log(" šŸ¤– " + name + (agentId ? " (#" + agentId + ")" : ""));`,
215
+ ` if (desc) console.log(" " + desc.slice(0, 120) + (desc.length > 120 ? "..." : ""));`,
216
+ ` console.log(" Address: " + addr);`,
217
+ ` console.log("");`,
218
+ ` }`,
219
+ ` console.log("Total: " + data.items.length + " agent(s)");`,
220
+ ` if (data.hasMore) console.log("(more agents available)");`,
221
+ ` } catch (err: any) {`,
222
+ ` console.error("Failed to fetch agents:", err.message);`,
223
+ ` }`,
224
+ `}`,
225
+ ``,
226
+ `main().catch(console.error);`,
227
+ ].join('\n');
228
+ fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
229
+ // 6. README.md
230
+ const readmeLines = [
231
+ `# OpenAgent Hirer CLI`,
232
+ ``,
233
+ `A persistent XMTP chat CLI for hiring agents on [OpenAgent Market](https://openagent.market).`,
234
+ ``,
235
+ `## Getting Started`,
236
+ ``,
237
+ '```bash',
238
+ `npm install`,
239
+ `npm start`,
240
+ '```',
241
+ ``,
242
+ `## Commands`,
243
+ ``,
244
+ `| Command | Description |`,
245
+ `|---------|-------------|`,
246
+ '| `/discover` | Browse available agents from the marketplace |',
247
+ '| `/chat <address> <message>` | Send a plain text message to an agent |',
248
+ '| `/task <address> <method> [json]` | Send a JSON-RPC task to an agent |',
249
+ '| `/help` | Show available commands |',
250
+ '| `/quit` | Exit the CLI |',
251
+ ``,
252
+ `## Examples`,
253
+ ``,
254
+ '```',
255
+ `> /discover`,
256
+ `> /chat 0xAgentAddr What can you do?`,
257
+ `> /task 0xAgentAddr say_hello {"name": "World"}`,
258
+ '```',
259
+ ``,
260
+ `## Environment Variables`,
261
+ ``,
262
+ `| Variable | Description |`,
263
+ `|----------|-------------|`,
264
+ '| `MNEMONIC` | Wallet seed phrase (auto-generated for you) |',
265
+ ``,
266
+ `## Resources`,
267
+ ``,
268
+ `- [Discover API](https://openagent.market/discover?protocol=openagentmarket)`,
269
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
270
+ `- [Explorer](https://8004agents.ai)`,
271
+ ].join('\n');
272
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
273
+ // Print success
274
+ console.log(kleur.green('āœ… Hirer CLI project created!\n'));
275
+ console.log(` ${kleur.bold('Wallet address:')} ${kleur.cyan(wallet.address)}`);
276
+ console.log(` ${kleur.bold('Mnemonic:')} saved to ${kleur.yellow('.env')}\n`);
277
+ console.log(kleur.bold('Next steps:'));
278
+ console.log(` cd ${dirName}`);
279
+ console.log(' npm install');
280
+ console.log(' npm start');
281
+ console.log(' # Then type /discover to find agents\n');
282
+ }
283
+ // ── Worker Flow (interactive) ────────────────────────────────────────────────
284
+ async function scaffoldWorker() {
10
285
  const response = await prompts([
11
286
  {
12
287
  type: 'text',
13
- name: 'projectName',
14
- message: 'What is the name of your agent?',
288
+ name: 'agentName',
289
+ message: 'What is your agent\'s name?',
15
290
  initial: 'my-agent'
291
+ },
292
+ {
293
+ type: 'text',
294
+ name: 'description',
295
+ message: 'Describe your agent:',
296
+ initial: 'A freshly created OpenAgent.'
297
+ },
298
+ {
299
+ type: 'text',
300
+ name: 'skills',
301
+ message: 'Skills (comma-separated):',
302
+ initial: 'say_hello'
303
+ },
304
+ {
305
+ type: 'confirm',
306
+ name: 'includeRegistration',
307
+ message: 'Include on-chain registration (ERC-8004)?',
308
+ initial: false
309
+ },
310
+ {
311
+ type: 'confirm',
312
+ name: 'includePayments',
313
+ message: 'Include x402 payments?',
314
+ initial: false
16
315
  }
17
316
  ]);
18
- const { projectName } = response;
19
- if (!projectName) {
317
+ const { agentName, description, skills, includeRegistration, includePayments } = response;
318
+ if (!agentName) {
20
319
  console.log(kleur.red('Cancelled.'));
21
320
  process.exit(1);
22
321
  }
23
- const targetDir = path.join(process.cwd(), projectName);
322
+ const skillList = skills.split(',').map((s) => s.trim()).filter(Boolean);
323
+ const dirName = agentName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
324
+ const targetDir = path.join(process.cwd(), dirName);
24
325
  if (fs.existsSync(targetDir)) {
25
- console.log(kleur.red(`Error: Directory ${projectName} already exists!`));
326
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
26
327
  process.exit(1);
27
328
  }
28
- console.log(`\nCreating project in ${kleur.green(targetDir)}...\n`);
329
+ console.log(`\nCreating agent in ${kleur.green(targetDir)}...\n`);
29
330
  fs.mkdirSync(targetDir, { recursive: true });
30
331
  // 1. package.json
31
332
  const packageJson = {
32
- name: projectName,
333
+ name: dirName,
33
334
  version: "1.0.0",
34
- description: "An OpenAgent bot",
335
+ description: description,
35
336
  type: "module",
36
337
  scripts: {
37
- "start": "tsx index.ts",
38
- "dev": "tsx watch index.ts"
338
+ start: "tsx index.ts",
339
+ dev: "tsx watch index.ts"
39
340
  },
40
341
  dependencies: {
41
342
  "@openagentmarket/nodejs": "^1.0.0",
42
- "ethers": "^6.10.0",
43
- "dotenv": "^16.4.0"
343
+ ethers: "^6.10.0",
344
+ dotenv: "^16.4.0"
44
345
  },
45
346
  devDependencies: {
46
- "tsx": "^4.7.0",
47
- "typescript": "^5.3.3",
347
+ tsx: "^4.7.0",
348
+ typescript: "^5.3.3",
48
349
  "@types/node": "^20.11.0"
49
350
  }
50
351
  };
51
352
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
52
353
  // 2. tsconfig.json
53
- const tsConfig = {
54
- "compilerOptions": {
55
- "target": "ES2022",
56
- "module": "NodeNext",
57
- "moduleResolution": "NodeNext",
58
- "strict": true,
59
- "esModuleInterop": true,
60
- "skipLibCheck": true,
61
- "forceConsistentCasingInFileNames": true
62
- }
63
- };
64
- fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
354
+ writeSharedTsConfig(targetDir);
65
355
  // 3. .env
66
- fs.writeFileSync(path.join(targetDir, '.env'), 'MNEMONIC="your twelve word mnemonic phrase here"\n');
67
- fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\n');
68
- // 4. index.ts (Template)
356
+ let envContent = 'MNEMONIC="your twelve word mnemonic phrase here"\n';
357
+ if (includeRegistration) {
358
+ envContent += 'REGISTRATION_PRIVATE_KEY=""\n';
359
+ envContent += 'PINATA_JWT=""\n';
360
+ }
361
+ fs.writeFileSync(path.join(targetDir, '.env'), envContent);
362
+ // 4. .gitignore
363
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
364
+ // 5. index.ts
365
+ const taskHandlers = skillList.map((skill) => `
366
+ agent.onTask("${skill}", async (input) => {
367
+ return { message: \`Handled ${skill} with \${JSON.stringify(input)}\` };
368
+ });`).join('\n');
369
+ const paymentConfig = includePayments ? `
370
+ payment: {
371
+ amount: 5,
372
+ currency: "USDC",
373
+ recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
374
+ },` : '';
375
+ const registrationBlock = includeRegistration ? `
376
+
377
+ // ── Register on-chain (optional) ──
378
+ async function registerAgent(agent: any) {
379
+ const result = await agent.register(
380
+ {
381
+ name: "${agentName}",
382
+ description: "${description}",
383
+ image: "https://example.com/avatar.png",
384
+ metadata: {
385
+ skills: ${JSON.stringify(skillList)},
386
+ pricing: { amount: "5.0", currency: "USDC", chain: "base" },
387
+ category: "utility",
388
+ tags: ["openagent"]
389
+ }
390
+ },
391
+ {
392
+ privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
393
+ pinataJwt: process.env.PINATA_JWT!,
394
+ }
395
+ );
396
+ console.log("Registered:", result);
397
+ }` : '';
398
+ const registrationCall = includeRegistration
399
+ ? '\n // Uncomment to register on-chain:\n // await registerAgent(agent);\n'
400
+ : '';
69
401
  const indexTs = `import { OpenAgent } from '@openagentmarket/nodejs';
70
402
  import 'dotenv/config';
71
403
 
@@ -80,77 +412,91 @@ async function main() {
80
412
  mnemonic,
81
413
  env: "production",
82
414
  card: {
83
- name: "${projectName}",
84
- description: "A freshly created OpenAgent.",
85
- skills: ["say_hello"]
86
- },
87
- payment: {
88
- amount: 0.001, // 0.001 ETH
89
- currency: "ETH",
90
- recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
91
- }
92
- });
93
-
94
- agent.onTask("say_hello", async (input) => {
95
- return { message: \`Hello \${input.name || "World"}!\` };
415
+ name: "${agentName}",
416
+ description: "${description}",
417
+ skills: ${JSON.stringify(skillList)}
418
+ },${paymentConfig}
96
419
  });
420
+ ${taskHandlers}
97
421
 
98
- await agent.start();
422
+ await agent.start();${registrationCall}
99
423
  }
100
-
424
+ ${registrationBlock}
101
425
  main().catch(console.error);
102
426
  `;
103
427
  fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
104
- // 5. README.md
105
- const readme = `# ${projectName}
106
-
107
- ## Getting Started
108
-
109
- This project was scaffolded with \`@openagentmarket/create-agent\`.
110
-
111
- ### Prerequisites
112
-
113
- - Node.js (v18 or higher)
114
- - pnpm (recommended) or npm
115
-
116
- ### Installation
117
-
118
- \`\`\`bash
119
- pnpm install
120
- \`\`\`
121
-
122
- ### Configuration
123
-
124
- 1. Copy the example environment file:
125
- \`\`\`bash
126
- cp .env.example .env
127
- \`\`\`
128
- 2. Edit \`.env\` and add your 12-word mnemonic phrase.
129
-
130
- ### Running the Agent
131
-
132
- \`\`\`bash
133
- # Development mode with hot-reload
134
- pnpm dev
135
-
136
- # Production build
137
- pnpm build
138
- pnpm start
139
- \`\`\`
140
-
141
- ## Agent Details
142
-
143
- - **Name**: ${projectName}
144
- - **Framework**: @openagentmarket/nodejs
145
- - **Network**: Base (Coinbase L2)
146
- `;
147
- fs.writeFileSync(path.join(targetDir, 'README.md'), readme);
148
- console.log(kleur.green('Success! Project created.'));
149
- console.log('\nNext steps:');
150
- console.log(` cd ${projectName}`);
151
- console.log(' pnpm install');
152
- console.log(' # Update .env with your mnemonic');
153
- console.log(' pnpm start\n');
428
+ // 6. README.md
429
+ const readmeLines = [
430
+ `# ${agentName}`,
431
+ ``,
432
+ description,
433
+ ``,
434
+ `## Getting Started`,
435
+ ``,
436
+ '```bash',
437
+ `cd ${dirName}`,
438
+ `npm install`,
439
+ '```',
440
+ ``,
441
+ `### Configuration`,
442
+ ``,
443
+ `Edit \`.env\` and set your 12-word mnemonic phrase.`,
444
+ includeRegistration ? `\nFor on-chain registration, also set \`REGISTRATION_PRIVATE_KEY\` and \`PINATA_JWT\`.\n` : '',
445
+ `### Running`,
446
+ ``,
447
+ '```bash',
448
+ `# Development mode with hot-reload`,
449
+ `npm run dev`,
450
+ ``,
451
+ `# Production`,
452
+ `npm start`,
453
+ '```',
454
+ ``,
455
+ `## Agent Details`,
456
+ ``,
457
+ `| Property | Value |`,
458
+ `|----------|-------|`,
459
+ `| **Name** | ${agentName} |`,
460
+ `| **Skills** | ${skillList.join(', ')} |`,
461
+ `| **Payments** | ${includePayments ? 'Enabled (x402)' : 'Disabled'} |`,
462
+ `| **Registration** | ${includeRegistration ? 'Included' : 'Not included'} |`,
463
+ ``,
464
+ `## Environment Variables`,
465
+ ``,
466
+ `| Variable | Required | Description |`,
467
+ `|----------|----------|-------------|`,
468
+ '| `MNEMONIC` | Yes | Agent wallet seed phrase |',
469
+ includeRegistration ? '| `REGISTRATION_PRIVATE_KEY` | For registration | Wallet paying gas |\n| `PINATA_JWT` | For registration | IPFS metadata upload |' : '',
470
+ ``,
471
+ `## Resources`,
472
+ ``,
473
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
474
+ `- [Explorer](https://8004agents.ai)`,
475
+ `- [GitHub](https://github.com/openagentmarket)`,
476
+ ].join('\n');
477
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
478
+ // Print success
479
+ console.log(kleur.green('āœ… Agent project created!\n'));
480
+ console.log(kleur.bold('Next steps:'));
481
+ console.log(` cd ${dirName}`);
482
+ console.log(' npm install');
483
+ console.log(' # Set your MNEMONIC in .env');
484
+ console.log(' npm start\n');
485
+ }
486
+ // ── Shared helpers ───────────────────────────────────────────────────────────
487
+ function writeSharedTsConfig(targetDir) {
488
+ const tsConfig = {
489
+ compilerOptions: {
490
+ target: "ES2022",
491
+ module: "NodeNext",
492
+ moduleResolution: "NodeNext",
493
+ strict: true,
494
+ esModuleInterop: true,
495
+ skipLibCheck: true,
496
+ forceConsistentCasingInFileNames: true
497
+ }
498
+ };
499
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
154
500
  }
155
501
  main().catch((err) => {
156
502
  console.error(err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openagentmarket/create-agent",
3
- "version": "1.0.1",
4
- "description": "CLI to scaffold a new OpenAgent project",
3
+ "version": "1.2.0",
4
+ "description": "CLI to scaffold a new OpenAgent project (hirer or worker)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-openagent": "./dist/index.js"
@@ -14,7 +14,8 @@
14
14
  "dependencies": {
15
15
  "prompts": "^2.4.2",
16
16
  "kleur": "^4.1.5",
17
- "fs-extra": "^11.2.0"
17
+ "fs-extra": "^11.2.0",
18
+ "ethers": "^6.10.0"
18
19
  },
19
20
  "devDependencies": {
20
21
  "@types/prompts": "^2.4.9",
package/src/index.ts CHANGED
@@ -2,81 +2,434 @@
2
2
  import fs from 'fs-extra';
3
3
  import path from 'node:path';
4
4
  import prompts from 'prompts';
5
- import { fileURLToPath } from 'node:url';
6
5
  import kleur from 'kleur';
7
-
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ import { Wallet } from 'ethers';
9
7
 
10
8
  async function main() {
11
- console.log(kleur.bold().cyan('\nšŸš€ Create OpenAgent Project\n'));
9
+ console.log(kleur.bold().cyan('\nšŸš€ OpenAgent Market — Project Scaffolder\n'));
10
+
11
+ // Step 1: Choose role
12
+ const { role } = await prompts({
13
+ type: 'select',
14
+ name: 'role',
15
+ message: 'What do you want to do?',
16
+ choices: [
17
+ { title: 'šŸ”Ø Worker — Build an agent that handles tasks', value: 'worker' },
18
+ { title: 'šŸ’¼ Hirer — Hire agents to do work for you', value: 'hirer' }
19
+ ]
20
+ });
21
+
22
+ if (!role) {
23
+ console.log(kleur.red('Cancelled.'));
24
+ process.exit(1);
25
+ }
26
+
27
+ if (role === 'hirer') {
28
+ await scaffoldHirer();
29
+ } else {
30
+ await scaffoldWorker();
31
+ }
32
+ }
33
+
34
+ // ── Hirer Flow (zero-config) ─────────────────────────────────────────────────
35
+
36
+ async function scaffoldHirer() {
37
+ const { folderName } = await prompts({
38
+ type: 'text',
39
+ name: 'folderName',
40
+ message: 'Folder name:',
41
+ initial: 'my-hirer'
42
+ });
12
43
 
44
+ if (!folderName) {
45
+ console.log(kleur.red('Cancelled.'));
46
+ process.exit(1);
47
+ }
48
+
49
+ const dirName = folderName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
50
+ const targetDir = path.join(process.cwd(), dirName);
51
+
52
+ if (fs.existsSync(targetDir)) {
53
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
54
+ process.exit(1);
55
+ }
56
+
57
+ console.log(`\nCreating hirer project in ${kleur.green(targetDir)}...\n`);
58
+ fs.mkdirSync(targetDir, { recursive: true });
59
+
60
+ // Auto-generate wallet
61
+ const wallet = Wallet.createRandom();
62
+ const mnemonic = wallet.mnemonic!.phrase;
63
+
64
+ // 1. package.json
65
+ const packageJson = {
66
+ name: dirName,
67
+ version: "1.0.0",
68
+ description: "Hire agents on OpenAgent Market",
69
+ type: "module",
70
+ scripts: {
71
+ start: "tsx index.ts",
72
+ dev: "tsx watch index.ts"
73
+ },
74
+ dependencies: {
75
+ "@openagentmarket/nodejs": "^1.0.0",
76
+ ethers: "^6.10.0",
77
+ dotenv: "^16.4.0"
78
+ },
79
+ devDependencies: {
80
+ tsx: "^4.7.0",
81
+ typescript: "^5.3.3",
82
+ "@types/node": "^20.11.0"
83
+ }
84
+ };
85
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
86
+
87
+ // 2. tsconfig.json
88
+ writeSharedTsConfig(targetDir);
89
+
90
+ // 3. .env (with auto-generated mnemonic)
91
+ fs.writeFileSync(path.join(targetDir, '.env'), `MNEMONIC="${mnemonic}"\n`);
92
+
93
+ // 4. .gitignore
94
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
95
+
96
+ // 5. index.ts — persistent XMTP chat CLI
97
+ const indexTs = [
98
+ `import { OpenAgentClient } from '@openagentmarket/nodejs';`,
99
+ `import * as readline from 'node:readline';`,
100
+ `import 'dotenv/config';`,
101
+ ``,
102
+ `const DISCOVER_URL = "https://openagent.market/discover?protocol=openagentmarket";`,
103
+ ``,
104
+ `async function main() {`,
105
+ ` const client = await OpenAgentClient.create({`,
106
+ ` mnemonic: process.env.MNEMONIC,`,
107
+ ` env: "production"`,
108
+ ` });`,
109
+ ``,
110
+ ` console.log("");`,
111
+ ` console.log("āœ… Connected to XMTP");`,
112
+ ` console.log(" Wallet: " + client.getAddress());`,
113
+ ` console.log("");`,
114
+ ` printHelp();`,
115
+ ``,
116
+ ` // ── Start REPL ──`,
117
+ ` const rl = readline.createInterface({`,
118
+ ` input: process.stdin,`,
119
+ ` output: process.stdout,`,
120
+ ` prompt: "> "`,
121
+ ` });`,
122
+ ``,
123
+ ` // ── Background message listener ──`,
124
+ ` await client.streamAllMessages((sender, content, convId) => {`,
125
+ ` if (!content || !content.trim()) return;`,
126
+ ` process.stdout.write("\\r\\x1b[K");`,
127
+ ` console.log("šŸ“Ø [" + sender.slice(0, 10) + "...] " + content);`,
128
+ ` rl.prompt(true);`,
129
+ ` });`,
130
+ ``,
131
+ ` rl.prompt();`,
132
+ ``,
133
+ ` rl.on("line", async (line: string) => {`,
134
+ ` const input = line.trim();`,
135
+ ` if (!input) { rl.prompt(); return; }`,
136
+ ``,
137
+ ` try {`,
138
+ ` if (input === "/help") {`,
139
+ ` printHelp();`,
140
+ ` }`,
141
+ ` else if (input === "/discover") {`,
142
+ ` await discoverAgents();`,
143
+ ` }`,
144
+ ` else if (input.startsWith("/chat ")) {`,
145
+ ` const parts = input.slice(6).trim().split(" ");`,
146
+ ` const address = parts[0];`,
147
+ ` const message = parts.slice(1).join(" ");`,
148
+ ` if (!address || !message) {`,
149
+ ` console.log("Usage: /chat <agent-address> <message>");`,
150
+ ` } else {`,
151
+ ` console.log("šŸ“¤ Sending to " + address + "...");`,
152
+ ` const reply = await client.chat(address, message);`,
153
+ ` if (reply.success) {`,
154
+ ` console.log("šŸ“© Reply:", reply.result ?? reply.raw ?? "(empty)");`,
155
+ ` } else {`,
156
+ ` console.log("āŒ Error:", reply.error ?? "Unknown error");`,
157
+ ` }`,
158
+ ` }`,
159
+ ` }`,
160
+ ` else if (input.startsWith("/task ")) {`,
161
+ ` const parts = input.slice(6).trim().split(" ");`,
162
+ ` const address = parts[0];`,
163
+ ` const method = parts[1];`,
164
+ ` const paramsStr = parts.slice(2).join(" ") || "{}";`,
165
+ ` if (!address || !method) {`,
166
+ ` console.log("Usage: /task <agent-address> <method> [json-params]");`,
167
+ ` } else {`,
168
+ ` let params: any = {};`,
169
+ ` try { params = JSON.parse(paramsStr); } catch { params = { input: paramsStr }; }`,
170
+ ` console.log("šŸ“¤ Sending task '" + method + "' to " + address + "...");`,
171
+ ` const result = await client.sendTask(address, method, params);`,
172
+ ` if (result.paymentRequired) {`,
173
+ ` console.log("šŸ’° Payment required:", result.paymentRequired);`,
174
+ ` } else if (result.success) {`,
175
+ ` console.log("āœ… Result:", JSON.stringify(result.result, null, 2));`,
176
+ ` } else {`,
177
+ ` console.log("āŒ Error:", result.error);`,
178
+ ` }`,
179
+ ` }`,
180
+ ` }`,
181
+ ` else if (input === "/quit" || input === "/exit") {`,
182
+ ` console.log("Bye! šŸ‘‹");`,
183
+ ` process.exit(0);`,
184
+ ` }`,
185
+ ` else {`,
186
+ ` console.log("Unknown command. Type /help for available commands.");`,
187
+ ` }`,
188
+ ` } catch (err: any) {`,
189
+ ` console.error("āŒ Error:", err.message || err);`,
190
+ ` }`,
191
+ ``,
192
+ ` rl.prompt();`,
193
+ ` });`,
194
+ ``,
195
+ ` rl.on("close", () => {`,
196
+ ` console.log("\\nBye! šŸ‘‹");`,
197
+ ` process.exit(0);`,
198
+ ` });`,
199
+ `}`,
200
+ ``,
201
+ `function printHelp() {`,
202
+ ` console.log("šŸ“– Commands:");`,
203
+ ` console.log(" /discover — Browse available agents");`,
204
+ ` console.log(" /chat <agent-address> <message> — Send a chat message");`,
205
+ ` console.log(" /task <agent-address> <method> [params] — Send a task (params as JSON)");`,
206
+ ` console.log(" /help — Show this help");`,
207
+ ` console.log(" /quit — Exit");`,
208
+ `}`,
209
+ ``,
210
+ `async function discoverAgents() {`,
211
+ ` console.log("šŸ” Fetching agents from OpenAgent Market...\\n");`,
212
+ ` try {`,
213
+ ` const res = await fetch(DISCOVER_URL);`,
214
+ ` const data = await res.json() as any;`,
215
+ ` if (!data.success || !data.items?.length) {`,
216
+ ` console.log("No agents found.");`,
217
+ ` return;`,
218
+ ` }`,
219
+ ` for (const item of data.items) {`,
220
+ ` const reg = item.registrationFile || {};`,
221
+ ` const name = reg.name || "Unknown";`,
222
+ ` const desc = reg.description || "";`,
223
+ ` // Find xmtpAddress from metadata array`,
224
+ ` const meta = item.metadata || [];`,
225
+ ` let addr = item.owner || "?";`,
226
+ ` const walletMeta = meta.find((m: any) => m.key === "agentWallet");`,
227
+ ` if (walletMeta) addr = walletMeta.value;`,
228
+ ` const agentId = item.agentId || "";`,
229
+ ` console.log(" šŸ¤– " + name + (agentId ? " (#" + agentId + ")" : ""));`,
230
+ ` if (desc) console.log(" " + desc.slice(0, 120) + (desc.length > 120 ? "..." : ""));`,
231
+ ` console.log(" Address: " + addr);`,
232
+ ` console.log("");`,
233
+ ` }`,
234
+ ` console.log("Total: " + data.items.length + " agent(s)");`,
235
+ ` if (data.hasMore) console.log("(more agents available)");`,
236
+ ` } catch (err: any) {`,
237
+ ` console.error("Failed to fetch agents:", err.message);`,
238
+ ` }`,
239
+ `}`,
240
+ ``,
241
+ `main().catch(console.error);`,
242
+ ].join('\n');
243
+ fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
244
+
245
+ // 6. README.md
246
+ const readmeLines = [
247
+ `# OpenAgent Hirer CLI`,
248
+ ``,
249
+ `A persistent XMTP chat CLI for hiring agents on [OpenAgent Market](https://openagent.market).`,
250
+ ``,
251
+ `## Getting Started`,
252
+ ``,
253
+ '```bash',
254
+ `npm install`,
255
+ `npm start`,
256
+ '```',
257
+ ``,
258
+ `## Commands`,
259
+ ``,
260
+ `| Command | Description |`,
261
+ `|---------|-------------|`,
262
+ '| `/discover` | Browse available agents from the marketplace |',
263
+ '| `/chat <address> <message>` | Send a plain text message to an agent |',
264
+ '| `/task <address> <method> [json]` | Send a JSON-RPC task to an agent |',
265
+ '| `/help` | Show available commands |',
266
+ '| `/quit` | Exit the CLI |',
267
+ ``,
268
+ `## Examples`,
269
+ ``,
270
+ '```',
271
+ `> /discover`,
272
+ `> /chat 0xAgentAddr What can you do?`,
273
+ `> /task 0xAgentAddr say_hello {"name": "World"}`,
274
+ '```',
275
+ ``,
276
+ `## Environment Variables`,
277
+ ``,
278
+ `| Variable | Description |`,
279
+ `|----------|-------------|`,
280
+ '| `MNEMONIC` | Wallet seed phrase (auto-generated for you) |',
281
+ ``,
282
+ `## Resources`,
283
+ ``,
284
+ `- [Discover API](https://openagent.market/discover?protocol=openagentmarket)`,
285
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
286
+ `- [Explorer](https://8004agents.ai)`,
287
+ ].join('\n');
288
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
289
+
290
+ // Print success
291
+ console.log(kleur.green('āœ… Hirer CLI project created!\n'));
292
+ console.log(` ${kleur.bold('Wallet address:')} ${kleur.cyan(wallet.address)}`);
293
+ console.log(` ${kleur.bold('Mnemonic:')} saved to ${kleur.yellow('.env')}\n`);
294
+ console.log(kleur.bold('Next steps:'));
295
+ console.log(` cd ${dirName}`);
296
+ console.log(' npm install');
297
+ console.log(' npm start');
298
+ console.log(' # Then type /discover to find agents\n');
299
+ }
300
+
301
+ // ── Worker Flow (interactive) ────────────────────────────────────────────────
302
+
303
+ async function scaffoldWorker() {
13
304
  const response = await prompts([
14
305
  {
15
306
  type: 'text',
16
- name: 'projectName',
17
- message: 'What is the name of your agent?',
307
+ name: 'agentName',
308
+ message: 'What is your agent\'s name?',
18
309
  initial: 'my-agent'
310
+ },
311
+ {
312
+ type: 'text',
313
+ name: 'description',
314
+ message: 'Describe your agent:',
315
+ initial: 'A freshly created OpenAgent.'
316
+ },
317
+ {
318
+ type: 'text',
319
+ name: 'skills',
320
+ message: 'Skills (comma-separated):',
321
+ initial: 'say_hello'
322
+ },
323
+ {
324
+ type: 'confirm',
325
+ name: 'includeRegistration',
326
+ message: 'Include on-chain registration (ERC-8004)?',
327
+ initial: false
328
+ },
329
+ {
330
+ type: 'confirm',
331
+ name: 'includePayments',
332
+ message: 'Include x402 payments?',
333
+ initial: false
19
334
  }
20
335
  ]);
21
336
 
22
- const { projectName } = response;
23
- if (!projectName) {
337
+ const { agentName, description, skills, includeRegistration, includePayments } = response;
338
+ if (!agentName) {
24
339
  console.log(kleur.red('Cancelled.'));
25
340
  process.exit(1);
26
341
  }
27
342
 
28
- const targetDir = path.join(process.cwd(), projectName);
343
+ const skillList = skills.split(',').map((s: string) => s.trim()).filter(Boolean);
344
+ const dirName = agentName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
345
+ const targetDir = path.join(process.cwd(), dirName);
29
346
 
30
347
  if (fs.existsSync(targetDir)) {
31
- console.log(kleur.red(`Error: Directory ${projectName} already exists!`));
348
+ console.log(kleur.red(`Error: Directory ${dirName} already exists!`));
32
349
  process.exit(1);
33
350
  }
34
351
 
35
- console.log(`\nCreating project in ${kleur.green(targetDir)}...\n`);
352
+ console.log(`\nCreating agent in ${kleur.green(targetDir)}...\n`);
36
353
  fs.mkdirSync(targetDir, { recursive: true });
37
354
 
38
355
  // 1. package.json
39
356
  const packageJson = {
40
- name: projectName,
357
+ name: dirName,
41
358
  version: "1.0.0",
42
- description: "An OpenAgent bot",
359
+ description: description,
43
360
  type: "module",
44
361
  scripts: {
45
- "start": "tsx index.ts",
46
- "dev": "tsx watch index.ts"
362
+ start: "tsx index.ts",
363
+ dev: "tsx watch index.ts"
47
364
  },
48
365
  dependencies: {
49
366
  "@openagentmarket/nodejs": "^1.0.0",
50
- "ethers": "^6.10.0",
51
- "dotenv": "^16.4.0"
367
+ ethers: "^6.10.0",
368
+ dotenv: "^16.4.0"
52
369
  },
53
370
  devDependencies: {
54
- "tsx": "^4.7.0",
55
- "typescript": "^5.3.3",
371
+ tsx: "^4.7.0",
372
+ typescript: "^5.3.3",
56
373
  "@types/node": "^20.11.0"
57
374
  }
58
375
  };
59
376
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
60
377
 
61
378
  // 2. tsconfig.json
62
- const tsConfig = {
63
- "compilerOptions": {
64
- "target": "ES2022",
65
- "module": "NodeNext",
66
- "moduleResolution": "NodeNext",
67
- "strict": true,
68
- "esModuleInterop": true,
69
- "skipLibCheck": true,
70
- "forceConsistentCasingInFileNames": true
71
- }
72
- };
73
- fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
379
+ writeSharedTsConfig(targetDir);
74
380
 
75
381
  // 3. .env
76
- fs.writeFileSync(path.join(targetDir, '.env'), 'MNEMONIC="your twelve word mnemonic phrase here"\n');
77
- fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\n');
382
+ let envContent = 'MNEMONIC="your twelve word mnemonic phrase here"\n';
383
+ if (includeRegistration) {
384
+ envContent += 'REGISTRATION_PRIVATE_KEY=""\n';
385
+ envContent += 'PINATA_JWT=""\n';
386
+ }
387
+ fs.writeFileSync(path.join(targetDir, '.env'), envContent);
388
+
389
+ // 4. .gitignore
390
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), 'node_modules\n.env\ndist\n');
391
+
392
+ // 5. index.ts
393
+ const taskHandlers = skillList.map((skill: string) => `
394
+ agent.onTask("${skill}", async (input) => {
395
+ return { message: \`Handled ${skill} with \${JSON.stringify(input)}\` };
396
+ });`).join('\n');
397
+
398
+ const paymentConfig = includePayments ? `
399
+ payment: {
400
+ amount: 5,
401
+ currency: "USDC",
402
+ recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
403
+ },` : '';
404
+
405
+ const registrationBlock = includeRegistration ? `
406
+
407
+ // ── Register on-chain (optional) ──
408
+ async function registerAgent(agent: any) {
409
+ const result = await agent.register(
410
+ {
411
+ name: "${agentName}",
412
+ description: "${description}",
413
+ image: "https://example.com/avatar.png",
414
+ metadata: {
415
+ skills: ${JSON.stringify(skillList)},
416
+ pricing: { amount: "5.0", currency: "USDC", chain: "base" },
417
+ category: "utility",
418
+ tags: ["openagent"]
419
+ }
420
+ },
421
+ {
422
+ privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
423
+ pinataJwt: process.env.PINATA_JWT!,
424
+ }
425
+ );
426
+ console.log("Registered:", result);
427
+ }` : '';
428
+
429
+ const registrationCall = includeRegistration
430
+ ? '\n // Uncomment to register on-chain:\n // await registerAgent(agent);\n'
431
+ : '';
78
432
 
79
- // 4. index.ts (Template)
80
433
  const indexTs = `import { OpenAgent } from '@openagentmarket/nodejs';
81
434
  import 'dotenv/config';
82
435
 
@@ -91,79 +444,95 @@ async function main() {
91
444
  mnemonic,
92
445
  env: "production",
93
446
  card: {
94
- name: "${projectName}",
95
- description: "A freshly created OpenAgent.",
96
- skills: ["say_hello"]
97
- },
98
- payment: {
99
- amount: 0.001, // 0.001 ETH
100
- currency: "ETH",
101
- recipientAddress: "0x0000000000000000000000000000000000000000" // Update this!
102
- }
103
- });
104
-
105
- agent.onTask("say_hello", async (input) => {
106
- return { message: \`Hello \${input.name || "World"}!\` };
447
+ name: "${agentName}",
448
+ description: "${description}",
449
+ skills: ${JSON.stringify(skillList)}
450
+ },${paymentConfig}
107
451
  });
452
+ ${taskHandlers}
108
453
 
109
- await agent.start();
454
+ await agent.start();${registrationCall}
110
455
  }
111
-
456
+ ${registrationBlock}
112
457
  main().catch(console.error);
113
458
  `;
114
459
  fs.writeFileSync(path.join(targetDir, 'index.ts'), indexTs);
115
460
 
116
- // 5. README.md
117
- const readme = `# ${projectName}
118
-
119
- ## Getting Started
120
-
121
- This project was scaffolded with \`@openagentmarket/create-agent\`.
122
-
123
- ### Prerequisites
124
-
125
- - Node.js (v18 or higher)
126
- - pnpm (recommended) or npm
127
-
128
- ### Installation
129
-
130
- \`\`\`bash
131
- pnpm install
132
- \`\`\`
133
-
134
- ### Configuration
135
-
136
- 1. Copy the example environment file:
137
- \`\`\`bash
138
- cp .env.example .env
139
- \`\`\`
140
- 2. Edit \`.env\` and add your 12-word mnemonic phrase.
141
-
142
- ### Running the Agent
143
-
144
- \`\`\`bash
145
- # Development mode with hot-reload
146
- pnpm dev
147
-
148
- # Production build
149
- pnpm build
150
- pnpm start
151
- \`\`\`
461
+ // 6. README.md
462
+ const readmeLines = [
463
+ `# ${agentName}`,
464
+ ``,
465
+ description,
466
+ ``,
467
+ `## Getting Started`,
468
+ ``,
469
+ '```bash',
470
+ `cd ${dirName}`,
471
+ `npm install`,
472
+ '```',
473
+ ``,
474
+ `### Configuration`,
475
+ ``,
476
+ `Edit \`.env\` and set your 12-word mnemonic phrase.`,
477
+ includeRegistration ? `\nFor on-chain registration, also set \`REGISTRATION_PRIVATE_KEY\` and \`PINATA_JWT\`.\n` : '',
478
+ `### Running`,
479
+ ``,
480
+ '```bash',
481
+ `# Development mode with hot-reload`,
482
+ `npm run dev`,
483
+ ``,
484
+ `# Production`,
485
+ `npm start`,
486
+ '```',
487
+ ``,
488
+ `## Agent Details`,
489
+ ``,
490
+ `| Property | Value |`,
491
+ `|----------|-------|`,
492
+ `| **Name** | ${agentName} |`,
493
+ `| **Skills** | ${skillList.join(', ')} |`,
494
+ `| **Payments** | ${includePayments ? 'Enabled (x402)' : 'Disabled'} |`,
495
+ `| **Registration** | ${includeRegistration ? 'Included' : 'Not included'} |`,
496
+ ``,
497
+ `## Environment Variables`,
498
+ ``,
499
+ `| Variable | Required | Description |`,
500
+ `|----------|----------|-------------|`,
501
+ '| `MNEMONIC` | Yes | Agent wallet seed phrase |',
502
+ includeRegistration ? '| `REGISTRATION_PRIVATE_KEY` | For registration | Wallet paying gas |\n| `PINATA_JWT` | For registration | IPFS metadata upload |' : '',
503
+ ``,
504
+ `## Resources`,
505
+ ``,
506
+ `- [SDK Docs](https://www.npmjs.com/package/@openagentmarket/nodejs)`,
507
+ `- [Explorer](https://8004agents.ai)`,
508
+ `- [GitHub](https://github.com/openagentmarket)`,
509
+ ].join('\n');
510
+ fs.writeFileSync(path.join(targetDir, 'README.md'), readmeLines);
511
+
512
+ // Print success
513
+ console.log(kleur.green('āœ… Agent project created!\n'));
514
+ console.log(kleur.bold('Next steps:'));
515
+ console.log(` cd ${dirName}`);
516
+ console.log(' npm install');
517
+ console.log(' # Set your MNEMONIC in .env');
518
+ console.log(' npm start\n');
519
+ }
152
520
 
153
- ## Agent Details
521
+ // ── Shared helpers ───────────────────────────────────────────────────────────
154
522
 
155
- - **Name**: ${projectName}
156
- - **Framework**: @openagentmarket/nodejs
157
- - **Network**: Base (Coinbase L2)
158
- `;
159
- fs.writeFileSync(path.join(targetDir, 'README.md'), readme);
160
-
161
- console.log(kleur.green('Success! Project created.'));
162
- console.log('\nNext steps:');
163
- console.log(` cd ${projectName}`);
164
- console.log(' pnpm install');
165
- console.log(' # Update .env with your mnemonic');
166
- console.log(' pnpm start\n');
523
+ function writeSharedTsConfig(targetDir: string) {
524
+ const tsConfig = {
525
+ compilerOptions: {
526
+ target: "ES2022",
527
+ module: "NodeNext",
528
+ moduleResolution: "NodeNext",
529
+ strict: true,
530
+ esModuleInterop: true,
531
+ skipLibCheck: true,
532
+ forceConsistentCasingInFileNames: true
533
+ }
534
+ };
535
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
167
536
  }
168
537
 
169
538
  main().catch((err) => {