agentlaunch 1.0.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 (70) hide show
  1. package/README.md +475 -0
  2. package/dist/__tests__/config.test.d.ts +20 -0
  3. package/dist/__tests__/config.test.d.ts.map +1 -0
  4. package/dist/__tests__/config.test.js +155 -0
  5. package/dist/__tests__/config.test.js.map +1 -0
  6. package/dist/commands/buy.d.ts +10 -0
  7. package/dist/commands/buy.d.ts.map +1 -0
  8. package/dist/commands/buy.js +126 -0
  9. package/dist/commands/buy.js.map +1 -0
  10. package/dist/commands/comments.d.ts +15 -0
  11. package/dist/commands/comments.d.ts.map +1 -0
  12. package/dist/commands/comments.js +137 -0
  13. package/dist/commands/comments.js.map +1 -0
  14. package/dist/commands/config.d.ts +10 -0
  15. package/dist/commands/config.d.ts.map +1 -0
  16. package/dist/commands/config.js +63 -0
  17. package/dist/commands/config.js.map +1 -0
  18. package/dist/commands/create.d.ts +33 -0
  19. package/dist/commands/create.d.ts.map +1 -0
  20. package/dist/commands/create.js +1177 -0
  21. package/dist/commands/create.js.map +1 -0
  22. package/dist/commands/deploy.d.ts +18 -0
  23. package/dist/commands/deploy.d.ts.map +1 -0
  24. package/dist/commands/deploy.js +178 -0
  25. package/dist/commands/deploy.js.map +1 -0
  26. package/dist/commands/holders.d.ts +13 -0
  27. package/dist/commands/holders.d.ts.map +1 -0
  28. package/dist/commands/holders.js +114 -0
  29. package/dist/commands/holders.js.map +1 -0
  30. package/dist/commands/init.d.ts +3 -0
  31. package/dist/commands/init.d.ts.map +1 -0
  32. package/dist/commands/init.js +506 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/commands/list.d.ts +10 -0
  35. package/dist/commands/list.d.ts.map +1 -0
  36. package/dist/commands/list.js +135 -0
  37. package/dist/commands/list.js.map +1 -0
  38. package/dist/commands/optimize.d.ts +12 -0
  39. package/dist/commands/optimize.d.ts.map +1 -0
  40. package/dist/commands/optimize.js +134 -0
  41. package/dist/commands/optimize.js.map +1 -0
  42. package/dist/commands/scaffold.d.ts +25 -0
  43. package/dist/commands/scaffold.d.ts.map +1 -0
  44. package/dist/commands/scaffold.js +123 -0
  45. package/dist/commands/scaffold.js.map +1 -0
  46. package/dist/commands/sell.d.ts +10 -0
  47. package/dist/commands/sell.d.ts.map +1 -0
  48. package/dist/commands/sell.js +109 -0
  49. package/dist/commands/sell.js.map +1 -0
  50. package/dist/commands/status.d.ts +10 -0
  51. package/dist/commands/status.d.ts.map +1 -0
  52. package/dist/commands/status.js +124 -0
  53. package/dist/commands/status.js.map +1 -0
  54. package/dist/commands/tokenize.d.ts +16 -0
  55. package/dist/commands/tokenize.d.ts.map +1 -0
  56. package/dist/commands/tokenize.js +207 -0
  57. package/dist/commands/tokenize.js.map +1 -0
  58. package/dist/config.d.ts +44 -0
  59. package/dist/config.d.ts.map +1 -0
  60. package/dist/config.js +137 -0
  61. package/dist/config.js.map +1 -0
  62. package/dist/http.d.ts +24 -0
  63. package/dist/http.d.ts.map +1 -0
  64. package/dist/http.js +53 -0
  65. package/dist/http.js.map +1 -0
  66. package/dist/index.d.ts +19 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +86 -0
  69. package/dist/index.js.map +1 -0
  70. package/package.json +50 -0
@@ -0,0 +1,1177 @@
1
+ /**
2
+ * CLI-002 + CLI-006: create command
3
+ *
4
+ * agentlaunch create [--name "My Agent"] [--ticker MYAG] [--template custom]
5
+ * [--description "..."] [--chain 97] [--deploy] [--tokenize] [--json]
6
+ *
7
+ * Flagship one-command flow:
8
+ * 1. Scaffold agent project (always) — via agentlaunch-templates
9
+ * 2. Deploy to Agentverse (if --deploy) — via agentlaunch-sdk deployAgent
10
+ * 3. Tokenize on AgentLaunch (if --tokenize) — via agentlaunch-sdk client
11
+ * 4. Print handoff link
12
+ *
13
+ * When name/ticker are omitted, uses readline for interactive prompts.
14
+ *
15
+ * Platform constants (source of truth: deployed smart contracts):
16
+ * - Deploy fee: 120 FET (read dynamically, can change via multi-sig)
17
+ * - Graduation target: 30,000 FET -> auto DEX listing
18
+ * - Trading fee: 2% -> 100% to protocol treasury (NO creator fee)
19
+ */
20
+ import fs from "node:fs";
21
+ import path from "node:path";
22
+ import readline from "node:readline";
23
+ import { spawn } from "node:child_process";
24
+ import { deployAgent, getFrontendUrl } from "agentlaunch-sdk";
25
+ import { execSync } from "node:child_process";
26
+ import { generateFromTemplate, listTemplates, RULES, SKILLS, DOCS, EXAMPLES, buildPackageJson, CURSOR_MCP_CONFIG, CURSOR_RULES, buildSwarmClaudeMd, buildSwarmConfig, buildSwarmPackageJson, buildProjectSkills } from "agentlaunch-templates";
27
+ import { getClient } from "../http.js";
28
+ import { requireApiKey } from "../config.js";
29
+ const TEMPLATES = {};
30
+ // Populate from templates package at module load time
31
+ for (const t of listTemplates()) {
32
+ TEMPLATES[t.name] = { label: t.name, description: t.description };
33
+ }
34
+ // Legacy CLI type aliases -> template names
35
+ const LEGACY_ALIAS = {
36
+ faucet: "custom",
37
+ research: "research",
38
+ trading: "trading-bot",
39
+ data: "data-analyzer",
40
+ genesis: "swarm-starter", // Legacy alias for swarm-starter
41
+ };
42
+ // Genesis preset names for swarm mode
43
+ const GENESIS_PRESETS = [
44
+ { name: "oracle", label: "Oracle", description: "Market data provider (0.001 FET/call)" },
45
+ { name: "brain", label: "Brain", description: "LLM reasoning engine (0.01 FET/call)" },
46
+ { name: "analyst", label: "Analyst", description: "Token scoring & evaluation (0.005 FET/call)" },
47
+ { name: "coordinator", label: "Coordinator", description: "Query routing (0.0005 FET/call)" },
48
+ { name: "sentinel", label: "Sentinel", description: "Real-time alerts (0.002 FET/call)" },
49
+ { name: "launcher", label: "Launcher", description: "Autonomous agent creation (0.02 FET/call)" },
50
+ { name: "scout", label: "Scout", description: "Agent & opportunity discovery (0.01 FET/call)" },
51
+ ];
52
+ const TEMPLATE_LIST = listTemplates().map((t) => ({
53
+ id: t.name,
54
+ label: t.name,
55
+ description: t.description,
56
+ }));
57
+ // ---------------------------------------------------------------------------
58
+ // Helpers
59
+ // ---------------------------------------------------------------------------
60
+ function prompt(rl, question) {
61
+ return new Promise((resolve) => rl.question(question, resolve));
62
+ }
63
+ function sanitizeDirName(s) {
64
+ return s.replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
65
+ }
66
+ /** Auto-generate a ticker from the agent name (e.g. "Price Oracle" → "PRICE") */
67
+ function autoTicker(name) {
68
+ const words = name.trim().split(/\s+/);
69
+ if (words.length === 1) {
70
+ return words[0].slice(0, 6).toUpperCase();
71
+ }
72
+ // Use first word if it's short enough, otherwise initials
73
+ const first = words[0].toUpperCase();
74
+ if (first.length >= 3 && first.length <= 6)
75
+ return first;
76
+ return words.map((w) => w[0]).join("").slice(0, 6).toUpperCase();
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Workflow system prompt for Claude Code sessions
80
+ // ---------------------------------------------------------------------------
81
+ function buildWorkflowSystemPrompt(opts) {
82
+ return `You are guiding a developer through building a valuable AI agent on Fetch.ai Agentverse. This is not a tutorial — you are a collaborator helping them build something worth using and worth holding tokens for.
83
+
84
+ ## Context
85
+ - Agent name: ${opts.agentName}
86
+ - Agent address: ${opts.agentAddress || "(not yet deployed)"}
87
+ - Description: ${opts.description}
88
+ - Auto-generated ticker: $${opts.ticker} (can change during tokenization)
89
+ - Status: ${opts.isDeployed ? "Deployed on Agentverse" : "Code scaffolded, not yet deployed"}
90
+
91
+ ## Workflow — Follow These Steps In Order
92
+
93
+ ### Step 1: Understand the Vision
94
+ Ask the developer what problem this agent solves. Probe with:
95
+ - Who would pay for this? Why can't they just use an API directly?
96
+ - What data or intelligence does this agent accumulate over time?
97
+ - What gets better the more people use it?
98
+
99
+ Teach the 3 pillars of agent value:
100
+ 1. **Proprietary intelligence** — it knows something others don't (curated data, trained models, analyzed patterns)
101
+ 2. **Network effects** — it gets better with more users/agents (shared state, cross-agent data, collective learning)
102
+ 3. **Defensibility** — it's hard to replicate (unique data sources, accumulated history, agent relationships)
103
+
104
+ If the description is vague ("a helpful assistant"), push back. Help them find a specific, valuable niche.
105
+
106
+ ### Step 2: Build the Agent Logic
107
+ Read agent.py and propose real code changes based on the vision from Step 1. This is where you write REAL business logic — not placeholder TODOs.
108
+
109
+ Available packages on Agentverse (most people only use 3 — use more):
110
+ - **AI**: anthropic, openai, langchain-anthropic, langchain-core, langchain-community, langchain-google-genai, transformers, sentence-transformers
111
+ - **Data**: pandas, numpy, scipy, scikit-learn, faiss-cpu
112
+ - **Web**: requests, aiohttp, beautifulsoup4, feedparser
113
+ - **Blockchain**: web3, eth-account, cosmpy
114
+ - **Storage**: pymongo, redis, sqlalchemy
115
+ - **Utils**: pydantic, pillow, matplotlib, networkx
116
+
117
+ Key patterns to use:
118
+ - **Chat Protocol** (on_message): Request/response interactions with users and other agents
119
+ - **ctx.storage**: Persistent state across restarts — this is how agents accumulate intelligence
120
+ - **on_interval**: Background tasks that run 24/7 — monitoring, data collection, analysis
121
+ - **External APIs**: Connect to real data sources (price feeds, news, on-chain data)
122
+ - **Payment Protocol**: Charge for premium services (RequestPayment → CommitPayment → CompletePayment)
123
+
124
+ Write production code. Explain each pattern as you introduce it. Pause and ask if the developer wants to adjust before continuing.
125
+
126
+ ### Step 3: Deploy
127
+ ${opts.isDeployed ? "The agent is already deployed. If you made code changes, push updated code to Agentverse using the deploy_to_agentverse MCP tool or 'npx agentlaunch deploy'." : "Deploy the agent to Agentverse using the deploy_to_agentverse MCP tool or 'npx agentlaunch deploy'. Explain what happens: code uploads, agent compiles, goes live in ~30 seconds."}
128
+
129
+ After deploy, verify it's running (check logs). Mention the agent takes 15-60s to compile.
130
+
131
+ ### Step 4: Make It Beautiful
132
+ A well-presented agent gets more discovery, more interactions, more token holders. Do all of these:
133
+
134
+ 1. **README** — Write a compelling README.md with:
135
+ - One-line value proposition (what problem it solves)
136
+ - Capabilities list (what it can actually do)
137
+ - Example interactions (show 2-3 real conversations)
138
+ - Pricing table (if it charges for services)
139
+ - Technical architecture (brief)
140
+
141
+ 2. **Short description** — Write a 1-2 sentence description for the Agentverse directory
142
+
143
+ 3. **Push metadata** — Use 'npx agentlaunch optimize <agent-address>' or the update_agent_metadata MCP tool
144
+
145
+ 4. **Optimization checklist** (7 factors for Agentverse ranking):
146
+ - [ ] README is detailed and well-formatted
147
+ - [ ] Short description is clear and specific
148
+ - [ ] Chat Protocol is implemented (for DeltaV discovery)
149
+ - [ ] Agent has a custom avatar
150
+ - [ ] Agent has real interactions (test it!)
151
+ - [ ] Agent has a memorable handle
152
+ - [ ] publish_manifest=True is set
153
+
154
+ ### Step 5: Tokenize
155
+ Help the developer tokenize their agent:
156
+
157
+ 1. **Choose ticker** — Suggest a memorable ticker (2-6 chars). The auto-generated $${opts.ticker} is a starting point.
158
+ 2. **Explain bonding curves** — The bonding curve is NOT a fundraiser. It's a continuous, real-time reputation system:
159
+ - Agent delivers value → people buy → price rises
160
+ - Agent stops delivering → people sell → price drops
161
+ - Every holder has skin in the game. The market doesn't lie.
162
+ 3. **Create the token** — Use the create_token_record MCP tool or 'npx agentlaunch tokenize'
163
+ 4. **Explain the handoff** — The link goes to a human who connects a wallet and pays the 120 FET deploy fee. Agents never hold private keys.
164
+ 5. **Graduation** — At 30,000 FET liquidity, the token auto-lists on DEX. The 2% trading fee goes 100% to protocol treasury.
165
+
166
+ ### Step 6: Share & Next Steps
167
+ Provide all links:
168
+ - Agentverse page: https://agentverse.ai/agents/details/<agent-address>
169
+ - Trade page: https://agent-launch.ai/token/<token-address> (after tokenization)
170
+ - Handoff link: (from tokenization step)
171
+
172
+ Suggest next steps:
173
+ - Test the agent by chatting with it on Agentverse
174
+ - Share the trade link to get early holders
175
+ - Build a complementary agent (data feeds, analysis, monitoring)
176
+ - Add cross-holdings (buy tokens of agents yours depends on)
177
+
178
+ ## 6 Architecture Patterns for Inspiration
179
+ 1. **Intelligence Agent** — Uses AI to analyze, summarize, or reason (Brain, Analyst)
180
+ 2. **Sentinel Agent** — Monitors 24/7, alerts on conditions (price drops, anomalies)
181
+ 3. **Research Agent** — Deep dives on demand (on-chain analysis, market research)
182
+ 4. **Oracle Agent** — Serves live data feeds (prices, metrics, events)
183
+ 5. **Coordinator Agent** — Routes queries to specialists, takes a routing fee
184
+ 6. **Incubator Agent** — Finds gaps in the ecosystem, scaffolds new agents
185
+
186
+ ## Style Guide
187
+ - Be educational but action-oriented. Explain WHY as you DO.
188
+ - Pause between major steps — don't dump everything at once.
189
+ - Never write placeholder code (# TODO: implement this). Write real logic or ask what to build.
190
+ - Use the MCP tools (deploy_to_agentverse, create_token_record, update_agent_metadata, etc.) when possible.
191
+ - Be encouraging but honest. If an idea won't create value, say so and suggest alternatives.
192
+ - Keep responses focused. Don't repeat information the developer already knows.`;
193
+ }
194
+ async function validateApiKey(apiKey) {
195
+ try {
196
+ const response = await fetch("https://agentverse.ai/v1/hosting/agents", {
197
+ method: "GET",
198
+ headers: {
199
+ Authorization: `Bearer ${apiKey}`,
200
+ },
201
+ });
202
+ if (response.ok) {
203
+ return { valid: true };
204
+ }
205
+ if (response.status === 401) {
206
+ return { valid: false, error: "Invalid API key" };
207
+ }
208
+ return { valid: false, error: `API returned ${response.status}` };
209
+ }
210
+ catch (err) {
211
+ const msg = err instanceof Error ? err.message : String(err);
212
+ return { valid: false, error: msg };
213
+ }
214
+ }
215
+ // ---------------------------------------------------------------------------
216
+ // Tokenize
217
+ // ---------------------------------------------------------------------------
218
+ async function tokenizeAgent(agentAddress, name, ticker, description, chainId, json) {
219
+ if (!json)
220
+ console.log("\nTokenizing agent on AgentLaunch...");
221
+ const client = getClient();
222
+ const result = await client.post("/agents/tokenize", {
223
+ agentAddress,
224
+ name,
225
+ symbol: ticker.toUpperCase(),
226
+ description,
227
+ chainId,
228
+ });
229
+ const tokenId = result.token_id ?? result.tokenId ?? result.data?.token_id;
230
+ const tokenAddress = result.token_address;
231
+ const frontendUrl = getFrontendUrl();
232
+ const handoffLink = result.handoff_link ??
233
+ result.data?.handoff_link ??
234
+ (tokenId !== undefined ? `${frontendUrl}/deploy/${tokenId}` : undefined);
235
+ return { tokenId, tokenAddress, handoffLink };
236
+ }
237
+ // ---------------------------------------------------------------------------
238
+ // Command registration
239
+ // ---------------------------------------------------------------------------
240
+ export function registerCreateCommand(program) {
241
+ program
242
+ .command("create")
243
+ .description("Flagship one-command flow: scaffold -> deploy -> tokenize an AI agent")
244
+ .option("--name <name>", "Agent name (prompted if omitted)")
245
+ .option("--ticker <ticker>", "Token ticker symbol e.g. MYAG (prompted if omitted)")
246
+ .option("--template <template>", "Agent template: chat-memory (default), swarm-starter, custom, research, trading-bot, data-analyzer, price-monitor, gifter")
247
+ .option("--description <desc>", "Token description (max 500 chars)")
248
+ .option("--chain <chainId>", "Chain ID: 97 (BSC testnet) or 56 (BSC mainnet) (default: 97)", "97")
249
+ .option("--deploy", "Deploy agent to Agentverse after scaffolding")
250
+ .option("--tokenize", "Create token record on AgentLaunch after deploy")
251
+ .option("--mode <mode>", "Build mode: quick (single agent), swarm (multi-agent), genesis (full 7-agent economy)")
252
+ .option("--preset <preset>", "Agent preset: oracle, brain, analyst, coordinator, sentinel, launcher, scout")
253
+ .option("--no-deploy", "Scaffold only, don't deploy to Agentverse")
254
+ .option("--json", "Output only JSON (machine-readable, disables prompts)")
255
+ .action(async (options) => {
256
+ const isJson = options.json === true;
257
+ // When --json is set we never prompt — fail fast with errors
258
+ let name = options.name?.trim() ?? "";
259
+ let ticker = options.ticker?.trim() ?? "";
260
+ // Resolve legacy aliases and raw template names
261
+ let templateRaw = options.template ?? "";
262
+ let template = LEGACY_ALIAS[templateRaw] ?? templateRaw;
263
+ let doDeploy = options.deploy === true;
264
+ let doTokenize = options.tokenize === true;
265
+ // Interactive prompts (only when not --json)
266
+ let description = options.description?.trim() ?? "";
267
+ let apiKey = process.env.AGENTVERSE_API_KEY ?? "";
268
+ if (!isJson) {
269
+ const rl = readline.createInterface({
270
+ input: process.stdin,
271
+ output: process.stdout,
272
+ });
273
+ if (!name) {
274
+ name = (await prompt(rl, "Agent name: ")).trim();
275
+ }
276
+ // Auto-generate ticker from name (user can refine during tokenization)
277
+ if (!ticker) {
278
+ ticker = autoTicker(name);
279
+ }
280
+ if (!description) {
281
+ description = (await prompt(rl, "Describe what your agent does: ")).trim();
282
+ }
283
+ // Always prompt for API key
284
+ console.log("\nGet your API key at: https://agentverse.ai/profile/api-keys\n");
285
+ const existingKey = apiKey ? ` [${apiKey.slice(0, 6)}...${apiKey.slice(-4)}]` : "";
286
+ const keyInput = (await prompt(rl, `Agentverse API key${existingKey}: `)).trim();
287
+ if (keyInput) {
288
+ apiKey = keyInput;
289
+ }
290
+ // Set API key in env for SDK calls
291
+ if (apiKey) {
292
+ process.env.AGENTVERSE_API_KEY = apiKey;
293
+ }
294
+ // Validate API key if we're going to deploy
295
+ // --no-deploy flag sets options.deploy to false
296
+ const skipDeploy = options.deploy === false;
297
+ if (!skipDeploy && apiKey) {
298
+ console.log("\n Validating API key...");
299
+ const validation = await validateApiKey(apiKey);
300
+ if (!validation.valid) {
301
+ console.error(` Error: ${validation.error ?? "Invalid API key"}`);
302
+ console.log(" Get your API key at: https://agentverse.ai/profile/api-keys");
303
+ rl.close();
304
+ process.exit(1);
305
+ }
306
+ console.log(" Valid.\n");
307
+ }
308
+ // Default to single agent mode; swarm reachable via --mode=swarm
309
+ const buildMode = options.mode ?? "single";
310
+ let selectedPresets;
311
+ if (buildMode === "swarm") {
312
+ // Show preset selection for swarm mode only
313
+ console.log("\n What kind of agents?\n");
314
+ console.log(" Recommended for earning fees:");
315
+ console.log(" 1) Oracle Sell market data (price feeds, OHLC) — 0.001 FET/call");
316
+ console.log(" 2) Brain Sell AI reasoning (analysis, summaries) — 0.01 FET/call");
317
+ console.log(" 3) Analyst Sell token scoring (quality, risk) — 0.005 FET/call");
318
+ console.log("");
319
+ console.log(" Infrastructure (other agents pay you):");
320
+ console.log(" 4) Coordinator Route queries to specialists — 0.0005 FET/call");
321
+ console.log(" 5) Sentinel Real-time monitoring & alerts — 0.002 FET/call");
322
+ console.log("");
323
+ console.log(" Advanced (autonomous growth):");
324
+ console.log(" 6) Launcher Find gaps, scaffold new agents — 0.02 FET/call");
325
+ console.log(" 7) Scout Discover & evaluate agents — 0.01 FET/call");
326
+ const pickInput = (await prompt(rl, "\n Pick agents (comma-separated, e.g. 1,2,4): ")).trim();
327
+ const picks = pickInput
328
+ .split(",")
329
+ .map((s) => parseInt(s.trim(), 10))
330
+ .filter((n) => n >= 1 && n <= GENESIS_PRESETS.length);
331
+ if (picks.length === 0) {
332
+ // Default to oracle + brain if no valid input
333
+ selectedPresets = ["oracle", "brain"];
334
+ console.log(" Defaulting to Oracle + Brain.");
335
+ }
336
+ else {
337
+ selectedPresets = picks.map((n) => GENESIS_PRESETS[n - 1].name);
338
+ }
339
+ }
340
+ else {
341
+ // Single agent mode - use "chat-memory" (the base template with LLM + memory)
342
+ selectedPresets = ["chat-memory"];
343
+ }
344
+ const isSingleAgent = buildMode === "single";
345
+ const baseName = name || (isSingleAgent ? selectedPresets[0] : "Swarm");
346
+ if (skipDeploy) {
347
+ console.log(`\n Scaffolding ${isSingleAgent ? baseName : selectedPresets.length + " agents"}...\n`);
348
+ }
349
+ else if (isSingleAgent) {
350
+ console.log(`\n Deploying ${baseName}...\n`);
351
+ }
352
+ else {
353
+ console.log(`\n Deploying ${selectedPresets.length} agents as "${baseName}"...\n`);
354
+ }
355
+ rl.close();
356
+ // Generate/deploy each agent in sequence
357
+ const deployResults = [];
358
+ const peerAddresses = {};
359
+ for (const presetName of selectedPresets) {
360
+ // For single agent, use baseName directly; for swarm, append preset
361
+ const agentName = isSingleAgent
362
+ ? baseName
363
+ : `${baseName}-${presetName.charAt(0).toUpperCase() + presetName.slice(1)}`;
364
+ const presetInfo = GENESIS_PRESETS.find((p) => p.name === presetName);
365
+ const action = skipDeploy ? "Scaffolding" : "Deploying";
366
+ console.log(` [${deployResults.length + 1}/${selectedPresets.length}] ${action} ${agentName}...`);
367
+ try {
368
+ // Generate agent code — use chat-memory for single agents, swarm-starter for swarm presets
369
+ let agentCode;
370
+ let templateUsed;
371
+ if (isSingleAgent || presetName === "chat-memory") {
372
+ const generated = generateFromTemplate("chat-memory", {
373
+ agent_name: agentName,
374
+ description: description || presetInfo?.description || "",
375
+ });
376
+ agentCode = generated.code;
377
+ templateUsed = "chat-memory";
378
+ }
379
+ else {
380
+ try {
381
+ const generated = generateFromTemplate("swarm-starter", {
382
+ agent_name: agentName,
383
+ description: presetInfo?.description ?? "",
384
+ preset: presetName,
385
+ });
386
+ agentCode = generated.code;
387
+ templateUsed = "swarm-starter";
388
+ }
389
+ catch {
390
+ const generated = generateFromTemplate("chat-memory", {
391
+ agent_name: agentName,
392
+ description: description || presetInfo?.description || "",
393
+ });
394
+ agentCode = generated.code;
395
+ templateUsed = "chat-memory";
396
+ }
397
+ }
398
+ if (skipDeploy) {
399
+ // Scaffold only - no deployment
400
+ deployResults.push({
401
+ name: agentName,
402
+ preset: presetName,
403
+ address: "",
404
+ status: "scaffolded",
405
+ code: agentCode,
406
+ });
407
+ console.log(" Scaffolded (not deployed)");
408
+ }
409
+ else {
410
+ // Deploy to Agentverse
411
+ const deploySecrets = {
412
+ ...peerAddresses,
413
+ AGENTVERSE_API_KEY: apiKey,
414
+ AGENTLAUNCH_API_KEY: apiKey,
415
+ };
416
+ // For chat-memory agents, set the default ASI1 LLM key
417
+ if (templateUsed === "chat-memory") {
418
+ deploySecrets.LLM_API_KEY = "sk_2a3c92a0b11e4f18b50708cca1a55179ab38a7c2fb7f4eee95fd68e1e28f860b";
419
+ }
420
+ const result = await deployAgent({
421
+ apiKey,
422
+ agentName,
423
+ sourceCode: agentCode,
424
+ secrets: deploySecrets,
425
+ });
426
+ peerAddresses[`${presetName.toUpperCase()}_ADDRESS`] = result.agentAddress;
427
+ deployResults.push({
428
+ name: agentName,
429
+ preset: presetName,
430
+ address: result.agentAddress,
431
+ status: result.status,
432
+ code: agentCode,
433
+ });
434
+ console.log(` Address: ${result.agentAddress}`);
435
+ console.log(` Status: ${result.status}`);
436
+ }
437
+ }
438
+ catch (err) {
439
+ const errMsg = err instanceof Error ? err.message : String(err);
440
+ deployResults.push({
441
+ name: agentName,
442
+ preset: presetName,
443
+ address: "",
444
+ status: "failed",
445
+ error: errMsg,
446
+ });
447
+ console.error(` FAILED: ${errMsg}`);
448
+ }
449
+ }
450
+ // Summary
451
+ const successful = deployResults.filter((r) => r.status !== "failed");
452
+ const failed = deployResults.filter((r) => r.status === "failed");
453
+ // Create project directory
454
+ const dirName = sanitizeDirName(baseName);
455
+ const targetDir = path.resolve(process.cwd(), dirName);
456
+ if (!fs.existsSync(targetDir)) {
457
+ fs.mkdirSync(targetDir, { recursive: true });
458
+ }
459
+ if (!isSingleAgent) {
460
+ fs.mkdirSync(path.join(targetDir, "agents"), { recursive: true });
461
+ }
462
+ fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
463
+ fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
464
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
465
+ fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
466
+ fs.mkdirSync(path.join(targetDir, "docs"), { recursive: true });
467
+ fs.mkdirSync(path.join(targetDir, "examples"), { recursive: true });
468
+ // Write agent code files
469
+ for (const agent of successful) {
470
+ if (agent.code) {
471
+ // Single agent: agent.py at root; Swarm: agents/<preset>.py
472
+ const codePath = isSingleAgent
473
+ ? path.join(targetDir, "agent.py")
474
+ : path.join(targetDir, "agents", `${agent.preset}.py`);
475
+ fs.writeFileSync(codePath, agent.code, "utf8");
476
+ }
477
+ }
478
+ // Build context for CLAUDE.md (works for both single and swarm)
479
+ const swarmContext = {
480
+ swarmName: baseName,
481
+ agents: successful.map((a) => ({
482
+ name: a.name,
483
+ preset: a.preset,
484
+ address: a.address,
485
+ status: a.status,
486
+ })),
487
+ peerAddresses,
488
+ deployedAt: new Date().toISOString(),
489
+ };
490
+ // Write context files
491
+ fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), buildSwarmClaudeMd(swarmContext), "utf8");
492
+ fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), buildSwarmConfig(swarmContext), "utf8");
493
+ fs.writeFileSync(path.join(targetDir, "package.json"), buildSwarmPackageJson(baseName), "utf8");
494
+ // Write .env with API key
495
+ let envContent = `# AgentLaunch Environment Variables
496
+ AGENTVERSE_API_KEY=${apiKey}
497
+ AGENT_LAUNCH_API_URL=https://agent-launch.ai/api
498
+ `;
499
+ if (!isSingleAgent && Object.keys(peerAddresses).length > 0) {
500
+ envContent += `
501
+ # Peer addresses (for agent-to-agent communication)
502
+ ${Object.entries(peerAddresses).map(([k, v]) => `${k}=${v}`).join("\n")}
503
+ `;
504
+ }
505
+ if (successful.length > 0) {
506
+ envContent += `
507
+ # Your agent address
508
+ AGENT_ADDRESS=${successful[0].address}
509
+ `;
510
+ }
511
+ fs.writeFileSync(path.join(targetDir, ".env"), envContent, "utf8");
512
+ // Write Claude rules
513
+ for (const [filename, content] of Object.entries(RULES)) {
514
+ fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
515
+ }
516
+ // Write Claude skills (generic)
517
+ for (const [filepath, content] of Object.entries(SKILLS)) {
518
+ const skillDir = path.dirname(filepath);
519
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
520
+ fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
521
+ }
522
+ // Write project-specific skills (with real agent addresses)
523
+ const agentSkillContexts = successful.map((a) => ({
524
+ name: a.name,
525
+ preset: a.preset,
526
+ address: a.address,
527
+ symbol: a.preset.slice(0, 4).toUpperCase(),
528
+ }));
529
+ const projectSkills = buildProjectSkills(agentSkillContexts, isSingleAgent);
530
+ for (const [filepath, content] of Object.entries(projectSkills)) {
531
+ const skillDir = path.dirname(filepath);
532
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
533
+ fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
534
+ }
535
+ // Write .claude/settings.json
536
+ const claudeSettings = JSON.stringify({
537
+ mcpServers: {
538
+ "agent-launch": {
539
+ command: "npx",
540
+ args: ["-y", "agent-launch-mcp"],
541
+ env: { AGENTVERSE_API_KEY: "${AGENTVERSE_API_KEY}" },
542
+ },
543
+ },
544
+ }, null, 2);
545
+ fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), claudeSettings, "utf8");
546
+ // Write MCP config
547
+ fs.writeFileSync(path.join(targetDir, ".mcp.json"), claudeSettings, "utf8");
548
+ // Write Cursor config
549
+ fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
550
+ fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
551
+ // Write docs
552
+ for (const [filename, content] of Object.entries(DOCS)) {
553
+ fs.writeFileSync(path.join(targetDir, "docs", filename), content, "utf8");
554
+ }
555
+ // Write examples
556
+ for (const [filename, content] of Object.entries(EXAMPLES)) {
557
+ fs.writeFileSync(path.join(targetDir, "examples", filename), content, "utf8");
558
+ }
559
+ if (isJson) {
560
+ console.log(JSON.stringify({
561
+ mode: buildMode,
562
+ baseName,
563
+ directory: targetDir,
564
+ totalDeployed: successful.length,
565
+ totalFailed: failed.length,
566
+ agents: deployResults.map((a) => ({ name: a.name, preset: a.preset, address: a.address, status: a.status })),
567
+ peerAddresses,
568
+ }));
569
+ return;
570
+ }
571
+ const firstAgent = successful[0];
572
+ const agentAddr = firstAgent?.address ?? "";
573
+ const isDeployed = !skipDeploy && !!agentAddr;
574
+ if (failed.length > 0) {
575
+ console.log(`\n Failed (${failed.length}):`);
576
+ for (const agent of failed) {
577
+ console.log(` ${agent.preset}: ${agent.error}`);
578
+ }
579
+ }
580
+ // Install dependencies
581
+ console.log(`\n Installing dependencies...`);
582
+ try {
583
+ execSync("npm install --silent", { cwd: targetDir, stdio: "ignore" });
584
+ console.log(` Done.`);
585
+ }
586
+ catch {
587
+ console.log(` Warning: npm install failed. Run 'npm install' manually.`);
588
+ }
589
+ // ---------------------------------------------------------------
590
+ // Post-deployment summary
591
+ // ---------------------------------------------------------------
592
+ if (isSingleAgent && isDeployed) {
593
+ console.log(`\n ${"=".repeat(56)}`);
594
+ console.log(` Agent deployed!`);
595
+ console.log(` ${"=".repeat(56)}`);
596
+ console.log(` Name: ${baseName}`);
597
+ console.log(` Address: ${agentAddr}`);
598
+ console.log(` Directory: ${targetDir}`);
599
+ console.log(`\n Features installed:`);
600
+ console.log(` Conversation memory Remembers last 10 exchanges per user`);
601
+ console.log(` LLM (ASI1-mini) AI responses via api.asi1.ai`);
602
+ console.log(` Chat Protocol v0.3.0 Discoverable on Agentverse + DeltaV`);
603
+ console.log(` Agent wallet Fetch.ai wallet (auto-created)`);
604
+ console.log(` Session persistence State saved via ctx.storage`);
605
+ console.log(` Commands help, clear, status built in`);
606
+ console.log(`\n Links:`);
607
+ console.log(` Chat: https://agentverse.ai/agents/details/${agentAddr}`);
608
+ console.log(` Docs: https://docs.agentverse.ai`);
609
+ console.log(`\n Created files:`);
610
+ console.log(` agent.py Your agent code (edit this!)`);
611
+ console.log(` CLAUDE.md Context for Claude Code`);
612
+ console.log(` .claude/ Rules, skills, MCP config`);
613
+ console.log(` .cursor/ Cursor IDE config`);
614
+ }
615
+ else if (isSingleAgent && skipDeploy) {
616
+ console.log(`\n Agent scaffolded!`);
617
+ console.log(` Name: ${baseName}`);
618
+ console.log(` Directory: ${targetDir}`);
619
+ console.log(`\n To deploy: npx agentlaunch deploy --code agent.py`);
620
+ }
621
+ else if (!isSingleAgent) {
622
+ console.log(`\n Swarm deployed!`);
623
+ console.log(` Deployed: ${successful.length}/${deployResults.length} agents`);
624
+ if (successful.length > 0) {
625
+ console.log("\n Agent Addresses:");
626
+ for (const agent of successful) {
627
+ console.log(` ${agent.preset.padEnd(14)} ${agent.address}`);
628
+ }
629
+ }
630
+ console.log(` Directory: ${targetDir}`);
631
+ }
632
+ // ---------------------------------------------------------------
633
+ // Editor selection
634
+ // ---------------------------------------------------------------
635
+ const rl2 = readline.createInterface({
636
+ input: process.stdin,
637
+ output: process.stdout,
638
+ });
639
+ console.log(`\n Open project in:\n`);
640
+ console.log(` 1) Claude Code (Recommended)`);
641
+ console.log(` 2) Cursor`);
642
+ console.log(` 3) Windsurf`);
643
+ console.log(` 4) Skip\n`);
644
+ const editorChoice = (await prompt(rl2, " Choice [1]: ")).trim() || "1";
645
+ rl2.close();
646
+ if (editorChoice === "4") {
647
+ console.log(`\n To open later:`);
648
+ console.log(` cd ${dirName} && claude\n`);
649
+ return;
650
+ }
651
+ if (editorChoice === "2") {
652
+ console.log(`\n Opening in Cursor...`);
653
+ const cursor = spawn("cursor", [targetDir], {
654
+ stdio: "inherit",
655
+ detached: true,
656
+ });
657
+ cursor.unref();
658
+ cursor.on("error", () => {
659
+ console.log(` Could not launch Cursor. Open manually:`);
660
+ console.log(` cursor ${targetDir}`);
661
+ });
662
+ return;
663
+ }
664
+ if (editorChoice === "3") {
665
+ console.log(`\n Opening in Windsurf...`);
666
+ const windsurf = spawn("windsurf", [targetDir], {
667
+ stdio: "inherit",
668
+ detached: true,
669
+ });
670
+ windsurf.unref();
671
+ windsurf.on("error", () => {
672
+ console.log(` Could not launch Windsurf. Open manually:`);
673
+ console.log(` windsurf ${targetDir}`);
674
+ });
675
+ return;
676
+ }
677
+ // Default: Claude Code
678
+ console.log(`\n Launching Claude Code...`);
679
+ let welcomePrompt;
680
+ if (isSingleAgent) {
681
+ welcomePrompt = isDeployed
682
+ ? `I just created an agent called "${baseName}" and deployed it to Agentverse at ${agentAddr}.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`
683
+ : `I just scaffolded an agent called "${baseName}". The code is in agent.py but it's NOT deployed yet.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`;
684
+ }
685
+ else {
686
+ const agentList = successful.map((a) => `${a.preset}: ${a.address}`).join(", ");
687
+ welcomePrompt = `I just deployed a ${successful.length}-agent swarm called "${baseName}" with: ${agentList}.\n\nDescription: ${description}\n\nPlease start from Step 1 of the workflow.`;
688
+ }
689
+ const systemPrompt = buildWorkflowSystemPrompt({
690
+ agentName: baseName,
691
+ agentAddress: agentAddr,
692
+ description,
693
+ ticker,
694
+ isDeployed,
695
+ });
696
+ const claudeArgs = [
697
+ welcomePrompt,
698
+ "--append-system-prompt", systemPrompt,
699
+ "--allowedTools", "Bash(npm run *),Bash(npx agentlaunch *),Bash(agentlaunch *),Edit,Read,Write,Glob,Grep",
700
+ ];
701
+ const claude = spawn("claude", claudeArgs, {
702
+ cwd: targetDir,
703
+ stdio: "inherit",
704
+ });
705
+ claude.on("error", (err) => {
706
+ console.error(` Could not launch Claude Code: ${err.message}`);
707
+ console.log(`\n Run manually:`);
708
+ console.log(` cd ${dirName} && claude`);
709
+ });
710
+ return;
711
+ }
712
+ // Validate inputs
713
+ const errors = [];
714
+ if (!name)
715
+ errors.push("--name is required");
716
+ else if (name.length > 32)
717
+ errors.push("--name must be 32 characters or fewer");
718
+ if (!ticker)
719
+ errors.push("--ticker is required");
720
+ else {
721
+ const t = ticker.toUpperCase();
722
+ if (t.length < 2 || t.length > 11) {
723
+ errors.push("--ticker must be 2-11 characters");
724
+ }
725
+ }
726
+ if (!template || !TEMPLATES[template]) {
727
+ template = "chat-memory";
728
+ }
729
+ const chainId = parseInt(options.chain, 10);
730
+ if (![56, 97].includes(chainId)) {
731
+ errors.push("--chain must be 97 (BSC testnet) or 56 (BSC mainnet)");
732
+ }
733
+ if (errors.length > 0) {
734
+ if (isJson) {
735
+ console.log(JSON.stringify({ error: errors.join("; ") }));
736
+ }
737
+ else {
738
+ errors.forEach((e) => console.error(`Error: ${e}`));
739
+ }
740
+ process.exit(1);
741
+ }
742
+ const result = {
743
+ name,
744
+ ticker: ticker.toUpperCase(),
745
+ template,
746
+ };
747
+ // Step 1: Scaffold
748
+ const dirName = sanitizeDirName(name);
749
+ const targetDir = path.resolve(process.cwd(), dirName);
750
+ if (!isJson) {
751
+ console.log(`\nScaffolding agent: ${name} (${TEMPLATES[template]?.label ?? template})`);
752
+ console.log(`Directory: ${targetDir}`);
753
+ }
754
+ if (fs.existsSync(targetDir)) {
755
+ const msg = `Directory "${dirName}" already exists.`;
756
+ if (isJson) {
757
+ console.log(JSON.stringify({ error: msg }));
758
+ }
759
+ else {
760
+ console.error(`Error: ${msg}`);
761
+ }
762
+ process.exit(1);
763
+ }
764
+ // Truncate description if too long
765
+ const presetInfo = options.preset ? GENESIS_PRESETS.find((p) => p.name === options.preset) : undefined;
766
+ const finalDescription = (description || presetInfo?.description || TEMPLATES[template]?.description || "").slice(0, 500);
767
+ // Generate files from templates package
768
+ // If a preset is specified, use swarm-starter template with the preset
769
+ const templateToUse = options.preset ? "swarm-starter" : template;
770
+ const templateVars = {
771
+ agent_name: name,
772
+ description: finalDescription,
773
+ };
774
+ if (options.preset) {
775
+ templateVars.preset = options.preset;
776
+ }
777
+ const generated = generateFromTemplate(templateToUse, templateVars);
778
+ fs.mkdirSync(targetDir, { recursive: true });
779
+ fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
780
+ fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
781
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
782
+ fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
783
+ // Core files
784
+ fs.writeFileSync(path.join(targetDir, "agent.py"), generated.code, "utf8");
785
+ fs.writeFileSync(path.join(targetDir, "README.md"), generated.readme, "utf8");
786
+ fs.writeFileSync(path.join(targetDir, ".env.example"), generated.envExample, "utf8");
787
+ // Write .env with API key if provided
788
+ if (apiKey) {
789
+ const envContent = `# AgentLaunch Environment Variables
790
+ AGENTVERSE_API_KEY=${apiKey}
791
+ AGENT_LAUNCH_API_URL=https://agent-launch.ai/api
792
+ `;
793
+ fs.writeFileSync(path.join(targetDir, ".env"), envContent, "utf8");
794
+ }
795
+ fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), generated.claudeMd, "utf8");
796
+ fs.writeFileSync(path.join(targetDir, "package.json"), buildPackageJson(name), "utf8");
797
+ fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), generated.claudeSettings, "utf8");
798
+ fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), generated.agentlaunchConfig, "utf8");
799
+ // Claude rules (for AI context)
800
+ for (const [filename, content] of Object.entries(RULES)) {
801
+ fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
802
+ }
803
+ // Claude skills (slash commands for AI)
804
+ for (const [filepath, content] of Object.entries(SKILLS)) {
805
+ const skillDir = path.dirname(filepath);
806
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
807
+ fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
808
+ }
809
+ // MCP config (project root - recommended location)
810
+ fs.writeFileSync(path.join(targetDir, ".mcp.json"), generated.claudeSettings, "utf8");
811
+ // Cursor IDE config
812
+ fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
813
+ fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
814
+ // Documentation
815
+ fs.mkdirSync(path.join(targetDir, "docs"), { recursive: true });
816
+ for (const [filename, content] of Object.entries(DOCS)) {
817
+ fs.writeFileSync(path.join(targetDir, "docs", filename), content, "utf8");
818
+ }
819
+ // Examples
820
+ fs.mkdirSync(path.join(targetDir, "examples"), { recursive: true });
821
+ for (const [filename, content] of Object.entries(EXAMPLES)) {
822
+ fs.writeFileSync(path.join(targetDir, "examples", filename), content, "utf8");
823
+ }
824
+ result.scaffoldDir = targetDir;
825
+ if (!isJson) {
826
+ console.log(` Created: agent.py`);
827
+ console.log(` Created: README.md`);
828
+ console.log(` Created: CLAUDE.md`);
829
+ console.log(` Created: package.json`);
830
+ console.log(` Created: .claude/ (settings, rules, skills)`);
831
+ console.log(` Created: .cursor/ (MCP config)`);
832
+ console.log(` Created: docs/ (${Object.keys(DOCS).length} guides)`);
833
+ console.log(` Created: examples/ (${Object.keys(EXAMPLES).length} samples)`);
834
+ }
835
+ // Install dependencies (quiet mode)
836
+ if (!isJson) {
837
+ console.log(`\nInstalling dependencies...`);
838
+ }
839
+ try {
840
+ execSync("npm install --silent", { cwd: targetDir, stdio: "ignore" });
841
+ if (!isJson) {
842
+ console.log(` Done.`);
843
+ }
844
+ }
845
+ catch {
846
+ if (!isJson) {
847
+ console.log(` Warning: npm install failed. Run 'npm install' manually.`);
848
+ }
849
+ }
850
+ // Step 2: Deploy (optional)
851
+ if (doDeploy) {
852
+ let apiKey;
853
+ try {
854
+ apiKey = requireApiKey();
855
+ }
856
+ catch (err) {
857
+ if (isJson) {
858
+ console.log(JSON.stringify({ error: err.message }));
859
+ }
860
+ else {
861
+ console.error(err.message);
862
+ }
863
+ process.exit(1);
864
+ }
865
+ if (!isJson)
866
+ console.log("\n[1/5] Creating agent on Agentverse...");
867
+ if (!isJson)
868
+ console.log("[2/5] Uploading code...");
869
+ if (!isJson)
870
+ console.log("[3/5] Setting secrets...");
871
+ if (!isJson)
872
+ console.log("[4/5] Starting agent...");
873
+ if (!isJson)
874
+ console.log("[5/5] Waiting for compilation...");
875
+ try {
876
+ const agentCode = fs.readFileSync(path.join(targetDir, "agent.py"), "utf8");
877
+ const deployed = await deployAgent({
878
+ apiKey,
879
+ agentName: name,
880
+ sourceCode: agentCode,
881
+ metadata: {
882
+ readme: generated.readme,
883
+ short_description: generated.shortDescription,
884
+ },
885
+ });
886
+ result.agentAddress = deployed.agentAddress;
887
+ result.walletAddress = deployed.walletAddress;
888
+ if (!isJson) {
889
+ console.log(` Address: ${deployed.agentAddress}`);
890
+ if (deployed.walletAddress) {
891
+ console.log(` Wallet: ${deployed.walletAddress}`);
892
+ }
893
+ if (deployed.status === "compiled" || deployed.status === "running") {
894
+ console.log(" Compiled.");
895
+ }
896
+ if (deployed.secretErrors?.length) {
897
+ for (const se of deployed.secretErrors) {
898
+ console.log(` Warning: ${se}`);
899
+ }
900
+ }
901
+ }
902
+ if (deployed.status === "starting") {
903
+ const msg = "Compilation timeout — agent created but did not compile within 60 seconds.";
904
+ if (isJson) {
905
+ console.log(JSON.stringify({
906
+ error: msg,
907
+ agentAddress: deployed.agentAddress,
908
+ partial: result,
909
+ }));
910
+ }
911
+ else {
912
+ console.error(`\nWarning: ${msg}`);
913
+ console.error(`Agent address: ${deployed.agentAddress}`);
914
+ console.error("Check at: https://agentverse.ai/agents");
915
+ }
916
+ process.exit(1);
917
+ }
918
+ }
919
+ catch (err) {
920
+ if (isJson) {
921
+ console.log(JSON.stringify({
922
+ error: `Deploy failed: ${err.message}`,
923
+ partial: result,
924
+ }));
925
+ }
926
+ else {
927
+ console.error(`\nDeploy failed: ${err.message}`);
928
+ }
929
+ process.exit(1);
930
+ }
931
+ }
932
+ // Step 3: Tokenize (optional)
933
+ if (doTokenize) {
934
+ if (!result.agentAddress) {
935
+ if (isJson) {
936
+ console.log(JSON.stringify({
937
+ error: "Cannot tokenize without deploying first. Add --deploy flag.",
938
+ partial: result,
939
+ }));
940
+ }
941
+ else {
942
+ console.error("\nError: Cannot tokenize without deploying first. Add --deploy flag.");
943
+ }
944
+ process.exit(1);
945
+ }
946
+ try {
947
+ const tokenized = await tokenizeAgent(result.agentAddress, name, result.ticker, finalDescription, chainId, isJson);
948
+ result.tokenId = tokenized.tokenId;
949
+ result.tokenAddress = tokenized.tokenAddress;
950
+ result.handoffLink = tokenized.handoffLink;
951
+ }
952
+ catch (err) {
953
+ if (isJson) {
954
+ console.log(JSON.stringify({
955
+ error: `Tokenize failed: ${err.message}`,
956
+ partial: result,
957
+ }));
958
+ }
959
+ else {
960
+ console.error(`\nTokenize failed: ${err.message}`);
961
+ }
962
+ process.exit(1);
963
+ }
964
+ }
965
+ // Output
966
+ if (isJson) {
967
+ console.log(JSON.stringify(result));
968
+ return;
969
+ }
970
+ // Show deployment info if deployed/tokenized
971
+ if (result.agentAddress) {
972
+ console.log(`\nAgent: ${result.agentAddress}`);
973
+ }
974
+ if (result.handoffLink) {
975
+ console.log(`Handoff: ${result.handoffLink}`);
976
+ }
977
+ // Launch Claude Code in the new directory with workflow prompt
978
+ console.log(`\nLaunching Claude Code in ${dirName}...`);
979
+ const batchIsDeployed = !!result.agentAddress;
980
+ const batchWelcome = batchIsDeployed
981
+ ? `I just created an agent called "${name}" and deployed it to Agentverse at ${result.agentAddress}.\n\nDescription: ${description || "No description provided."}\n\nPlease start from Step 1 of the workflow.`
982
+ : `I just scaffolded an agent called "${name}". The code is in agent.py but it's NOT deployed yet.\n\nDescription: ${description || "No description provided."}\n\nPlease start from Step 1 of the workflow.`;
983
+ const batchSystemPrompt = buildWorkflowSystemPrompt({
984
+ agentName: name,
985
+ agentAddress: result.agentAddress ?? "",
986
+ description: description || "No description provided.",
987
+ ticker: result.ticker,
988
+ isDeployed: batchIsDeployed,
989
+ });
990
+ const claudeArgs = [
991
+ batchWelcome,
992
+ "--append-system-prompt", batchSystemPrompt,
993
+ "--allowedTools", "Bash(npm run *),Bash(npx agentlaunch *),Bash(agentlaunch *),Edit,Read,Write,Glob,Grep",
994
+ ];
995
+ const claude = spawn("claude", claudeArgs, {
996
+ cwd: targetDir,
997
+ stdio: "inherit",
998
+ });
999
+ claude.on("error", (err) => {
1000
+ console.error(`Could not launch Claude Code: ${err.message}`);
1001
+ console.log(`\nRun manually:`);
1002
+ console.log(` cd ${dirName} && claude`);
1003
+ });
1004
+ });
1005
+ }
1006
+ /**
1007
+ * Simplified create flow for the default CLI command.
1008
+ * Called by index.ts when running `npx agentlaunch [name]`.
1009
+ */
1010
+ export async function runCreate(options) {
1011
+ const isJson = options.json === true;
1012
+ const skipDeploy = options.skipDeploy === true;
1013
+ let name = options.name?.trim() ?? "";
1014
+ let description = options.description?.trim() ?? "";
1015
+ let apiKey = process.env.AGENTVERSE_API_KEY ?? "";
1016
+ // Interactive prompts (only when not --json)
1017
+ if (!isJson) {
1018
+ const rl = readline.createInterface({
1019
+ input: process.stdin,
1020
+ output: process.stdout,
1021
+ });
1022
+ console.log("\n agentlaunch — Build AI agents on Fetch.ai\n");
1023
+ if (!name) {
1024
+ name = (await prompt(rl, " Agent name: ")).trim();
1025
+ }
1026
+ if (!name) {
1027
+ console.error(" Error: Agent name is required.");
1028
+ rl.close();
1029
+ process.exit(1);
1030
+ }
1031
+ if (!description) {
1032
+ description = (await prompt(rl, " What does it do? ")).trim();
1033
+ }
1034
+ // Prompt for API key
1035
+ if (!skipDeploy) {
1036
+ console.log("\n Get your API key at: https://agentverse.ai/profile/api-keys\n");
1037
+ const existingKey = apiKey ? ` [${apiKey.slice(0, 6)}...${apiKey.slice(-4)}]` : "";
1038
+ const keyInput = (await prompt(rl, ` Agentverse API key${existingKey}: `)).trim();
1039
+ if (keyInput) {
1040
+ apiKey = keyInput;
1041
+ }
1042
+ if (!apiKey) {
1043
+ console.log("\n No API key provided. Scaffolding locally only.\n");
1044
+ }
1045
+ else {
1046
+ process.env.AGENTVERSE_API_KEY = apiKey;
1047
+ console.log("\n Validating API key...");
1048
+ const validation = await validateApiKey(apiKey);
1049
+ if (!validation.valid) {
1050
+ console.error(` Error: ${validation.error ?? "Invalid API key"}`);
1051
+ console.log(" Get your API key at: https://agentverse.ai/profile/api-keys");
1052
+ rl.close();
1053
+ process.exit(1);
1054
+ }
1055
+ console.log(" Valid.\n");
1056
+ }
1057
+ }
1058
+ rl.close();
1059
+ }
1060
+ const shouldDeploy = !skipDeploy && !!apiKey;
1061
+ const baseName = name;
1062
+ const dirName = sanitizeDirName(baseName);
1063
+ const targetDir = path.resolve(process.cwd(), dirName);
1064
+ // Check if directory exists
1065
+ if (fs.existsSync(targetDir)) {
1066
+ if (isJson) {
1067
+ console.log(JSON.stringify({ error: `Directory "${dirName}" already exists.` }));
1068
+ }
1069
+ else {
1070
+ console.error(` Error: Directory "${dirName}" already exists.`);
1071
+ }
1072
+ process.exit(1);
1073
+ }
1074
+ // Generate agent code
1075
+ if (!isJson) {
1076
+ console.log(` ${shouldDeploy ? "Deploying" : "Scaffolding"} ${baseName}...`);
1077
+ }
1078
+ const generated = generateFromTemplate("chat-memory", {
1079
+ agent_name: baseName,
1080
+ description: description || "AI assistant with conversation memory",
1081
+ });
1082
+ // Create directory structure
1083
+ fs.mkdirSync(targetDir, { recursive: true });
1084
+ fs.mkdirSync(path.join(targetDir, ".claude"), { recursive: true });
1085
+ fs.mkdirSync(path.join(targetDir, ".claude", "rules"), { recursive: true });
1086
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills"), { recursive: true });
1087
+ fs.mkdirSync(path.join(targetDir, ".cursor"), { recursive: true });
1088
+ // Write files
1089
+ fs.writeFileSync(path.join(targetDir, "agent.py"), generated.code, "utf8");
1090
+ fs.writeFileSync(path.join(targetDir, "README.md"), generated.readme, "utf8");
1091
+ fs.writeFileSync(path.join(targetDir, ".env.example"), generated.envExample, "utf8");
1092
+ fs.writeFileSync(path.join(targetDir, "CLAUDE.md"), generated.claudeMd, "utf8");
1093
+ fs.writeFileSync(path.join(targetDir, ".claude", "settings.json"), generated.claudeSettings, "utf8");
1094
+ fs.writeFileSync(path.join(targetDir, "agentlaunch.config.json"), generated.agentlaunchConfig, "utf8");
1095
+ // Write .env with API key
1096
+ if (apiKey) {
1097
+ fs.writeFileSync(path.join(targetDir, ".env"), `AGENTVERSE_API_KEY=${apiKey}\nAGENT_LAUNCH_API_URL=https://agent-launch.ai/api\n`, "utf8");
1098
+ }
1099
+ // Write rules
1100
+ for (const [filename, content] of Object.entries(RULES)) {
1101
+ fs.writeFileSync(path.join(targetDir, ".claude", "rules", filename), content, "utf8");
1102
+ }
1103
+ // Write skills
1104
+ for (const [filepath, content] of Object.entries(SKILLS)) {
1105
+ const skillDir = path.dirname(filepath);
1106
+ fs.mkdirSync(path.join(targetDir, ".claude", "skills", skillDir), { recursive: true });
1107
+ fs.writeFileSync(path.join(targetDir, ".claude", "skills", filepath), content, "utf8");
1108
+ }
1109
+ // Write Cursor config
1110
+ fs.writeFileSync(path.join(targetDir, ".cursor", "mcp.json"), CURSOR_MCP_CONFIG, "utf8");
1111
+ fs.writeFileSync(path.join(targetDir, ".cursorrules"), CURSOR_RULES, "utf8");
1112
+ let agentAddress = "";
1113
+ // Deploy if API key provided
1114
+ if (shouldDeploy) {
1115
+ try {
1116
+ const deploySecrets = {
1117
+ AGENTVERSE_API_KEY: apiKey,
1118
+ AGENTLAUNCH_API_KEY: apiKey,
1119
+ LLM_API_KEY: "sk_2a3c92a0b11e4f18b50708cca1a55179ab38a7c2fb7f4eee95fd68e1e28f860b",
1120
+ };
1121
+ const result = await deployAgent({
1122
+ apiKey,
1123
+ agentName: baseName,
1124
+ sourceCode: generated.code,
1125
+ secrets: deploySecrets,
1126
+ metadata: {
1127
+ readme: generated.readme,
1128
+ short_description: generated.shortDescription,
1129
+ },
1130
+ });
1131
+ agentAddress = result.agentAddress;
1132
+ // Update .env with agent address
1133
+ fs.appendFileSync(path.join(targetDir, ".env"), `AGENT_ADDRESS=${agentAddress}\n`);
1134
+ if (!isJson) {
1135
+ console.log(` Address: ${agentAddress}`);
1136
+ if (result.status === "compiled" || result.status === "running") {
1137
+ console.log(" Status: Running");
1138
+ }
1139
+ }
1140
+ }
1141
+ catch (err) {
1142
+ if (isJson) {
1143
+ console.log(JSON.stringify({ error: `Deploy failed: ${err.message}` }));
1144
+ }
1145
+ else {
1146
+ console.error(`\n Deploy failed: ${err.message}`);
1147
+ console.log(` Your code is saved in ${targetDir}/agent.py`);
1148
+ console.log(" Run 'npx agentlaunch deploy' to try again.");
1149
+ }
1150
+ process.exit(1);
1151
+ }
1152
+ }
1153
+ // JSON output
1154
+ if (isJson) {
1155
+ console.log(JSON.stringify({
1156
+ name: baseName,
1157
+ directory: targetDir,
1158
+ agentAddress: agentAddress || undefined,
1159
+ deployed: shouldDeploy,
1160
+ }));
1161
+ return;
1162
+ }
1163
+ // Success output
1164
+ console.log(`\n ${"=".repeat(56)}`);
1165
+ console.log(` ${shouldDeploy ? "Agent deployed!" : "Agent created!"}`);
1166
+ console.log(` ${"=".repeat(56)}`);
1167
+ console.log(` Name: ${baseName}`);
1168
+ if (agentAddress) {
1169
+ console.log(` Address: ${agentAddress}`);
1170
+ }
1171
+ console.log(` Directory: ${targetDir}`);
1172
+ if (agentAddress) {
1173
+ console.log(`\n Chat: https://agentverse.ai/agents/details/${agentAddress}`);
1174
+ }
1175
+ console.log(`\n Next: cd ${dirName} && claude`);
1176
+ }
1177
+ //# sourceMappingURL=create.js.map