@standardagents/cli 0.10.1-next.bbd142a → 0.11.0-next.14455ae

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) => {
@@ -115,30 +116,32 @@ agents/
115
116
 
116
117
  Files are **auto-discovered** at runtime. No manual registration needed.
117
118
 
118
- ## FlowState
119
+ ## ThreadState
119
120
 
120
- \`FlowState\` is the central state object available in tools and hooks:
121
+ \`ThreadState\` is the state object available in tools and hooks:
121
122
 
122
123
  \`\`\`typescript
123
- interface FlowState {
124
- thread: {
125
- id: string; // Thread identifier
126
- instance: DurableThread; // Durable Object instance
127
- };
128
- agent: AgentConfig; // Current agent configuration
129
- prompt: PromptConfig; // Current prompt configuration
130
- model: ModelConfig; // Current model configuration
131
- messages: Message[]; // Conversation history
132
- env: Env; // Cloudflare Worker environment
133
- rootState: FlowState; // Parent state (for sub-prompts)
124
+ interface ThreadState {
125
+ // Identity (readonly)
126
+ threadId: string; // Thread identifier
127
+ agentId: string; // Agent identifier
128
+ userId: string | null; // User identifier (if set)
129
+ createdAt: number; // Creation timestamp
130
+
131
+ // Methods
132
+ getMessages(options?): Promise<MessagesResult>; // Get conversation history
133
+ injectMessage(input): Promise<Message>; // Add message to thread
134
+ loadModel(name): Promise<ModelConfig>; // Load model definition
135
+ loadPrompt(name): Promise<PromptConfig>; // Load prompt definition
136
+ loadAgent(name): Promise<AgentConfig>; // Load agent definition
134
137
  }
135
138
  \`\`\`
136
139
 
137
140
  Access in tools:
138
141
  \`\`\`typescript
139
142
  export default defineTool('...', schema, async (flow, args) => {
140
- const threadId = flow.thread.id;
141
- const messages = flow.messages;
143
+ const threadId = flow.threadId;
144
+ const { messages } = await flow.getMessages();
142
145
  // ...
143
146
  });
144
147
  \`\`\`
@@ -205,15 +208,24 @@ Models define which LLM provider and model to use for prompts.
205
208
  import { defineModel } from '@standardagents/builder';
206
209
 
207
210
  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,
211
+ name: 'default',
212
+ provider: 'openrouter',
213
+ model: 'google/gemini-3-flash-preview',
214
+ fallbacks: ['fast', 'cheap-heavy'],
214
215
  });
215
216
  \`\`\`
216
217
 
218
+ ## Recommended Models (OpenRouter)
219
+
220
+ | Use Case | Model ID | Description |
221
+ |----------|----------|-------------|
222
+ | Fast/Cheap | \`z-ai/glm-4.5-air\` | Quick responses, low cost |
223
+ | Mid-tier | \`google/gemini-3-flash-preview\` | Good balance of speed and quality |
224
+ | Cheap Heavy | \`z-ai/glm-4.7\` | More capable, still affordable |
225
+ | Heavy | \`google/gemini-3-pro-preview\` | Most capable, for complex tasks |
226
+
227
+ **\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.
228
+
217
229
  ## Provider API Keys
218
230
 
219
231
  Set these environment variables in \`.dev.vars\` (local) or Cloudflare secrets (production):
@@ -241,16 +253,16 @@ For OpenRouter, use the full model path:
241
253
 
242
254
  \`\`\`typescript
243
255
  export default defineModel({
244
- name: 'claude-3-opus',
256
+ name: 'heavy',
245
257
  provider: 'openrouter',
246
- model: 'anthropic/claude-3-opus',
247
- includedProviders: ['anthropic'], // Prefer Anthropic's servers
258
+ model: 'google/gemini-3-pro-preview',
259
+ includedProviders: ['google'], // Prefer Google's servers
248
260
  });
249
261
  \`\`\`
250
262
 
251
263
  ## Best Practices
252
264
 
253
- - **Name by use case**, not model ID (e.g., \`fast_reasoning\` not \`gpt-4o-mini\`)
265
+ - **Name by use case**, not model ID (e.g., \`fast\`, \`default\`, \`heavy\`)
254
266
  - **Configure fallbacks** for production reliability
255
267
  - **Set pricing** for cost tracking in logs
256
268
  - **Use environment variables** for API keys, never hardcode
@@ -273,15 +285,12 @@ Prompts define system instructions, model selection, and available tools for LLM
273
285
  | \`toolDescription\` | \`string\` | Yes | Description shown when used as a tool |
274
286
  | \`prompt\` | \`string \\| StructuredPrompt\` | Yes | System instructions |
275
287
  | \`model\` | \`string\` | Yes | Model name to use (references \`agents/models/\`) |
276
- | \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools for LLM |
277
- | \`handoffAgents\` | \`string[]\` | No | Agents this prompt can hand off to |
288
+ | \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools (include ai_human agent names for handoffs) |
278
289
  | \`includeChat\` | \`boolean\` | No | Include full conversation history |
279
290
  | \`includePastTools\` | \`boolean\` | No | Include previous tool results |
280
291
  | \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
281
292
  | \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
282
293
  | \`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
294
  | \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
286
295
 
287
296
  ## Basic Example
@@ -292,7 +301,7 @@ import { definePrompt } from '@standardagents/builder';
292
301
  export default definePrompt({
293
302
  name: 'customer_support',
294
303
  toolDescription: 'Handle customer support inquiries',
295
- model: 'gpt-4o',
304
+ model: 'default',
296
305
  prompt: \`You are a helpful customer support agent.
297
306
  Always be polite and professional.
298
307
  If you cannot help, escalate to a human.\`,
@@ -309,7 +318,7 @@ Include other prompts for reusable instruction blocks:
309
318
  export default definePrompt({
310
319
  name: 'main_assistant',
311
320
  toolDescription: 'Primary assistant',
312
- model: 'gpt-4o',
321
+ model: 'default',
313
322
  prompt: [
314
323
  { type: 'text', content: 'You are a helpful assistant.\\n\\n' },
315
324
  { type: 'include', prompt: 'common_rules' }, // Includes another prompt
@@ -345,7 +354,7 @@ import { z } from 'zod';
345
354
  export default definePrompt({
346
355
  name: 'data_extractor',
347
356
  toolDescription: 'Extract structured data from text',
348
- model: 'gpt-4o',
357
+ model: 'default',
349
358
  prompt: 'Extract the requested data from the input.',
350
359
  requiredSchema: z.object({
351
360
  text: z.string().describe('Text to extract from'),
@@ -362,7 +371,7 @@ Enable for complex reasoning tasks:
362
371
  export default definePrompt({
363
372
  name: 'code_reviewer',
364
373
  toolDescription: 'Review code for issues',
365
- model: 'claude-3-opus',
374
+ model: 'heavy',
366
375
  prompt: 'Review the code thoroughly...',
367
376
  reasoning: {
368
377
  effort: 'high', // 'low' | 'medium' | 'high'
@@ -372,6 +381,14 @@ export default definePrompt({
372
381
  });
373
382
  \`\`\`
374
383
 
384
+ **\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:
385
+
386
+ \`\`\`typescript
387
+ reasoning: {
388
+ effort: 'low', // Can be 'low', 'medium', or 'high'
389
+ },
390
+ \`\`\`
391
+
375
392
  ## Best Practices
376
393
 
377
394
  - **Write clear instructions** - Structure with headers and bullet points
@@ -473,7 +490,7 @@ export default defineAgent({
473
490
  });
474
491
  \`\`\`
475
492
 
476
- Other prompts can then include it in their \`handoffAgents\` array.
493
+ Other prompts can then include the agent name in their \`tools\` array to enable handoffs.
477
494
 
478
495
  ## Stop Conditions (Priority Order)
479
496
 
@@ -483,9 +500,15 @@ Other prompts can then include it in their \`handoffAgents\` array.
483
500
  4. **stopOnResponse** - Ends turn when LLM returns text
484
501
  5. **maxSessionTurns** - Ends conversation (hard limit: 250)
485
502
 
503
+ ## Naming Convention
504
+
505
+ Agent names **must** end with the \`_agent\` suffix (e.g., \`support_agent\`, \`research_agent\`).
506
+ This convention is enforced by the builder UI and makes agents easily identifiable in logs and code.
507
+
486
508
  ## Best Practices
487
509
 
488
510
  - **Use descriptive names** (\`customer_support_agent\` not \`agent1\`)
511
+ - **Always use the _agent suffix** - names like \`support_agent\`, \`research_agent\`
489
512
  - **Always set maxTurns** as a safety limit
490
513
  - **Match stop conditions to use case** - chat apps use stopOnResponse, workflows use stopTool
491
514
  - **Use labels** for clarity in logs and UI
@@ -504,12 +527,12 @@ Tools extend agent capabilities beyond text generation. They allow LLMs to fetch
504
527
  ## Function Signature
505
528
 
506
529
  \`\`\`typescript
507
- defineTool(
530
+ defineTool({
508
531
  description: string, // What the tool does (shown to LLM)
509
- args: z.ZodObject, // Input validation schema
510
- handler: (flow, args) => ToolResult, // Implementation
511
- returnSchema?: z.ZodObject // Optional output schema
512
- )
532
+ args?: z.ZodObject, // Input validation schema (optional)
533
+ execute: (state, args) => ToolResult, // Implementation
534
+ tenvs?: z.ZodObject, // Thread environment variables (optional)
535
+ })
513
536
  \`\`\`
514
537
 
515
538
  ## Basic Example
@@ -518,20 +541,20 @@ defineTool(
518
541
  import { defineTool } from '@standardagents/builder';
519
542
  import { z } from 'zod';
520
543
 
521
- export default defineTool(
522
- 'Search the knowledge base for articles matching a query',
523
- z.object({
544
+ export default defineTool({
545
+ description: 'Search the knowledge base for articles matching a query',
546
+ args: z.object({
524
547
  query: z.string().describe('Search query'),
525
548
  limit: z.number().optional().default(10).describe('Max results'),
526
549
  }),
527
- async (flow, args) => {
550
+ execute: async (state, args) => {
528
551
  const results = await searchKB(args.query, args.limit);
529
552
  return {
530
553
  status: 'success',
531
554
  result: JSON.stringify(results),
532
555
  };
533
- }
534
- );
556
+ },
557
+ });
535
558
  \`\`\`
536
559
 
537
560
  ## ToolResult Interface
@@ -541,44 +564,45 @@ export default defineTool(
541
564
  | \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
542
565
  | \`result\` | \`string\` | Tool output (required for success) |
543
566
  | \`error\` | \`string\` | Error message (for error status) |
567
+ | \`attachments\` | \`ToolAttachment[]\` | Files to attach (images, etc.) |
544
568
 
545
- ## FlowState Access
569
+ ## ThreadState Access
546
570
 
547
- The \`flow\` parameter provides access to:
571
+ The \`state\` parameter provides access to:
548
572
 
549
573
  \`\`\`typescript
550
- async (flow, args) => {
574
+ execute: async (state, args) => {
551
575
  // Thread information
552
- const threadId = flow.thread.id;
553
- const thread = flow.thread.instance;
576
+ const threadId = state.thread.id;
577
+ const thread = state.thread.instance;
554
578
 
555
579
  // Configuration
556
- const agentName = flow.agent.name;
557
- const modelName = flow.model.name;
580
+ const agentName = state.agent.name;
581
+ const modelName = state.model.name;
558
582
 
559
583
  // Message history
560
- const messages = flow.messages;
584
+ const messages = state.messages;
561
585
 
562
586
  // Environment bindings
563
- const env = flow.env;
587
+ const env = state.env;
564
588
 
565
589
  // Parent state (for sub-prompts)
566
- const root = flow.rootState;
590
+ const root = state.rootState;
567
591
  }
568
592
  \`\`\`
569
593
 
570
594
  ## Tool Without Arguments
571
595
 
572
596
  \`\`\`typescript
573
- export default defineTool(
574
- 'Get current server time',
575
- async (flow) => {
597
+ export default defineTool({
598
+ description: 'Get current server time',
599
+ execute: async (state) => {
576
600
  return {
577
601
  status: 'success',
578
602
  result: new Date().toISOString(),
579
603
  };
580
- }
581
- );
604
+ },
605
+ });
582
606
  \`\`\`
583
607
 
584
608
  ## Error Handling
@@ -586,10 +610,10 @@ export default defineTool(
586
610
  Return errors gracefully instead of throwing:
587
611
 
588
612
  \`\`\`typescript
589
- export default defineTool(
590
- 'Fetch user data',
591
- z.object({ userId: z.string() }),
592
- async (flow, args) => {
613
+ export default defineTool({
614
+ description: 'Fetch user data',
615
+ args: z.object({ userId: z.string() }),
616
+ execute: async (state, args) => {
593
617
  try {
594
618
  const user = await fetchUser(args.userId);
595
619
  return { status: 'success', result: JSON.stringify(user) };
@@ -599,8 +623,8 @@ export default defineTool(
599
623
  error: \`Failed to fetch user: \${error.message}\`,
600
624
  };
601
625
  }
602
- }
603
- );
626
+ },
627
+ });
604
628
  \`\`\`
605
629
 
606
630
  ## Queueing Additional Tools
@@ -610,21 +634,21 @@ Queue another tool to run after the current one:
610
634
  \`\`\`typescript
611
635
  import { queueTool } from '@standardagents/builder';
612
636
 
613
- export default defineTool(
614
- 'Create order and send confirmation',
615
- z.object({ items: z.array(z.string()) }),
616
- async (flow, args) => {
637
+ export default defineTool({
638
+ description: 'Create order and send confirmation',
639
+ args: z.object({ items: z.array(z.string()) }),
640
+ execute: async (state, args) => {
617
641
  const order = await createOrder(args.items);
618
642
 
619
643
  // Queue email tool to run next
620
- queueTool(flow, 'send_confirmation_email', {
644
+ queueTool(state, 'send_confirmation_email', {
621
645
  orderId: order.id,
622
- email: flow.thread.metadata.userEmail,
646
+ email: state.thread.metadata.userEmail,
623
647
  });
624
648
 
625
649
  return { status: 'success', result: JSON.stringify(order) };
626
- }
627
- );
650
+ },
651
+ });
628
652
  \`\`\`
629
653
 
630
654
  ## Injecting Messages
@@ -634,17 +658,61 @@ Add messages without triggering re-execution:
634
658
  \`\`\`typescript
635
659
  import { injectMessage } from '@standardagents/builder';
636
660
 
637
- export default defineTool(
638
- 'Log audit event',
639
- z.object({ event: z.string() }),
640
- async (flow, args) => {
641
- await injectMessage(flow, {
661
+ export default defineTool({
662
+ description: 'Log audit event',
663
+ args: z.object({ event: z.string() }),
664
+ execute: async (state, args) => {
665
+ await injectMessage(state, {
642
666
  role: 'system',
643
667
  content: \`[AUDIT] \${args.event}\`,
644
668
  });
645
669
  return { status: 'success', result: 'Logged' };
646
- }
647
- );
670
+ },
671
+ });
672
+ \`\`\`
673
+
674
+ ## Thread Environment Variables (tenvs)
675
+
676
+ Tools can declare required thread environment variables:
677
+
678
+ \`\`\`typescript
679
+ export default defineTool({
680
+ description: 'Search uploaded documents',
681
+ args: z.object({ query: z.string() }),
682
+ execute: async (state, args) => {
683
+ const vectorStoreId = state.tenvs.vectorStoreId;
684
+ const results = await searchVectorStore(vectorStoreId, args.query);
685
+ return { status: 'success', result: JSON.stringify(results) };
686
+ },
687
+ tenvs: z.object({
688
+ vectorStoreId: z.string().describe('OpenAI Vector Store ID'),
689
+ }),
690
+ });
691
+ \`\`\`
692
+
693
+ ## Returning Attachments
694
+
695
+ Tools can return file attachments (e.g., generated images):
696
+
697
+ \`\`\`typescript
698
+ export default defineTool({
699
+ description: 'Generate a chart from data',
700
+ args: z.object({ data: z.array(z.number()) }),
701
+ execute: async (state, args) => {
702
+ const chartImage = await generateChart(args.data);
703
+ return {
704
+ status: 'success',
705
+ result: 'Chart generated successfully',
706
+ attachments: [{
707
+ name: 'chart.png',
708
+ mimeType: 'image/png',
709
+ data: chartImage.base64,
710
+ width: 800,
711
+ height: 600,
712
+ }],
713
+ };
714
+ },
715
+ });
648
716
  \`\`\`
649
717
 
650
718
  ## Best Practices
@@ -654,7 +722,7 @@ export default defineTool(
654
722
  - **Handle errors gracefully** - return error status, don't throw
655
723
  - **Use snake_case** for file names (\`search_knowledge_base.ts\`)
656
724
  - **Keep tools focused** - one task per tool
657
- - **Use \`flow.rootState\`** when queueing from sub-prompts
725
+ - **Use \`state.rootState\`** when queueing from sub-prompts
658
726
 
659
727
  ## Supported Zod Types
660
728
 
@@ -958,17 +1026,19 @@ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
958
1026
  `;
959
1027
 
960
1028
  // src/commands/scaffold.ts
961
- var WRANGLER_TEMPLATE = (name) => `{
1029
+ var WRANGLER_TEMPLATE = (name) => {
1030
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1031
+ return `{
962
1032
  "$schema": "node_modules/wrangler/config-schema.json",
963
1033
  "name": "${name}",
964
1034
  "main": "worker/index.ts",
965
- "compatibility_date": "2025-01-01",
1035
+ "compatibility_date": "${today}",
966
1036
  "compatibility_flags": ["nodejs_compat"],
967
1037
  "observability": {
968
1038
  "enabled": true
969
1039
  },
970
1040
  "assets": {
971
- "directory": "dist",
1041
+ "directory": "dist/client",
972
1042
  "not_found_handling": "single-page-application",
973
1043
  "binding": "ASSETS",
974
1044
  "run_worker_first": ["/**"]
@@ -997,6 +1067,7 @@ var WRANGLER_TEMPLATE = (name) => `{
997
1067
  ]
998
1068
  }
999
1069
  `;
1070
+ };
1000
1071
  var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
1001
1072
 
1002
1073
  export default class Thread extends DurableThread {}
@@ -1019,23 +1090,23 @@ export default {
1019
1090
  export { DurableThread, DurableAgentBuilder }
1020
1091
  `;
1021
1092
  function getProjectName(cwd) {
1022
- const packageJsonPath = path.join(cwd, "package.json");
1023
- if (fs.existsSync(packageJsonPath)) {
1093
+ const packageJsonPath = path3.join(cwd, "package.json");
1094
+ if (fs3.existsSync(packageJsonPath)) {
1024
1095
  try {
1025
- const pkg2 = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
1096
+ const pkg2 = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1026
1097
  if (pkg2.name) {
1027
1098
  return pkg2.name.replace(/^@[^/]+\//, "");
1028
1099
  }
1029
1100
  } catch {
1030
1101
  }
1031
1102
  }
1032
- return path.basename(cwd);
1103
+ return path3.basename(cwd);
1033
1104
  }
1034
1105
  function findViteConfig(cwd) {
1035
1106
  const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
1036
1107
  for (const candidate of candidates) {
1037
- const configPath = path.join(cwd, candidate);
1038
- if (fs.existsSync(configPath)) {
1108
+ const configPath = path3.join(cwd, candidate);
1109
+ if (fs3.existsSync(configPath)) {
1039
1110
  return configPath;
1040
1111
  }
1041
1112
  }
@@ -1044,8 +1115,8 @@ function findViteConfig(cwd) {
1044
1115
  function findWranglerConfig(cwd) {
1045
1116
  const candidates = ["wrangler.jsonc", "wrangler.json"];
1046
1117
  for (const candidate of candidates) {
1047
- const configPath = path.join(cwd, candidate);
1048
- if (fs.existsSync(configPath)) {
1118
+ const configPath = path3.join(cwd, candidate);
1119
+ if (fs3.existsSync(configPath)) {
1049
1120
  return configPath;
1050
1121
  }
1051
1122
  }
@@ -1054,21 +1125,13 @@ function findWranglerConfig(cwd) {
1054
1125
  async function modifyViteConfig(configPath, force) {
1055
1126
  try {
1056
1127
  const mod = await loadFile(configPath);
1057
- const code = fs.readFileSync(configPath, "utf-8");
1128
+ const code = fs3.readFileSync(configPath, "utf-8");
1058
1129
  const hasCloudflare = code.includes("@cloudflare/vite-plugin") || code.includes("cloudflare()");
1059
1130
  const hasAgentBuilder = code.includes("@standardagents/builder") || code.includes("agentbuilder()");
1060
1131
  if (hasCloudflare && hasAgentBuilder && !force) {
1061
1132
  logger.info("Vite config already includes Standard Agents plugins");
1062
1133
  return true;
1063
1134
  }
1064
- if (!hasCloudflare || force) {
1065
- addVitePlugin(mod, {
1066
- from: "@cloudflare/vite-plugin",
1067
- imported: "cloudflare",
1068
- constructor: "cloudflare"
1069
- });
1070
- logger.success("Added cloudflare plugin to vite.config");
1071
- }
1072
1135
  if (!hasAgentBuilder || force) {
1073
1136
  addVitePlugin(mod, {
1074
1137
  from: "@standardagents/builder",
@@ -1078,6 +1141,14 @@ async function modifyViteConfig(configPath, force) {
1078
1141
  });
1079
1142
  logger.success("Added agentbuilder plugin to vite.config");
1080
1143
  }
1144
+ if (!hasCloudflare || force) {
1145
+ addVitePlugin(mod, {
1146
+ from: "@cloudflare/vite-plugin",
1147
+ imported: "cloudflare",
1148
+ constructor: "cloudflare"
1149
+ });
1150
+ logger.success("Added cloudflare plugin to vite.config");
1151
+ }
1081
1152
  await writeFile(mod, configPath);
1082
1153
  return true;
1083
1154
  } catch (error) {
@@ -1085,10 +1156,10 @@ async function modifyViteConfig(configPath, force) {
1085
1156
  logger.log("");
1086
1157
  logger.log("Please manually add the following to your vite.config.ts:");
1087
1158
  logger.log("");
1088
- logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1089
1159
  logger.log(` import { agentbuilder } from '@standardagents/builder'`);
1160
+ logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1090
1161
  logger.log("");
1091
- logger.log(` plugins: [cloudflare(), agentbuilder({ mountPoint: '/' })]`);
1162
+ logger.log(` plugins: [agentbuilder({ mountPoint: '/' }), cloudflare()]`);
1092
1163
  logger.log("");
1093
1164
  return false;
1094
1165
  }
@@ -1097,7 +1168,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1097
1168
  const existingConfig = findWranglerConfig(cwd);
1098
1169
  if (existingConfig && !force) {
1099
1170
  try {
1100
- const text = fs.readFileSync(existingConfig, "utf-8");
1171
+ const text = fs3.readFileSync(existingConfig, "utf-8");
1101
1172
  const config = parse(text);
1102
1173
  const hasBindings = config.durable_objects?.bindings?.some(
1103
1174
  (b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
@@ -1137,14 +1208,14 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1137
1208
  }
1138
1209
  if (!config.assets) {
1139
1210
  const edits = modify(result, ["assets"], {
1140
- directory: "dist",
1211
+ directory: "dist/client",
1141
1212
  not_found_handling: "single-page-application",
1142
1213
  binding: "ASSETS",
1143
1214
  run_worker_first: ["/**"]
1144
1215
  }, {});
1145
1216
  result = applyEdits(result, edits);
1146
1217
  }
1147
- fs.writeFileSync(existingConfig, result, "utf-8");
1218
+ fs3.writeFileSync(existingConfig, result, "utf-8");
1148
1219
  logger.success("Updated wrangler.jsonc with Standard Agents configuration");
1149
1220
  return true;
1150
1221
  } catch (error) {
@@ -1152,9 +1223,9 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1152
1223
  return false;
1153
1224
  }
1154
1225
  }
1155
- const wranglerPath = path.join(cwd, "wrangler.jsonc");
1226
+ const wranglerPath = path3.join(cwd, "wrangler.jsonc");
1156
1227
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
1157
- fs.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
1228
+ fs3.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
1158
1229
  logger.success("Created wrangler.jsonc");
1159
1230
  return true;
1160
1231
  }
@@ -1162,33 +1233,33 @@ function getWorkerEntryPoint(cwd) {
1162
1233
  const wranglerConfig = findWranglerConfig(cwd);
1163
1234
  if (wranglerConfig) {
1164
1235
  try {
1165
- const text = fs.readFileSync(wranglerConfig, "utf-8");
1236
+ const text = fs3.readFileSync(wranglerConfig, "utf-8");
1166
1237
  const config = parse(text);
1167
1238
  if (config.main) {
1168
- return path.join(cwd, config.main);
1239
+ return path3.join(cwd, config.main);
1169
1240
  }
1170
1241
  } catch {
1171
1242
  }
1172
1243
  }
1173
- return path.join(cwd, "worker", "index.ts");
1244
+ return path3.join(cwd, "worker", "index.ts");
1174
1245
  }
1175
1246
  async function createOrUpdateWorkerEntry(cwd, force) {
1176
1247
  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`);
1248
+ const entryDir = path3.dirname(entryPath);
1249
+ if (!fs3.existsSync(entryDir)) {
1250
+ fs3.mkdirSync(entryDir, { recursive: true });
1251
+ logger.success(`Created ${path3.relative(cwd, entryDir)} directory`);
1181
1252
  }
1182
- if (!fs.existsSync(entryPath)) {
1183
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1184
- logger.success(`Created ${path.relative(cwd, entryPath)}`);
1253
+ if (!fs3.existsSync(entryPath)) {
1254
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1255
+ logger.success(`Created ${path3.relative(cwd, entryPath)}`);
1185
1256
  return true;
1186
1257
  }
1187
- const content = fs.readFileSync(entryPath, "utf-8");
1258
+ const content = fs3.readFileSync(entryPath, "utf-8");
1188
1259
  const hasRouter = content.includes("virtual:@standardagents/builder") && content.includes("router");
1189
1260
  const hasDurableExports = content.includes("DurableThread") && content.includes("DurableAgentBuilder");
1190
1261
  if (hasRouter && hasDurableExports && !force) {
1191
- logger.info(`${path.relative(cwd, entryPath)} already configured`);
1262
+ logger.info(`${path3.relative(cwd, entryPath)} already configured`);
1192
1263
  return true;
1193
1264
  }
1194
1265
  try {
@@ -1216,17 +1287,17 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1216
1287
  }
1217
1288
  const { code } = generateCode(mod);
1218
1289
  if (force || !hasRouter) {
1219
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1220
- logger.success(`Updated ${path.relative(cwd, entryPath)} with Standard Agents router`);
1290
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1291
+ logger.success(`Updated ${path3.relative(cwd, entryPath)} with Standard Agents router`);
1221
1292
  }
1222
1293
  return true;
1223
1294
  } catch (error) {
1224
1295
  if (force) {
1225
- fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1226
- logger.success(`Created ${path.relative(cwd, entryPath)}`);
1296
+ fs3.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
1297
+ logger.success(`Created ${path3.relative(cwd, entryPath)}`);
1227
1298
  return true;
1228
1299
  }
1229
- logger.warning(`Could not automatically modify ${path.relative(cwd, entryPath)}`);
1300
+ logger.warning(`Could not automatically modify ${path3.relative(cwd, entryPath)}`);
1230
1301
  logger.log("");
1231
1302
  logger.log("Please ensure your worker entry point includes:");
1232
1303
  logger.log("");
@@ -1245,31 +1316,31 @@ async function createOrUpdateWorkerEntry(cwd, force) {
1245
1316
  }
1246
1317
  }
1247
1318
  function createDurableObjects(cwd) {
1248
- const agentsDir = path.join(cwd, "agents");
1249
- if (!fs.existsSync(agentsDir)) {
1250
- fs.mkdirSync(agentsDir, { recursive: true });
1319
+ const agentsDir = path3.join(cwd, "agents");
1320
+ if (!fs3.existsSync(agentsDir)) {
1321
+ fs3.mkdirSync(agentsDir, { recursive: true });
1251
1322
  }
1252
- const threadPath = path.join(agentsDir, "Thread.ts");
1253
- if (!fs.existsSync(threadPath)) {
1254
- fs.writeFileSync(threadPath, THREAD_TS, "utf-8");
1323
+ const threadPath = path3.join(agentsDir, "Thread.ts");
1324
+ if (!fs3.existsSync(threadPath)) {
1325
+ fs3.writeFileSync(threadPath, THREAD_TS, "utf-8");
1255
1326
  logger.success("Created agents/Thread.ts");
1256
1327
  } else {
1257
1328
  logger.info("agents/Thread.ts already exists");
1258
1329
  }
1259
- const agentBuilderPath = path.join(agentsDir, "AgentBuilder.ts");
1260
- if (!fs.existsSync(agentBuilderPath)) {
1261
- fs.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
1330
+ const agentBuilderPath = path3.join(agentsDir, "AgentBuilder.ts");
1331
+ if (!fs3.existsSync(agentBuilderPath)) {
1332
+ fs3.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
1262
1333
  logger.success("Created agents/AgentBuilder.ts");
1263
1334
  } else {
1264
1335
  logger.info("agents/AgentBuilder.ts already exists");
1265
1336
  }
1266
1337
  }
1267
1338
  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");
1339
+ path3.join(cwd, "agents");
1340
+ const rootDocPath = path3.join(cwd, "CLAUDE.md");
1341
+ if (!fs3.existsSync(rootDocPath)) {
1342
+ fs3.writeFileSync(rootDocPath, ROOT_CLAUDE_MD, "utf-8");
1343
+ logger.success("Created CLAUDE.md");
1273
1344
  }
1274
1345
  const directories = [
1275
1346
  { path: "agents/agents", doc: AGENTS_CLAUDE_MD },
@@ -1280,26 +1351,26 @@ function createDirectoriesAndDocs(cwd) {
1280
1351
  { path: "agents/api", doc: API_CLAUDE_MD }
1281
1352
  ];
1282
1353
  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 });
1354
+ const dirPath = path3.join(cwd, dir.path);
1355
+ const docPath = path3.join(dirPath, "CLAUDE.md");
1356
+ if (!fs3.existsSync(dirPath)) {
1357
+ fs3.mkdirSync(dirPath, { recursive: true });
1287
1358
  logger.success(`Created ${dir.path}`);
1288
1359
  }
1289
- if (!fs.existsSync(docPath)) {
1290
- fs.writeFileSync(docPath, dir.doc, "utf-8");
1360
+ if (!fs3.existsSync(docPath)) {
1361
+ fs3.writeFileSync(docPath, dir.doc, "utf-8");
1291
1362
  logger.success(`Created ${dir.path}/CLAUDE.md`);
1292
1363
  }
1293
1364
  }
1294
1365
  }
1295
1366
  function updateTsConfig(cwd) {
1296
- const tsconfigPath = path.join(cwd, "tsconfig.json");
1297
- if (!fs.existsSync(tsconfigPath)) {
1367
+ const tsconfigPath = path3.join(cwd, "tsconfig.json");
1368
+ if (!fs3.existsSync(tsconfigPath)) {
1298
1369
  logger.info("No tsconfig.json found, skipping TypeScript configuration");
1299
1370
  return;
1300
1371
  }
1301
1372
  try {
1302
- const text = fs.readFileSync(tsconfigPath, "utf-8");
1373
+ const text = fs3.readFileSync(tsconfigPath, "utf-8");
1303
1374
  const config = parse(text);
1304
1375
  let result = text;
1305
1376
  const types = config.compilerOptions?.types || [];
@@ -1321,7 +1392,7 @@ function updateTsConfig(cwd) {
1321
1392
  result = applyEdits(result, edits);
1322
1393
  }
1323
1394
  if (newTypes.length > 0 || newIncludes.length > 0) {
1324
- fs.writeFileSync(tsconfigPath, result, "utf-8");
1395
+ fs3.writeFileSync(tsconfigPath, result, "utf-8");
1325
1396
  logger.success("Updated tsconfig.json with Standard Agents types");
1326
1397
  } else {
1327
1398
  logger.info("tsconfig.json already configured");
@@ -1342,21 +1413,21 @@ function cleanupViteDefaults(cwd) {
1342
1413
  ];
1343
1414
  const dirsToRemove = ["src", "public"];
1344
1415
  for (const file of filesToRemove) {
1345
- const filePath = path.join(cwd, file);
1346
- if (fs.existsSync(filePath)) {
1416
+ const filePath = path3.join(cwd, file);
1417
+ if (fs3.existsSync(filePath)) {
1347
1418
  try {
1348
- fs.unlinkSync(filePath);
1419
+ fs3.unlinkSync(filePath);
1349
1420
  } catch {
1350
1421
  }
1351
1422
  }
1352
1423
  }
1353
1424
  for (const dir of dirsToRemove) {
1354
- const dirPath = path.join(cwd, dir);
1355
- if (fs.existsSync(dirPath)) {
1425
+ const dirPath = path3.join(cwd, dir);
1426
+ if (fs3.existsSync(dirPath)) {
1356
1427
  try {
1357
- const files = fs.readdirSync(dirPath);
1428
+ const files = fs3.readdirSync(dirPath);
1358
1429
  if (files.length === 0) {
1359
- fs.rmdirSync(dirPath);
1430
+ fs3.rmdirSync(dirPath);
1360
1431
  }
1361
1432
  } catch {
1362
1433
  }
@@ -1388,8 +1459,8 @@ async function scaffold(options = {}) {
1388
1459
  logger.log("");
1389
1460
  logger.log("Your project structure:");
1390
1461
  logger.log("");
1462
+ logger.log(" CLAUDE.md # Architecture documentation");
1391
1463
  logger.log(" agents/");
1392
- logger.log(" \u251C\u2500\u2500 CLAUDE.md # Architecture documentation");
1393
1464
  logger.log(" \u251C\u2500\u2500 Thread.ts # Durable Object for threads");
1394
1465
  logger.log(" \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for agent state");
1395
1466
  logger.log(" \u251C\u2500\u2500 agents/ # Agent definitions");
@@ -1490,12 +1561,22 @@ function detectPackageManager() {
1490
1561
  if (userAgent.includes("yarn")) return "yarn";
1491
1562
  if (userAgent.includes("bun")) return "bun";
1492
1563
  }
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";
1564
+ if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
1565
+ if (fs3.existsSync("yarn.lock")) return "yarn";
1566
+ if (fs3.existsSync("bun.lockb")) return "bun";
1567
+ if (fs3.existsSync("package-lock.json")) return "npm";
1497
1568
  return "npm";
1498
1569
  }
1570
+ function getCliVersion() {
1571
+ try {
1572
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1573
+ const pkgPath = path3.resolve(__dirname2, "../../package.json");
1574
+ const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1575
+ return pkg2.version;
1576
+ } catch {
1577
+ return "latest";
1578
+ }
1579
+ }
1499
1580
  async function selectPackageManager(detected) {
1500
1581
  const options = ["npm", "pnpm", "yarn", "bun"];
1501
1582
  const detectedIndex = options.indexOf(detected);
@@ -1577,8 +1658,8 @@ async function init(projectNameArg, options = {}) {
1577
1658
  logger.error("Project name is required");
1578
1659
  process.exit(1);
1579
1660
  }
1580
- const projectPath = path.join(cwd, projectName);
1581
- if (fs.existsSync(projectPath)) {
1661
+ const projectPath = path3.join(cwd, projectName);
1662
+ if (fs3.existsSync(projectPath)) {
1582
1663
  logger.error(`Directory "${projectName}" already exists`);
1583
1664
  process.exit(1);
1584
1665
  }
@@ -1610,51 +1691,28 @@ async function init(projectNameArg, options = {}) {
1610
1691
  logger.error("Failed to create Vite project");
1611
1692
  process.exit(1);
1612
1693
  }
1613
- const viteConfigPath = path.join(projectPath, "vite.config.ts");
1614
- if (!fs.existsSync(viteConfigPath)) {
1694
+ const viteConfigPath = path3.join(projectPath, "vite.config.ts");
1695
+ if (!fs3.existsSync(viteConfigPath)) {
1615
1696
  const viteConfigContent = `import { defineConfig } from 'vite'
1616
1697
 
1617
1698
  export default defineConfig({
1618
1699
  plugins: [],
1619
1700
  })
1620
1701
  `;
1621
- fs.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
1702
+ fs3.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
1622
1703
  logger.success("Created vite.config.ts");
1623
1704
  }
1624
- const packageJsonPath = path.join(projectPath, "package.json");
1625
- if (fs.existsSync(packageJsonPath)) {
1626
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
1705
+ const packageJsonPath = path3.join(projectPath, "package.json");
1706
+ if (fs3.existsSync(packageJsonPath)) {
1707
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1627
1708
  const kebabName = toKebabCase(projectName);
1628
1709
  if (packageJson.name !== kebabName) {
1629
1710
  packageJson.name = kebabName;
1630
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
1711
+ fs3.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
1631
1712
  }
1632
1713
  }
1633
1714
  logger.log("");
1634
- logger.info("Step 2: Installing Standard Agents dependencies...");
1635
- logger.log("");
1636
- try {
1637
- const installCmd = pm === "npm" ? "install" : "add";
1638
- const devFlag = pm === "npm" ? "--save-dev" : "-D";
1639
- await runCommand(pm, [
1640
- installCmd,
1641
- devFlag,
1642
- "@cloudflare/vite-plugin",
1643
- "@standardagents/builder",
1644
- "wrangler",
1645
- "zod"
1646
- ], projectPath);
1647
- } catch (error) {
1648
- logger.error("Failed to install dependencies");
1649
- process.exit(1);
1650
- }
1651
- logger.log("");
1652
- logger.info("Step 3: Configuring Standard Agents...");
1653
- logger.log("");
1654
- process.chdir(projectPath);
1655
- await scaffold({ force: true });
1656
- logger.log("");
1657
- logger.info("Step 4: Configuring environment variables...");
1715
+ logger.info("Step 2: Configuring environment variables...");
1658
1716
  logger.log("");
1659
1717
  const encryptionKey = crypto.randomBytes(32).toString("hex");
1660
1718
  let openrouterKey = "";
@@ -1689,6 +1747,42 @@ export default defineConfig({
1689
1747
  if (!adminPassword) {
1690
1748
  adminPassword = "admin";
1691
1749
  }
1750
+ logger.log("");
1751
+ logger.info("Step 3: Installing Standard Agents dependencies...");
1752
+ logger.log("");
1753
+ try {
1754
+ const installCmd = pm === "npm" ? "install" : "add";
1755
+ const devFlag = pm === "npm" ? "--save-dev" : "-D";
1756
+ const cliVersion = getCliVersion();
1757
+ await runCommand(pm, [
1758
+ installCmd,
1759
+ devFlag,
1760
+ "@cloudflare/vite-plugin",
1761
+ "wrangler"
1762
+ ], projectPath);
1763
+ const deps = [
1764
+ `@standardagents/builder@${cliVersion}`,
1765
+ "zod"
1766
+ ];
1767
+ if (openaiKey) {
1768
+ deps.push(`@standardagents/openai@${cliVersion}`);
1769
+ }
1770
+ if (openrouterKey) {
1771
+ deps.push(`@standardagents/openrouter@${cliVersion}`);
1772
+ }
1773
+ await runCommand(pm, [installCmd, ...deps], projectPath);
1774
+ } catch (error) {
1775
+ logger.error("Failed to install dependencies");
1776
+ process.exit(1);
1777
+ }
1778
+ logger.log("");
1779
+ logger.info("Step 4: Configuring Standard Agents...");
1780
+ logger.log("");
1781
+ process.chdir(projectPath);
1782
+ await scaffold({ force: true });
1783
+ logger.log("");
1784
+ logger.info("Writing environment configuration...");
1785
+ logger.log("");
1692
1786
  const devVarsLines = [
1693
1787
  "# Standard Agents Environment Variables",
1694
1788
  "# This file contains secrets for local development only",
@@ -1714,14 +1808,14 @@ export default defineConfig({
1714
1808
  devVarsLines.push("# OPENAI_API_KEY=your-openai-key");
1715
1809
  }
1716
1810
  devVarsLines.push("");
1717
- const devVarsPath = path.join(projectPath, ".dev.vars");
1718
- fs.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
1811
+ const devVarsPath = path3.join(projectPath, ".dev.vars");
1812
+ fs3.writeFileSync(devVarsPath, devVarsLines.join("\n"), "utf-8");
1719
1813
  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");
1814
+ const gitignorePath = path3.join(projectPath, ".gitignore");
1815
+ if (fs3.existsSync(gitignorePath)) {
1816
+ const gitignoreContent = fs3.readFileSync(gitignorePath, "utf-8");
1723
1817
  if (!gitignoreContent.includes(".dev.vars")) {
1724
- fs.appendFileSync(gitignorePath, "\n# Local environment variables\n.dev.vars\n");
1818
+ fs3.appendFileSync(gitignorePath, "\n# Local environment variables\n.dev.vars\n");
1725
1819
  logger.success("Added .dev.vars to .gitignore");
1726
1820
  }
1727
1821
  }
@@ -1743,6 +1837,535 @@ export default defineConfig({
1743
1837
  logger.log("");
1744
1838
  logger.log("For more information, visit: https://standardagents.ai/docs");
1745
1839
  }
1840
+ function getReactPackageVersion() {
1841
+ try {
1842
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1843
+ const pkgPath = path3.resolve(__dirname2, "../package.json");
1844
+ const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1845
+ const version = pkg2.version;
1846
+ const prereleaseMatch = version.match(/-([a-z]+)\./i);
1847
+ if (prereleaseMatch) {
1848
+ return prereleaseMatch[1];
1849
+ }
1850
+ return `^${version}`;
1851
+ } catch {
1852
+ return "latest";
1853
+ }
1854
+ }
1855
+ function getChatSourceDir() {
1856
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1857
+ return path3.resolve(__dirname2, "../chat");
1858
+ }
1859
+ async function prompt2(question) {
1860
+ const rl = readline.createInterface({
1861
+ input: process.stdin,
1862
+ output: process.stdout
1863
+ });
1864
+ return new Promise((resolve2) => {
1865
+ rl.question(question, (answer) => {
1866
+ rl.close();
1867
+ resolve2(answer.trim());
1868
+ });
1869
+ });
1870
+ }
1871
+ function runCommand2(command, args, cwd) {
1872
+ return new Promise((resolve2, reject) => {
1873
+ const child = spawn(command, args, {
1874
+ cwd,
1875
+ stdio: "inherit",
1876
+ shell: false
1877
+ });
1878
+ child.on("close", (code) => {
1879
+ if (code === 0) {
1880
+ resolve2();
1881
+ } else {
1882
+ reject(new Error(`Command failed with exit code ${code}`));
1883
+ }
1884
+ });
1885
+ child.on("error", reject);
1886
+ });
1887
+ }
1888
+ function detectPackageManager2() {
1889
+ const userAgent = process.env.npm_config_user_agent;
1890
+ if (userAgent) {
1891
+ if (userAgent.includes("pnpm")) return "pnpm";
1892
+ if (userAgent.includes("yarn")) return "yarn";
1893
+ if (userAgent.includes("bun")) return "bun";
1894
+ }
1895
+ if (fs3.existsSync("pnpm-lock.yaml")) return "pnpm";
1896
+ if (fs3.existsSync("yarn.lock")) return "yarn";
1897
+ if (fs3.existsSync("bun.lockb")) return "bun";
1898
+ if (fs3.existsSync("package-lock.json")) return "npm";
1899
+ return "npm";
1900
+ }
1901
+ async function selectPackageManager2(detected) {
1902
+ const options = ["npm", "pnpm", "yarn", "bun"];
1903
+ const detectedIndex = options.indexOf(detected);
1904
+ return new Promise((resolve2) => {
1905
+ const stdin = process.stdin;
1906
+ const stdout = process.stdout;
1907
+ let selectedIndex = detectedIndex;
1908
+ const renderOptions = () => {
1909
+ stdout.write("\x1B[?25l");
1910
+ stdout.write(`\x1B[${options.length + 1}A`);
1911
+ stdout.write("\x1B[0J");
1912
+ stdout.write("Select a package manager:\n");
1913
+ options.forEach((opt, i) => {
1914
+ const marker = i === detectedIndex ? " (detected)" : "";
1915
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1916
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1917
+ stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
1918
+ `);
1919
+ });
1920
+ };
1921
+ stdout.write("Select a package manager:\n");
1922
+ options.forEach((opt, i) => {
1923
+ const marker = i === detectedIndex ? " (detected)" : "";
1924
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1925
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1926
+ stdout.write(`${prefix} ${highlight}${opt}${marker}\x1B[0m
1927
+ `);
1928
+ });
1929
+ const wasRaw = stdin.isRaw;
1930
+ if (stdin.isTTY) {
1931
+ stdin.setRawMode(true);
1932
+ }
1933
+ stdin.resume();
1934
+ stdin.setEncoding("utf8");
1935
+ const cleanup = () => {
1936
+ stdin.setRawMode(wasRaw ?? false);
1937
+ stdin.pause();
1938
+ stdin.removeListener("data", onData);
1939
+ stdout.write("\x1B[?25h");
1940
+ };
1941
+ const onData = (key) => {
1942
+ if (key === "\x1B[A" || key === "k") {
1943
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
1944
+ renderOptions();
1945
+ } else if (key === "\x1B[B" || key === "j") {
1946
+ selectedIndex = (selectedIndex + 1) % options.length;
1947
+ renderOptions();
1948
+ } else if (key === "\r" || key === "\n") {
1949
+ cleanup();
1950
+ resolve2(options[selectedIndex]);
1951
+ } else if (key === "") {
1952
+ cleanup();
1953
+ stdout.write("\n");
1954
+ process.exit(1);
1955
+ }
1956
+ };
1957
+ stdin.on("data", onData);
1958
+ });
1959
+ }
1960
+ async function selectFramework() {
1961
+ const options = [
1962
+ { value: "vite", label: "Vite (React + TypeScript)" },
1963
+ { value: "nextjs", label: "Next.js (App Router)" }
1964
+ ];
1965
+ return new Promise((resolve2) => {
1966
+ const stdin = process.stdin;
1967
+ const stdout = process.stdout;
1968
+ let selectedIndex = 0;
1969
+ const renderOptions = () => {
1970
+ stdout.write("\x1B[?25l");
1971
+ stdout.write(`\x1B[${options.length + 1}A`);
1972
+ stdout.write("\x1B[0J");
1973
+ stdout.write("Select a framework:\n");
1974
+ options.forEach((opt, i) => {
1975
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1976
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1977
+ stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
1978
+ `);
1979
+ });
1980
+ };
1981
+ stdout.write("Select a framework:\n");
1982
+ options.forEach((opt, i) => {
1983
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
1984
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
1985
+ stdout.write(`${prefix} ${highlight}${opt.label}\x1B[0m
1986
+ `);
1987
+ });
1988
+ const wasRaw = stdin.isRaw;
1989
+ if (stdin.isTTY) {
1990
+ stdin.setRawMode(true);
1991
+ }
1992
+ stdin.resume();
1993
+ stdin.setEncoding("utf8");
1994
+ const cleanup = () => {
1995
+ stdin.setRawMode(wasRaw ?? false);
1996
+ stdin.pause();
1997
+ stdin.removeListener("data", onData);
1998
+ stdout.write("\x1B[?25h");
1999
+ };
2000
+ const onData = (key) => {
2001
+ if (key === "\x1B[A" || key === "k") {
2002
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
2003
+ renderOptions();
2004
+ } else if (key === "\x1B[B" || key === "j") {
2005
+ selectedIndex = (selectedIndex + 1) % options.length;
2006
+ renderOptions();
2007
+ } else if (key === "\r" || key === "\n") {
2008
+ cleanup();
2009
+ resolve2(options[selectedIndex].value);
2010
+ } else if (key === "") {
2011
+ cleanup();
2012
+ stdout.write("\n");
2013
+ process.exit(1);
2014
+ }
2015
+ };
2016
+ stdin.on("data", onData);
2017
+ });
2018
+ }
2019
+ function isValidUrl(url) {
2020
+ try {
2021
+ new URL(url);
2022
+ return true;
2023
+ } catch {
2024
+ return false;
2025
+ }
2026
+ }
2027
+ async function isPortOpen(port) {
2028
+ const tryConnect = (host) => {
2029
+ return new Promise((resolve2) => {
2030
+ const socket = new net.Socket();
2031
+ socket.setTimeout(200);
2032
+ socket.on("connect", () => {
2033
+ socket.destroy();
2034
+ resolve2(true);
2035
+ });
2036
+ socket.on("timeout", () => {
2037
+ socket.destroy();
2038
+ resolve2(false);
2039
+ });
2040
+ socket.on("error", () => {
2041
+ socket.destroy();
2042
+ resolve2(false);
2043
+ });
2044
+ socket.connect(port, host);
2045
+ });
2046
+ };
2047
+ if (await tryConnect("127.0.0.1")) return true;
2048
+ if (await tryConnect("::1")) return true;
2049
+ return false;
2050
+ }
2051
+ async function isAgentbuilderInstance(url) {
2052
+ try {
2053
+ const controller = new AbortController();
2054
+ const timeout = setTimeout(() => controller.abort(), 1e3);
2055
+ const response = await fetch(`${url}/api/agents`, {
2056
+ signal: controller.signal,
2057
+ headers: { "Accept": "application/json" }
2058
+ });
2059
+ clearTimeout(timeout);
2060
+ if (response.status === 401) {
2061
+ return true;
2062
+ }
2063
+ if (response.ok) {
2064
+ const data = await response.json();
2065
+ return Array.isArray(data.agents);
2066
+ }
2067
+ return false;
2068
+ } catch {
2069
+ return false;
2070
+ }
2071
+ }
2072
+ async function detectAgentbuilderInstance() {
2073
+ const portsToScan = [
2074
+ 5173,
2075
+ 5174,
2076
+ 5175,
2077
+ 5176,
2078
+ 5177,
2079
+ 5178,
2080
+ 5179,
2081
+ 5180,
2082
+ 3e3,
2083
+ 3001,
2084
+ 3002,
2085
+ 3003,
2086
+ 8080,
2087
+ 8e3,
2088
+ 4e3
2089
+ ];
2090
+ logger.log("\x1B[90mScanning for running agentbuilder instance...\x1B[0m");
2091
+ const openPorts = [];
2092
+ await Promise.all(
2093
+ portsToScan.map(async (port) => {
2094
+ if (await isPortOpen(port)) {
2095
+ openPorts.push(port);
2096
+ }
2097
+ })
2098
+ );
2099
+ for (const port of openPorts.sort((a, b) => a - b)) {
2100
+ const url = `http://localhost:${port}`;
2101
+ if (await isAgentbuilderInstance(url)) {
2102
+ return url;
2103
+ }
2104
+ }
2105
+ return null;
2106
+ }
2107
+ function copyDir(src, dest, skip = []) {
2108
+ if (!fs3.existsSync(dest)) {
2109
+ fs3.mkdirSync(dest, { recursive: true });
2110
+ }
2111
+ const entries = fs3.readdirSync(src, { withFileTypes: true });
2112
+ for (const entry of entries) {
2113
+ if (skip.includes(entry.name)) continue;
2114
+ const srcPath = path3.join(src, entry.name);
2115
+ const destPath = path3.join(dest, entry.name);
2116
+ if (entry.isDirectory()) {
2117
+ copyDir(srcPath, destPath, skip);
2118
+ } else {
2119
+ fs3.copyFileSync(srcPath, destPath);
2120
+ }
2121
+ }
2122
+ }
2123
+ async function initChat(projectNameArg, options = {}) {
2124
+ const cwd = process.cwd();
2125
+ const chatSourceDir = getChatSourceDir();
2126
+ if (!fs3.existsSync(chatSourceDir)) {
2127
+ logger.error("Chat source files not found. This CLI installation may be corrupted.");
2128
+ process.exit(1);
2129
+ }
2130
+ logger.log("");
2131
+ logger.info("Scaffolding a frontend chat application...");
2132
+ logger.log("");
2133
+ let projectName = projectNameArg;
2134
+ if (!projectName && !options.yes) {
2135
+ projectName = await prompt2("Project name: ");
2136
+ }
2137
+ if (!projectName) {
2138
+ logger.error("Project name is required");
2139
+ process.exit(1);
2140
+ }
2141
+ const projectPath = path3.join(cwd, projectName);
2142
+ if (fs3.existsSync(projectPath)) {
2143
+ logger.error(`Directory "${projectName}" already exists`);
2144
+ process.exit(1);
2145
+ }
2146
+ let framework;
2147
+ if (options.framework) {
2148
+ framework = options.framework;
2149
+ } else if (options.yes) {
2150
+ framework = "vite";
2151
+ } else {
2152
+ framework = await selectFramework();
2153
+ logger.log("");
2154
+ }
2155
+ let serverUrl;
2156
+ if (options.yes) {
2157
+ serverUrl = "http://localhost:5173";
2158
+ } else {
2159
+ const detectedUrl = await detectAgentbuilderInstance();
2160
+ if (detectedUrl) {
2161
+ logger.success(`Found agentbuilder at ${detectedUrl}`);
2162
+ const urlInput = await prompt2(`Agentbuilder dev server URL [${detectedUrl}]: `);
2163
+ serverUrl = urlInput || detectedUrl;
2164
+ } else {
2165
+ logger.log("\x1B[90mNo running agentbuilder instance found\x1B[0m");
2166
+ const urlInput = await prompt2("Agentbuilder dev server URL [http://localhost:5173]: ");
2167
+ serverUrl = urlInput || "http://localhost:5173";
2168
+ }
2169
+ if (!isValidUrl(serverUrl)) {
2170
+ logger.error("Invalid URL format");
2171
+ process.exit(1);
2172
+ }
2173
+ }
2174
+ const detectedPm = detectPackageManager2();
2175
+ let pm;
2176
+ if (options.yes) {
2177
+ pm = detectedPm;
2178
+ } else {
2179
+ pm = await selectPackageManager2(detectedPm);
2180
+ logger.log("");
2181
+ }
2182
+ logger.log("");
2183
+ logger.info(`Creating ${framework === "vite" ? "Vite" : "Next.js"} project...`);
2184
+ logger.log("");
2185
+ fs3.mkdirSync(projectPath, { recursive: true });
2186
+ const reactVersion = getReactPackageVersion();
2187
+ if (framework === "vite") {
2188
+ await scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
2189
+ } else {
2190
+ await scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion);
2191
+ }
2192
+ logger.log("");
2193
+ logger.info("Installing dependencies...");
2194
+ logger.log("");
2195
+ try {
2196
+ await runCommand2(pm, pm === "npm" ? ["install"] : ["install"], projectPath);
2197
+ } catch (error) {
2198
+ logger.error("Failed to install dependencies");
2199
+ process.exit(1);
2200
+ }
2201
+ logger.log("");
2202
+ logger.success("Chat UI scaffolded successfully!");
2203
+ logger.log("");
2204
+ logger.info("Starting development server...");
2205
+ logger.log("");
2206
+ const devArgs = pm === "npm" ? ["run", "dev"] : ["dev"];
2207
+ const devProcess = spawn(pm, devArgs, {
2208
+ cwd: projectPath,
2209
+ stdio: ["inherit", "pipe", "pipe"],
2210
+ shell: false
2211
+ });
2212
+ let browserOpened = false;
2213
+ const openBrowser = (url) => {
2214
+ if (browserOpened) return;
2215
+ browserOpened = true;
2216
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2217
+ spawn(openCmd, [url], { stdio: "ignore", detached: true }).unref();
2218
+ };
2219
+ devProcess.stdout?.on("data", (data) => {
2220
+ const text = data.toString();
2221
+ process.stdout.write(data);
2222
+ const urlMatch = text.match(/Local:\s+(https?:\/\/localhost:\d+\/?)/);
2223
+ if (urlMatch && !browserOpened) {
2224
+ openBrowser(urlMatch[1]);
2225
+ }
2226
+ });
2227
+ devProcess.stderr?.on("data", (data) => {
2228
+ process.stderr.write(data);
2229
+ });
2230
+ devProcess.on("error", (error) => {
2231
+ logger.error(`Failed to start dev server: ${error.message}`);
2232
+ process.exit(1);
2233
+ });
2234
+ }
2235
+ async function scaffoldVite(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
2236
+ copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
2237
+ logger.success("Copied src/");
2238
+ const viteDir = path3.join(chatSourceDir, "vite");
2239
+ let indexHtml = fs3.readFileSync(path3.join(viteDir, "index.html"), "utf-8");
2240
+ indexHtml = indexHtml.replace('src="/main.tsx"', 'src="/src/main.tsx"');
2241
+ fs3.writeFileSync(path3.join(projectPath, "index.html"), indexHtml, "utf-8");
2242
+ logger.success("Created index.html");
2243
+ fs3.copyFileSync(path3.join(viteDir, "main.tsx"), path3.join(projectPath, "src", "main.tsx"));
2244
+ logger.success("Created src/main.tsx");
2245
+ let viteConfig = fs3.readFileSync(path3.join(viteDir, "vite.config.ts"), "utf-8");
2246
+ viteConfig = `import { defineConfig } from 'vite'
2247
+ import react from '@vitejs/plugin-react'
2248
+ import tailwindcss from '@tailwindcss/vite'
2249
+
2250
+ export default defineConfig({
2251
+ plugins: [
2252
+ react(),
2253
+ tailwindcss(),
2254
+ ],
2255
+ })
2256
+ `;
2257
+ fs3.writeFileSync(path3.join(projectPath, "vite.config.ts"), viteConfig, "utf-8");
2258
+ logger.success("Created vite.config.ts");
2259
+ fs3.copyFileSync(path3.join(chatSourceDir, "tsconfig.json"), path3.join(projectPath, "tsconfig.json"));
2260
+ const tsconfig = JSON.parse(fs3.readFileSync(path3.join(projectPath, "tsconfig.json"), "utf-8"));
2261
+ tsconfig.include = ["src"];
2262
+ delete tsconfig.compilerOptions?.paths;
2263
+ fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2264
+ logger.success("Created tsconfig.json");
2265
+ fs3.copyFileSync(path3.join(chatSourceDir, "package.json"), path3.join(projectPath, "package.json"));
2266
+ const pkg2 = JSON.parse(fs3.readFileSync(path3.join(projectPath, "package.json"), "utf-8"));
2267
+ pkg2.name = projectName;
2268
+ pkg2.scripts = {
2269
+ dev: "vite",
2270
+ build: "vite build",
2271
+ preview: "vite preview"
2272
+ };
2273
+ delete pkg2.private;
2274
+ if (pkg2.dependencies?.["@standardagents/react"]) {
2275
+ pkg2.dependencies["@standardagents/react"] = reactVersion;
2276
+ }
2277
+ delete pkg2.dependencies?.["next"];
2278
+ delete pkg2.devDependencies?.["@tailwindcss/postcss"];
2279
+ delete pkg2.devDependencies?.["postcss"];
2280
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2281
+ logger.success("Created package.json");
2282
+ const envContent = `# Agentbuilder connection
2283
+ VITE_AGENTBUILDER_URL=${serverUrl}
2284
+ `;
2285
+ fs3.writeFileSync(path3.join(projectPath, ".env"), envContent, "utf-8");
2286
+ logger.success("Created .env");
2287
+ fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
2288
+ dist
2289
+ .env
2290
+ `, "utf-8");
2291
+ logger.success("Created .gitignore");
2292
+ let mainTsx = fs3.readFileSync(path3.join(projectPath, "src", "main.tsx"), "utf-8");
2293
+ mainTsx = mainTsx.replace(/from '\.\.\/src\//g, "from './").replace(/import '\.\.\/src\//g, "import './");
2294
+ fs3.writeFileSync(path3.join(projectPath, "src", "main.tsx"), mainTsx, "utf-8");
2295
+ if (fs3.existsSync(path3.join(viteDir, "favicon.svg"))) {
2296
+ fs3.copyFileSync(path3.join(viteDir, "favicon.svg"), path3.join(projectPath, "favicon.svg"));
2297
+ logger.success("Created favicon.svg");
2298
+ }
2299
+ }
2300
+ async function scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl, reactVersion) {
2301
+ copyDir(path3.join(chatSourceDir, "src"), path3.join(projectPath, "src"), []);
2302
+ logger.success("Copied src/");
2303
+ const nextDir = path3.join(chatSourceDir, "next");
2304
+ copyDir(path3.join(nextDir, "app"), path3.join(projectPath, "app"), []);
2305
+ logger.success("Copied app/");
2306
+ fs3.copyFileSync(path3.join(nextDir, "next.config.ts"), path3.join(projectPath, "next.config.ts"));
2307
+ logger.success("Created next.config.ts");
2308
+ fs3.copyFileSync(path3.join(nextDir, "postcss.config.mjs"), path3.join(projectPath, "postcss.config.mjs"));
2309
+ logger.success("Created postcss.config.mjs");
2310
+ const tsconfig = {
2311
+ compilerOptions: {
2312
+ target: "ES2017",
2313
+ lib: ["dom", "dom.iterable", "esnext"],
2314
+ allowJs: true,
2315
+ skipLibCheck: true,
2316
+ strict: true,
2317
+ noEmit: true,
2318
+ esModuleInterop: true,
2319
+ module: "esnext",
2320
+ moduleResolution: "bundler",
2321
+ resolveJsonModule: true,
2322
+ isolatedModules: true,
2323
+ jsx: "preserve",
2324
+ incremental: true,
2325
+ plugins: [{ name: "next" }],
2326
+ paths: {
2327
+ "@/*": ["./src/*"]
2328
+ }
2329
+ },
2330
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2331
+ exclude: ["node_modules"]
2332
+ };
2333
+ fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2334
+ logger.success("Created tsconfig.json");
2335
+ const pkg2 = JSON.parse(fs3.readFileSync(path3.join(chatSourceDir, "package.json"), "utf-8"));
2336
+ pkg2.name = projectName;
2337
+ pkg2.scripts = {
2338
+ dev: "next dev",
2339
+ build: "next build",
2340
+ start: "next start"
2341
+ };
2342
+ delete pkg2.private;
2343
+ if (pkg2.dependencies?.["@standardagents/react"]) {
2344
+ pkg2.dependencies["@standardagents/react"] = reactVersion;
2345
+ }
2346
+ delete pkg2.devDependencies?.["@tailwindcss/vite"];
2347
+ delete pkg2.devDependencies?.["@vitejs/plugin-react"];
2348
+ delete pkg2.devDependencies?.["vite"];
2349
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2350
+ logger.success("Created package.json");
2351
+ const envContent = `# Agentbuilder connection
2352
+ NEXT_PUBLIC_AGENTBUILDER_URL=${serverUrl}
2353
+ `;
2354
+ fs3.writeFileSync(path3.join(projectPath, ".env.local"), envContent, "utf-8");
2355
+ logger.success("Created .env.local");
2356
+ fs3.writeFileSync(path3.join(projectPath, ".gitignore"), `node_modules
2357
+ .next
2358
+ out
2359
+ .env.local
2360
+ `, "utf-8");
2361
+ logger.success("Created .gitignore");
2362
+ let layoutTsx = fs3.readFileSync(path3.join(projectPath, "app", "layout.tsx"), "utf-8");
2363
+ layoutTsx = layoutTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
2364
+ fs3.writeFileSync(path3.join(projectPath, "app", "layout.tsx"), layoutTsx, "utf-8");
2365
+ let pageTsx = fs3.readFileSync(path3.join(projectPath, "app", "page.tsx"), "utf-8");
2366
+ pageTsx = pageTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
2367
+ fs3.writeFileSync(path3.join(projectPath, "app", "page.tsx"), pageTsx, "utf-8");
2368
+ }
1746
2369
 
1747
2370
  // src/index.ts
1748
2371
  var __dirname$1 = dirname(fileURLToPath(import.meta.url));
@@ -1751,6 +2374,7 @@ var program = new Command();
1751
2374
  program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version(pkg.version);
1752
2375
  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
2376
  program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
2377
+ 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
2378
  program.parse();
1755
2379
  //# sourceMappingURL=index.js.map
1756
2380
  //# sourceMappingURL=index.js.map