@standardagents/cli 0.12.0-next.30cb17a → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import fs3, { readFileSync } from 'fs';
4
- import path3, { dirname, resolve } from 'path';
5
- import { fileURLToPath } from 'url';
3
+ import path3 from 'path';
4
+ import fs3 from 'fs';
6
5
  import crypto from 'crypto';
7
6
  import readline from 'readline';
8
7
  import { spawn } from 'child_process';
@@ -11,6 +10,7 @@ import { parse, modify, applyEdits } from 'jsonc-parser';
11
10
  import { loadFile, writeFile, generateCode } from 'magicast';
12
11
  import { addVitePlugin } from 'magicast/helpers';
13
12
  import net from 'net';
13
+ import { fileURLToPath } from 'url';
14
14
 
15
15
  var logger = {
16
16
  success: (message) => {
@@ -116,34 +116,48 @@ agents/
116
116
 
117
117
  Files are **auto-discovered** at runtime. No manual registration needed.
118
118
 
119
- ## FlowState
119
+ ## ThreadState
120
120
 
121
- \`FlowState\` is the central state object available in tools and hooks:
121
+ \`ThreadState\` is the state object available in tools and hooks:
122
122
 
123
123
  \`\`\`typescript
124
- interface FlowState {
125
- thread: {
126
- id: string; // Thread identifier
127
- instance: DurableThread; // Durable Object instance
128
- };
129
- agent: AgentConfig; // Current agent configuration
130
- prompt: PromptConfig; // Current prompt configuration
131
- model: ModelConfig; // Current model configuration
132
- messages: Message[]; // Conversation history
133
- env: Env; // Cloudflare Worker environment
134
- 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
135
137
  }
136
138
  \`\`\`
137
139
 
138
140
  Access in tools:
139
141
  \`\`\`typescript
140
- export default defineTool('...', schema, async (flow, args) => {
141
- const threadId = flow.thread.id;
142
- const messages = flow.messages;
142
+ export default defineTool('...', schema, async (state, args) => {
143
+ const threadId = state.threadId;
144
+ const { messages } = await state.getMessages();
143
145
  // ...
144
146
  });
145
147
  \`\`\`
146
148
 
149
+ Access in hooks:
150
+ \`\`\`typescript
151
+ export default defineHook({
152
+ hook: 'before_create_message',
153
+ id: 'my_hook',
154
+ execute: async (state, message) => {
155
+ console.log(\`Thread: \${state.threadId}, Agent: \${state.agentId}\`);
156
+ return message;
157
+ }
158
+ });
159
+ \`\`\`
160
+
147
161
  ## Quick Reference
148
162
 
149
163
  | Function | Purpose | Directory |
@@ -178,7 +192,7 @@ Full documentation: https://docs.standardagentbuilder.com
178
192
  - [Agents](https://docs.standardagentbuilder.com/core-concepts/agents)
179
193
  - [Tools](https://docs.standardagentbuilder.com/core-concepts/tools)
180
194
  - [Hooks](https://docs.standardagentbuilder.com/core-concepts/hooks)
181
- - [FlowState](https://docs.standardagentbuilder.com/core-concepts/flowstate)
195
+ - [ThreadState](https://docs.standardagentbuilder.com/core-concepts/threadstate)
182
196
  - [Thread API](https://docs.standardagentbuilder.com/core-concepts/api)
183
197
  `;
184
198
 
@@ -289,6 +303,7 @@ Prompts define system instructions, model selection, and available tools for LLM
289
303
  | \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
290
304
  | \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
291
305
  | \`requiredSchema\` | \`z.ZodObject\` | No | Input validation when called as tool |
306
+ | \`hooks\` | \`string[]\` | No | Hook IDs to run for this prompt (overrides agent hooks) |
292
307
  | \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
293
308
 
294
309
  ## Basic Example
@@ -387,6 +402,22 @@ reasoning: {
387
402
  },
388
403
  \`\`\`
389
404
 
405
+ ## Hooks
406
+
407
+ Attach lifecycle hooks to a prompt:
408
+
409
+ \`\`\`typescript
410
+ export default definePrompt({
411
+ name: 'customer_support',
412
+ toolDescription: 'Handle customer support',
413
+ model: 'default',
414
+ prompt: 'You are a support agent...',
415
+ hooks: ['limit_to_20', 'redact_pii'], // Hook IDs from agents/hooks/
416
+ });
417
+ \`\`\`
418
+
419
+ Prompt hooks take precedence over agent hooks. See \`agents/hooks/CLAUDE.md\` for details.
420
+
390
421
  ## Best Practices
391
422
 
392
423
  - **Write clear instructions** - Structure with headers and bullet points
@@ -416,6 +447,7 @@ Agents orchestrate conversations by defining prompts, stop conditions, and turn
416
447
  | \`maxSessionTurns\` | \`number\` | No | Max total turns across both sides |
417
448
  | \`exposeAsTool\` | \`boolean\` | No | Allow other prompts to hand off to this agent |
418
449
  | \`toolDescription\` | \`string\` | No | Description when used as tool (required if exposeAsTool) |
450
+ | \`hooks\` | \`string[]\` | No | Hook IDs to run for this agent (fallback if prompt has no hooks) |
419
451
  | \`tags\` | \`string[]\` | No | Tags for categorization and filtering |
420
452
 
421
453
  ## Side Configuration
@@ -498,6 +530,23 @@ Other prompts can then include the agent name in their \`tools\` array to enable
498
530
  4. **stopOnResponse** - Ends turn when LLM returns text
499
531
  5. **maxSessionTurns** - Ends conversation (hard limit: 250)
500
532
 
533
+ ## Hooks
534
+
535
+ Attach lifecycle hooks at the agent level:
536
+
537
+ \`\`\`typescript
538
+ export default defineAgent({
539
+ name: 'support_agent',
540
+ type: 'ai_human',
541
+ hooks: ['log_all_messages', 'sanitize_results'],
542
+ sideA: {
543
+ prompt: 'customer_support',
544
+ },
545
+ });
546
+ \`\`\`
547
+
548
+ Agent hooks run as a fallback when the active prompt has no hooks defined. Prompt hooks always take precedence. See \`agents/hooks/CLAUDE.md\` for details.
549
+
501
550
  ## Naming Convention
502
551
 
503
552
  Agent names **must** end with the \`_agent\` suffix (e.g., \`support_agent\`, \`research_agent\`).
@@ -525,12 +574,12 @@ Tools extend agent capabilities beyond text generation. They allow LLMs to fetch
525
574
  ## Function Signature
526
575
 
527
576
  \`\`\`typescript
528
- defineTool(
577
+ defineTool({
529
578
  description: string, // What the tool does (shown to LLM)
530
- args: z.ZodObject, // Input validation schema
531
- handler: (flow, args) => ToolResult, // Implementation
532
- returnSchema?: z.ZodObject // Optional output schema
533
- )
579
+ args?: z.ZodObject, // Input validation schema (optional)
580
+ execute: (state, args) => ToolResult, // Implementation
581
+ tenvs?: z.ZodObject, // Thread environment variables (optional)
582
+ })
534
583
  \`\`\`
535
584
 
536
585
  ## Basic Example
@@ -539,20 +588,20 @@ defineTool(
539
588
  import { defineTool } from '@standardagents/builder';
540
589
  import { z } from 'zod';
541
590
 
542
- export default defineTool(
543
- 'Search the knowledge base for articles matching a query',
544
- z.object({
591
+ export default defineTool({
592
+ description: 'Search the knowledge base for articles matching a query',
593
+ args: z.object({
545
594
  query: z.string().describe('Search query'),
546
595
  limit: z.number().optional().default(10).describe('Max results'),
547
596
  }),
548
- async (flow, args) => {
597
+ execute: async (state, args) => {
549
598
  const results = await searchKB(args.query, args.limit);
550
599
  return {
551
600
  status: 'success',
552
601
  result: JSON.stringify(results),
553
602
  };
554
- }
555
- );
603
+ },
604
+ });
556
605
  \`\`\`
557
606
 
558
607
  ## ToolResult Interface
@@ -562,44 +611,45 @@ export default defineTool(
562
611
  | \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
563
612
  | \`result\` | \`string\` | Tool output (required for success) |
564
613
  | \`error\` | \`string\` | Error message (for error status) |
614
+ | \`attachments\` | \`ToolAttachment[]\` | Files to attach (images, etc.) |
565
615
 
566
- ## FlowState Access
616
+ ## ThreadState Access
567
617
 
568
- The \`flow\` parameter provides access to:
618
+ The \`state\` parameter provides access to:
569
619
 
570
620
  \`\`\`typescript
571
- async (flow, args) => {
621
+ execute: async (state, args) => {
572
622
  // Thread information
573
- const threadId = flow.thread.id;
574
- const thread = flow.thread.instance;
623
+ const threadId = state.thread.id;
624
+ const thread = state.thread.instance;
575
625
 
576
626
  // Configuration
577
- const agentName = flow.agent.name;
578
- const modelName = flow.model.name;
627
+ const agentName = state.agent.name;
628
+ const modelName = state.model.name;
579
629
 
580
630
  // Message history
581
- const messages = flow.messages;
631
+ const messages = state.messages;
582
632
 
583
633
  // Environment bindings
584
- const env = flow.env;
634
+ const env = state.env;
585
635
 
586
636
  // Parent state (for sub-prompts)
587
- const root = flow.rootState;
637
+ const root = state.rootState;
588
638
  }
589
639
  \`\`\`
590
640
 
591
641
  ## Tool Without Arguments
592
642
 
593
643
  \`\`\`typescript
594
- export default defineTool(
595
- 'Get current server time',
596
- async (flow) => {
644
+ export default defineTool({
645
+ description: 'Get current server time',
646
+ execute: async (state) => {
597
647
  return {
598
648
  status: 'success',
599
649
  result: new Date().toISOString(),
600
650
  };
601
- }
602
- );
651
+ },
652
+ });
603
653
  \`\`\`
604
654
 
605
655
  ## Error Handling
@@ -607,10 +657,10 @@ export default defineTool(
607
657
  Return errors gracefully instead of throwing:
608
658
 
609
659
  \`\`\`typescript
610
- export default defineTool(
611
- 'Fetch user data',
612
- z.object({ userId: z.string() }),
613
- async (flow, args) => {
660
+ export default defineTool({
661
+ description: 'Fetch user data',
662
+ args: z.object({ userId: z.string() }),
663
+ execute: async (state, args) => {
614
664
  try {
615
665
  const user = await fetchUser(args.userId);
616
666
  return { status: 'success', result: JSON.stringify(user) };
@@ -620,8 +670,8 @@ export default defineTool(
620
670
  error: \`Failed to fetch user: \${error.message}\`,
621
671
  };
622
672
  }
623
- }
624
- );
673
+ },
674
+ });
625
675
  \`\`\`
626
676
 
627
677
  ## Queueing Additional Tools
@@ -631,21 +681,21 @@ Queue another tool to run after the current one:
631
681
  \`\`\`typescript
632
682
  import { queueTool } from '@standardagents/builder';
633
683
 
634
- export default defineTool(
635
- 'Create order and send confirmation',
636
- z.object({ items: z.array(z.string()) }),
637
- async (flow, args) => {
684
+ export default defineTool({
685
+ description: 'Create order and send confirmation',
686
+ args: z.object({ items: z.array(z.string()) }),
687
+ execute: async (state, args) => {
638
688
  const order = await createOrder(args.items);
639
689
 
640
690
  // Queue email tool to run next
641
- queueTool(flow, 'send_confirmation_email', {
691
+ queueTool(state, 'send_confirmation_email', {
642
692
  orderId: order.id,
643
- email: flow.thread.metadata.userEmail,
693
+ email: state.thread.metadata.userEmail,
644
694
  });
645
695
 
646
696
  return { status: 'success', result: JSON.stringify(order) };
647
- }
648
- );
697
+ },
698
+ });
649
699
  \`\`\`
650
700
 
651
701
  ## Injecting Messages
@@ -655,17 +705,61 @@ Add messages without triggering re-execution:
655
705
  \`\`\`typescript
656
706
  import { injectMessage } from '@standardagents/builder';
657
707
 
658
- export default defineTool(
659
- 'Log audit event',
660
- z.object({ event: z.string() }),
661
- async (flow, args) => {
662
- await injectMessage(flow, {
708
+ export default defineTool({
709
+ description: 'Log audit event',
710
+ args: z.object({ event: z.string() }),
711
+ execute: async (state, args) => {
712
+ await injectMessage(state, {
663
713
  role: 'system',
664
714
  content: \`[AUDIT] \${args.event}\`,
665
715
  });
666
716
  return { status: 'success', result: 'Logged' };
667
- }
668
- );
717
+ },
718
+ });
719
+ \`\`\`
720
+
721
+ ## Thread Environment Variables (tenvs)
722
+
723
+ Tools can declare required thread environment variables:
724
+
725
+ \`\`\`typescript
726
+ export default defineTool({
727
+ description: 'Search uploaded documents',
728
+ args: z.object({ query: z.string() }),
729
+ execute: async (state, args) => {
730
+ const vectorStoreId = state.tenvs.vectorStoreId;
731
+ const results = await searchVectorStore(vectorStoreId, args.query);
732
+ return { status: 'success', result: JSON.stringify(results) };
733
+ },
734
+ tenvs: z.object({
735
+ vectorStoreId: z.string().describe('OpenAI Vector Store ID'),
736
+ }),
737
+ });
738
+ \`\`\`
739
+
740
+ ## Returning Attachments
741
+
742
+ Tools can return file attachments (e.g., generated images):
743
+
744
+ \`\`\`typescript
745
+ export default defineTool({
746
+ description: 'Generate a chart from data',
747
+ args: z.object({ data: z.array(z.number()) }),
748
+ execute: async (state, args) => {
749
+ const chartImage = await generateChart(args.data);
750
+ return {
751
+ status: 'success',
752
+ result: 'Chart generated successfully',
753
+ attachments: [{
754
+ name: 'chart.png',
755
+ mimeType: 'image/png',
756
+ data: chartImage.base64,
757
+ width: 800,
758
+ height: 600,
759
+ }],
760
+ };
761
+ },
762
+ });
669
763
  \`\`\`
670
764
 
671
765
  ## Best Practices
@@ -675,7 +769,7 @@ export default defineTool(
675
769
  - **Handle errors gracefully** - return error status, don't throw
676
770
  - **Use snake_case** for file names (\`search_knowledge_base.ts\`)
677
771
  - **Keep tools focused** - one task per tool
678
- - **Use \`flow.rootState\`** when queueing from sub-prompts
772
+ - **Use \`state.rootState\`** when queueing from sub-prompts
679
773
 
680
774
  ## Supported Zod Types
681
775
 
@@ -701,31 +795,69 @@ Hooks intercept and modify agent execution at specific lifecycle points.
701
795
 
702
796
  | Hook | Signature | Purpose |
703
797
  |------|-----------|---------|
704
- | \`filter_messages\` | \`(state, rows) => Message[]\` | Filter raw message rows before LLM context |
705
- | \`prefilter_llm_history\` | \`(state, messages) => Message[]\` | Modify messages after filtering, before LLM |
706
- | \`before_create_message\` | \`(state, message) => Message\` | Modify message before database insert |
707
- | \`after_create_message\` | \`(state, message) => void\` | Run logic after message created |
708
- | \`before_update_message\` | \`(state, id, updates) => Updates\` | Modify updates before message update |
709
- | \`after_update_message\` | \`(state, message) => void\` | Run logic after message updated |
710
- | \`before_store_tool_result\` | \`(state, toolCall, result) => Result\` | Modify tool result before storage |
711
- | \`after_tool_call_success\` | \`(state, toolCall, result) => Result\` | Process successful tool execution |
712
- | \`after_tool_call_failure\` | \`(state, toolCall, result) => Result\` | Process failed tool execution |
798
+ | \`filter_messages\` | \`(state: ThreadState, messages: Message[]) => Message[]\` | Filter raw messages before LLM context |
799
+ | \`prefilter_llm_history\` | \`(state: ThreadState, messages: LLMMessage[]) => LLMMessage[]\` | Modify chat completion messages before LLM |
800
+ | \`before_create_message\` | \`(state: ThreadState, message: Message) => Message\` | Modify message before database insert |
801
+ | \`after_create_message\` | \`(state: ThreadState, message: Message) => void\` | Run logic after message created |
802
+ | \`before_update_message\` | \`(state: ThreadState, id: string, updates: Record) => Record\` | Modify updates before message update |
803
+ | \`after_update_message\` | \`(state: ThreadState, message: Message) => void\` | Run logic after message updated |
804
+ | \`before_store_tool_result\` | \`(state: ThreadState, toolCall: HookToolCall, result: HookToolResult) => HookToolResult\` | Modify tool result before storage |
805
+ | \`after_tool_call_success\` | \`(state: ThreadState, toolCall: HookToolCall, result: HookToolResult) => HookToolResult \\| null\` | Process successful tool execution |
806
+ | \`after_tool_call_failure\` | \`(state: ThreadState, toolCall: HookToolCall, result: HookToolResult) => HookToolResult \\| null\` | Process failed tool execution |
807
+
808
+ All hooks receive \`ThreadState\` as their first parameter, providing access to thread identity, message history, and resource loading.
809
+
810
+ ## Creating a Hook
811
+
812
+ Each hook file exports a default \`defineHook\` call with three properties:
813
+
814
+ \`\`\`typescript
815
+ import { defineHook } from '@standardagents/builder';
816
+
817
+ export default defineHook({
818
+ hook: 'filter_messages', // Hook type
819
+ id: 'limit_recent_messages', // Unique ID (snake_case)
820
+ execute: async (state, messages) => {
821
+ return messages.slice(-20);
822
+ }
823
+ });
824
+ \`\`\`
825
+
826
+ ## Hook Scoping
827
+
828
+ Hooks are scoped to **prompts** or **agents** via their ID:
829
+
830
+ \`\`\`typescript
831
+ // In a prompt definition
832
+ definePrompt({
833
+ name: 'customer_support',
834
+ hooks: ['limit_recent_messages', 'redact_pii'],
835
+ // ...
836
+ });
837
+
838
+ // In an agent definition
839
+ defineAgent({
840
+ name: 'support_agent',
841
+ hooks: ['log_all_messages'],
842
+ // ...
843
+ });
844
+ \`\`\`
845
+
846
+ - **Prompt hooks** take precedence over agent hooks
847
+ - If a prompt defines hooks, only those run (agent hooks are skipped)
848
+ - If no prompt hooks are defined, agent hooks run as fallback
849
+ - Hooks are filtered by type at runtime
713
850
 
714
851
  ## File Naming
715
852
 
716
- Hook files **must** be named exactly as the hook type:
853
+ Hook files can use **any name** \u2014 the hook type and ID are defined inside the file:
717
854
 
718
855
  \`\`\`
719
856
  agents/hooks/
720
- \u251C\u2500\u2500 filter_messages.ts
721
- \u251C\u2500\u2500 prefilter_llm_history.ts
722
- \u251C\u2500\u2500 before_create_message.ts
723
- \u251C\u2500\u2500 after_create_message.ts
724
- \u251C\u2500\u2500 before_update_message.ts
725
- \u251C\u2500\u2500 after_update_message.ts
726
- \u251C\u2500\u2500 before_store_tool_result.ts
727
- \u251C\u2500\u2500 after_tool_call_success.ts
728
- \u2514\u2500\u2500 after_tool_call_failure.ts
857
+ \u251C\u2500\u2500 my_filter.ts # Could contain any hook type
858
+ \u251C\u2500\u2500 redact_sensitive_data.ts
859
+ \u251C\u2500\u2500 log_tool_usage.ts
860
+ \u2514\u2500\u2500 add_metadata.ts
729
861
  \`\`\`
730
862
 
731
863
  ## Examples
@@ -735,23 +867,31 @@ agents/hooks/
735
867
  \`\`\`typescript
736
868
  import { defineHook } from '@standardagents/builder';
737
869
 
738
- export default defineHook('filter_messages', async (state, rows) => {
739
- // Only include last 20 messages
740
- return rows.slice(-20);
870
+ export default defineHook({
871
+ hook: 'filter_messages',
872
+ id: 'limit_to_20',
873
+ execute: async (state, messages) => {
874
+ return messages.slice(-20);
875
+ }
741
876
  });
742
877
  \`\`\`
743
878
 
744
- ### Prefilter LLM History (Modify Content)
879
+ ### Prefilter LLM History (Redact Content)
745
880
 
746
881
  \`\`\`typescript
747
882
  import { defineHook } from '@standardagents/builder';
748
883
 
749
- export default defineHook('prefilter_llm_history', async (state, messages) => {
750
- // Remove sensitive data from message content
751
- return messages.map(msg => ({
752
- ...msg,
753
- content: msg.content?.replace(/\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b/g, '[REDACTED]'),
754
- }));
884
+ export default defineHook({
885
+ hook: 'prefilter_llm_history',
886
+ id: 'redact_pii',
887
+ execute: async (state, messages) => {
888
+ return messages.map(msg => ({
889
+ ...msg,
890
+ content: typeof msg.content === 'string'
891
+ ? msg.content.replace(/\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b/g, '[REDACTED]')
892
+ : msg.content,
893
+ }));
894
+ }
755
895
  });
756
896
  \`\`\`
757
897
 
@@ -760,15 +900,34 @@ export default defineHook('prefilter_llm_history', async (state, messages) => {
760
900
  \`\`\`typescript
761
901
  import { defineHook } from '@standardagents/builder';
762
902
 
763
- export default defineHook('before_create_message', async (state, message) => {
764
- return {
765
- ...message,
766
- metadata: {
767
- ...message.metadata,
768
- createdAt: Date.now(),
769
- agentVersion: '1.0.0',
770
- },
771
- };
903
+ export default defineHook({
904
+ hook: 'before_create_message',
905
+ id: 'add_version_metadata',
906
+ execute: async (state, message) => {
907
+ if (message.role === 'user' && message.content) {
908
+ return { ...message, content: \`[v1.0] \${message.content}\` };
909
+ }
910
+ return message;
911
+ }
912
+ });
913
+ \`\`\`
914
+
915
+ ### Before Store Tool Result (Sanitize)
916
+
917
+ \`\`\`typescript
918
+ import { defineHook } from '@standardagents/builder';
919
+
920
+ export default defineHook({
921
+ hook: 'before_store_tool_result',
922
+ id: 'sanitize_results',
923
+ execute: async (state, toolCall, toolResult) => {
924
+ // toolCall: { id, type: 'function', function: { name, arguments } }
925
+ // toolResult: { status, result?, error?, stack?, attachments? }
926
+ if (toolResult.result) {
927
+ return { ...toolResult, result: toolResult.result.replace(/secret/gi, '[REDACTED]') };
928
+ }
929
+ return toolResult;
930
+ }
772
931
  });
773
932
  \`\`\`
774
933
 
@@ -777,9 +936,13 @@ export default defineHook('before_create_message', async (state, message) => {
777
936
  \`\`\`typescript
778
937
  import { defineHook } from '@standardagents/builder';
779
938
 
780
- export default defineHook('after_tool_call_success', async (state, toolCall, result) => {
781
- console.log(\`Tool \${toolCall.function.name} succeeded:\`, result);
782
- return result; // Return result unchanged, or modify it
939
+ export default defineHook({
940
+ hook: 'after_tool_call_success',
941
+ id: 'log_tool_success',
942
+ execute: async (state, toolCall, result) => {
943
+ console.log(\`Tool \${toolCall.function.name} succeeded\`);
944
+ return null; // Return null to use original result
945
+ }
783
946
  });
784
947
  \`\`\`
785
948
 
@@ -788,15 +951,18 @@ export default defineHook('after_tool_call_success', async (state, toolCall, res
788
951
  \`\`\`typescript
789
952
  import { defineHook } from '@standardagents/builder';
790
953
 
791
- export default defineHook('after_tool_call_failure', async (state, toolCall, result) => {
792
- // Override error with fallback result
793
- if (toolCall.function.name === 'fetch_weather') {
794
- return {
795
- status: 'success',
796
- result: JSON.stringify({ temp: 'unavailable', fallback: true }),
797
- };
954
+ export default defineHook({
955
+ hook: 'after_tool_call_failure',
956
+ id: 'weather_fallback',
957
+ execute: async (state, toolCall, result) => {
958
+ if (toolCall.function.name === 'fetch_weather') {
959
+ return {
960
+ status: 'success',
961
+ result: JSON.stringify({ temp: 'unavailable', fallback: true }),
962
+ };
963
+ }
964
+ return null; // Use original error for other tools
798
965
  }
799
- return result; // Return original error
800
966
  });
801
967
  \`\`\`
802
968
 
@@ -808,8 +974,8 @@ export default defineHook('after_tool_call_failure', async (state, toolCall, res
808
974
  - \`before_create_message\`
809
975
  - \`before_update_message\`
810
976
  - \`before_store_tool_result\`
811
- - \`after_tool_call_success\`
812
- - \`after_tool_call_failure\`
977
+ - \`after_tool_call_success\` (return modified result or null)
978
+ - \`after_tool_call_failure\` (return modified result or null)
813
979
 
814
980
  **Event hooks** (side effects only):
815
981
  - \`after_create_message\`
@@ -818,8 +984,9 @@ export default defineHook('after_tool_call_failure', async (state, toolCall, res
818
984
  ## Best Practices
819
985
 
820
986
  - **Keep hooks fast** - target <100ms execution
821
- - **Wrap in try-catch** - hooks continue on error
822
- - **Validate input data** before processing
987
+ - **Wrap in try-catch** - hooks continue on error (original data preserved)
988
+ - **Use unique IDs** - hook IDs must be snake_case and unique across the project
989
+ - **Scope appropriately** - assign hooks to prompts or agents that need them
823
990
  - **Document purpose** with clear comments
824
991
  - **Use for cross-cutting concerns** - logging, redaction, enrichment
825
992
 
@@ -831,7 +998,7 @@ Full reference: https://docs.standardagentbuilder.com/api-reference/define/hook
831
998
  // src/templates/api.ts
832
999
  var API_CLAUDE_MD = `# Thread-Specific API Endpoints
833
1000
 
834
- Define custom API endpoints that operate on specific threads with access to the thread's Durable Object instance and SQLite storage.
1001
+ Define custom API endpoints that operate on specific threads with access to the thread's ThreadState.
835
1002
 
836
1003
  ## File-Based Routing
837
1004
 
@@ -848,15 +1015,14 @@ Define custom API endpoints that operate on specific threads with access to the
848
1015
 
849
1016
  \`\`\`typescript
850
1017
  // agents/api/status.get.ts -> GET /api/threads/:id/status
851
- import { defineThreadEndpoint } from '@standardagents/builder';
1018
+ import { defineThreadEndpoint } from '@standardagents/spec';
852
1019
 
853
- export default defineThreadEndpoint(async (thread, request, env) => {
854
- const messages = await thread.getMessages();
855
- return new Response(JSON.stringify({
856
- messageCount: messages.length,
1020
+ export default defineThreadEndpoint(async (req, state) => {
1021
+ const { messages, total } = await state.getMessages();
1022
+ return Response.json({
1023
+ threadId: state.threadId,
1024
+ messageCount: total,
857
1025
  status: 'active',
858
- }), {
859
- headers: { 'Content-Type': 'application/json' },
860
1026
  });
861
1027
  });
862
1028
  \`\`\`
@@ -865,30 +1031,40 @@ export default defineThreadEndpoint(async (thread, request, env) => {
865
1031
 
866
1032
  | Parameter | Type | Description |
867
1033
  |-----------|------|-------------|
868
- | \`thread\` | \`DurableThread\` | The thread's Durable Object instance |
869
- | \`request\` | \`Request\` | The incoming HTTP request |
870
- | \`env\` | \`Env\` | Cloudflare Worker environment bindings |
1034
+ | \`req\` | \`Request\` | The incoming HTTP request |
1035
+ | \`state\` | \`ThreadState\` | Thread state with messages, identity, file system, etc. |
1036
+
1037
+ Note: \`state.execution\` is always \`null\` in endpoints since the thread is at rest.
871
1038
 
872
- ## Thread Methods
1039
+ ## ThreadState Methods
873
1040
 
874
- Access thread data through the instance:
1041
+ Access thread data through the state object:
875
1042
 
876
1043
  \`\`\`typescript
877
- export default defineThreadEndpoint(async (thread, request, env) => {
1044
+ import { defineThreadEndpoint } from '@standardagents/spec';
1045
+
1046
+ export default defineThreadEndpoint(async (req, state) => {
1047
+ // Thread identity
1048
+ console.log('Thread ID:', state.threadId);
1049
+ console.log('Agent ID:', state.agentId);
1050
+ console.log('User ID:', state.userId);
1051
+
878
1052
  // Message operations
879
- const messages = await thread.getMessages({ limit: 100, offset: 0 });
880
- const count = await thread.getMessageCount();
1053
+ const { messages, total, hasMore } = await state.getMessages({
1054
+ limit: 100,
1055
+ offset: 0,
1056
+ order: 'desc',
1057
+ });
881
1058
 
882
- // Thread metadata
883
- const metadata = thread.getMetadata();
1059
+ // File system operations
1060
+ const files = await state.findFiles('**/*.json');
1061
+ const data = await state.readFile('/data/config.json');
884
1062
 
885
- // Execution control
886
- await thread.stop();
1063
+ // Resource loading
1064
+ const agent = await state.loadAgent('my-agent');
1065
+ const prompt = await state.loadPrompt('my-prompt');
887
1066
 
888
- // Custom SQL queries
889
- const result = thread.ctx.storage.sql.exec(\`
890
- SELECT * FROM messages WHERE role = 'user'
891
- \`);
1067
+ return Response.json({ messages, total });
892
1068
  });
893
1069
  \`\`\`
894
1070
 
@@ -896,13 +1072,13 @@ export default defineThreadEndpoint(async (thread, request, env) => {
896
1072
 
897
1073
  \`\`\`typescript
898
1074
  // agents/api/export.post.ts -> POST /api/threads/:id/export
899
- import { defineThreadEndpoint } from '@standardagents/builder';
1075
+ import { defineThreadEndpoint } from '@standardagents/spec';
900
1076
 
901
- export default defineThreadEndpoint(async (thread, request, env) => {
902
- const body = await request.json();
1077
+ export default defineThreadEndpoint(async (req, state) => {
1078
+ const body = await req.json();
903
1079
  const { format = 'json' } = body;
904
1080
 
905
- const messages = await thread.getMessages();
1081
+ const { messages } = await state.getMessages();
906
1082
 
907
1083
  if (format === 'csv') {
908
1084
  const csv = messages.map(m => \`\${m.role},\${m.content}\`).join('\\n');
@@ -911,9 +1087,7 @@ export default defineThreadEndpoint(async (thread, request, env) => {
911
1087
  });
912
1088
  }
913
1089
 
914
- return new Response(JSON.stringify(messages), {
915
- headers: { 'Content-Type': 'application/json' },
916
- });
1090
+ return Response.json(messages);
917
1091
  });
918
1092
  \`\`\`
919
1093
 
@@ -933,35 +1107,38 @@ agents/api/
933
1107
  ## Error Handling
934
1108
 
935
1109
  \`\`\`typescript
936
- export default defineThreadEndpoint(async (thread, request, env) => {
1110
+ import { defineThreadEndpoint } from '@standardagents/spec';
1111
+
1112
+ export default defineThreadEndpoint(async (req, state) => {
937
1113
  try {
938
1114
  const data = await riskyOperation();
939
- return new Response(JSON.stringify(data), {
940
- headers: { 'Content-Type': 'application/json' },
941
- });
1115
+ return Response.json(data);
942
1116
  } catch (error) {
943
- return new Response(JSON.stringify({
944
- error: error.message,
945
- }), {
946
- status: 500,
947
- headers: { 'Content-Type': 'application/json' },
948
- });
1117
+ return Response.json({ error: error.message }, { status: 500 });
949
1118
  }
950
1119
  });
951
1120
  \`\`\`
952
1121
 
953
- ## Using Environment Variables
1122
+ ## File System Operations
954
1123
 
955
1124
  \`\`\`typescript
956
- export default defineThreadEndpoint(async (thread, request, env) => {
957
- // Access secrets and bindings
958
- const apiKey = env.EXTERNAL_API_KEY;
959
- const kv = env.MY_KV_NAMESPACE;
1125
+ import { defineThreadEndpoint } from '@standardagents/spec';
1126
+
1127
+ export default defineThreadEndpoint(async (req, state) => {
1128
+ // Read files
1129
+ const content = await state.readFile('/data/file.txt');
1130
+
1131
+ // Write files
1132
+ await state.writeFile('/output/result.json', JSON.stringify(data), 'application/json');
960
1133
 
961
- const data = await fetchExternalAPI(apiKey);
962
- await kv.put(\`thread:\${thread.id}\`, JSON.stringify(data));
1134
+ // List directory
1135
+ const { entries } = await state.readdirFile('/data');
963
1136
 
964
- return new Response('OK');
1137
+ // Search files
1138
+ const matches = await state.grepFiles('pattern');
1139
+ const paths = await state.findFiles('**/*.ts');
1140
+
1141
+ return Response.json({ success: true });
965
1142
  });
966
1143
  \`\`\`
967
1144
 
@@ -972,6 +1149,7 @@ export default defineThreadEndpoint(async (thread, request, env) => {
972
1149
  - **Validate input data** - check request body before processing
973
1150
  - **Use descriptive file names** - \`export.post.ts\` not \`ep1.ts\`
974
1151
  - **Return proper status codes** - 200, 400, 404, 500
1152
+ - **Import from spec** - use \`@standardagents/spec\` for \`defineThreadEndpoint\`
975
1153
 
976
1154
  ## Documentation
977
1155
 
@@ -979,17 +1157,19 @@ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
979
1157
  `;
980
1158
 
981
1159
  // src/commands/scaffold.ts
982
- var WRANGLER_TEMPLATE = (name) => `{
1160
+ var WRANGLER_TEMPLATE = (name) => {
1161
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1162
+ return `{
983
1163
  "$schema": "node_modules/wrangler/config-schema.json",
984
1164
  "name": "${name}",
985
1165
  "main": "worker/index.ts",
986
- "compatibility_date": "2025-01-01",
1166
+ "compatibility_date": "${today}",
987
1167
  "compatibility_flags": ["nodejs_compat"],
988
1168
  "observability": {
989
1169
  "enabled": true
990
1170
  },
991
1171
  "assets": {
992
- "directory": "dist",
1172
+ "directory": "dist/client",
993
1173
  "not_found_handling": "single-page-application",
994
1174
  "binding": "ASSETS",
995
1175
  "run_worker_first": ["/**"]
@@ -1018,6 +1198,7 @@ var WRANGLER_TEMPLATE = (name) => `{
1018
1198
  ]
1019
1199
  }
1020
1200
  `;
1201
+ };
1021
1202
  var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
1022
1203
 
1023
1204
  export default class Thread extends DurableThread {}
@@ -1043,9 +1224,9 @@ function getProjectName(cwd) {
1043
1224
  const packageJsonPath = path3.join(cwd, "package.json");
1044
1225
  if (fs3.existsSync(packageJsonPath)) {
1045
1226
  try {
1046
- const pkg2 = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1047
- if (pkg2.name) {
1048
- return pkg2.name.replace(/^@[^/]+\//, "");
1227
+ const pkg = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
1228
+ if (pkg.name) {
1229
+ return pkg.name.replace(/^@[^/]+\//, "");
1049
1230
  }
1050
1231
  } catch {
1051
1232
  }
@@ -1082,14 +1263,6 @@ async function modifyViteConfig(configPath, force) {
1082
1263
  logger.info("Vite config already includes Standard Agents plugins");
1083
1264
  return true;
1084
1265
  }
1085
- if (!hasCloudflare || force) {
1086
- addVitePlugin(mod, {
1087
- from: "@cloudflare/vite-plugin",
1088
- imported: "cloudflare",
1089
- constructor: "cloudflare"
1090
- });
1091
- logger.success("Added cloudflare plugin to vite.config");
1092
- }
1093
1266
  if (!hasAgentBuilder || force) {
1094
1267
  addVitePlugin(mod, {
1095
1268
  from: "@standardagents/builder",
@@ -1099,6 +1272,14 @@ async function modifyViteConfig(configPath, force) {
1099
1272
  });
1100
1273
  logger.success("Added agentbuilder plugin to vite.config");
1101
1274
  }
1275
+ if (!hasCloudflare || force) {
1276
+ addVitePlugin(mod, {
1277
+ from: "@cloudflare/vite-plugin",
1278
+ imported: "cloudflare",
1279
+ constructor: "cloudflare"
1280
+ });
1281
+ logger.success("Added cloudflare plugin to vite.config");
1282
+ }
1102
1283
  await writeFile(mod, configPath);
1103
1284
  return true;
1104
1285
  } catch (error) {
@@ -1106,10 +1287,10 @@ async function modifyViteConfig(configPath, force) {
1106
1287
  logger.log("");
1107
1288
  logger.log("Please manually add the following to your vite.config.ts:");
1108
1289
  logger.log("");
1109
- logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1110
1290
  logger.log(` import { agentbuilder } from '@standardagents/builder'`);
1291
+ logger.log(` import { cloudflare } from '@cloudflare/vite-plugin'`);
1111
1292
  logger.log("");
1112
- logger.log(` plugins: [cloudflare(), agentbuilder({ mountPoint: '/' })]`);
1293
+ logger.log(` plugins: [agentbuilder({ mountPoint: '/' }), cloudflare()]`);
1113
1294
  logger.log("");
1114
1295
  return false;
1115
1296
  }
@@ -1158,7 +1339,7 @@ function createOrUpdateWranglerConfig(cwd, projectName, force) {
1158
1339
  }
1159
1340
  if (!config.assets) {
1160
1341
  const edits = modify(result, ["assets"], {
1161
- directory: "dist",
1342
+ directory: "dist/client",
1162
1343
  not_found_handling: "single-page-application",
1163
1344
  binding: "ASSETS",
1164
1345
  run_worker_first: ["/**"]
@@ -1430,15 +1611,15 @@ async function prompt(question) {
1430
1611
  input: process.stdin,
1431
1612
  output: process.stdout
1432
1613
  });
1433
- return new Promise((resolve2) => {
1614
+ return new Promise((resolve) => {
1434
1615
  rl.question(question, (answer) => {
1435
1616
  rl.close();
1436
- resolve2(answer.trim());
1617
+ resolve(answer.trim());
1437
1618
  });
1438
1619
  });
1439
1620
  }
1440
1621
  async function promptPassword(question) {
1441
- return new Promise((resolve2) => {
1622
+ return new Promise((resolve) => {
1442
1623
  const stdin = process.stdin;
1443
1624
  const stdout = process.stdout;
1444
1625
  stdout.write(question);
@@ -1459,7 +1640,7 @@ async function promptPassword(question) {
1459
1640
  stdin.pause();
1460
1641
  stdin.removeListener("data", onData);
1461
1642
  stdout.write("\n");
1462
- resolve2(password);
1643
+ resolve(password);
1463
1644
  break;
1464
1645
  case "":
1465
1646
  stdout.write("\n");
@@ -1485,7 +1666,7 @@ async function promptPassword(question) {
1485
1666
  });
1486
1667
  }
1487
1668
  function runCommand(command, args, cwd) {
1488
- return new Promise((resolve2, reject) => {
1669
+ return new Promise((resolve, reject) => {
1489
1670
  const child = spawn(command, args, {
1490
1671
  cwd,
1491
1672
  stdio: "inherit",
@@ -1493,7 +1674,7 @@ function runCommand(command, args, cwd) {
1493
1674
  });
1494
1675
  child.on("close", (code) => {
1495
1676
  if (code === 0) {
1496
- resolve2();
1677
+ resolve();
1497
1678
  } else {
1498
1679
  reject(new Error(`Command failed with exit code ${code}`));
1499
1680
  }
@@ -1517,25 +1698,16 @@ function detectPackageManager() {
1517
1698
  if (fs3.existsSync("package-lock.json")) return "npm";
1518
1699
  return "npm";
1519
1700
  }
1520
- function getBuilderPackageVersion() {
1521
- try {
1522
- const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1523
- const pkgPath = path3.resolve(__dirname2, "../../package.json");
1524
- const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1525
- const version = pkg2.version;
1526
- const prereleaseMatch = version.match(/-([a-z]+)\./i);
1527
- if (prereleaseMatch) {
1528
- return `@${prereleaseMatch[1]}`;
1529
- }
1530
- return `@^${version}`;
1531
- } catch {
1532
- return "";
1701
+ function getPackageVersion() {
1702
+ if (process.env.STANDARDAGENTS_WORKSPACE === "1") {
1703
+ return "workspace:*";
1533
1704
  }
1705
+ return "0.12.0";
1534
1706
  }
1535
1707
  async function selectPackageManager(detected) {
1536
1708
  const options = ["npm", "pnpm", "yarn", "bun"];
1537
1709
  const detectedIndex = options.indexOf(detected);
1538
- return new Promise((resolve2) => {
1710
+ return new Promise((resolve) => {
1539
1711
  const stdin = process.stdin;
1540
1712
  const stdout = process.stdout;
1541
1713
  let selectedIndex = detectedIndex;
@@ -1581,7 +1753,7 @@ async function selectPackageManager(detected) {
1581
1753
  renderOptions();
1582
1754
  } else if (key === "\r" || key === "\n") {
1583
1755
  cleanup();
1584
- resolve2(options[selectedIndex]);
1756
+ resolve(options[selectedIndex]);
1585
1757
  } else if (key === "") {
1586
1758
  cleanup();
1587
1759
  stdout.write("\n");
@@ -1667,31 +1839,7 @@ export default defineConfig({
1667
1839
  }
1668
1840
  }
1669
1841
  logger.log("");
1670
- logger.info("Step 2: Installing Standard Agents dependencies...");
1671
- logger.log("");
1672
- try {
1673
- const installCmd = pm === "npm" ? "install" : "add";
1674
- const devFlag = pm === "npm" ? "--save-dev" : "-D";
1675
- const builderVersion = getBuilderPackageVersion();
1676
- await runCommand(pm, [
1677
- installCmd,
1678
- devFlag,
1679
- "@cloudflare/vite-plugin",
1680
- `@standardagents/builder${builderVersion}`,
1681
- "wrangler",
1682
- "zod"
1683
- ], projectPath);
1684
- } catch (error) {
1685
- logger.error("Failed to install dependencies");
1686
- process.exit(1);
1687
- }
1688
- logger.log("");
1689
- logger.info("Step 3: Configuring Standard Agents...");
1690
- logger.log("");
1691
- process.chdir(projectPath);
1692
- await scaffold({ force: true });
1693
- logger.log("");
1694
- logger.info("Step 4: Configuring environment variables...");
1842
+ logger.info("Step 2: Configuring environment variables...");
1695
1843
  logger.log("");
1696
1844
  const encryptionKey = crypto.randomBytes(32).toString("hex");
1697
1845
  let openrouterKey = "";
@@ -1726,6 +1874,44 @@ export default defineConfig({
1726
1874
  if (!adminPassword) {
1727
1875
  adminPassword = "admin";
1728
1876
  }
1877
+ logger.log("");
1878
+ logger.info("Step 3: Installing Standard Agents dependencies...");
1879
+ logger.log("");
1880
+ try {
1881
+ const installCmd = pm === "npm" ? "install" : "add";
1882
+ const devFlag = pm === "npm" ? "--save-dev" : "-D";
1883
+ const packageVersion = getPackageVersion();
1884
+ await runCommand(pm, [
1885
+ installCmd,
1886
+ devFlag,
1887
+ "@cloudflare/vite-plugin",
1888
+ "wrangler"
1889
+ ], projectPath);
1890
+ const deps = [
1891
+ `@standardagents/builder@${packageVersion}`,
1892
+ `@standardagents/spec@${packageVersion}`,
1893
+ `@standardagents/sip@${packageVersion}`,
1894
+ "zod"
1895
+ ];
1896
+ if (openaiKey) {
1897
+ deps.push(`@standardagents/openai@${packageVersion}`);
1898
+ }
1899
+ if (openrouterKey) {
1900
+ deps.push(`@standardagents/openrouter@${packageVersion}`);
1901
+ }
1902
+ await runCommand(pm, [installCmd, ...deps], projectPath);
1903
+ } catch (error) {
1904
+ logger.error("Failed to install dependencies");
1905
+ process.exit(1);
1906
+ }
1907
+ logger.log("");
1908
+ logger.info("Step 4: Configuring Standard Agents...");
1909
+ logger.log("");
1910
+ process.chdir(projectPath);
1911
+ await scaffold({ force: true });
1912
+ logger.log("");
1913
+ logger.info("Writing environment configuration...");
1914
+ logger.log("");
1729
1915
  const devVarsLines = [
1730
1916
  "# Standard Agents Environment Variables",
1731
1917
  "# This file contains secrets for local development only",
@@ -1782,10 +1968,10 @@ export default defineConfig({
1782
1968
  }
1783
1969
  function getReactPackageVersion() {
1784
1970
  try {
1785
- const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1786
- const pkgPath = path3.resolve(__dirname2, "../package.json");
1787
- const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1788
- const version = pkg2.version;
1971
+ const __dirname = path3.dirname(fileURLToPath(import.meta.url));
1972
+ const pkgPath = path3.resolve(__dirname, "../package.json");
1973
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1974
+ const version = pkg.version;
1789
1975
  const prereleaseMatch = version.match(/-([a-z]+)\./i);
1790
1976
  if (prereleaseMatch) {
1791
1977
  return prereleaseMatch[1];
@@ -1796,23 +1982,23 @@ function getReactPackageVersion() {
1796
1982
  }
1797
1983
  }
1798
1984
  function getChatSourceDir() {
1799
- const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
1800
- return path3.resolve(__dirname2, "../chat");
1985
+ const __dirname = path3.dirname(fileURLToPath(import.meta.url));
1986
+ return path3.resolve(__dirname, "../chat");
1801
1987
  }
1802
1988
  async function prompt2(question) {
1803
1989
  const rl = readline.createInterface({
1804
1990
  input: process.stdin,
1805
1991
  output: process.stdout
1806
1992
  });
1807
- return new Promise((resolve2) => {
1993
+ return new Promise((resolve) => {
1808
1994
  rl.question(question, (answer) => {
1809
1995
  rl.close();
1810
- resolve2(answer.trim());
1996
+ resolve(answer.trim());
1811
1997
  });
1812
1998
  });
1813
1999
  }
1814
2000
  function runCommand2(command, args, cwd) {
1815
- return new Promise((resolve2, reject) => {
2001
+ return new Promise((resolve, reject) => {
1816
2002
  const child = spawn(command, args, {
1817
2003
  cwd,
1818
2004
  stdio: "inherit",
@@ -1820,7 +2006,7 @@ function runCommand2(command, args, cwd) {
1820
2006
  });
1821
2007
  child.on("close", (code) => {
1822
2008
  if (code === 0) {
1823
- resolve2();
2009
+ resolve();
1824
2010
  } else {
1825
2011
  reject(new Error(`Command failed with exit code ${code}`));
1826
2012
  }
@@ -1844,7 +2030,7 @@ function detectPackageManager2() {
1844
2030
  async function selectPackageManager2(detected) {
1845
2031
  const options = ["npm", "pnpm", "yarn", "bun"];
1846
2032
  const detectedIndex = options.indexOf(detected);
1847
- return new Promise((resolve2) => {
2033
+ return new Promise((resolve) => {
1848
2034
  const stdin = process.stdin;
1849
2035
  const stdout = process.stdout;
1850
2036
  let selectedIndex = detectedIndex;
@@ -1890,7 +2076,7 @@ async function selectPackageManager2(detected) {
1890
2076
  renderOptions();
1891
2077
  } else if (key === "\r" || key === "\n") {
1892
2078
  cleanup();
1893
- resolve2(options[selectedIndex]);
2079
+ resolve(options[selectedIndex]);
1894
2080
  } else if (key === "") {
1895
2081
  cleanup();
1896
2082
  stdout.write("\n");
@@ -1905,7 +2091,7 @@ async function selectFramework() {
1905
2091
  { value: "vite", label: "Vite (React + TypeScript)" },
1906
2092
  { value: "nextjs", label: "Next.js (App Router)" }
1907
2093
  ];
1908
- return new Promise((resolve2) => {
2094
+ return new Promise((resolve) => {
1909
2095
  const stdin = process.stdin;
1910
2096
  const stdout = process.stdout;
1911
2097
  let selectedIndex = 0;
@@ -1949,7 +2135,7 @@ async function selectFramework() {
1949
2135
  renderOptions();
1950
2136
  } else if (key === "\r" || key === "\n") {
1951
2137
  cleanup();
1952
- resolve2(options[selectedIndex].value);
2138
+ resolve(options[selectedIndex].value);
1953
2139
  } else if (key === "") {
1954
2140
  cleanup();
1955
2141
  stdout.write("\n");
@@ -1969,20 +2155,20 @@ function isValidUrl(url) {
1969
2155
  }
1970
2156
  async function isPortOpen(port) {
1971
2157
  const tryConnect = (host) => {
1972
- return new Promise((resolve2) => {
2158
+ return new Promise((resolve) => {
1973
2159
  const socket = new net.Socket();
1974
2160
  socket.setTimeout(200);
1975
2161
  socket.on("connect", () => {
1976
2162
  socket.destroy();
1977
- resolve2(true);
2163
+ resolve(true);
1978
2164
  });
1979
2165
  socket.on("timeout", () => {
1980
2166
  socket.destroy();
1981
- resolve2(false);
2167
+ resolve(false);
1982
2168
  });
1983
2169
  socket.on("error", () => {
1984
2170
  socket.destroy();
1985
- resolve2(false);
2171
+ resolve(false);
1986
2172
  });
1987
2173
  socket.connect(port, host);
1988
2174
  });
@@ -2206,21 +2392,21 @@ export default defineConfig({
2206
2392
  fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2207
2393
  logger.success("Created tsconfig.json");
2208
2394
  fs3.copyFileSync(path3.join(chatSourceDir, "package.json"), path3.join(projectPath, "package.json"));
2209
- const pkg2 = JSON.parse(fs3.readFileSync(path3.join(projectPath, "package.json"), "utf-8"));
2210
- pkg2.name = projectName;
2211
- pkg2.scripts = {
2395
+ const pkg = JSON.parse(fs3.readFileSync(path3.join(projectPath, "package.json"), "utf-8"));
2396
+ pkg.name = projectName;
2397
+ pkg.scripts = {
2212
2398
  dev: "vite",
2213
2399
  build: "vite build",
2214
2400
  preview: "vite preview"
2215
2401
  };
2216
- delete pkg2.private;
2217
- if (pkg2.dependencies?.["@standardagents/react"]) {
2218
- pkg2.dependencies["@standardagents/react"] = reactVersion;
2219
- }
2220
- delete pkg2.dependencies?.["next"];
2221
- delete pkg2.devDependencies?.["@tailwindcss/postcss"];
2222
- delete pkg2.devDependencies?.["postcss"];
2223
- fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2402
+ delete pkg.private;
2403
+ if (pkg.dependencies?.["@standardagents/react"]) {
2404
+ pkg.dependencies["@standardagents/react"] = reactVersion;
2405
+ }
2406
+ delete pkg.dependencies?.["next"];
2407
+ delete pkg.devDependencies?.["@tailwindcss/postcss"];
2408
+ delete pkg.devDependencies?.["postcss"];
2409
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg, null, 2) + "\n", "utf-8");
2224
2410
  logger.success("Created package.json");
2225
2411
  const envContent = `# Agentbuilder connection
2226
2412
  VITE_AGENTBUILDER_URL=${serverUrl}
@@ -2275,21 +2461,21 @@ async function scaffoldNextjs(projectPath, chatSourceDir, projectName, serverUrl
2275
2461
  };
2276
2462
  fs3.writeFileSync(path3.join(projectPath, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n", "utf-8");
2277
2463
  logger.success("Created tsconfig.json");
2278
- const pkg2 = JSON.parse(fs3.readFileSync(path3.join(chatSourceDir, "package.json"), "utf-8"));
2279
- pkg2.name = projectName;
2280
- pkg2.scripts = {
2464
+ const pkg = JSON.parse(fs3.readFileSync(path3.join(chatSourceDir, "package.json"), "utf-8"));
2465
+ pkg.name = projectName;
2466
+ pkg.scripts = {
2281
2467
  dev: "next dev",
2282
2468
  build: "next build",
2283
2469
  start: "next start"
2284
2470
  };
2285
- delete pkg2.private;
2286
- if (pkg2.dependencies?.["@standardagents/react"]) {
2287
- pkg2.dependencies["@standardagents/react"] = reactVersion;
2288
- }
2289
- delete pkg2.devDependencies?.["@tailwindcss/vite"];
2290
- delete pkg2.devDependencies?.["@vitejs/plugin-react"];
2291
- delete pkg2.devDependencies?.["vite"];
2292
- fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2471
+ delete pkg.private;
2472
+ if (pkg.dependencies?.["@standardagents/react"]) {
2473
+ pkg.dependencies["@standardagents/react"] = reactVersion;
2474
+ }
2475
+ delete pkg.devDependencies?.["@tailwindcss/vite"];
2476
+ delete pkg.devDependencies?.["@vitejs/plugin-react"];
2477
+ delete pkg.devDependencies?.["vite"];
2478
+ fs3.writeFileSync(path3.join(projectPath, "package.json"), JSON.stringify(pkg, null, 2) + "\n", "utf-8");
2293
2479
  logger.success("Created package.json");
2294
2480
  const envContent = `# Agentbuilder connection
2295
2481
  NEXT_PUBLIC_AGENTBUILDER_URL=${serverUrl}
@@ -2309,15 +2495,437 @@ out
2309
2495
  pageTsx = pageTsx.replace(/from '\.\.\/\.\.\/src\//g, "from '../src/");
2310
2496
  fs3.writeFileSync(path3.join(projectPath, "app", "page.tsx"), pageTsx, "utf-8");
2311
2497
  }
2498
+ async function selectAgent(agents) {
2499
+ return new Promise((resolve) => {
2500
+ const stdin = process.stdin;
2501
+ const stdout = process.stdout;
2502
+ let selectedIndex = 0;
2503
+ const renderOptions = () => {
2504
+ stdout.write("\x1B[?25l");
2505
+ stdout.write(`\x1B[${agents.length + 1}A`);
2506
+ stdout.write("\x1B[0J");
2507
+ stdout.write("Select an agent to pack (use arrows, enter to confirm):\n");
2508
+ agents.forEach((agent, i) => {
2509
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
2510
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
2511
+ stdout.write(`${prefix} ${highlight}${agent}\x1B[0m
2512
+ `);
2513
+ });
2514
+ };
2515
+ stdout.write("Select an agent to pack (use arrows, enter to confirm):\n");
2516
+ agents.forEach((agent, i) => {
2517
+ const prefix = i === selectedIndex ? "\x1B[36m\u276F\x1B[0m" : " ";
2518
+ const highlight = i === selectedIndex ? "\x1B[36m" : "\x1B[90m";
2519
+ stdout.write(`${prefix} ${highlight}${agent}\x1B[0m
2520
+ `);
2521
+ });
2522
+ const wasRaw = stdin.isRaw;
2523
+ if (stdin.isTTY) {
2524
+ stdin.setRawMode(true);
2525
+ }
2526
+ stdin.resume();
2527
+ stdin.setEncoding("utf8");
2528
+ const cleanup = () => {
2529
+ stdin.setRawMode(wasRaw ?? false);
2530
+ stdin.pause();
2531
+ stdin.removeListener("data", onData);
2532
+ stdout.write("\x1B[?25h");
2533
+ };
2534
+ const onData = (key) => {
2535
+ if (key === "\x1B[A" || key === "k") {
2536
+ selectedIndex = (selectedIndex - 1 + agents.length) % agents.length;
2537
+ renderOptions();
2538
+ } else if (key === "\x1B[B" || key === "j") {
2539
+ selectedIndex = (selectedIndex + 1) % agents.length;
2540
+ renderOptions();
2541
+ } else if (key === "\r" || key === "\n") {
2542
+ cleanup();
2543
+ resolve(agents[selectedIndex]);
2544
+ } else if (key === "") {
2545
+ cleanup();
2546
+ stdout.write("\n");
2547
+ process.exit(1);
2548
+ }
2549
+ };
2550
+ stdin.on("data", onData);
2551
+ });
2552
+ }
2553
+ async function selectItemModes(items, _agentName) {
2554
+ return new Promise((resolve) => {
2555
+ const stdin = process.stdin;
2556
+ const stdout = process.stdout;
2557
+ let selectedIndex = 0;
2558
+ const getDisplayLines = () => {
2559
+ const lines = [];
2560
+ lines.push("");
2561
+ lines.push("Remove originals? (space to toggle, enter to confirm)");
2562
+ lines.push("");
2563
+ for (let i = 0; i < items.length; i++) {
2564
+ const item = items[i];
2565
+ const isSelected = i === selectedIndex;
2566
+ const cursor = isSelected ? "\x1B[36m\u276F\x1B[0m" : " ";
2567
+ const modeText = item.mode === "extract" ? "\x1B[32m(extract)\x1B[0m" : "\x1B[90m(copy)\x1B[0m";
2568
+ lines.push(`${cursor} ${item.name} \x1B[90m(${item.type})\x1B[0m ${modeText}`);
2569
+ }
2570
+ lines.push("");
2571
+ return lines;
2572
+ };
2573
+ const render = (initial = false) => {
2574
+ const lines = getDisplayLines();
2575
+ if (!initial) {
2576
+ stdout.write("\x1B[?25l");
2577
+ stdout.write(`\x1B[${lines.length}A`);
2578
+ stdout.write("\x1B[0J");
2579
+ }
2580
+ stdout.write(lines.join("\n") + "\n");
2581
+ };
2582
+ render(true);
2583
+ const wasRaw = stdin.isRaw;
2584
+ if (stdin.isTTY) {
2585
+ stdin.setRawMode(true);
2586
+ }
2587
+ stdin.resume();
2588
+ stdin.setEncoding("utf8");
2589
+ const cleanup = () => {
2590
+ stdin.setRawMode(wasRaw ?? false);
2591
+ stdin.pause();
2592
+ stdin.removeListener("data", onData);
2593
+ stdout.write("\x1B[?25h");
2594
+ };
2595
+ const onData = (key) => {
2596
+ if (key === "\x1B[A" || key === "k") {
2597
+ selectedIndex = (selectedIndex - 1 + items.length) % items.length;
2598
+ render();
2599
+ } else if (key === "\x1B[B" || key === "j") {
2600
+ selectedIndex = (selectedIndex + 1) % items.length;
2601
+ render();
2602
+ } else if (key === " ") {
2603
+ items[selectedIndex].mode = items[selectedIndex].mode === "extract" ? "copy" : "extract";
2604
+ render();
2605
+ } else if (key === "\r" || key === "\n") {
2606
+ cleanup();
2607
+ resolve(items);
2608
+ } else if (key === "") {
2609
+ cleanup();
2610
+ stdout.write("\n");
2611
+ process.exit(1);
2612
+ }
2613
+ };
2614
+ stdin.on("data", onData);
2615
+ });
2616
+ }
2617
+ function isStandardAgentsProject() {
2618
+ if (!fs3.existsSync("agents")) {
2619
+ return false;
2620
+ }
2621
+ try {
2622
+ const pkgJson = JSON.parse(fs3.readFileSync("package.json", "utf-8"));
2623
+ const deps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
2624
+ return "@standardagents/builder" in deps;
2625
+ } catch {
2626
+ return false;
2627
+ }
2628
+ }
2629
+ async function pack(agentName, options) {
2630
+ logger.info("Packing agent...\n");
2631
+ if (!isStandardAgentsProject()) {
2632
+ logger.error("This does not appear to be a Standard Agents project.");
2633
+ logger.info("Make sure you have @standardagents/builder installed and an agents/ directory.");
2634
+ process.exit(1);
2635
+ }
2636
+ const rootDir = process.cwd();
2637
+ let PackingService;
2638
+ try {
2639
+ const packing = await import('@standardagents/builder/packing');
2640
+ PackingService = packing.PackingService;
2641
+ } catch (err) {
2642
+ logger.error("Failed to load packing service from @standardagents/builder");
2643
+ logger.info("Make sure @standardagents/builder is installed and built.");
2644
+ if (err instanceof Error) {
2645
+ logger.info(`Error: ${err.message}`);
2646
+ }
2647
+ process.exit(1);
2648
+ }
2649
+ const packingService = new PackingService();
2650
+ if (!agentName) {
2651
+ const agents = packingService.listAgents(rootDir);
2652
+ if (agents.length === 0) {
2653
+ logger.error("No agents found in agents/agents/");
2654
+ process.exit(1);
2655
+ }
2656
+ if (agents.length === 1) {
2657
+ agentName = agents[0];
2658
+ logger.info(`Found one agent: ${agentName}`);
2659
+ } else {
2660
+ console.log("");
2661
+ agentName = await selectAgent(agents);
2662
+ console.log("");
2663
+ }
2664
+ }
2665
+ if (!agentName) {
2666
+ logger.error("No agent selected");
2667
+ process.exit(1);
2668
+ }
2669
+ const selectedAgent = agentName;
2670
+ logger.info(`Analyzing agent: ${selectedAgent}`);
2671
+ const analysis = await packingService.analyzeAgent(selectedAgent, rootDir);
2672
+ if (analysis.errors.length > 0) {
2673
+ logger.error("Analysis failed:");
2674
+ for (const error of analysis.errors) {
2675
+ console.log(` - ${error}`);
2676
+ }
2677
+ process.exit(1);
2678
+ }
2679
+ console.log("");
2680
+ logger.info("Discovered constituents:");
2681
+ console.log(` Agents: ${analysis.constituents.agents.map((a) => a.name).join(", ") || "(none)"}`);
2682
+ console.log(` Prompts: ${analysis.constituents.prompts.map((p) => p.name).join(", ") || "(none)"}`);
2683
+ console.log(` Tools: ${analysis.constituents.tools.map((t) => t.name).join(", ") || "(none)"}`);
2684
+ console.log(` Models: ${analysis.constituents.models.map((m) => m.name).join(", ") || "(none)"}`);
2685
+ console.log(` Hooks: ${analysis.constituents.hooks.map((h) => h.name).join(", ") || "(none)"}`);
2686
+ if (analysis.warnings.length > 0) {
2687
+ console.log("");
2688
+ logger.warning("Warnings:");
2689
+ for (const warning of analysis.warnings) {
2690
+ console.log(` - ${warning}`);
2691
+ }
2692
+ }
2693
+ const selectionItems = [];
2694
+ for (const agent of analysis.constituents.agents) {
2695
+ selectionItems.push({
2696
+ name: agent.name,
2697
+ type: "agent",
2698
+ filePath: agent.filePath,
2699
+ mode: agent.sharedWith.length > 0 ? "copy" : "extract",
2700
+ isShared: agent.sharedWith.length > 0,
2701
+ sharedWith: agent.sharedWith
2702
+ });
2703
+ }
2704
+ for (const prompt3 of analysis.constituents.prompts) {
2705
+ selectionItems.push({
2706
+ name: prompt3.name,
2707
+ type: "prompt",
2708
+ filePath: prompt3.filePath,
2709
+ mode: prompt3.sharedWith.length > 0 ? "copy" : "extract",
2710
+ isShared: prompt3.sharedWith.length > 0,
2711
+ sharedWith: prompt3.sharedWith
2712
+ });
2713
+ }
2714
+ for (const tool of analysis.constituents.tools) {
2715
+ selectionItems.push({
2716
+ name: tool.name,
2717
+ type: "tool",
2718
+ filePath: tool.filePath,
2719
+ mode: tool.sharedWith.length > 0 ? "copy" : "extract",
2720
+ isShared: tool.sharedWith.length > 0,
2721
+ sharedWith: tool.sharedWith
2722
+ });
2723
+ }
2724
+ for (const model of analysis.constituents.models) {
2725
+ selectionItems.push({
2726
+ name: model.name,
2727
+ type: "model",
2728
+ filePath: model.filePath,
2729
+ mode: model.sharedWith.length > 0 ? "copy" : "extract",
2730
+ isShared: model.sharedWith.length > 0,
2731
+ sharedWith: model.sharedWith
2732
+ });
2733
+ }
2734
+ for (const hook of analysis.constituents.hooks) {
2735
+ selectionItems.push({
2736
+ name: hook.name,
2737
+ type: "hook",
2738
+ filePath: hook.filePath,
2739
+ mode: hook.sharedWith.length > 0 ? "copy" : "extract",
2740
+ isShared: hook.sharedWith.length > 0,
2741
+ sharedWith: hook.sharedWith
2742
+ });
2743
+ }
2744
+ let itemSelections;
2745
+ if (!options.noInteractive && process.stdin.isTTY && selectionItems.length > 0) {
2746
+ console.log("");
2747
+ const selections = await selectItemModes(selectionItems);
2748
+ itemSelections = selections.map((s) => ({
2749
+ name: s.name,
2750
+ type: s.type,
2751
+ filePath: s.filePath,
2752
+ mode: s.mode
2753
+ }));
2754
+ console.log("");
2755
+ }
2756
+ const outputDir = options.output || path3.join(rootDir, "agents", "packed");
2757
+ logger.info(`Packing to: ${outputDir}`);
2758
+ const result = await packingService.pack({
2759
+ agentName: selectedAgent,
2760
+ rootDir,
2761
+ outputDir,
2762
+ version: options.version || "1.0.0",
2763
+ npm: options.npm || false,
2764
+ removeOriginals: itemSelections ? false : !options.noRemove,
2765
+ // Let itemSelections handle removal
2766
+ packageId: `standardagent-${selectedAgent.replace(/_/g, "-")}`,
2767
+ itemSelections
2768
+ });
2769
+ if (!result.success) {
2770
+ logger.error(`Packing failed: ${result.error}`);
2771
+ process.exit(1);
2772
+ }
2773
+ console.log("");
2774
+ logger.success("Packing complete!");
2775
+ console.log(` Package ID: ${result.packageId}`);
2776
+ console.log(` Output: ${result.outputPath}`);
2777
+ console.log(` Files: ${result.filesCreated.length} created`);
2778
+ if (result.filesRemoved && result.filesRemoved.length > 0) {
2779
+ console.log(` Removed: ${result.filesRemoved.length} original files`);
2780
+ }
2781
+ if (result.warnings.length > 0) {
2782
+ console.log("");
2783
+ logger.warning("Warnings:");
2784
+ for (const warning of result.warnings) {
2785
+ console.log(` - ${warning}`);
2786
+ }
2787
+ }
2788
+ if (options.npm) {
2789
+ console.log("");
2790
+ logger.info("NPM package structure created. To publish:");
2791
+ console.log(` cd ${result.outputPath}`);
2792
+ console.log(" npm publish");
2793
+ }
2794
+ }
2795
+ function isStandardAgentsProject2() {
2796
+ if (!fs3.existsSync("agents")) {
2797
+ return false;
2798
+ }
2799
+ try {
2800
+ const pkgJson = JSON.parse(fs3.readFileSync("package.json", "utf-8"));
2801
+ const deps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
2802
+ return "@standardagents/builder" in deps;
2803
+ } catch {
2804
+ return false;
2805
+ }
2806
+ }
2807
+ async function unpack(pkgIdentifier, options) {
2808
+ logger.info("Unpacking agent...\n");
2809
+ if (!isStandardAgentsProject2()) {
2810
+ logger.error("This does not appear to be a Standard Agents project.");
2811
+ logger.info("Make sure you have @standardagents/builder installed and an agents/ directory.");
2812
+ process.exit(1);
2813
+ }
2814
+ const rootDir = process.cwd();
2815
+ let UnpackingService;
2816
+ try {
2817
+ const builder = await import('@standardagents/builder/runtime');
2818
+ UnpackingService = builder.UnpackingService;
2819
+ if (!UnpackingService) {
2820
+ const packing = await import('@standardagents/builder');
2821
+ UnpackingService = packing.UnpackingService;
2822
+ }
2823
+ } catch (err) {
2824
+ logger.error("Failed to load unpacking service from @standardagents/builder");
2825
+ logger.info("Make sure @standardagents/builder is installed.");
2826
+ process.exit(1);
2827
+ }
2828
+ const unpackingService = new UnpackingService();
2829
+ if (!pkgIdentifier) {
2830
+ const packages = await unpackingService.listPackages(rootDir);
2831
+ if (packages.length === 0) {
2832
+ logger.info("No packed agents found.");
2833
+ logger.info("Pack an agent with: agents pack <agent-name>");
2834
+ process.exit(0);
2835
+ }
2836
+ logger.info("Available packed agents:");
2837
+ for (const pkg of packages) {
2838
+ console.log(` - ${pkg.packageId} (${pkg.version}) [${pkg.source}]`);
2839
+ console.log(` Entry: ${pkg.entryAgent}`);
2840
+ console.log(` Path: ${pkg.path}`);
2841
+ console.log("");
2842
+ }
2843
+ logger.info("Run: agents unpack <package-id>");
2844
+ process.exit(0);
2845
+ }
2846
+ logger.info(`Unpacking: ${pkgIdentifier}`);
2847
+ const result = await unpackingService.unpack({
2848
+ package: pkgIdentifier,
2849
+ rootDir,
2850
+ keepSignatures: !options.noSignatures
2851
+ });
2852
+ if (!result.success) {
2853
+ logger.error(`Unpacking failed: ${result.error}`);
2854
+ process.exit(1);
2855
+ }
2856
+ console.log("");
2857
+ logger.success("Unpacking complete!");
2858
+ console.log(` Package: ${result.packageId}`);
2859
+ console.log(` Files: ${result.filesCreated.length} created`);
2860
+ if (result.filesCreated.length > 0) {
2861
+ console.log("");
2862
+ logger.info("Created files:");
2863
+ for (const file of result.filesCreated) {
2864
+ const relPath = path3.relative(rootDir, file);
2865
+ console.log(` - ${relPath}`);
2866
+ }
2867
+ }
2868
+ if (result.warnings.length > 0) {
2869
+ console.log("");
2870
+ logger.warning("Warnings:");
2871
+ for (const warning of result.warnings) {
2872
+ console.log(` - ${warning}`);
2873
+ }
2874
+ }
2875
+ if (options.noSignatures) {
2876
+ console.log("");
2877
+ logger.info("Package signatures removed - files are fully editable.");
2878
+ } else {
2879
+ console.log("");
2880
+ logger.info("Package signatures preserved - files are editable but marked as packed.");
2881
+ }
2882
+ }
2883
+ async function listPacked() {
2884
+ if (!isStandardAgentsProject2()) {
2885
+ logger.error("This does not appear to be a Standard Agents project.");
2886
+ logger.info("Make sure you have @standardagents/builder installed and an agents/ directory.");
2887
+ process.exit(1);
2888
+ }
2889
+ const rootDir = process.cwd();
2890
+ let UnpackingService;
2891
+ try {
2892
+ const builder = await import('@standardagents/builder/runtime');
2893
+ UnpackingService = builder.UnpackingService;
2894
+ if (!UnpackingService) {
2895
+ const packing = await import('@standardagents/builder');
2896
+ UnpackingService = packing.UnpackingService;
2897
+ }
2898
+ } catch (err) {
2899
+ logger.error("Failed to load unpacking service from @standardagents/builder");
2900
+ logger.info("Make sure @standardagents/builder is installed.");
2901
+ process.exit(1);
2902
+ }
2903
+ const unpackingService = new UnpackingService();
2904
+ const packages = await unpackingService.listPackages(rootDir);
2905
+ if (packages.length === 0) {
2906
+ logger.info("No packed agents found.");
2907
+ logger.info("Pack an agent with: agents pack <agent-name>");
2908
+ return;
2909
+ }
2910
+ logger.info("Discovered packed agents:\n");
2911
+ for (const pkg of packages) {
2912
+ console.log(`${pkg.packageId} (${pkg.version})`);
2913
+ console.log(` Source: ${pkg.source}`);
2914
+ console.log(` Entry: ${pkg.entryAgent}`);
2915
+ console.log(` Path: ${pkg.path}`);
2916
+ console.log("");
2917
+ }
2918
+ }
2312
2919
 
2313
2920
  // src/index.ts
2314
- var __dirname$1 = dirname(fileURLToPath(import.meta.url));
2315
- var pkg = JSON.parse(readFileSync(resolve(__dirname$1, "../package.json"), "utf-8"));
2316
2921
  var program = new Command();
2317
- program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version(pkg.version);
2922
+ program.name("agents").description("CLI tool for Standard Agents / AgentBuilder").version("0.12.0");
2318
2923
  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);
2319
2924
  program.command("scaffold").description("Add Standard Agents to an existing Vite project").option("--force", "Overwrite existing configuration").action(scaffold);
2320
2925
  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);
2926
+ program.command("pack [agent-name]").description("Pack an agent with all its dependencies").option("--output <dir>", "Output directory (default: agents/packed)").option("--npm", "Generate npm-ready package structure").option("--version <version>", "Package version (default: 1.0.0)").option("--no-remove", "Keep original files after packing").option("--no-interactive", "Skip interactive item mode selector").action(pack);
2927
+ program.command("unpack [package]").description("Unpack a packed agent to the local agents directory").option("--no-signatures", "Remove package signatures (fully editable)").action(unpack);
2928
+ program.command("list-packed").description("List all discovered packed agents").action(listPacked);
2321
2929
  program.parse();
2322
2930
  //# sourceMappingURL=index.js.map
2323
2931
  //# sourceMappingURL=index.js.map