@standardagents/cli 0.9.2 → 0.9.3

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,53 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import path3 from 'path';
4
- import fs from 'fs';
5
- import { parse, modify, applyEdits } from 'jsonc-parser';
3
+ import fs, { readFileSync } from 'fs';
4
+ import path, { dirname, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import readline from 'readline';
7
+ import { spawn } from 'child_process';
6
8
  import chalk from 'chalk';
9
+ import { parse, modify, applyEdits } from 'jsonc-parser';
10
+ import { loadFile, writeFile, generateCode } from 'magicast';
11
+ import { addVitePlugin } from 'magicast/helpers';
7
12
 
8
- function findWranglerConfig(cwd) {
9
- const jsonc = path3.join(cwd, "wrangler.jsonc");
10
- const json = path3.join(cwd, "wrangler.json");
11
- if (fs.existsSync(jsonc)) {
12
- return jsonc;
13
- }
14
- if (fs.existsSync(json)) {
15
- return json;
16
- }
17
- return null;
18
- }
19
- function readWranglerConfig(filePath) {
20
- const text = fs.readFileSync(filePath, "utf-8");
21
- const config = parse(text);
22
- return { config, text };
23
- }
24
- function updateWranglerConfig(filePath, text, updates) {
25
- let result = text;
26
- if (updates.durable_objects) {
27
- const edits = modify(result, ["durable_objects"], updates.durable_objects, {});
28
- result = applyEdits(result, edits);
29
- }
30
- if (updates.migrations) {
31
- const edits = modify(result, ["migrations"], updates.migrations, {});
32
- result = applyEdits(result, edits);
33
- }
34
- if (updates.env) {
35
- for (const [envName, envConfig] of Object.entries(updates.env)) {
36
- if (envConfig.durable_objects) {
37
- const edits = modify(result, ["env", envName, "durable_objects"], envConfig.durable_objects, {});
38
- result = applyEdits(result, edits);
39
- }
40
- if (envConfig.migrations) {
41
- const edits = modify(result, ["env", envName, "migrations"], envConfig.migrations, {});
42
- result = applyEdits(result, edits);
43
- }
44
- }
45
- }
46
- return result;
47
- }
48
- function writeWranglerConfig(filePath, content) {
49
- fs.writeFileSync(filePath, content, "utf-8");
50
- }
51
13
  var logger = {
52
14
  success: (message) => {
53
15
  console.log(chalk.green("\u2713"), message);
@@ -65,24 +27,21 @@ var logger = {
65
27
  console.log(message);
66
28
  }
67
29
  };
68
-
69
- // src/commands/init.ts
70
- async function init(options = {}) {
71
- var _a, _b, _c, _d, _e;
72
- const cwd = process.cwd();
73
- logger.info("Initializing Standard Agents configuration...");
74
- const configPath = findWranglerConfig(cwd);
75
- if (!configPath) {
76
- logger.error("No wrangler.jsonc or wrangler.json found in current directory");
77
- logger.log("\nTo use Standard Agents, you need a Cloudflare Workers project with wrangler configuration.");
78
- logger.log("\nExample wrangler.jsonc:");
79
- logger.log(`
80
- {
81
- "name": "my-agent",
82
- "main": "server/index.ts",
83
- "compatibility_date": "2025-08-13",
30
+ var WRANGLER_TEMPLATE = (name) => `{
31
+ "$schema": "node_modules/wrangler/config-schema.json",
32
+ "name": "${name}",
33
+ "main": "worker/index.ts",
34
+ "compatibility_date": "2025-01-01",
84
35
  "compatibility_flags": ["nodejs_compat"],
85
-
36
+ "observability": {
37
+ "enabled": true
38
+ },
39
+ "assets": {
40
+ "directory": "dist",
41
+ "not_found_handling": "single-page-application",
42
+ "binding": "ASSETS",
43
+ "run_worker_first": ["/**"]
44
+ },
86
45
  "durable_objects": {
87
46
  "bindings": [
88
47
  {
@@ -95,103 +54,39 @@ async function init(options = {}) {
95
54
  }
96
55
  ]
97
56
  },
98
-
99
57
  "migrations": [
100
58
  {
101
59
  "tag": "v1",
102
- "new_sqlite_classes": ["DurableThread", "DurableAgentBuilder"]
60
+ "new_sqlite_classes": ["DurableThread"]
61
+ },
62
+ {
63
+ "tag": "v2",
64
+ "new_sqlite_classes": ["DurableAgentBuilder"]
103
65
  }
104
66
  ]
105
67
  }
106
- `);
107
- process.exit(1);
108
- }
109
- logger.success(`Found configuration: ${path3.relative(cwd, configPath)}`);
110
- const { config, text } = readWranglerConfig(configPath);
111
- const hasAgentBuilderThread = (_b = (_a = config.durable_objects) == null ? void 0 : _a.bindings) == null ? void 0 : _b.some(
112
- (binding) => binding.name === "AGENT_BUILDER_THREAD"
113
- );
114
- const hasAgentBuilder = (_d = (_c = config.durable_objects) == null ? void 0 : _c.bindings) == null ? void 0 : _d.some(
115
- (binding) => binding.name === "AGENT_BUILDER"
116
- );
117
- if (hasAgentBuilderThread && hasAgentBuilder && !options.force) {
118
- logger.success("Standard Agents is already configured!");
119
- logger.info("Use --force to overwrite existing configuration");
120
- return;
121
- }
122
- const updates = {};
123
- updates.durable_objects = config.durable_objects || { bindings: [] };
124
- if (options.force) {
125
- updates.durable_objects.bindings = updates.durable_objects.bindings.filter(
126
- (binding) => binding.name !== "AGENT_BUILDER_THREAD" && binding.name !== "AGENT_BUILDER"
127
- );
128
- }
129
- if (!hasAgentBuilderThread || options.force) {
130
- updates.durable_objects.bindings.push({
131
- name: "AGENT_BUILDER_THREAD",
132
- class_name: "DurableThread"
133
- });
134
- logger.info("Added AGENT_BUILDER_THREAD binding");
135
- }
136
- if (!hasAgentBuilder || options.force) {
137
- updates.durable_objects.bindings.push({
138
- name: "AGENT_BUILDER",
139
- class_name: "DurableAgentBuilder"
140
- });
141
- logger.info("Added AGENT_BUILDER binding");
142
- }
143
- if (!config.migrations || config.migrations.length === 0) {
144
- updates.migrations = [
145
- {
146
- tag: "v1",
147
- new_sqlite_classes: ["DurableThread", "DurableAgentBuilder"]
148
- }
149
- ];
150
- logger.info("Added Durable Object migrations");
151
- }
152
- if ((_e = config.env) == null ? void 0 : _e.test) {
153
- updates.env = updates.env || {};
154
- updates.env.test = config.env.test;
155
- if (!updates.env.test.durable_objects) {
156
- updates.env.test.durable_objects = { bindings: [] };
157
- }
158
- const hasTestThread = updates.env.test.durable_objects.bindings.some(
159
- (binding) => binding.name === "AGENT_BUILDER_THREAD"
160
- );
161
- const hasTestBuilder = updates.env.test.durable_objects.bindings.some(
162
- (binding) => binding.name === "AGENT_BUILDER"
163
- );
164
- if (!hasTestThread) {
165
- updates.env.test.durable_objects.bindings.push({
166
- name: "AGENT_BUILDER_THREAD",
167
- class_name: "DurableThread"
168
- });
169
- logger.info("Added test environment AGENT_BUILDER_THREAD binding");
170
- }
171
- if (!hasTestBuilder) {
172
- updates.env.test.durable_objects.bindings.push({
173
- name: "AGENT_BUILDER",
174
- class_name: "DurableAgentBuilder"
175
- });
176
- logger.info("Added test environment AGENT_BUILDER binding");
177
- }
178
- if (!updates.env.test.migrations) {
179
- updates.env.test.migrations = [
180
- {
181
- tag: "v1",
182
- new_sqlite_classes: ["DurableThread", "DurableAgentBuilder"]
183
- }
184
- ];
185
- logger.info("Added test environment Durable Object migrations");
186
- }
187
- }
188
- const updatedText = updateWranglerConfig(configPath, text, updates);
189
- writeWranglerConfig(configPath, updatedText);
190
- logger.success("Configuration updated successfully!");
191
- logger.log("\nNext steps:");
192
- logger.log("1. Create your agent definitions in agents/agents/");
193
- logger.log("2. Start your development server");
194
- }
68
+ `;
69
+ var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
70
+
71
+ export default class Thread extends DurableThread {}
72
+ `;
73
+ var AGENT_BUILDER_TS = `import { DurableAgentBuilder } from 'virtual:@standardagents/builder'
74
+
75
+ export default class AgentBuilder extends DurableAgentBuilder {}
76
+ `;
77
+ var WORKER_INDEX = `import { router } from "virtual:@standardagents/builder"
78
+ import DurableThread from '../agents/Thread';
79
+ import DurableAgentBuilder from '../agents/AgentBuilder';
80
+
81
+ export default {
82
+ async fetch(request, env) {
83
+ const res = await router(request, env)
84
+ return res ?? new Response(null, { status: 404 })
85
+ },
86
+ } satisfies ExportedHandler<Env>
87
+
88
+ export { DurableThread, DurableAgentBuilder }
89
+ `;
195
90
  var TOOLS_CLAUDE_MD = `# Custom Tools
196
91
 
197
92
  This directory contains custom tools that your AI agents can call during execution.
@@ -206,216 +101,28 @@ Tools are functions that extend your agent's capabilities beyond text generation
206
101
 
207
102
  ## Creating a Tool
208
103
 
209
- Create a new file in this directory following the naming convention:
210
-
211
- \`\`\`
212
- agents/tools/
213
- \u251C\u2500\u2500 my_tool.ts # \u2713 snake_case recommended
214
- \u251C\u2500\u2500 another_tool.ts # \u2713 Valid
215
- \u2514\u2500\u2500 CamelCaseTool.ts # \u26A0 Works but triggers warning
216
- \`\`\`
217
-
218
- ### Tool File Structure
219
-
220
- Each tool file should export a default function created with \`defineTool\`:
104
+ Create a new file in this directory:
221
105
 
222
106
  \`\`\`typescript
223
107
  import { defineTool } from '@standardagents/builder';
224
108
  import { z } from 'zod';
225
109
 
226
- // With arguments
227
110
  export default defineTool(
228
111
  'Description of what this tool does',
229
112
  z.object({
230
113
  param1: z.string().describe('Description of param1'),
231
- param2: z.number().optional().describe('Optional parameter'),
232
114
  }),
233
115
  async (flow, args) => {
234
116
  // Tool implementation
235
- const result = await doSomething(args.param1, args.param2);
236
-
237
- return {
238
- status: 'success',
239
- result: JSON.stringify(result)
240
- };
241
- }
242
- );
243
-
244
- // Without arguments
245
- export default defineTool(
246
- 'Simple tool with no parameters',
247
- async (flow) => {
248
117
  return {
249
118
  status: 'success',
250
- result: 'Done!'
119
+ result: JSON.stringify({ data: 'result' })
251
120
  };
252
121
  }
253
122
  );
254
123
  \`\`\`
255
124
 
256
- ### FlowState Object
257
-
258
- The \`flow\` parameter provides execution context:
259
-
260
- \`\`\`typescript
261
- interface FlowState {
262
- env: Env; // Cloudflare bindings (KV, R2, etc.)
263
- storage: DurableObjectStorage; // Thread's SQLite storage
264
- threadId: string; // Current thread ID
265
- agentId: string; // Current agent ID
266
- currentSide: 'a' | 'b'; // Which side is executing
267
- turnCount: number; // Current turn number
268
- context: Record<string, any>; // Arbitrary state data
269
- // ... and more
270
- }
271
- \`\`\`
272
-
273
- ### Return Value
274
-
275
- Tools must return a ToolResult object:
276
-
277
- \`\`\`typescript
278
- interface ToolResult {
279
- status: 'success' | 'error';
280
- result?: string; // Success data
281
- error?: string; // Error message
282
- }
283
- \`\`\`
284
-
285
- ## Tool Discovery
286
-
287
- Tools are **auto-discovered** at runtime. No manual registration needed!
288
-
289
- 1. Vite plugin scans this directory on startup
290
- 2. Generates virtual module with dynamic imports
291
- 3. Tools become available to all agents
292
- 4. HMR (Hot Module Replacement) works in development
293
-
294
- ## Examples
295
-
296
- ### API Fetch Tool
297
-
298
- \`\`\`typescript
299
- import { defineTool } from '@standardagents/builder';
300
- import { z } from 'zod';
301
-
302
- export default defineTool(
303
- 'Fetch weather data for a city',
304
- z.object({
305
- city: z.string().describe('City name'),
306
- units: z.enum(['metric', 'imperial']).optional(),
307
- }),
308
- async (flow, args) => {
309
- try {
310
- const response = await fetch(
311
- \`https://api.weather.com/\${args.city}\`
312
- );
313
- const data = await response.json();
314
-
315
- return {
316
- status: 'success',
317
- result: JSON.stringify(data),
318
- };
319
- } catch (error) {
320
- return {
321
- status: 'error',
322
- error: error.message,
323
- };
324
- }
325
- }
326
- );
327
- \`\`\`
328
-
329
- ### Thread Storage Tool
330
-
331
- \`\`\`typescript
332
- import { defineTool } from '@standardagents/builder';
333
- import { z } from 'zod';
334
-
335
- export default defineTool(
336
- 'Get custom data from thread storage',
337
- z.object({
338
- key: z.string().describe('Storage key to look up'),
339
- }),
340
- async (flow, args) => {
341
- try {
342
- // Use thread's SQLite storage
343
- const result = await flow.storage.sql.exec(
344
- \`SELECT value FROM custom_data WHERE key = ?\`,
345
- args.key
346
- ).toArray();
347
-
348
- if (result.length === 0) {
349
- return {
350
- status: 'error',
351
- error: 'Data not found',
352
- };
353
- }
354
-
355
- return {
356
- status: 'success',
357
- result: JSON.stringify(result[0]),
358
- };
359
- } catch (error) {
360
- return {
361
- status: 'error',
362
- error: error.message,
363
- };
364
- }
365
- }
366
- );
367
- \`\`\`
368
-
369
- ## Best Practices
370
-
371
- 1. **Descriptive Names**: Use clear, action-oriented names (e.g., \`fetch_weather\`, not \`weather\`)
372
- 2. **Detailed Descriptions**: The description helps the LLM understand when to use the tool
373
- 3. **Zod Descriptions**: Add \`.describe()\` to all schema fields for better LLM understanding
374
- 4. **Error Handling**: Always wrap in try/catch and return proper error status
375
- 5. **Type Safety**: Use Zod for runtime validation and TypeScript inference
376
- 6. **Keep It Simple**: Each tool should do one thing well
377
- 7. **Stateless**: Don't rely on global state; use FlowState for context
378
-
379
- ## Debugging
380
-
381
- - Check console output for tool execution logs
382
- - Tool errors appear in the logs table
383
- - Use \`console.log\` within tools (visible in dev mode)
384
- - Check message history to see tool calls and responses
385
-
386
- ## Testing
387
-
388
- Tools can be tested independently:
389
-
390
- \`\`\`typescript
391
- import myTool from './my_tool';
392
-
393
- const [description, schema, handler] = myTool;
394
-
395
- // Mock FlowState
396
- const mockFlow = {
397
- env: mockEnv,
398
- storage: mockStorage,
399
- threadId: 'test-123',
400
- // ...
401
- };
402
-
403
- const result = await handler(mockFlow, { param1: 'test' });
404
- console.log(result);
405
- \`\`\`
406
-
407
- ## Limitations
408
-
409
- - No nested directories (tools must be directly in this folder)
410
- - Tool execution is **sequential** (no parallel execution)
411
- - Tool results must be JSON-serializable strings
412
- - Maximum execution time limited by Workers CPU time
413
-
414
- ## Related
415
-
416
- - **Hooks**: \`agents/hooks/CLAUDE.md\` - Lifecycle hooks
417
- - **APIs**: \`agents/api/CLAUDE.md\` - Thread-specific endpoints
418
- - **Documentation**: Project root \`CLAUDE.md\` for architecture overview
125
+ Tools are auto-discovered at runtime. No manual registration needed!
419
126
  `;
420
127
  var HOOKS_CLAUDE_MD = `# Lifecycle Hooks
421
128
 
@@ -428,901 +135,637 @@ Hooks are optional functions that run at specific points during agent execution:
428
135
  - **After** LLM responses (post-process messages)
429
136
  - At other lifecycle events (message creation, tool execution, etc.)
430
137
 
431
- Unlike tools (which agents explicitly call), hooks run **automatically** when their trigger point occurs.
432
-
433
- ## Using defineHook for Type Safety
434
-
435
- All hooks should use the \`defineHook\` utility for strict typing:
436
-
437
- \`\`\`typescript
438
- import { defineHook } from '@standardagents/builder';
439
-
440
- export default defineHook('filter_messages', async (state, rows) => {
441
- // TypeScript knows exactly what state and rows are!
442
- return rows;
443
- });
444
- \`\`\`
445
-
446
- The \`defineHook\` function provides:
447
- - **Strict typing** for hook parameters based on the hook name
448
- - **IntelliSense** support in your editor
449
- - **Type checking** to catch errors before runtime
450
- - **Better documentation** through type hints
451
-
452
- ## Available Hooks
453
-
454
- ### \`filter_messages\`
455
-
456
- **When**: Before message history is transformed to chat completion format
457
- **Purpose**: Filter or modify SQL row data with access to ALL database columns
458
-
459
- \`\`\`typescript
460
- import { defineHook, type MessageRow } from '@standardagents/builder';
461
-
462
- export default defineHook('filter_messages', async (state, rows) => {
463
- // rows contains ALL columns from messages table:
464
- // id, role, content, name, tool_calls, tool_call_id, log_id,
465
- // created_at, request_sent_at, response_completed_at, status,
466
- // silent, tool_status
467
-
468
- // Your filtering logic here
469
- return rows;
470
- });
471
- \`\`\`
472
-
473
- **Common Use Cases**:
474
- - Filter out failed tool messages (\`tool_status = 'error'\`)
475
- - Remove messages older than a certain time
476
- - Filter by status (pending, completed, failed)
477
- - Access columns not available in chat format
478
- - Filter based on database-specific metadata
479
-
480
- **Example - Filter Out Failed Tools**:
481
- \`\`\`typescript
482
- export default defineHook('filter_messages', async (state, rows) => {
483
- // Remove tool messages that failed execution
484
- return rows.filter(row => {
485
- if (row.role === 'tool' && row.tool_status === 'error') {
486
- return false;
487
- }
488
- return true;
489
- });
490
- });
491
- \`\`\`
492
-
493
- **Important**: This hook runs **before** messages are transformed to Message objects and receives raw SQL data. Use this when you need access to database columns like \`tool_status\`, \`silent\`, or \`status\`.
494
-
495
- ### \`prefilter_llm_history\`
496
-
497
- **When**: Immediately after message transformation, before sending to LLM
498
- **Purpose**: Modify, filter, or limit message history in chat completion format
138
+ ## Creating a Hook
499
139
 
500
140
  \`\`\`typescript
501
141
  import { defineHook } from '@standardagents/builder';
502
142
 
503
143
  export default defineHook('prefilter_llm_history', async (state, messages) => {
504
- // messages are in chat completion format (already transformed from SQL rows)
505
- // Available fields: role, content, tool_calls, tool_call_id, name
506
-
507
- // Your filtering logic here
144
+ // Filter or modify messages before sending to LLM
508
145
  return messages;
509
146
  });
510
147
  \`\`\`
511
148
 
512
- **Common Use Cases**:
513
- - Limit conversation history to last N messages
514
- - Remove old tool messages to reduce token usage
515
- - Summarize old messages before sending
516
- - Add dynamic context based on message patterns
517
- - Filter out sensitive information
149
+ Hook files must be named exactly as the hook type (e.g., \`prefilter_llm_history.ts\`).
150
+ `;
151
+ var API_CLAUDE_MD = `# Thread-Specific API Endpoints
518
152
 
519
- **Example - Limit to Last 10 Messages**:
520
- \`\`\`typescript
521
- import { defineHook } from '@standardagents/builder';
153
+ This directory contains custom API endpoints that operate on specific threads.
522
154
 
523
- export default defineHook('prefilter_llm_history', async (state, messages) => {
524
- // Keep all system messages
525
- const systemMessages = messages.filter(m => m.role === 'system');
155
+ ## What Are Thread Endpoints?
526
156
 
527
- // Take only last 10 non-system messages
528
- const otherMessages = messages
529
- .filter(m => m.role !== 'system')
530
- .slice(-10);
157
+ Thread endpoints are API routes that:
158
+ - Automatically receive a specific thread's Durable Object instance
159
+ - Can access thread-local SQLite storage
160
+ - Perform operations in the context of a conversation
161
+ - Use file-based routing for automatic discovery
531
162
 
532
- return [...systemMessages, ...otherMessages];
533
- });
534
- \`\`\`
163
+ ## Creating an Endpoint
535
164
 
536
- **Example - Remove Old Tool Messages**:
537
165
  \`\`\`typescript
538
- import { defineHook } from '@standardagents/builder';
539
-
540
- export default defineHook('prefilter_llm_history', async (state, messages) => {
541
- // Keep messages from last 5 turns, remove old tool messages
542
- const recentThreshold = messages.length - 10;
166
+ // agents/api/summary.get.ts -> GET /agents/api/threads/:id/summary
167
+ import { defineThreadEndpoint } from '@standardagents/builder';
543
168
 
544
- return messages.filter((m, index) => {
545
- if (m.role === 'tool' && index < recentThreshold) {
546
- return false; // Remove old tool messages
547
- }
548
- return true;
169
+ export default defineThreadEndpoint(async (thread, request, env) => {
170
+ const messages = await thread.getMessages();
171
+ return new Response(JSON.stringify({ count: messages.length }), {
172
+ headers: { 'Content-Type': 'application/json' }
549
173
  });
550
174
  });
551
175
  \`\`\`
552
176
 
553
- **Important**: The **filtered messages are logged**, so you can see exactly what was sent to the LLM in the logs table.
554
-
555
- ### \`post_process_message\`
556
-
557
- **When**: After receiving a response from the LLM
558
- **Purpose**: Modify or enhance the assistant's message
559
-
560
- \`\`\`typescript
561
- import { defineHook } from '@standardagents/builder';
177
+ Pattern: \`{name}.{method}.ts\` (e.g., \`summary.get.ts\`, \`export.post.ts\`)
178
+ `;
179
+ var AGENTS_CLAUDE_MD = `# Agent Definitions
562
180
 
563
- export default defineHook('post_process_message', async (state, message) => {
564
- // Your post-processing logic here
565
- return message;
566
- });
567
- \`\`\`
181
+ This directory contains your AI agent definitions.
568
182
 
569
- **Common Use Cases**:
570
- - Format or clean up LLM output
571
- - Add metadata or tracking information
572
- - Inject additional context into responses
573
- - Transform tool call formats
183
+ ## Creating an Agent
574
184
 
575
- **Example - Add Metadata**:
576
185
  \`\`\`typescript
577
- import { defineHook } from '@standardagents/builder';
186
+ import { defineAgent } from '@standardagents/builder';
578
187
 
579
- export default defineHook('post_process_message', async (state, message) => {
580
- if (message.content) {
581
- message.content += \`\\n\\n_Generated at turn \${state.turnCount}_\`;
582
- }
583
- return message;
188
+ export default defineAgent({
189
+ name: 'my-agent',
190
+ type: 'ai_human',
191
+ title: 'My Agent',
192
+ defaultPrompt: 'my-prompt',
193
+ defaultModel: 'gpt-4o',
194
+ tools: ['my_tool'],
584
195
  });
585
196
  \`\`\`
586
197
 
587
- **Note**: This hook is currently defined but **not yet invoked** in FlowEngine. It will be activated in a future update.
588
-
589
- ### Message Lifecycle Hooks
590
-
591
- The following hooks run for **ANY** message being created or updated in the database, whether it's an agent message, tool message, user message, or system message.
592
-
593
- #### \`before_create_message\`
594
-
595
- **When**: Immediately before a message is inserted into the database
596
- **Purpose**: Modify message data before it's stored
597
-
598
- \`\`\`typescript
599
- import { defineHook } from '@standardagents/builder';
600
-
601
- export default defineHook('before_create_message', async (state, message) => {
602
- // Your modification logic here
603
- return message;
604
- });
605
- \`\`\`
198
+ Agent types:
199
+ - \`ai_human\`: Human interacts with AI agent
200
+ - \`dual_ai\`: Two AI agents interact with each other
201
+ `;
202
+ var PROMPTS_CLAUDE_MD = `# Prompt Definitions
606
203
 
607
- **Message Structure**:
608
- \`\`\`typescript
609
- {
610
- id: string;
611
- role: string;
612
- content: string | null;
613
- tool_calls?: string | null;
614
- tool_call_id?: string | null;
615
- name?: string | null;
616
- created_at: number;
617
- status?: string;
618
- silent?: boolean;
619
- }
620
- \`\`\`
204
+ This directory contains your prompt/system message definitions.
621
205
 
622
- **Common Use Cases**:
623
- - Add prefixes or metadata to message content
624
- - Modify message based on agent configuration
625
- - Add tracking IDs or identifiers
626
- - Transform message format before storage
206
+ ## Creating a Prompt
627
207
 
628
- **Example - Add Metadata**:
629
208
  \`\`\`typescript
630
- import { defineHook } from '@standardagents/builder';
209
+ import { definePrompt } from '@standardagents/builder';
631
210
 
632
- export default defineHook('before_create_message', async (state, message) => {
633
- // Add agent ID to all messages
634
- if (message.content && message.role === 'assistant') {
635
- message.name = state.agentConfig.title;
636
- }
637
- return message;
211
+ export default definePrompt({
212
+ name: 'my-prompt',
213
+ model: 'gpt-4o',
214
+ systemPrompt: 'You are a helpful assistant...',
215
+ tools: ['search', 'calculate'],
638
216
  });
639
217
  \`\`\`
218
+ `;
219
+ var MODELS_CLAUDE_MD = `# Model Definitions
640
220
 
641
- #### \`after_create_message\`
221
+ This directory contains your AI model configurations.
642
222
 
643
- **When**: Immediately after a message is inserted into the database
644
- **Purpose**: Perform actions based on newly created messages
223
+ ## Creating a Model
645
224
 
646
225
  \`\`\`typescript
647
- import { defineHook } from '@standardagents/builder';
226
+ import { defineModel } from '@standardagents/builder';
648
227
 
649
- export default defineHook('after_create_message', async (state, message) => {
650
- // Your post-creation logic here
651
- // No return value needed
228
+ export default defineModel({
229
+ name: 'gpt-4o',
230
+ model: 'gpt-4o',
231
+ provider: 'openai',
232
+ inputPrice: 2.5,
233
+ outputPrice: 10,
652
234
  });
653
235
  \`\`\`
654
236
 
655
- **Common Use Cases**:
656
- - Log messages to external systems
657
- - Trigger webhooks or notifications
658
- - Update analytics or metrics
659
- - Send real-time updates to monitoring services
660
-
661
- **Example - Log to External Service**:
662
- \`\`\`typescript
663
- import { defineHook } from '@standardagents/builder';
664
-
665
- export default defineHook('after_create_message', async (state, message) => {
666
- // Log all assistant messages to analytics
667
- if (message.role === 'assistant' && message.content) {
237
+ Supported providers: \`openai\`, \`anthropic\`, \`openrouter\`, \`google\`
238
+ `;
239
+ function getProjectName(cwd) {
240
+ const packageJsonPath = path.join(cwd, "package.json");
241
+ if (fs.existsSync(packageJsonPath)) {
668
242
  try {
669
- await fetch('https://analytics.example.com/message', {
670
- method: 'POST',
671
- body: JSON.stringify({
672
- threadId: state.threadId,
673
- messageId: message.id,
674
- contentLength: message.content.length,
675
- })
676
- });
677
- } catch (error) {
678
- console.error('Analytics logging failed:', error);
243
+ const pkg2 = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
244
+ if (pkg2.name) {
245
+ return pkg2.name.replace(/^@[^/]+\//, "");
246
+ }
247
+ } catch {
679
248
  }
680
249
  }
681
- });
682
- \`\`\`
683
-
684
- #### \`before_update_message\`
685
-
686
- **When**: Immediately before a message is updated in the database
687
- **Purpose**: Modify update data before it's applied
688
-
689
- \`\`\`typescript
690
- import { defineHook } from '@standardagents/builder';
691
-
692
- export default defineHook('before_update_message', async (state, messageId, updates) => {
693
- // Your modification logic here
694
- return updates;
695
- });
696
- \`\`\`
697
-
698
- **Common Use Cases**:
699
- - Validate or sanitize updated content
700
- - Add completion timestamps
701
- - Transform status values
702
- - Inject metadata into updates
703
-
704
- **Example - Add Completion Timestamp**:
705
- \`\`\`typescript
706
- import { defineHook } from '@standardagents/builder';
707
-
708
- export default defineHook('before_update_message', async (state, messageId, updates) => {
709
- // Add custom completion timestamp for completed messages
710
- if (updates.status === 'completed' && !updates.response_completed_at) {
711
- updates.response_completed_at = Date.now() * 1000; // microseconds
250
+ return path.basename(cwd);
251
+ }
252
+ function findViteConfig(cwd) {
253
+ const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
254
+ for (const candidate of candidates) {
255
+ const configPath = path.join(cwd, candidate);
256
+ if (fs.existsSync(configPath)) {
257
+ return configPath;
258
+ }
712
259
  }
713
- return updates;
714
- });
715
- \`\`\`
716
-
717
- #### \`after_update_message\`
718
-
719
- **When**: Immediately after a message is updated in the database
720
- **Purpose**: Perform actions based on message updates
721
-
722
- \`\`\`typescript
723
- import { defineHook } from '@standardagents/builder';
724
-
725
- export default defineHook('after_update_message', async (state, messageId, updates) => {
726
- // Your post-update logic here
727
- // No return value needed
728
- });
729
- \`\`\`
730
-
731
- **Common Use Cases**:
732
- - Track message status changes
733
- - Trigger notifications on completion
734
- - Update external systems
735
- - Log state transitions
736
-
737
- **Example - Notify on Failure**:
738
- \`\`\`typescript
739
- import { defineHook } from '@standardagents/builder';
740
-
741
- export default defineHook('after_update_message', async (state, messageId, updates) => {
742
- // Send notification when message is marked as failed
743
- if (updates.status === 'failed') {
744
- console.log(\`[Alert] Message \${messageId} failed in thread \${state.threadId}\`);
745
- }
746
- });
747
- \`\`\`
748
-
749
- **Important**:
750
- - \`before_*\` hooks must return the modified data object
751
- - \`after_*\` hooks don't need to return anything
752
- - All 4 hooks run for ANY message operation (agent, tool, user, system messages)
753
- - Updates passed to update hooks contain only the fields being updated
754
-
755
- ## Creating a Hook
756
-
757
- 1. Create a file named exactly as the hook (e.g., \`prefilter_llm_history.ts\`)
758
- 2. Use \`defineHook\` to ensure correct typing
759
- 3. Return the modified data (or original if no changes)
760
-
761
- \`\`\`typescript
762
- // agents/hooks/prefilter_llm_history.ts
763
- import { defineHook } from '@standardagents/builder';
764
-
765
- export default defineHook('prefilter_llm_history', async (state, messages) => {
766
- console.log(\`Processing \${messages.length} messages\`);
767
-
768
- // Your logic here
769
-
770
- return messages; // Return modified or original
771
- });
772
- \`\`\`
773
-
774
- ## Hook Lifecycle
775
-
776
- 1. **Discovery**: Vite plugin scans this directory on startup
777
- 2. **Virtual Module**: Generates \`virtual:@standardagents-hooks\` with lazy imports
778
- 3. **Lazy Loading**: Hooks are loaded on first use (not at startup)
779
- 4. **Caching**: Once loaded, hooks are cached for performance
780
- 5. **HMR**: Changes trigger hot reload in development
781
-
782
- ## Error Handling
783
-
784
- Hooks are designed to be **safe**:
785
- - If hook file doesn't exist \u2192 Silently skipped (no error)
786
- - If hook throws error \u2192 Logged to console, original data returned
787
- - If hook returns invalid data \u2192 Original data used as fallback
788
-
789
- This ensures hooks never break agent execution.
790
-
791
- ## FlowState Object
792
-
793
- All hooks receive the FlowState context:
794
-
795
- \`\`\`typescript
796
- interface FlowState {
797
- threadId: string; // Current thread ID
798
- flowId: string; // Unique execution ID
799
- agentConfig: Agent; // Full agent configuration
800
- currentSide: 'a' | 'b'; // Which side is executing (dual_ai)
801
- turnCount: number; // Current turn number
802
- stopped: boolean; // Whether execution should stop
803
- messageHistory: Message[]; // Full conversation history
804
- env: Env; // Cloudflare bindings
805
- storage: DurableObjectStorage; // Thread's SQLite storage
806
- context: Record<string, any>; // Arbitrary state data
807
- // ... and more
260
+ return null;
808
261
  }
809
- \`\`\`
810
-
811
- Use this to make context-aware decisions in your hooks.
812
-
813
- ## Best Practices
814
-
815
- 1. **Keep Hooks Fast**: They run on every execution, avoid heavy operations
816
- 2. **Always Return Data**: Never return \`undefined\` or \`null\`
817
- 3. **Log Thoughtfully**: Use \`console.log\` sparingly (visible in logs)
818
- 4. **Handle Errors**: Wrap risky operations in try/catch
819
- 5. **Document Behavior**: Add comments explaining what your hook does
820
- 6. **Test Thoroughly**: Hooks affect ALL agent executions
821
-
822
- ## Debugging
823
-
824
- - Check console output for hook loading/execution logs
825
- - Hook errors are logged with \`[Hooks] \u2717\` prefix
826
- - Inspect logs table to see filtered messages (for prefilter hook)
827
- - Use \`console.log\` within hooks for debugging
828
-
829
- ## Performance Considerations
830
-
831
- Hooks run on **every turn**, so:
832
- - Avoid expensive operations (heavy computation, slow APIs)
833
- - Cache results when possible
834
- - Consider using FlowState context to skip unnecessary work
835
- - Use async operations efficiently
836
-
837
- ## Message Data & Tool Status
838
-
839
- The \`filter_messages\` hook receives SQL row data with ALL columns from the messages table:
840
-
841
- \`\`\`typescript
842
- interface MessageRow {
843
- id: string;
844
- role: 'system' | 'user' | 'assistant' | 'tool';
845
- content: string | null;
846
- name: string | null;
847
- tool_calls: string | null; // JSON string of tool calls
848
- tool_call_id: string | null; // For role='tool' messages
849
- log_id: string | null; // Reference to logs table
850
- created_at: number; // Microseconds timestamp
851
- request_sent_at: number | null; // When request was sent to LLM
852
- response_completed_at: number | null; // When response completed
853
- status: 'pending' | 'completed' | 'failed' | null;
854
- silent: number | null; // 1 if message should be hidden, 0/null otherwise
855
- tool_status: 'success' | 'error' | null; // Status of tool execution (tool messages only)
262
+ function findWranglerConfig(cwd) {
263
+ const candidates = ["wrangler.jsonc", "wrangler.json"];
264
+ for (const candidate of candidates) {
265
+ const configPath = path.join(cwd, candidate);
266
+ if (fs.existsSync(configPath)) {
267
+ return configPath;
268
+ }
269
+ }
270
+ return null;
856
271
  }
857
- \`\`\`
858
-
859
- The \`tool_status\` column is automatically set when tool messages are created:
860
- - \`'success'\` - Tool executed successfully
861
- - \`'error'\` - Tool execution failed or threw an error
862
- - \`null\` - Not a tool message (role !== 'tool')
863
-
864
- ## Testing
865
-
866
- Example test for a hook:
867
-
868
- \`\`\`typescript
869
- import { defineHook } from '@standardagents/builder';
870
- import prefilterHook from './prefilter_llm_history';
871
-
872
- const mockState = {
873
- turnCount: 5,
874
- currentSide: 'a',
875
- // ... other FlowState fields
876
- };
877
-
878
- const mockMessages = [
879
- { role: 'system', content: 'You are helpful' },
880
- { role: 'user', content: 'Hello' },
881
- { role: 'assistant', content: 'Hi there!' },
882
- // ... more messages
883
- ];
884
-
885
- const result = await prefilterHook(mockState, mockMessages);
886
- console.log('Filtered:', result.length, 'messages');
887
- \`\`\`
888
-
889
- ## Hook File Naming
890
-
891
- **Critical**: Hook files must be named **exactly** as expected:
892
- - \u2713 \`filter_messages.ts\`
893
- - \u2713 \`prefilter_llm_history.ts\`
894
- - \u2713 \`post_process_message.ts\`
895
- - \u2713 \`before_create_message.ts\`
896
- - \u2713 \`after_create_message.ts\`
897
- - \u2713 \`before_update_message.ts\`
898
- - \u2713 \`after_update_message.ts\`
899
- - \u2717 \`filterMessages.ts\` (wrong case)
900
- - \u2717 \`prefilterLLMHistory.ts\` (wrong case)
901
- - \u2717 \`before-create-message.ts\` (wrong separator)
902
-
903
- The file name determines which hook point it intercepts.
904
-
905
- ## Available Hooks
906
-
907
- Currently implemented hooks:
908
- - \`filter_messages\` - Filter SQL row data before transformation to chat format (access to all DB columns including tool_status)
909
- - \`prefilter_llm_history\` - Filter messages before sending to LLM (after transformation to chat format)
910
- - \`before_create_message\` - Before inserting message into database
911
- - \`after_create_message\` - After message is created in database
912
- - \`before_update_message\` - Before updating message in database
913
- - \`after_update_message\` - After message is updated in database
914
- - \`after_tool_call_success\` - After successful tool execution (can modify result or return null to remove)
915
- - \`after_tool_call_failure\` - After failed tool execution (can modify error or return null to remove)
916
-
917
- ## Related
918
-
919
- - **Tools**: \`agents/tools/CLAUDE.md\` - Custom tools
920
- - **APIs**: \`agents/api/CLAUDE.md\` - Thread endpoints
921
- - **Documentation**: Project root \`CLAUDE.md\` for architecture
922
- `;
923
- var API_CLAUDE_MD = `# Thread-Specific API Endpoints
924
-
925
- This directory contains custom API endpoints that operate on specific threads.
926
-
927
- ## What Are Thread Endpoints?
928
-
929
- Thread endpoints are API routes that:
930
- - Automatically receive a specific thread's Durable Object instance
931
- - Can access thread-local SQLite storage
932
- - Perform operations in the context of a conversation
933
- - Use file-based routing for automatic discovery
934
-
935
- ## File-Based Routing
936
-
937
- Create files following this naming convention:
938
-
939
- \`\`\`
940
- agents/api/
941
- \u251C\u2500\u2500 summary.get.ts # GET /agents/api/threads/:id/summary
942
- \u251C\u2500\u2500 export.post.ts # POST /agents/api/threads/:id/export
943
- \u251C\u2500\u2500 metadata.put.ts # PUT /agents/api/threads/:id/metadata
944
- \u2514\u2500\u2500 archive.delete.ts # DELETE /agents/api/threads/:id/archive
945
- \`\`\`
946
-
947
- **Pattern**: \`{name}.{method}.ts\`
948
-
949
- **Methods**: \`get\`, \`post\`, \`put\`, \`delete\`, \`patch\`
950
-
951
- **URL**: \`/agents/api/threads/:id/{name}\`
952
-
953
- ## Creating a Thread Endpoint
954
-
955
- Use \`defineThreadEndpoint\` from \`@standardagents/builder\`:
956
-
957
- \`\`\`typescript
958
- import { defineThreadEndpoint } from '@standardagents/builder';
959
-
960
- export default defineThreadEndpoint(async (thread, request, env) => {
961
- // thread is the DurableObject stub for the requested thread
962
- // request is the incoming Request object
963
- // env is the Cloudflare environment with bindings
964
-
965
- // Access thread's storage directly
966
- const messages = await thread.getMessages();
967
-
968
- // Perform operations
969
- const summary = generateSummary(messages);
970
-
971
- // Return response
972
- return new Response(JSON.stringify({ summary }), {
973
- headers: { 'Content-Type': 'application/json' }
974
- });
975
- });
976
- \`\`\`
977
-
978
- ## Thread Object
979
-
980
- The \`thread\` parameter is a DurableObject stub with RPC methods:
981
-
982
- \`\`\`typescript
983
- interface DurableThread {
984
- // Get messages from thread's SQLite storage
985
- getMessages(limit?: number, offset?: number): Promise<Message[]>;
986
-
987
- // Get execution logs
988
- getLogs(limit?: number, offset?: number): Promise<Log[]>;
989
-
990
- // Process a new message (starts agent execution)
991
- processMessage(content: string, role?: string): Promise<Response>;
992
-
993
- // Get thread metadata
994
- getThreadMeta(threadId: string): Promise<ThreadMetadata>;
995
-
996
- // Direct storage access (advanced)
997
- storage: DurableObjectStorage;
272
+ async function modifyViteConfig(configPath, force) {
273
+ try {
274
+ const mod = await loadFile(configPath);
275
+ const code = fs.readFileSync(configPath, "utf-8");
276
+ const hasCloudflare = code.includes("@cloudflare/vite-plugin") || code.includes("cloudflare()");
277
+ const hasAgentBuilder = code.includes("@standardagents/builder") || code.includes("agentbuilder()");
278
+ if (hasCloudflare && hasAgentBuilder && !force) {
279
+ logger.info("Vite config already includes Standard Agents plugins");
280
+ return true;
281
+ }
282
+ if (!hasCloudflare || force) {
283
+ addVitePlugin(mod, {
284
+ from: "@cloudflare/vite-plugin",
285
+ imported: "cloudflare",
286
+ constructor: "cloudflare"
287
+ });
288
+ logger.success("Added cloudflare plugin to vite.config");
289
+ }
290
+ if (!hasAgentBuilder || force) {
291
+ addVitePlugin(mod, {
292
+ from: "@standardagents/builder",
293
+ imported: "agentbuilder",
294
+ constructor: "agentbuilder",
295
+ options: { mountPoint: "/" }
296
+ });
297
+ logger.success("Added agentbuilder plugin to vite.config");
298
+ }
299
+ await writeFile(mod, configPath);
300
+ return true;
301
+ } catch (error) {
302
+ logger.warning(`Could not automatically modify vite.config: ${error}`);
303
+ logger.log("");
304
+ logger.log("Please manually add the following to your vite.config.ts:");
305
+ logger.log("");
306
+ logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
307
+ logger.log(` import { agentbuilder } from '@standardagents/builder'`);
308
+ logger.log("");
309
+ logger.log(` plugins: [cloudflare(), agentbuilder({ mountPoint: '/' })]`);
310
+ logger.log("");
311
+ return false;
312
+ }
998
313
  }
999
- \`\`\`
1000
-
1001
- ## Examples
1002
-
1003
- ### GET Summary Endpoint
1004
-
1005
- \`\`\`typescript
1006
- // agents/api/summary.get.ts
1007
- import { defineThreadEndpoint } from '@standardagents/builder';
1008
-
1009
- export default defineThreadEndpoint(async (thread, request, env) => {
1010
- // Fetch messages from thread
1011
- const messages = await thread.getMessages();
1012
-
1013
- // Generate summary
1014
- const userMessages = messages.filter(m => m.role === 'user');
1015
- const assistantMessages = messages.filter(m => m.role === 'assistant');
1016
-
1017
- return new Response(JSON.stringify({
1018
- total_messages: messages.length,
1019
- user_messages: userMessages.length,
1020
- assistant_messages: assistantMessages.length,
1021
- first_message: messages[0]?.content,
1022
- last_message: messages[messages.length - 1]?.content,
1023
- }), {
1024
- headers: { 'Content-Type': 'application/json' }
1025
- });
1026
- });
1027
- \`\`\`
1028
-
1029
- **Usage**: \`GET /agents/api/threads/{threadId}/summary\`
1030
-
1031
- ### POST Export Endpoint
1032
-
1033
- \`\`\`typescript
1034
- // agents/api/export.post.ts
1035
- import { defineThreadEndpoint } from '@standardagents/builder';
1036
-
1037
- export default defineThreadEndpoint(async (thread, request, env) => {
1038
- const { format } = await request.json();
1039
-
1040
- // Get messages
1041
- const messages = await thread.getMessages();
1042
-
1043
- // Export based on format
1044
- let content: string;
1045
- let contentType: string;
1046
-
1047
- if (format === 'json') {
1048
- content = JSON.stringify(messages, null, 2);
1049
- contentType = 'application/json';
1050
- } else if (format === 'markdown') {
1051
- content = messages
1052
- .map(m => \`**\${m.role}**: \${m.content}\`)
1053
- .join('\\n\\n');
1054
- contentType = 'text/markdown';
1055
- } else {
1056
- return new Response('Invalid format', { status: 400 });
314
+ function createOrUpdateWranglerConfig(cwd, projectName, force) {
315
+ var _a, _b;
316
+ const existingConfig = findWranglerConfig(cwd);
317
+ if (existingConfig && !force) {
318
+ try {
319
+ const text = fs.readFileSync(existingConfig, "utf-8");
320
+ const config = parse(text);
321
+ const hasBindings = (_b = (_a = config.durable_objects) == null ? void 0 : _a.bindings) == null ? void 0 : _b.some(
322
+ (b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
323
+ );
324
+ if (hasBindings) {
325
+ logger.info("wrangler.jsonc already configured for Standard Agents");
326
+ return true;
327
+ }
328
+ let result = text;
329
+ if (!config.durable_objects) {
330
+ const edits = modify(result, ["durable_objects"], {
331
+ bindings: [
332
+ { name: "AGENT_BUILDER_THREAD", class_name: "DurableThread" },
333
+ { name: "AGENT_BUILDER", class_name: "DurableAgentBuilder" }
334
+ ]
335
+ }, {});
336
+ result = applyEdits(result, edits);
337
+ } else {
338
+ const bindings = config.durable_objects.bindings || [];
339
+ bindings.push(
340
+ { name: "AGENT_BUILDER_THREAD", class_name: "DurableThread" },
341
+ { name: "AGENT_BUILDER", class_name: "DurableAgentBuilder" }
342
+ );
343
+ const edits = modify(result, ["durable_objects", "bindings"], bindings, {});
344
+ result = applyEdits(result, edits);
345
+ }
346
+ if (!config.migrations) {
347
+ const edits = modify(result, ["migrations"], [
348
+ { tag: "v1", new_sqlite_classes: ["DurableThread"] },
349
+ { tag: "v2", new_sqlite_classes: ["DurableAgentBuilder"] }
350
+ ], {});
351
+ result = applyEdits(result, edits);
352
+ }
353
+ if (!config.main || !config.main.includes("worker")) {
354
+ const edits = modify(result, ["main"], "worker/index.ts", {});
355
+ result = applyEdits(result, edits);
356
+ }
357
+ if (!config.assets) {
358
+ const edits = modify(result, ["assets"], {
359
+ directory: "dist",
360
+ not_found_handling: "single-page-application",
361
+ binding: "ASSETS",
362
+ run_worker_first: ["/**"]
363
+ }, {});
364
+ result = applyEdits(result, edits);
365
+ }
366
+ fs.writeFileSync(existingConfig, result, "utf-8");
367
+ logger.success("Updated wrangler.jsonc with Standard Agents configuration");
368
+ return true;
369
+ } catch (error) {
370
+ logger.warning(`Could not update wrangler config: ${error}`);
371
+ return false;
372
+ }
1057
373
  }
1058
-
1059
- return new Response(content, {
1060
- headers: {
1061
- 'Content-Type': contentType,
1062
- 'Content-Disposition': \`attachment; filename="thread-export.\${format}"\`
374
+ const wranglerPath = path.join(cwd, "wrangler.jsonc");
375
+ const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
376
+ fs.writeFileSync(wranglerPath, WRANGLER_TEMPLATE(sanitizedName), "utf-8");
377
+ logger.success("Created wrangler.jsonc");
378
+ return true;
379
+ }
380
+ function getWorkerEntryPoint(cwd) {
381
+ const wranglerConfig = findWranglerConfig(cwd);
382
+ if (wranglerConfig) {
383
+ try {
384
+ const text = fs.readFileSync(wranglerConfig, "utf-8");
385
+ const config = parse(text);
386
+ if (config.main) {
387
+ return path.join(cwd, config.main);
388
+ }
389
+ } catch {
1063
390
  }
1064
- });
1065
- });
1066
- \`\`\`
1067
-
1068
- **Usage**: \`POST /agents/api/threads/{threadId}/export\`
1069
-
1070
- ### Direct Storage Access
1071
-
1072
- \`\`\`typescript
1073
- // agents/api/stats.get.ts
1074
- import { defineThreadEndpoint } from '@standardagents/builder';
1075
-
1076
- export default defineThreadEndpoint(async (thread, request, env) => {
1077
- // Access SQLite storage directly
1078
- const storage = (thread as any).storage;
1079
-
1080
- const cursor = await storage.sql.exec(\`
1081
- SELECT
1082
- COUNT(*) as count,
1083
- role,
1084
- AVG(LENGTH(content)) as avg_length
1085
- FROM messages
1086
- GROUP BY role
1087
- \`);
1088
-
1089
- const stats = cursor.toArray();
1090
-
1091
- return new Response(JSON.stringify({ stats }), {
1092
- headers: { 'Content-Type': 'application/json' }
1093
- });
1094
- });
1095
- \`\`\`
1096
-
1097
- ## Request Handling
1098
-
1099
- ### Reading Request Body
1100
-
1101
- \`\`\`typescript
1102
- export default defineThreadEndpoint(async (thread, request, env) => {
1103
- // JSON
1104
- const body = await request.json();
1105
-
1106
- // FormData
1107
- const formData = await request.formData();
1108
-
1109
- // Text
1110
- const text = await request.text();
1111
-
1112
- // ...
1113
- });
1114
- \`\`\`
1115
-
1116
- ### Query Parameters
1117
-
1118
- \`\`\`typescript
1119
- export default defineThreadEndpoint(async (thread, request, env) => {
1120
- const url = new URL(request.url);
1121
- const limit = url.searchParams.get('limit') || '10';
1122
-
1123
- const messages = await thread.getMessages(parseInt(limit));
1124
-
1125
- // ...
1126
- });
1127
- \`\`\`
1128
-
1129
- ### Headers
1130
-
1131
- \`\`\`typescript
1132
- export default defineThreadEndpoint(async (thread, request, env) => {
1133
- const authHeader = request.headers.get('Authorization');
1134
-
1135
- if (!authHeader) {
1136
- return new Response('Unauthorized', { status: 401 });
1137
391
  }
1138
-
1139
- // ...
1140
- });
1141
- \`\`\`
1142
-
1143
- ## Error Handling
1144
-
1145
- Always wrap in try/catch for robust error handling:
1146
-
1147
- \`\`\`typescript
1148
- export default defineThreadEndpoint(async (thread, request, env) => {
392
+ return path.join(cwd, "worker", "index.ts");
393
+ }
394
+ async function createOrUpdateWorkerEntry(cwd, force) {
395
+ const entryPath = getWorkerEntryPoint(cwd);
396
+ const entryDir = path.dirname(entryPath);
397
+ if (!fs.existsSync(entryDir)) {
398
+ fs.mkdirSync(entryDir, { recursive: true });
399
+ logger.success(`Created ${path.relative(cwd, entryDir)} directory`);
400
+ }
401
+ if (!fs.existsSync(entryPath)) {
402
+ fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
403
+ logger.success(`Created ${path.relative(cwd, entryPath)}`);
404
+ return true;
405
+ }
406
+ const content = fs.readFileSync(entryPath, "utf-8");
407
+ const hasRouter = content.includes("virtual:@standardagents/builder") && content.includes("router");
408
+ const hasDurableExports = content.includes("DurableThread") && content.includes("DurableAgentBuilder");
409
+ if (hasRouter && hasDurableExports && !force) {
410
+ logger.info(`${path.relative(cwd, entryPath)} already configured`);
411
+ return true;
412
+ }
1149
413
  try {
1150
- // Your endpoint logic
1151
- const result = await doSomething();
1152
-
1153
- return new Response(JSON.stringify(result), {
1154
- headers: { 'Content-Type': 'application/json' }
1155
- });
414
+ const mod = await loadFile(entryPath);
415
+ if (!content.includes("virtual:@standardagents/builder") || force) {
416
+ mod.imports.$add({
417
+ from: "virtual:@standardagents/builder",
418
+ imported: "router",
419
+ local: "router"
420
+ });
421
+ }
422
+ if (!content.includes("'../agents/Thread'") || force) {
423
+ mod.imports.$add({
424
+ from: "../agents/Thread",
425
+ imported: "default",
426
+ local: "DurableThread"
427
+ });
428
+ }
429
+ if (!content.includes("'../agents/AgentBuilder'") || force) {
430
+ mod.imports.$add({
431
+ from: "../agents/AgentBuilder",
432
+ imported: "default",
433
+ local: "DurableAgentBuilder"
434
+ });
435
+ }
436
+ const { code } = generateCode(mod);
437
+ if (force || !hasRouter) {
438
+ fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
439
+ logger.success(`Updated ${path.relative(cwd, entryPath)} with Standard Agents router`);
440
+ }
441
+ return true;
1156
442
  } catch (error) {
1157
- console.error('Endpoint error:', error);
1158
-
1159
- return new Response(
1160
- JSON.stringify({
1161
- error: error.message
1162
- }),
1163
- {
1164
- status: 500,
1165
- headers: { 'Content-Type': 'application/json' }
1166
- }
1167
- );
443
+ if (force) {
444
+ fs.writeFileSync(entryPath, WORKER_INDEX, "utf-8");
445
+ logger.success(`Created ${path.relative(cwd, entryPath)}`);
446
+ return true;
447
+ }
448
+ logger.warning(`Could not automatically modify ${path.relative(cwd, entryPath)}`);
449
+ logger.log("");
450
+ logger.log("Please ensure your worker entry point includes:");
451
+ logger.log("");
452
+ logger.log(` import { router } from "virtual:@standardagents/builder"`);
453
+ logger.log(` import DurableThread from '../agents/Thread';`);
454
+ logger.log(` import DurableAgentBuilder from '../agents/AgentBuilder';`);
455
+ logger.log("");
456
+ logger.log(" // In your fetch handler:");
457
+ logger.log(" const res = router(request, env)");
458
+ logger.log(" if (res) return res");
459
+ logger.log("");
460
+ logger.log(" // Export Durable Objects:");
461
+ logger.log(" export { DurableThread, DurableAgentBuilder }");
462
+ logger.log("");
463
+ return false;
1168
464
  }
1169
- });
1170
- \`\`\`
1171
-
1172
- ## Environment Bindings
1173
-
1174
- Access Cloudflare bindings via \`env\`:
1175
-
1176
- \`\`\`typescript
1177
- export default defineThreadEndpoint(async (thread, request, env) => {
1178
- // Durable Objects
1179
- const agentBuilder = env.AGENT_BUILDER.get(env.AGENT_BUILDER.idFromName("singleton"));
1180
- const threadData = await agentBuilder.getThread(threadId);
1181
-
1182
- // KV (if configured)
1183
- const cache = await env.MY_KV.get('cache-key');
1184
-
1185
- // R2 (if configured)
1186
- const object = await env.MY_BUCKET.get('file.txt');
1187
-
1188
- // ...
1189
- });
1190
- \`\`\`
1191
-
1192
- ## Auto-Discovery
1193
-
1194
- Thread endpoints are **auto-discovered**:
1195
-
1196
- 1. Vite plugin scans this directory on startup
1197
- 2. Generates virtual module with dynamic imports
1198
- 3. Routes registered in the main router
1199
- 4. HMR works in development
1200
-
1201
- No manual registration needed!
1202
-
1203
- ## URL Structure
1204
-
1205
- All thread endpoints follow this pattern:
1206
-
1207
- \`\`\`
1208
- /agents/api/threads/:id/{endpoint-name}
1209
- \`\`\`
1210
-
1211
- Examples:
1212
- - \`GET /agents/api/threads/abc-123/summary\`
1213
- - \`POST /agents/api/threads/abc-123/export\`
1214
- - \`PUT /agents/api/threads/abc-123/metadata\`
1215
-
1216
- The \`:id\` is automatically used to fetch the correct Durable Object.
1217
-
1218
- ## Built-In Endpoints
1219
-
1220
- Standard Agents includes built-in thread endpoints (these are in the framework, not this directory):
1221
-
1222
- - \`GET /agents/api/threads/:id/messages\` - Get message history
1223
- - \`POST /agents/api/threads/:id/messages\` - Send a message
1224
- - \`DELETE /agents/api/threads/:id\` - Delete thread
1225
- - \`GET /agents/api/threads/:id/logs\` - Get execution logs
1226
-
1227
- Your custom endpoints extend these built-in routes.
1228
-
1229
- ## Best Practices
1230
-
1231
- 1. **Descriptive Names**: Use clear endpoint names (e.g., \`summary\`, \`export\`)
1232
- 2. **Proper HTTP Methods**: Use GET for reads, POST for creates, etc.
1233
- 3. **Error Handling**: Always wrap in try/catch
1234
- 4. **Type Safety**: Use TypeScript interfaces for request/response
1235
- 5. **Performance**: Be mindful of SQLite query performance
1236
- 6. **Authentication**: Add auth checks if endpoints are sensitive
1237
- 7. **CORS**: Add CORS headers if accessed from browser
1238
-
1239
- ## Testing
1240
-
1241
- Test endpoints with curl or any HTTP client:
1242
-
1243
- \`\`\`bash
1244
- # GET summary
1245
- curl http://localhost:8787/agents/api/threads/abc-123/summary
1246
-
1247
- # POST export
1248
- curl -X POST http://localhost:8787/agents/api/threads/abc-123/export \\
1249
- -H "Content-Type: application/json" \\
1250
- -d '{"format": "json"}'
1251
- \`\`\`
1252
-
1253
- ## Limitations
1254
-
1255
- - No nested directories (endpoints must be directly in this folder)
1256
- - Thread ID must be in URL path (handled automatically)
1257
- - No support for multiple path parameters beyond thread ID
1258
- - Response must be a Web API \`Response\` object
1259
-
1260
- ## Related
1261
-
1262
- - **Tools**: \`agents/tools/CLAUDE.md\` - Custom tools
1263
- - **Hooks**: \`agents/hooks/CLAUDE.md\` - Lifecycle hooks
1264
- - **Built-in APIs**: \`packages/builder/src/api/\` - Framework endpoints
1265
- - **Documentation**: Project root \`CLAUDE.md\` for architecture
1266
- `;
1267
- async function scaffold() {
1268
- const cwd = process.cwd();
1269
- logger.info("Scaffolding Standard Agents directories...");
465
+ }
466
+ function createDurableObjects(cwd) {
467
+ const agentsDir = path.join(cwd, "agents");
468
+ if (!fs.existsSync(agentsDir)) {
469
+ fs.mkdirSync(agentsDir, { recursive: true });
470
+ }
471
+ const threadPath = path.join(agentsDir, "Thread.ts");
472
+ if (!fs.existsSync(threadPath)) {
473
+ fs.writeFileSync(threadPath, THREAD_TS, "utf-8");
474
+ logger.success("Created agents/Thread.ts");
475
+ } else {
476
+ logger.info("agents/Thread.ts already exists");
477
+ }
478
+ const agentBuilderPath = path.join(agentsDir, "AgentBuilder.ts");
479
+ if (!fs.existsSync(agentBuilderPath)) {
480
+ fs.writeFileSync(agentBuilderPath, AGENT_BUILDER_TS, "utf-8");
481
+ logger.success("Created agents/AgentBuilder.ts");
482
+ } else {
483
+ logger.info("agents/AgentBuilder.ts already exists");
484
+ }
485
+ }
486
+ function createDirectoriesAndDocs(cwd) {
1270
487
  const directories = [
1271
- {
1272
- path: path3.join(cwd, "agents", "tools"),
1273
- claudeMd: TOOLS_CLAUDE_MD,
1274
- name: "tools"
1275
- },
1276
- {
1277
- path: path3.join(cwd, "agents", "hooks"),
1278
- claudeMd: HOOKS_CLAUDE_MD,
1279
- name: "hooks"
1280
- },
1281
- {
1282
- path: path3.join(cwd, "agents", "api"),
1283
- claudeMd: API_CLAUDE_MD,
1284
- name: "api"
1285
- }
488
+ { path: "agents/agents", doc: AGENTS_CLAUDE_MD },
489
+ { path: "agents/prompts", doc: PROMPTS_CLAUDE_MD },
490
+ { path: "agents/models", doc: MODELS_CLAUDE_MD },
491
+ { path: "agents/tools", doc: TOOLS_CLAUDE_MD },
492
+ { path: "agents/hooks", doc: HOOKS_CLAUDE_MD },
493
+ { path: "agents/api", doc: API_CLAUDE_MD }
1286
494
  ];
1287
- let created = 0;
1288
- let skipped = 0;
1289
495
  for (const dir of directories) {
1290
- if (!fs.existsSync(dir.path)) {
1291
- fs.mkdirSync(dir.path, { recursive: true });
1292
- logger.success(`Created directory: agents/${dir.name}`);
1293
- created++;
1294
- } else {
1295
- logger.info(`Directory already exists: agents/${dir.name}`);
496
+ const dirPath = path.join(cwd, dir.path);
497
+ const docPath = path.join(dirPath, "CLAUDE.md");
498
+ if (!fs.existsSync(dirPath)) {
499
+ fs.mkdirSync(dirPath, { recursive: true });
500
+ logger.success(`Created ${dir.path}`);
501
+ }
502
+ if (!fs.existsSync(docPath)) {
503
+ fs.writeFileSync(docPath, dir.doc, "utf-8");
504
+ logger.success(`Created ${dir.path}/CLAUDE.md`);
505
+ }
506
+ }
507
+ }
508
+ function updateTsConfig(cwd) {
509
+ var _a;
510
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
511
+ if (!fs.existsSync(tsconfigPath)) {
512
+ logger.info("No tsconfig.json found, skipping TypeScript configuration");
513
+ return;
514
+ }
515
+ try {
516
+ const text = fs.readFileSync(tsconfigPath, "utf-8");
517
+ const config = parse(text);
518
+ let result = text;
519
+ const types = ((_a = config.compilerOptions) == null ? void 0 : _a.types) || [];
520
+ const newTypes = [
521
+ "./worker-configuration.d.ts",
522
+ "./.agents/types.d.ts",
523
+ "./.agents/virtual-module.d.ts"
524
+ ].filter((t) => !types.includes(t));
525
+ if (newTypes.length > 0) {
526
+ const allTypes = [...types, ...newTypes];
527
+ const edits = modify(result, ["compilerOptions", "types"], allTypes, {});
528
+ result = applyEdits(result, edits);
1296
529
  }
1297
- const claudeMdPath = path3.join(dir.path, "CLAUDE.md");
1298
- if (!fs.existsSync(claudeMdPath)) {
1299
- fs.writeFileSync(claudeMdPath, dir.claudeMd, "utf-8");
1300
- logger.success(`Created documentation: agents/${dir.name}/CLAUDE.md`);
1301
- created++;
530
+ const include = config.include || [];
531
+ const newIncludes = ["worker", "agents"].filter((i) => !include.includes(i));
532
+ if (newIncludes.length > 0) {
533
+ const allIncludes = [...include, ...newIncludes];
534
+ const edits = modify(result, ["include"], allIncludes, {});
535
+ result = applyEdits(result, edits);
536
+ }
537
+ if (newTypes.length > 0 || newIncludes.length > 0) {
538
+ fs.writeFileSync(tsconfigPath, result, "utf-8");
539
+ logger.success("Updated tsconfig.json with Standard Agents types");
1302
540
  } else {
1303
- logger.info(`Documentation already exists: agents/${dir.name}/CLAUDE.md (not overwriting)`);
1304
- skipped++;
541
+ logger.info("tsconfig.json already configured");
542
+ }
543
+ } catch (error) {
544
+ logger.warning("Could not update tsconfig.json, you may need to add types manually");
545
+ }
546
+ }
547
+ function cleanupViteDefaults(cwd) {
548
+ const filesToRemove = [
549
+ "src/main.ts",
550
+ "src/style.css",
551
+ "src/counter.ts",
552
+ "src/typescript.svg",
553
+ "src/vite-env.d.ts",
554
+ "index.html",
555
+ "public/vite.svg"
556
+ ];
557
+ const dirsToRemove = ["src", "public"];
558
+ for (const file of filesToRemove) {
559
+ const filePath = path.join(cwd, file);
560
+ if (fs.existsSync(filePath)) {
561
+ try {
562
+ fs.unlinkSync(filePath);
563
+ } catch {
564
+ }
565
+ }
566
+ }
567
+ for (const dir of dirsToRemove) {
568
+ const dirPath = path.join(cwd, dir);
569
+ if (fs.existsSync(dirPath)) {
570
+ try {
571
+ const files = fs.readdirSync(dirPath);
572
+ if (files.length === 0) {
573
+ fs.rmdirSync(dirPath);
574
+ }
575
+ } catch {
576
+ }
577
+ }
578
+ }
579
+ }
580
+ async function scaffold(options = {}) {
581
+ const cwd = process.cwd();
582
+ const projectName = getProjectName(cwd);
583
+ const force = options.force || false;
584
+ logger.info("Scaffolding Standard Agents...");
585
+ logger.log("");
586
+ const viteConfigPath = findViteConfig(cwd);
587
+ if (viteConfigPath) {
588
+ await modifyViteConfig(viteConfigPath, force);
589
+ } else {
590
+ logger.warning("No vite.config found. Please create one with cloudflare and agentbuilder plugins.");
591
+ }
592
+ createOrUpdateWranglerConfig(cwd, projectName, force);
593
+ await createOrUpdateWorkerEntry(cwd, force);
594
+ createDurableObjects(cwd);
595
+ createDirectoriesAndDocs(cwd);
596
+ updateTsConfig(cwd);
597
+ if (force) {
598
+ cleanupViteDefaults(cwd);
599
+ }
600
+ logger.log("");
601
+ logger.success("Standard Agents scaffolding complete!");
602
+ logger.log("");
603
+ logger.log("Your project structure:");
604
+ logger.log("");
605
+ logger.log(" agents/");
606
+ logger.log(" \u251C\u2500\u2500 Thread.ts # Durable Object for threads");
607
+ logger.log(" \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for agent state");
608
+ logger.log(" \u251C\u2500\u2500 agents/ # Agent definitions");
609
+ logger.log(" \u251C\u2500\u2500 prompts/ # Prompt definitions");
610
+ logger.log(" \u251C\u2500\u2500 models/ # Model configurations");
611
+ logger.log(" \u251C\u2500\u2500 tools/ # Custom tools");
612
+ logger.log(" \u251C\u2500\u2500 hooks/ # Lifecycle hooks");
613
+ logger.log(" \u2514\u2500\u2500 api/ # Thread API endpoints");
614
+ logger.log(" worker/");
615
+ logger.log(" \u2514\u2500\u2500 index.ts # Cloudflare Worker entry point");
616
+ logger.log("");
617
+ }
618
+
619
+ // src/commands/init.ts
620
+ async function prompt(question) {
621
+ const rl = readline.createInterface({
622
+ input: process.stdin,
623
+ output: process.stdout
624
+ });
625
+ return new Promise((resolve2) => {
626
+ rl.question(question, (answer) => {
627
+ rl.close();
628
+ resolve2(answer.trim());
629
+ });
630
+ });
631
+ }
632
+ function runCommand(command, args, cwd) {
633
+ return new Promise((resolve2, reject) => {
634
+ const child = spawn(command, args, {
635
+ cwd,
636
+ stdio: "inherit",
637
+ shell: true
638
+ });
639
+ child.on("close", (code) => {
640
+ if (code === 0) {
641
+ resolve2();
642
+ } else {
643
+ reject(new Error(`Command failed with exit code ${code}`));
644
+ }
645
+ });
646
+ child.on("error", reject);
647
+ });
648
+ }
649
+ function detectPackageManager() {
650
+ const userAgent = process.env.npm_config_user_agent;
651
+ if (userAgent) {
652
+ if (userAgent.includes("pnpm")) return "pnpm";
653
+ if (userAgent.includes("yarn")) return "yarn";
654
+ if (userAgent.includes("bun")) return "bun";
655
+ }
656
+ if (fs.existsSync("pnpm-lock.yaml")) return "pnpm";
657
+ if (fs.existsSync("yarn.lock")) return "yarn";
658
+ if (fs.existsSync("bun.lockb")) return "bun";
659
+ if (fs.existsSync("package-lock.json")) return "npm";
660
+ return "npm";
661
+ }
662
+ async function init(projectNameArg, options = {}) {
663
+ const cwd = process.cwd();
664
+ const pm = detectPackageManager();
665
+ const template = options.template || "vanilla-ts";
666
+ logger.log("");
667
+ logger.info("Creating a new Standard Agents project...");
668
+ logger.log("");
669
+ let projectName = projectNameArg;
670
+ if (!projectName && !options.yes) {
671
+ projectName = await prompt("Project name: ");
672
+ }
673
+ if (!projectName) {
674
+ logger.error("Project name is required");
675
+ process.exit(1);
676
+ }
677
+ const projectPath = path.join(cwd, projectName);
678
+ if (fs.existsSync(projectPath)) {
679
+ logger.error(`Directory "${projectName}" already exists`);
680
+ process.exit(1);
681
+ }
682
+ logger.log("");
683
+ logger.info(`Step 1: Creating Vite project with ${template} template...`);
684
+ logger.log("");
685
+ try {
686
+ let createCmd;
687
+ let createArgs;
688
+ switch (pm) {
689
+ case "pnpm":
690
+ createCmd = "pnpm";
691
+ createArgs = ["create", "vite@latest", projectName, "--template", template, "--no-interactive"];
692
+ break;
693
+ case "yarn":
694
+ createCmd = "yarn";
695
+ createArgs = ["create", "vite@latest", projectName, "--template", template, "--no-interactive"];
696
+ break;
697
+ case "bun":
698
+ createCmd = "bun";
699
+ createArgs = ["create", "vite@latest", projectName, "--template", template, "--no-interactive"];
700
+ break;
701
+ default:
702
+ createCmd = "npm";
703
+ createArgs = ["create", "vite@latest", projectName, "--", "--template", template, "--no-interactive"];
1305
704
  }
705
+ await runCommand(createCmd, createArgs, cwd);
706
+ } catch (error) {
707
+ logger.error("Failed to create Vite project");
708
+ process.exit(1);
1306
709
  }
710
+ const viteConfigPath = path.join(projectPath, "vite.config.ts");
711
+ if (!fs.existsSync(viteConfigPath)) {
712
+ const viteConfigContent = `import { defineConfig } from 'vite'
713
+
714
+ export default defineConfig({
715
+ plugins: [],
716
+ })
717
+ `;
718
+ fs.writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
719
+ logger.success("Created vite.config.ts");
720
+ }
721
+ logger.log("");
722
+ logger.info("Step 2: Installing Standard Agents dependencies...");
1307
723
  logger.log("");
1308
- if (created > 0) {
1309
- logger.success(`Scaffolding complete! Created ${created} file(s).`);
724
+ try {
725
+ const installCmd = pm === "npm" ? "install" : "add";
726
+ const devFlag = pm === "npm" ? "--save-dev" : "-D";
727
+ await runCommand(pm, [
728
+ installCmd,
729
+ devFlag,
730
+ "@cloudflare/vite-plugin",
731
+ "@standardagents/builder",
732
+ "wrangler"
733
+ ], projectPath);
734
+ } catch (error) {
735
+ logger.error("Failed to install dependencies");
736
+ process.exit(1);
1310
737
  }
1311
- if (skipped > 0) {
1312
- logger.info(`Skipped ${skipped} existing file(s).`);
738
+ logger.log("");
739
+ logger.info("Step 3: Configuring Standard Agents...");
740
+ logger.log("");
741
+ process.chdir(projectPath);
742
+ await scaffold({ force: true });
743
+ logger.log("");
744
+ logger.info("Step 4: Generating Cloudflare types...");
745
+ logger.log("");
746
+ try {
747
+ await runCommand("npx", ["wrangler", "types"], projectPath);
748
+ } catch (error) {
749
+ logger.warning('Could not generate types automatically. Run "npx wrangler types" manually.');
1313
750
  }
1314
- logger.log("\nNext steps:");
1315
- logger.log("1. Read the CLAUDE.md files in each directory for detailed documentation");
1316
- logger.log("2. Create your first tool in agents/tools/");
1317
- logger.log("3. Add lifecycle hooks in agents/hooks/ (optional)");
1318
- logger.log("4. Create custom thread endpoints in agents/api/ (optional)");
751
+ logger.log("");
752
+ logger.success("Project created successfully!");
753
+ logger.log("");
754
+ logger.log("Next steps:");
755
+ logger.log("");
756
+ logger.log(` cd ${projectName}`);
757
+ logger.log(` ${pm === "npm" ? "npm run" : pm} dev`);
758
+ logger.log("");
759
+ logger.log("For more information, visit: https://standardagents.ai/docs");
1319
760
  }
1320
761
 
1321
762
  // src/index.ts
763
+ var __dirname = dirname(fileURLToPath(import.meta.url));
764
+ var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
1322
765
  var program = new Command();
1323
- program.name("agentbuilder").description("CLI tool for AgentBuilder initialization").version("0.0.0");
1324
- program.command("init").description("Initialize AgentBuilder configuration in wrangler.jsonc").option("--force", "Overwrite existing configuration").action(init);
1325
- program.command("scaffold").description("Create agentbuilder directories (tools, hooks, api) with documentation").action(scaffold);
766
+ program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version(pkg.version);
767
+ 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);
768
+ program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
1326
769
  program.parse();
1327
770
  //# sourceMappingURL=index.js.map
1328
771
  //# sourceMappingURL=index.js.map