@standardagents/cli 0.10.1-next.bbd142a → 0.11.0-next.ab7e1ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import fs, { readFileSync } from 'fs';
4
- import path, { dirname, resolve } from 'path';
3
+ import fs3, { readFileSync } from 'fs';
4
+ import path3, { dirname, resolve } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import crypto from 'crypto';
7
7
  import readline from 'readline';
@@ -10,6 +10,7 @@ import chalk from 'chalk';
10
10
  import { parse, modify, applyEdits } from 'jsonc-parser';
11
11
  import { loadFile, writeFile, generateCode } from 'magicast';
12
12
  import { addVitePlugin } from 'magicast/helpers';
13
+ import net from 'net';
13
14
 
14
15
  var logger = {
15
16
  success: (message) => {
@@ -205,15 +206,24 @@ Models define which LLM provider and model to use for prompts.
205
206
  import { defineModel } from '@standardagents/builder';
206
207
 
207
208
  export default defineModel({
208
- name: 'gpt-4o',
209
- provider: 'openai',
210
- model: 'gpt-4o',
211
- fallbacks: ['gpt-4-turbo', 'gpt-3.5-turbo'],
212
- inputPrice: 2.5,
213
- outputPrice: 10,
209
+ name: 'default',
210
+ provider: 'openrouter',
211
+ model: 'google/gemini-3-flash-preview',
212
+ fallbacks: ['fast', 'cheap-heavy'],
214
213
  });
215
214
  \`\`\`
216
215
 
216
+ ## Recommended Models (OpenRouter)
217
+
218
+ | Use Case | Model ID | Description |
219
+ |----------|----------|-------------|
220
+ | Fast/Cheap | \`z-ai/glm-4.5-air\` | Quick responses, low cost |
221
+ | Mid-tier | \`google/gemini-3-flash-preview\` | Good balance of speed and quality |
222
+ | Cheap Heavy | \`z-ai/glm-4.7\` | More capable, still affordable |
223
+ | Heavy | \`google/gemini-3-pro-preview\` | Most capable, for complex tasks |
224
+
225
+ **\u26A0\uFE0F Google Models**: When using Google models (\`google/gemini-*\`), you must enable \`reasoning\` in your prompt configuration or tool calls will fail. See the prompts CLAUDE.md for details.
226
+
217
227
  ## Provider API Keys
218
228
 
219
229
  Set these environment variables in \`.dev.vars\` (local) or Cloudflare secrets (production):
@@ -241,16 +251,16 @@ For OpenRouter, use the full model path:
241
251
 
242
252
  \`\`\`typescript
243
253
  export default defineModel({
244
- name: 'claude-3-opus',
254
+ name: 'heavy',
245
255
  provider: 'openrouter',
246
- model: 'anthropic/claude-3-opus',
247
- includedProviders: ['anthropic'], // Prefer Anthropic's servers
256
+ model: 'google/gemini-3-pro-preview',
257
+ includedProviders: ['google'], // Prefer Google's servers
248
258
  });
249
259
  \`\`\`
250
260
 
251
261
  ## Best Practices
252
262
 
253
- - **Name by use case**, not model ID (e.g., \`fast_reasoning\` not \`gpt-4o-mini\`)
263
+ - **Name by use case**, not model ID (e.g., \`fast\`, \`default\`, \`heavy\`)
254
264
  - **Configure fallbacks** for production reliability
255
265
  - **Set pricing** for cost tracking in logs
256
266
  - **Use environment variables** for API keys, never hardcode
@@ -292,7 +302,7 @@ import { definePrompt } from '@standardagents/builder';
292
302
  export default definePrompt({
293
303
  name: 'customer_support',
294
304
  toolDescription: 'Handle customer support inquiries',
295
- model: 'gpt-4o',
305
+ model: 'default',
296
306
  prompt: \`You are a helpful customer support agent.
297
307
  Always be polite and professional.
298
308
  If you cannot help, escalate to a human.\`,
@@ -309,7 +319,7 @@ Include other prompts for reusable instruction blocks:
309
319
  export default definePrompt({
310
320
  name: 'main_assistant',
311
321
  toolDescription: 'Primary assistant',
312
- model: 'gpt-4o',
322
+ model: 'default',
313
323
  prompt: [
314
324
  { type: 'text', content: 'You are a helpful assistant.\\n\\n' },
315
325
  { type: 'include', prompt: 'common_rules' }, // Includes another prompt
@@ -345,7 +355,7 @@ import { z } from 'zod';
345
355
  export default definePrompt({
346
356
  name: 'data_extractor',
347
357
  toolDescription: 'Extract structured data from text',
348
- model: 'gpt-4o',
358
+ model: 'default',
349
359
  prompt: 'Extract the requested data from the input.',
350
360
  requiredSchema: z.object({
351
361
  text: z.string().describe('Text to extract from'),
@@ -362,7 +372,7 @@ Enable for complex reasoning tasks:
362
372
  export default definePrompt({
363
373
  name: 'code_reviewer',
364
374
  toolDescription: 'Review code for issues',
365
- model: 'claude-3-opus',
375
+ model: 'heavy',
366
376
  prompt: 'Review the code thoroughly...',
367
377
  reasoning: {
368
378
  effort: 'high', // 'low' | 'medium' | 'high'
@@ -372,6 +382,14 @@ export default definePrompt({
372
382
  });
373
383
  \`\`\`
374
384
 
385
+ **\u26A0\uFE0F Google Models Require Reasoning**: When using Google models (\`google/gemini-*\`) via OpenRouter, you **must** include \`reasoning\` configuration or tool calls will fail. At minimum:
386
+
387
+ \`\`\`typescript
388
+ reasoning: {
389
+ effort: 'low', // Can be 'low', 'medium', or 'high'
390
+ },
391
+ \`\`\`
392
+
375
393
  ## Best Practices
376
394
 
377
395
  - **Write clear instructions** - Structure with headers and bullet points
@@ -483,9 +501,15 @@ Other prompts can then include it in their \`handoffAgents\` array.
483
501
  4. **stopOnResponse** - Ends turn when LLM returns text
484
502
  5. **maxSessionTurns** - Ends conversation (hard limit: 250)
485
503
 
504
+ ## Naming Convention
505
+
506
+ Agent names **must** end with the \`_agent\` suffix (e.g., \`support_agent\`, \`research_agent\`).
507
+ This convention is enforced by the builder UI and makes agents easily identifiable in logs and code.
508
+
486
509
  ## Best Practices
487
510
 
488
511
  - **Use descriptive names** (\`customer_support_agent\` not \`agent1\`)
512
+ - **Always use the _agent suffix** - names like \`support_agent\`, \`research_agent\`
489
513
  - **Always set maxTurns** as a safety limit
490
514
  - **Match stop conditions to use case** - chat apps use stopOnResponse, workflows use stopTool
491
515
  - **Use labels** for clarity in logs and UI
@@ -1019,23 +1043,23 @@ export default {
1019
1043
  export { DurableThread, DurableAgentBuilder }
1020
1044
  `;
1021
1045
  function getProjectName(cwd) {
1022
- const packageJsonPath = path.join(cwd, "package.json");
1023
- if (fs.existsSync(packageJsonPath)) {
1046
+ const packageJsonPath = path3.join(cwd, "package.json");
1047
+ if (fs3.existsSync(packageJsonPath)) {
1024
1048
  try {
1025
- const pkg2 = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
1049
+ const pkg2 = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1026
1050
  if (pkg2.name) {
1027
1051
  return pkg2.name.replace(/^@[^/]+\//, "");
1028
1052
  }
1029
1053
  } catch {
1030
1054
  }
1031
1055
  }
1032
- return path.basename(cwd);
1056
+ return path3.basename(cwd);
1033
1057
  }
1034
1058
  function findViteConfig(cwd) {
1035
1059
  const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
1036
1060
  for (const candidate of candidates) {
1037
- const configPath = path.join(cwd, candidate);
1038
- if (fs.existsSync(configPath)) {
1061
+ const configPath = path3.join(cwd, candidate);
1062
+ if (fs3.existsSync(configPath)) {
1039
1063
  return configPath;
1040
1064
  }
1041
1065
  }
@@ -1044,8 +1068,8 @@ function findViteConfig(cwd) {
1044
1068
  function findWranglerConfig(cwd) {
1045
1069
  const candidates = ["wrangler.jsonc", "wrangler.json"];
1046
1070
  for (const candidate of candidates) {
1047
- const configPath = path.join(cwd, candidate);
1048
- if (fs.existsSync(configPath)) {
1071
+ const configPath = path3.join(cwd, candidate);
1072
+ if (fs3.existsSync(configPath)) {
1049
1073
  return configPath;
1050
1074
  }
1051
1075
  }
@@ -1054,7 +1078,7 @@ function findWranglerConfig(cwd) {
1054
1078
  async function modifyViteConfig(configPath, force) {
1055
1079
  try {
1056
1080
  const mod = await loadFile(configPath);
1057
- const code = fs.readFileSync(configPath, "utf-8");
1081
+ const code = fs3.readFileSync(configPath, "utf-8");
1058
1082
  const hasCloudflare = code.includes("@cloudflare/vite-plugin") || code.includes("cloudflare()");
1059
1083
  const hasAgentBuilder = code.includes("@standardagents/builder") || code.includes("agentbuilder()");
1060
1084
  if (hasCloudflare && hasAgentBuilder && !force) {
@@ -1097,7 +1121,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1097
1121
  const existingConfig = findWranglerConfig(cwd);
1098
1122
  if (existingConfig && !force) {
1099
1123
  try {
1100
- const text = fs.readFileSync(existingConfig, "utf-8");
1124
+ const text = fs3.readFileSync(existingConfig, "utf-8");
1101
1125
  const config = parse(text);
1102
1126
  const hasBindings = config.durable_objects?.bindings?.some(
1103
1127
  (b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
@@ -1144,7 +1168,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1144
1168
  }, {});
1145
1169
  result = applyEdits(result, edits);
1146
1170
  }
1147
- fs.writeFileSync(existingConfig, result, "utf-8");
1171
+ fs3.writeFileSync(existingConfig, result, "utf-8");
1148
1172
  logger.success("Updated wrangler.jsonc with Standard Agents configuration");
1149
1173
  return true;
1150
1174
  } catch (error) {
@@ -1152,9 +1176,9 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1152
1176
  return false;
1153
1177
  }
1154
1178
  }
1155
- const wranglerPath = path.join(cwd, "wrangler.jsonc");
1179
+ const wranglerPath = path3.join(cwd, "wrangler.jsonc");
1156
1180
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
1157
- fs.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
1181
+ fs3.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
1158
1182
  logger.success("Created wrangler.jsonc");
1159
1183
  return true;
1160
1184
  }
@@ -1162,33 +1186,33 @@ function getWorkerEntryPoint(cwd) {
1162
1186
  const wranglerConfig = findWranglerConfig(cwd);
1163
1187
  if (wranglerConfig) {
1164
1188
  try {
1165
- const text = fs.readFileSync(wranglerConfig, "utf-8");
1189
+ const text = fs3.readFileSync(wranglerConfig, "utf-8");
1166
1190
  const config = parse(text);
1167
1191
  if (config.main) {
1168
- return path.join(cwd, config.main);
1192
+ return path3.join(cwd, config.main);
1169
1193
  }
1170
1194
  } catch {
1171
1195
  }
1172
1196
  }
1173
- return path.join(cwd, "worker", "index.ts");
1197
+ return path3.join(cwd, "worker", "index.ts");
1174
1198
  }
1175
1199
  async function createOrUpdateWorkerEntry(cwd, force) {
1176
1200
  const entryPath = getWorkerEntryPoint(cwd);
1177
- const entryDir = path.dirname(entryPath);
1178
- if (!fs.existsSync(entryDir)) {
1179
- fs.mkdirSync(entryDir, { recursive: true });
1180
- logger.success(`Created ${path.relative(cwd, entryDir)} directory`);
1181
- }
1182
- if (!fs.existsSync(entryPath)) {
1183
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1184
- logger.success(`Created ${path.relative(cwd, entryPath)}`);
1201
+ const entryDir = path3.dirname(entryPath);
1202
+ if (!fs3.existsSync(entryDir)) {
1203
+ fs3.mkdirSync(entryDir, { recursive: true });
1204
+ logger.success(`Created ${path3.relative(cwd, entryDir)} directory`);
1205
+ }
1206
+ if (!fs3.existsSync(entryPath)) {
1207
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1208
+ logger.success(`Created ${path3.relative(cwd, entryPath)}`);
1185
1209
  return true;
1186
1210
  }
1187
- const content = fs.readFileSync(entryPath, "utf-8");
1211
+ const content = fs3.readFileSync(entryPath, "utf-8");
1188
1212
  const hasRouter = content.includes("virtual:@standardagents/builder") && content.includes("router");
1189
1213
  const hasDurableExports = content.includes("DurableThread") && content.includes("DurableAgentBuilder");
1190
1214
  if (hasRouter && hasDurableExports && !force) {
1191
- logger.info(`${path.relative(cwd, entryPath)} already configured`);
1215
+ logger.info(`${path3.relative(cwd, entryPath)} already configured`);
1192
1216
  return true;
1193
1217
  }
1194
1218
  try {
@@ -1216,17 +1240,17 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1216
1240
  }
1217
1241
  const { code } = generateCode(mod);
1218
1242
  if (force || !hasRouter) {
1219
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1220
- logger.success(`Updated ${path.relative(cwd, entryPath)} with Standard Agents router`);
1243
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1244
+ logger.success(`Updated ${path3.relative(cwd, entryPath)} with Standard Agents router`);
1221
1245
  }
1222
1246
  return true;
1223
1247
  } catch (error) {
1224
1248
  if (force) {
1225
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1226
- logger.success(`Created ${path.relative(cwd, entryPath)}`);
1249
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1250
+ logger.success(`Created ${path3.relative(cwd, entryPath)}`);
1227
1251
  return true;
1228
1252
  }
1229
- logger.warning(`Could not automatically modify ${path.relative(cwd, entryPath)}`);
1253
+ logger.warning(`Could not automatically modify ${path3.relative(cwd, entryPath)}`);
1230
1254
  logger.log("");
1231
1255
  logger.log("Please ensure your worker entry point includes:");
1232
1256
  logger.log("");
@@ -1245,31 +1269,31 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1245
1269
  }
1246
1270
  }
1247
1271
  function createDurableObjects(cwd) {
1248
- const agentsDir = path.join(cwd, "agents");
1249
- if (!fs.existsSync(agentsDir)) {
1250
- fs.mkdirSync(agentsDir, { recursive: true });
1272
+ const agentsDir = path3.join(cwd, "agents");
1273
+ if (!fs3.existsSync(agentsDir)) {
1274
+ fs3.mkdirSync(agentsDir, { recursive: true });
1251
1275
  }
1252
- const threadPath = path.join(agentsDir, "Thread.ts");
1253
- if (!fs.existsSync(threadPath)) {
1254
- fs.writeFileSync(threadPath, THREAD_TS, "utf-8");
1276
+ const threadPath = path3.join(agentsDir, "Thread.ts");
1277
+ if (!fs3.existsSync(threadPath)) {
1278
+ fs3.writeFileSync(threadPath, THREAD_TS, "utf-8");
1255
1279
  logger.success("Created agents/Thread.ts");
1256
1280
  } else {
1257
1281
  logger.info("agents/Thread.ts already exists");
1258
1282
  }
1259
- const agentBuilderPath = path.join(agentsDir, "AgentBuilder.ts");
1260
- if (!fs.existsSync(agentBuilderPath)) {
1261
- fs.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
1283
+ const agentBuilderPath = path3.join(agentsDir, "AgentBuilder.ts");
1284
+ if (!fs3.existsSync(agentBuilderPath)) {
1285
+ fs3.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
1262
1286
  logger.success("Created agents/AgentBuilder.ts");
1263
1287
  } else {
1264
1288
  logger.info("agents/AgentBuilder.ts already exists");
1265
1289
  }
1266
1290
  }
1267
1291
  function createDirectoriesAndDocs(cwd) {
1268
- const agentsDir = path.join(cwd, "agents");
1269
- const rootDocPath = path.join(agentsDir, "CLAUDE.md");
1270
- if (!fs.existsSync(rootDocPath)) {
1271
- fs.writeFileSync(rootDocPath, ROOT_CLAUDE_MD, "utf-8");
1272
- logger.success("Created agents/CLAUDE.md");
1292
+ path3.join(cwd, "agents");
1293
+ const rootDocPath = path3.join(cwd, "CLAUDE.md");
1294
+ if (!fs3.existsSync(rootDocPath)) {
1295
+ fs3.writeFileSync(rootDocPath, ROOT_CLAUDE_MD, "utf-8");
1296
+ logger.success("Created CLAUDE.md");
1273
1297
  }
1274
1298
  const directories = [
1275
1299
  { path: "agents/agents", doc: AGENTS_CLAUDE_MD },
@@ -1280,26 +1304,26 @@ function createDirectoriesAndDocs(cwd) {
1280
1304
  { path: "agents/api", doc: API_CLAUDE_MD }
1281
1305
  ];
1282
1306
  for (const dir of directories) {
1283
- const dirPath = path.join(cwd, dir.path);
1284
- const docPath = path.join(dirPath, "CLAUDE.md");
1285
- if (!fs.existsSync(dirPath)) {
1286
- fs.mkdirSync(dirPath, { recursive: true });
1307
+ const dirPath = path3.join(cwd, dir.path);
1308
+ const docPath = path3.join(dirPath, "CLAUDE.md");
1309
+ if (!fs3.existsSync(dirPath)) {
1310
+ fs3.mkdirSync(dirPath, { recursive: true });
1287
1311
  logger.success(`Created ${dir.path}`);
1288
1312
  }
1289
- if (!fs.existsSync(docPath)) {
1290
- fs.writeFileSync(docPath, dir.doc, "utf-8");
1313
+ if (!fs3.existsSync(docPath)) {
1314
+ fs3.writeFileSync(docPath, dir.doc, "utf-8");
1291
1315
  logger.success(`Created ${dir.path}/CLAUDE.md`);
1292
1316
  }
1293
1317
  }
1294
1318
  }
1295
1319
  function updateTsConfig(cwd) {
1296
- const tsconfigPath = path.join(cwd, "tsconfig.json");
1297
- if (!fs.existsSync(tsconfigPath)) {
1320
+ const tsconfigPath = path3.join(cwd, "tsconfig.json");
1321
+ if (!fs3.existsSync(tsconfigPath)) {
1298
1322
  logger.info("No tsconfig.json found, skipping TypeScript configuration");
1299
1323
  return;
1300
1324
  }
1301
1325
  try {
1302
- const text = fs.readFileSync(tsconfigPath, "utf-8");
1326
+ const text = fs3.readFileSync(tsconfigPath, "utf-8");
1303
1327
  const config = parse(text);
1304
1328
  let result = text;
1305
1329
  const types = config.compilerOptions?.types || [];
@@ -1321,7 +1345,7 @@ function updateTsConfig(cwd) {
1321
1345
  result = applyEdits(result, edits);
1322
1346
  }
1323
1347
  if (newTypes.length > 0 || newIncludes.length > 0) {
1324
- fs.writeFileSync(tsconfigPath, result, "utf-8");
1348
+ fs3.writeFileSync(tsconfigPath, result, "utf-8");
1325
1349
  logger.success("Updated tsconfig.json with Standard Agents types");
1326
1350
  } else {
1327
1351
  logger.info("tsconfig.json already configured");
@@ -1342,21 +1366,21 @@ function cleanupViteDefaults(cwd) {
1342
1366
  ];
1343
1367
  const dirsToRemove = ["src", "public"];
1344
1368
  for (const file of filesToRemove) {
1345
- const filePath = path.join(cwd, file);
1346
- if (fs.existsSync(filePath)) {
1369
+ const filePath = path3.join(cwd, file);
1370
+ if (fs3.existsSync(filePath)) {
1347
1371
  try {
1348
- fs.unlinkSync(filePath);
1372
+ fs3.unlinkSync(filePath);
1349
1373
  } catch {
1350
1374
  }
1351
1375
  }
1352
1376
  }
1353
1377
  for (const dir of dirsToRemove) {
1354
- const dirPath = path.join(cwd, dir);
1355
- if (fs.existsSync(dirPath)) {
1378
+ const dirPath = path3.join(cwd, dir);
1379
+ if (fs3.existsSync(dirPath)) {
1356
1380
  try {
1357
- const files = fs.readdirSync(dirPath);
1381
+ const files = fs3.readdirSync(dirPath);
1358
1382
  if (files.length === 0) {
1359
- fs.rmdirSync(dirPath);
1383
+ fs3.rmdirSync(dirPath);
1360
1384
  }
1361
1385
  } catch {
1362
1386
  }
@@ -1388,8 +1412,8 @@ async function scaffold(options = {}) {
1388
1412
  logger.log("");
1389
1413
  logger.log("Your project structure:");
1390
1414
  logger.log("");
1415
+ logger.log(" CLAUDE.md # Architecture documentation");
1391
1416
  logger.log(" agents/");
1392
- logger.log(" \u251C\u2500\u2500 CLAUDE.md # Architecture documentation");
1393
1417
  logger.log(" \u251C\u2500\u2500 Thread.ts # Durable Object for threads");
1394
1418
  logger.log(" \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for agent state");
1395
1419
  logger.log(" \u251C\u2500\u2500 agents/ # Agent definitions");
@@ -1490,12 +1514,27 @@ function detectPackageManager() {
1490
1514
  if (userAgent.includes("yarn")) return "yarn";
1491
1515
  if (userAgent.includes("bun")) return "bun";
1492
1516
  }
1493
- if (fs.existsSync("pnpm-lock.yaml")) return "pnpm";
1494
- if (fs.existsSync("yarn.lock")) return "yarn";
1495
- if (fs.existsSync("bun.lockb")) return "bun";
1496
- if (fs.existsSync("package-lock.json")) return "npm";
1517
+ if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
1518
+ if (fs3.existsSync("yarn.lock")) return "yarn";
1519
+ if (fs3.existsSync("bun.lockb")) return "bun";
1520
+ if (fs3.existsSync("package-lock.json")) return "npm";
1497
1521
  return "npm";
1498
1522
  }
1523
+ function getBuilderPackageVersion() {
1524
+ try {
1525
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1526
+ const pkgPath = path3.resolve(__dirname2, "../../package.json");
1527
+ const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1528
+ const version = pkg2.version;
1529
+ const prereleaseMatch = version.match(/-([a-z]+)\./i);
1530
+ if (prereleaseMatch) {
1531
+ return `@${prereleaseMatch[1]}`;
1532
+ }
1533
+ return `@^${version}`;
1534
+ } catch {
1535
+ return "";
1536
+ }
1537
+ }
1499
1538
  async function selectPackageManager(detected) {
1500
1539
  const options = ["npm", "pnpm", "yarn", "bun"];
1501
1540
  const detectedIndex = options.indexOf(detected);
@@ -1577,8 +1616,8 @@ async function init(projectNameArg, options = {}) {
1577
1616
  logger.error("Project name is required");
1578
1617
  process.exit(1);
1579
1618
  }
1580
- const projectPath = path.join(cwd, projectName);
1581
- if (fs.existsSync(projectPath)) {
1619
+ const projectPath = path3.join(cwd, projectName);
1620
+ if (fs3.existsSync(projectPath)) {
1582
1621
  logger.error(`Directory "${projectName}" already exists`);
1583
1622
  process.exit(1);
1584
1623
  }
@@ -1610,24 +1649,24 @@ async function init(projectNameArg, options = {}) {
1610
1649
  logger.error("Failed to create Vite project");
1611
1650
  process.exit(1);
1612
1651
  }
1613
- const viteConfigPath = path.join(projectPath, "vite.config.ts");
1614
- if (!fs.existsSync(viteConfigPath)) {
1652
+ const viteConfigPath = path3.join(projectPath, "vite.config.ts");
1653
+ if (!fs3.existsSync(viteConfigPath)) {
1615
1654
  const viteConfigContent = `import { defineConfig } from 'vite'
1616
1655
 
1617
1656
  export default defineConfig({
1618
1657
  plugins: [],
1619
1658
  })
1620
1659
  `;
1621
- fs.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
1660
+ fs3.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
1622
1661
  logger.success("Created vite.config.ts");
1623
1662
  }
1624
- const packageJsonPath = path.join(projectPath, "package.json");
1625
- if (fs.existsSync(packageJsonPath)) {
1626
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
1663
+ const packageJsonPath = path3.join(projectPath, "package.json");
1664
+ if (fs3.existsSync(packageJsonPath)) {
1665
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1627
1666
  const kebabName = toKebabCase(projectName);
1628
1667
  if (packageJson.name !== kebabName) {
1629
1668
  packageJson.name = kebabName;
1630
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
1669
+ fs3.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
1631
1670
  }
1632
1671
  }
1633
1672
  logger.log("");
@@ -1636,11 +1675,12 @@ export default defineConfig({
1636
1675
  try {
1637
1676
  const installCmd = pm === "npm" ? "install" : "add";
1638
1677
  const devFlag = pm === "npm" ? "--save-dev" : "-D";
1678
+ const builderVersion = getBuilderPackageVersion();
1639
1679
  await runCommand(pm, [
1640
1680
  installCmd,
1641
1681
  devFlag,
1642
1682
  "@cloudflare/vite-plugin",
1643
- "@standardagents/builder",
1683
+ `@standardagents/builder${builderVersion}`,
1644
1684
  "wrangler",
1645
1685
  "zod"
1646
1686
  ], projectPath);
@@ -1714,14 +1754,14 @@ export default defineConfig({
1714
1754
  devVarsLines.push("# OPENAI_API_KEY=your-openai-key");
1715
1755
  }
1716
1756
  devVarsLines.push("");
1717
- const devVarsPath = path.join(projectPath, ".dev.vars");
1718
- fs.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
1757
+ const devVarsPath = path3.join(projectPath, ".dev.vars");
1758
+ fs3.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
1719
1759
  logger.success("Created .dev.vars with encryption key");
1720
- const gitignorePath = path.join(projectPath, ".gitignore");
1721
- if (fs.existsSync(gitignorePath)) {
1722
- const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
1760
+ const gitignorePath = path3.join(projectPath, ".gitignore");
1761
+ if (fs3.existsSync(gitignorePath)) {
1762
+ const gitignoreContent = fs3.readFileSync(gitignorePath, "utf-8");
1723
1763
  if (!gitignoreContent.includes(".dev.vars")) {
1724
- fs.appendFileSync(gitignorePath, "\n# Local environment variables\n.dev.vars\n");
1764
+ fs3.appendFileSync(gitignorePath, "\n# Local environment variables\n.dev.vars\n");
1725
1765
  logger.success("Added .dev.vars to .gitignore");
1726
1766
  }
1727
1767
  }
@@ -1743,6 +1783,535 @@ export default defineConfig({
1743
1783
  logger.log("");
1744
1784
  logger.log("For more information, visit: https://standardagents.ai/docs");
1745
1785
  }
1786
+ function getReactPackageVersion() {
1787
+ try {
1788
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1789
+ const pkgPath = path3.resolve(__dirname2, "../package.json");
1790
+ const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1791
+ const version = pkg2.version;
1792
+ const prereleaseMatch = version.match(/-([a-z]+)\./i);
1793
+ if (prereleaseMatch) {
1794
+ return prereleaseMatch[1];
1795
+ }
1796
+ return `^${version}`;
1797
+ } catch {
1798
+ return "latest";
1799
+ }
1800
+ }
1801
+ function getChatSourceDir() {
1802
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1803
+ return path3.resolve(__dirname2, "../chat");
1804
+ }
1805
+ async function prompt2(question) {
1806
+ const rl = readline.createInterface({
1807
+ input: process.stdin,
1808
+ output: process.stdout
1809
+ });
1810
+ return new Promise((resolve2) => {
1811
+ rl.question(question, (answer) => {
1812
+ rl.close();
1813
+ resolve2(answer.trim());
1814
+ });
1815
+ });
1816
+ }
1817
+ function runCommand2(command, args, cwd) {
1818
+ return new Promise((resolve2, reject) => {
1819
+ const child = spawn(command, args, {
1820
+ cwd,
1821
+ stdio: "inherit",
1822
+ shell: false
1823
+ });
1824
+ child.on("close", (code) => {
1825
+ if (code === 0) {
1826
+ resolve2();
1827
+ } else {
1828
+ reject(new Error(`Command failed with exit code ${code}`));
1829
+ }
1830
+ });
1831
+ child.on("error", reject);
1832
+ });
1833
+ }
1834
+ function detectPackageManager2() {
1835
+ const userAgent = process.env.npm_config_user_agent;
1836
+ if (userAgent) {
1837
+ if (userAgent.includes("pnpm")) return "pnpm";
1838
+ if (userAgent.includes("yarn")) return "yarn";
1839
+ if (userAgent.includes("bun")) return "bun";
1840
+ }
1841
+ if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
1842
+ if (fs3.existsSync("yarn.lock")) return "yarn";
1843
+ if (fs3.existsSync("bun.lockb")) return "bun";
1844
+ if (fs3.existsSync("package-lock.json")) return "npm";
1845
+ return "npm";
1846
+ }
1847
+ async function selectPackageManager2(detected) {
1848
+ const options = ["npm", "pnpm", "yarn", "bun"];
1849
+ const detectedIndex = options.indexOf(detected);
1850
+ return new Promise((resolve2) => {
1851
+ const stdin = process.stdin;
1852
+ const stdout = process.stdout;
1853
+ let selectedIndex = detectedIndex;
1854
+ const renderOptions = () => {
1855
+ stdout.write("\x1B[?25l");
1856
+ stdout.write(`\x1B[${options.length + 1}A`);
1857
+ stdout.write("\x1B[0J");
1858
+ stdout.write("Select a package manager:\n");
1859
+ options.forEach((opt, i) => {
1860
+ const marker = i === detectedIndex ? " (detected)" : "";
1861
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1862
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1863
+ stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
1864
+ `);
1865
+ });
1866
+ };
1867
+ stdout.write("Select a package manager:\n");
1868
+ options.forEach((opt, i) => {
1869
+ const marker = i === detectedIndex ? " (detected)" : "";
1870
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1871
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1872
+ stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
1873
+ `);
1874
+ });
1875
+ const wasRaw = stdin.isRaw;
1876
+ if (stdin.isTTY) {
1877
+ stdin.setRawMode(true);
1878
+ }
1879
+ stdin.resume();
1880
+ stdin.setEncoding("utf8");
1881
+ const cleanup = () => {
1882
+ stdin.setRawMode(wasRaw ?? false);
1883
+ stdin.pause();
1884
+ stdin.removeListener("data", onData);
1885
+ stdout.write("\x1B[?25h");
1886
+ };
1887
+ const onData = (key) => {
1888
+ if (key === "\x1B[A" || key === "k") {
1889
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
1890
+ renderOptions();
1891
+ } else if (key === "\x1B[B" || key === "j") {
1892
+ selectedIndex = (selectedIndex + 1) % options.length;
1893
+ renderOptions();
1894
+ } else if (key === "\r" || key === "\n") {
1895
+ cleanup();
1896
+ resolve2(options[selectedIndex]);
1897
+ } else if (key === "") {
1898
+ cleanup();
1899
+ stdout.write("\n");
1900
+ process.exit(1);
1901
+ }
1902
+ };
1903
+ stdin.on("data", onData);
1904
+ });
1905
+ }
1906
+ async function selectFramework() {
1907
+ const options = [
1908
+ { value: "vite", label: "Vite (React + TypeScript)" },
1909
+ { value: "nextjs", label: "Next.js (App Router)" }
1910
+ ];
1911
+ return new Promise((resolve2) => {
1912
+ const stdin = process.stdin;
1913
+ const stdout = process.stdout;
1914
+ let selectedIndex = 0;
1915
+ const renderOptions = () => {
1916
+ stdout.write("\x1B[?25l");
1917
+ stdout.write(`\x1B[${options.length + 1}A`);
1918
+ stdout.write("\x1B[0J");
1919
+ stdout.write("Select a framework:\n");
1920
+ options.forEach((opt, i) => {
1921
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1922
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1923
+ stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
1924
+ `);
1925
+ });
1926
+ };
1927
+ stdout.write("Select a framework:\n");
1928
+ options.forEach((opt, i) => {
1929
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1930
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1931
+ stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
1932
+ `);
1933
+ });
1934
+ const wasRaw = stdin.isRaw;
1935
+ if (stdin.isTTY) {
1936
+ stdin.setRawMode(true);
1937
+ }
1938
+ stdin.resume();
1939
+ stdin.setEncoding("utf8");
1940
+ const cleanup = () => {
1941
+ stdin.setRawMode(wasRaw ?? false);
1942
+ stdin.pause();
1943
+ stdin.removeListener("data", onData);
1944
+ stdout.write("\x1B[?25h");
1945
+ };
1946
+ const onData = (key) => {
1947
+ if (key === "\x1B[A" || key === "k") {
1948
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
1949
+ renderOptions();
1950
+ } else if (key === "\x1B[B" || key === "j") {
1951
+ selectedIndex = (selectedIndex + 1) % options.length;
1952
+ renderOptions();
1953
+ } else if (key === "\r" || key === "\n") {
1954
+ cleanup();
1955
+ resolve2(options[selectedIndex].value);
1956
+ } else if (key === "") {
1957
+ cleanup();
1958
+ stdout.write("\n");
1959
+ process.exit(1);
1960
+ }
1961
+ };
1962
+ stdin.on("data", onData);
1963
+ });
1964
+ }
1965
+ function isValidUrl(url) {
1966
+ try {
1967
+ new URL(url);
1968
+ return true;
1969
+ } catch {
1970
+ return false;
1971
+ }
1972
+ }
1973
+ async function isPortOpen(port) {
1974
+ const tryConnect = (host) => {
1975
+ return new Promise((resolve2) => {
1976
+ const socket = new net.Socket();
1977
+ socket.setTimeout(200);
1978
+ socket.on("connect", () => {
1979
+ socket.destroy();
1980
+ resolve2(true);
1981
+ });
1982
+ socket.on("timeout", () => {
1983
+ socket.destroy();
1984
+ resolve2(false);
1985
+ });
1986
+ socket.on("error", () => {
1987
+ socket.destroy();
1988
+ resolve2(false);
1989
+ });
1990
+ socket.connect(port, host);
1991
+ });
1992
+ };
1993
+ if (await tryConnect("127.0.0.1")) return true;
1994
+ if (await tryConnect("::1")) return true;
1995
+ return false;
1996
+ }
1997
+ async function isAgentbuilderInstance(url) {
1998
+ try {
1999
+ const controller = new AbortController();
2000
+ const timeout = setTimeout(() => controller.abort(), 1e3);
2001
+ const response = await fetch(`${url}/api/agents`, {
2002
+ signal: controller.signal,
2003
+ headers: { "Accept": "application/json" }
2004
+ });
2005
+ clearTimeout(timeout);
2006
+ if (response.status === 401) {
2007
+ return true;
2008
+ }
2009
+ if (response.ok) {
2010
+ const data = await response.json();
2011
+ return Array.isArray(data.agents);
2012
+ }
2013
+ return false;
2014
+ } catch {
2015
+ return false;
2016
+ }
2017
+ }
2018
+ async function detectAgentbuilderInstance() {
2019
+ const portsToScan = [
2020
+ 5173,
2021
+ 5174,
2022
+ 5175,
2023
+ 5176,
2024
+ 5177,
2025
+ 5178,
2026
+ 5179,
2027
+ 5180,
2028
+ 3e3,
2029
+ 3001,
2030
+ 3002,
2031
+ 3003,
2032
+ 8080,
2033
+ 8e3,
2034
+ 4e3
2035
+ ];
2036
+ logger.log("\x1B[90mScanning for running agentbuilder instance...\x1B[0m");
2037
+ const openPorts = [];
2038
+ await Promise.all(
2039
+ portsToScan.map(async (port) => {
2040
+ if (await isPortOpen(port)) {
2041
+ openPorts.push(port);
2042
+ }
2043
+ })
2044
+ );
2045
+ for (const port of openPorts.sort((a, b) => a - b)) {
2046
+ const url = `http://localhost:${port}`;
2047
+ if (await isAgentbuilderInstance(url)) {
2048
+ return url;
2049
+ }
2050
+ }
2051
+ return null;
2052
+ }
2053
+ function copyDir(src, dest, skip = []) {
2054
+ if (!fs3.existsSync(dest)) {
2055
+ fs3.mkdirSync(dest, { recursive: true });
2056
+ }
2057
+ const entries = fs3.readdirSync(src, { withFileTypes: true });
2058
+ for (const entry of entries) {
2059
+ if (skip.includes(entry.name)) continue;
2060
+ const srcPath = path3.join(src, entry.name);
2061
+ const destPath = path3.join(dest, entry.name);
2062
+ if (entry.isDirectory()) {
2063
+ copyDir(srcPath, destPath, skip);
2064
+ } else {
2065
+ fs3.copyFileSync(srcPath, destPath);
2066
+ }
2067
+ }
2068
+ }
2069
+ async function initChat(projectNameArg, options = {}) {
2070
+ const cwd = process.cwd();
2071
+ const chatSourceDir = getChatSourceDir();
2072
+ if (!fs3.existsSync(chatSourceDir)) {
2073
+ logger.error("Chat source files not found. This CLI installation may be corrupted.");
2074
+ process.exit(1);
2075
+ }
2076
+ logger.log("");
2077
+ logger.info("Scaffolding a frontend chat application...");
2078
+ logger.log("");
2079
+ let projectName = projectNameArg;
2080
+ if (!projectName && !options.yes) {
2081
+ projectName = await prompt2("Project name: ");
2082
+ }
2083
+ if (!projectName) {
2084
+ logger.error("Project name is required");
2085
+ process.exit(1);
2086
+ }
2087
+ const projectPath = path3.join(cwd, projectName);
2088
+ if (fs3.existsSync(projectPath)) {
2089
+ logger.error(`Directory "${projectName}" already exists`);
2090
+ process.exit(1);
2091
+ }
2092
+ let framework;
2093
+ if (options.framework) {
2094
+ framework = options.framework;
2095
+ } else if (options.yes) {
2096
+ framework = "vite";
2097
+ } else {
2098
+ framework = await selectFramework();
2099
+ logger.log("");
2100
+ }
2101
+ let serverUrl;
2102
+ if (options.yes) {
2103
+ serverUrl = "http://localhost:5173";
2104
+ } else {
2105
+ const detectedUrl = await detectAgentbuilderInstance();
2106
+ if (detectedUrl) {
2107
+ logger.success(`Found agentbuilder at ${detectedUrl}`);
2108
+ const urlInput = await prompt2(`Agentbuilder dev server URL [${detectedUrl}]: `);
2109
+ serverUrl = urlInput || detectedUrl;
2110
+ } else {
2111
+ logger.log("\x1B[90mNo running agentbuilder instance found\x1B[0m");
2112
+ const urlInput = await prompt2("Agentbuilder dev server URL [http://localhost:5173]: ");
2113
+ serverUrl = urlInput || "http://localhost:5173";
2114
+ }
2115
+ if (!isValidUrl(serverUrl)) {
2116
+ logger.error("Invalid URL format");
2117
+ process.exit(1);
2118
+ }
2119
+ }
2120
+ const detectedPm = detectPackageManager2();
2121
+ let pm;
2122
+ if (options.yes) {
2123
+ pm = detectedPm;
2124
+ } else {
2125
+ pm = await selectPackageManager2(detectedPm);
2126
+ logger.log("");
2127
+ }
2128
+ logger.log("");
2129
+ logger.info(`Creating ${framework === "vite" ? "Vite" : "Next.js"} project...`);
2130
+ logger.log("");
2131
+ fs3.mkdirSync(projectPath, { recursive: true });
2132
+ const reactVersion = getReactPackageVersion();
2133
+ if (framework === "vite") {
2134
+ await scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
2135
+ } else {
2136
+ await scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
2137
+ }
2138
+ logger.log("");
2139
+ logger.info("Installing dependencies...");
2140
+ logger.log("");
2141
+ try {
2142
+ await runCommand2(pm, pm === "npm" ? ["install"] : ["install"], projectPath);
2143
+ } catch (error) {
2144
+ logger.error("Failed to install dependencies");
2145
+ process.exit(1);
2146
+ }
2147
+ logger.log("");
2148
+ logger.success("Chat UI scaffolded successfully!");
2149
+ logger.log("");
2150
+ logger.info("Starting development server...");
2151
+ logger.log("");
2152
+ const devArgs = pm === "npm" ? ["run", "dev"] : ["dev"];
2153
+ const devProcess = spawn(pm, devArgs, {
2154
+ cwd: projectPath,
2155
+ stdio: ["inherit", "pipe", "pipe"],
2156
+ shell: false
2157
+ });
2158
+ let browserOpened = false;
2159
+ const openBrowser = (url) => {
2160
+ if (browserOpened) return;
2161
+ browserOpened = true;
2162
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2163
+ spawn(openCmd, [url], { stdio: "ignore", detached: true }).unref();
2164
+ };
2165
+ devProcess.stdout?.on("data", (data) => {
2166
+ const text = data.toString();
2167
+ process.stdout.write(data);
2168
+ const urlMatch = text.match(/Local:\s+(https?:\/\/localhost:\d+\/?)/);
2169
+ if (urlMatch && !browserOpened) {
2170
+ openBrowser(urlMatch[1]);
2171
+ }
2172
+ });
2173
+ devProcess.stderr?.on("data", (data) => {
2174
+ process.stderr.write(data);
2175
+ });
2176
+ devProcess.on("error", (error) => {
2177
+ logger.error(`Failed to start dev server: ${error.message}`);
2178
+ process.exit(1);
2179
+ });
2180
+ }
2181
+ async function scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
2182
+ copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
2183
+ logger.success("Copied src/");
2184
+ const viteDir = path3.join(chatSourceDir, "vite");
2185
+ let indexHtml = fs3.readFileSync(path3.join(viteDir, "index.html"), "utf-8");
2186
+ indexHtml = indexHtml.replace('src="/main.tsx"', 'src="/src/main.tsx"');
2187
+ fs3.writeFileSync(path3.join(projectPath, "index.html"), indexHtml, "utf-8");
2188
+ logger.success("Created index.html");
2189
+ fs3.copyFileSync(path3.join(viteDir, "main.tsx"), path3.join(projectPath, "src", "main.tsx"));
2190
+ logger.success("Created src/main.tsx");
2191
+ let viteConfig = fs3.readFileSync(path3.join(viteDir, "vite.config.ts"), "utf-8");
2192
+ viteConfig = `import { defineConfig } from 'vite'
2193
+ import react from '@vitejs/plugin-react'
2194
+ import tailwindcss from '@tailwindcss/vite'
2195
+
2196
+ export default defineConfig({
2197
+ plugins: [
2198
+ react(),
2199
+ tailwindcss(),
2200
+ ],
2201
+ })
2202
+ `;
2203
+ fs3.writeFileSync(path3.join(projectPath, "vite.config.ts"), viteConfig, "utf-8");
2204
+ logger.success("Created vite.config.ts");
2205
+ fs3.copyFileSync(path3.join(chatSourceDir, "tsconfig.json"), path3.join(projectPath, "tsconfig.json"));
2206
+ const tsconfig = JSON.parse(fs3.readFileSync(path3.join(projectPath, "tsconfig.json"), "utf-8"));
2207
+ tsconfig.include = ["src"];
2208
+ delete tsconfig.compilerOptions?.paths;
2209
+ fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2210
+ logger.success("Created tsconfig.json");
2211
+ fs3.copyFileSync(path3.join(chatSourceDir, "package.json"), path3.join(projectPath, "package.json"));
2212
+ const pkg2 = JSON.parse(fs3.readFileSync(path3.join(projectPath, "package.json"), "utf-8"));
2213
+ pkg2.name = projectName;
2214
+ pkg2.scripts = {
2215
+ dev: "vite",
2216
+ build: "vite build",
2217
+ preview: "vite preview"
2218
+ };
2219
+ delete pkg2.private;
2220
+ if (pkg2.dependencies?.["@standardagents/react"]) {
2221
+ pkg2.dependencies["@standardagents/react"] = reactVersion;
2222
+ }
2223
+ delete pkg2.dependencies?.["next"];
2224
+ delete pkg2.devDependencies?.["@tailwindcss/postcss"];
2225
+ delete pkg2.devDependencies?.["postcss"];
2226
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2227
+ logger.success("Created package.json");
2228
+ const envContent = `# Agentbuilder connection
2229
+ VITE_AGENTBUILDER_URL=${serverUrl}
2230
+ `;
2231
+ fs3.writeFileSync(path3.join(projectPath, ".env"), envContent, "utf-8");
2232
+ logger.success("Created .env");
2233
+ fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
2234
+ dist
2235
+ .env
2236
+ `, "utf-8");
2237
+ logger.success("Created .gitignore");
2238
+ let mainTsx = fs3.readFileSync(path3.join(projectPath, "src", "main.tsx"), "utf-8");
2239
+ mainTsx = mainTsx.replace(/from '\.\.\/src\//g, "from './").replace(/import '\.\.\/src\//g, "import './");
2240
+ fs3.writeFileSync(path3.join(projectPath, "src", "main.tsx"), mainTsx, "utf-8");
2241
+ if (fs3.existsSync(path3.join(viteDir, "favicon.svg"))) {
2242
+ fs3.copyFileSync(path3.join(viteDir, "favicon.svg"), path3.join(projectPath, "favicon.svg"));
2243
+ logger.success("Created favicon.svg");
2244
+ }
2245
+ }
2246
+ async function scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
2247
+ copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
2248
+ logger.success("Copied src/");
2249
+ const nextDir = path3.join(chatSourceDir, "next");
2250
+ copyDir(path3.join(nextDir, "app"), path3.join(projectPath, "app"), []);
2251
+ logger.success("Copied app/");
2252
+ fs3.copyFileSync(path3.join(nextDir, "next.config.ts"), path3.join(projectPath, "next.config.ts"));
2253
+ logger.success("Created next.config.ts");
2254
+ fs3.copyFileSync(path3.join(nextDir, "postcss.config.mjs"), path3.join(projectPath, "postcss.config.mjs"));
2255
+ logger.success("Created postcss.config.mjs");
2256
+ const tsconfig = {
2257
+ compilerOptions: {
2258
+ target: "ES2017",
2259
+ lib: ["dom", "dom.iterable", "esnext"],
2260
+ allowJs: true,
2261
+ skipLibCheck: true,
2262
+ strict: true,
2263
+ noEmit: true,
2264
+ esModuleInterop: true,
2265
+ module: "esnext",
2266
+ moduleResolution: "bundler",
2267
+ resolveJsonModule: true,
2268
+ isolatedModules: true,
2269
+ jsx: "preserve",
2270
+ incremental: true,
2271
+ plugins: [{ name: "next" }],
2272
+ paths: {
2273
+ "@/*": ["./src/*"]
2274
+ }
2275
+ },
2276
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2277
+ exclude: ["node_modules"]
2278
+ };
2279
+ fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2280
+ logger.success("Created tsconfig.json");
2281
+ const pkg2 = JSON.parse(fs3.readFileSync(path3.join(chatSourceDir, "package.json"), "utf-8"));
2282
+ pkg2.name = projectName;
2283
+ pkg2.scripts = {
2284
+ dev: "next dev",
2285
+ build: "next build",
2286
+ start: "next start"
2287
+ };
2288
+ delete pkg2.private;
2289
+ if (pkg2.dependencies?.["@standardagents/react"]) {
2290
+ pkg2.dependencies["@standardagents/react"] = reactVersion;
2291
+ }
2292
+ delete pkg2.devDependencies?.["@tailwindcss/vite"];
2293
+ delete pkg2.devDependencies?.["@vitejs/plugin-react"];
2294
+ delete pkg2.devDependencies?.["vite"];
2295
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2296
+ logger.success("Created package.json");
2297
+ const envContent = `# Agentbuilder connection
2298
+ NEXT_PUBLIC_AGENTBUILDER_URL=${serverUrl}
2299
+ `;
2300
+ fs3.writeFileSync(path3.join(projectPath, ".env.local"), envContent, "utf-8");
2301
+ logger.success("Created .env.local");
2302
+ fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
2303
+ .next
2304
+ out
2305
+ .env.local
2306
+ `, "utf-8");
2307
+ logger.success("Created .gitignore");
2308
+ let layoutTsx = fs3.readFileSync(path3.join(projectPath, "app", "layout.tsx"), "utf-8");
2309
+ layoutTsx = layoutTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
2310
+ fs3.writeFileSync(path3.join(projectPath, "app", "layout.tsx"), layoutTsx, "utf-8");
2311
+ let pageTsx = fs3.readFileSync(path3.join(projectPath, "app", "page.tsx"), "utf-8");
2312
+ pageTsx = pageTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
2313
+ fs3.writeFileSync(path3.join(projectPath, "app", "page.tsx"), pageTsx, "utf-8");
2314
+ }
1746
2315
 
1747
2316
  // src/index.ts
1748
2317
  var __dirname$1 = dirname(fileURLToPath(import.meta.url));
@@ -1751,6 +2320,7 @@ var program = new Command();
1751
2320
  program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version(pkg.version);
1752
2321
  program.command("init [project-name]").description("Create a new Standard Agents project (runs create vite, then scaffold)").option("-y, --yes", "Skip prompts and use defaults").option("--template <template>", "Vite template to use", "vanilla-ts").action(init);
1753
2322
  program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
2323
+ program.command("init-chat [project-name]").description("Scaffold a frontend chat application").option("-y, --yes", "Skip prompts and use defaults").option("--framework <framework>", "Framework to use (vite or nextjs)").action(initChat);
1754
2324
  program.parse();
1755
2325
  //# sourceMappingURL=index.js.map
1756
2326
  //# sourceMappingURL=index.js.map