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