@orchagent/cli 0.3.86 → 0.3.87

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.
@@ -393,8 +393,385 @@ DISCORD_CHANNEL_IDS=
393
393
  # MODEL=claude-sonnet-4-5-20250929
394
394
  # MAX_TOKENS=1024
395
395
  `;
396
+ // ---------------------------------------------------------------------------
397
+ // Orchestration templates: fan-out, pipeline, map-reduce
398
+ // ---------------------------------------------------------------------------
399
+ const FANOUT_MAIN_PY = `"""
400
+ orchagent fan-out orchestrator.
401
+
402
+ Calls multiple agents in parallel, combines their results.
403
+
404
+ Usage:
405
+ echo '{"task": "analyze this"}' | python main.py
406
+ """
407
+
408
+ import asyncio
409
+ import json
410
+ import sys
411
+
412
+ from orchagent import AgentClient
413
+
414
+
415
+ async def run():
416
+ raw = sys.stdin.read()
417
+ try:
418
+ data = json.loads(raw) if raw.strip() else {}
419
+ except json.JSONDecodeError:
420
+ print(json.dumps({"error": "Invalid JSON input"}))
421
+ sys.exit(1)
422
+
423
+ task = data.get("task", "")
424
+ client = AgentClient()
425
+
426
+ # Fan-out: call all agents in parallel
427
+ # Replace these with your actual dependencies (must match manifest.dependencies)
428
+ agent_a, agent_b, agent_c = await asyncio.gather(
429
+ client.call("org/agent-a@v1", {"task": task}),
430
+ client.call("org/agent-b@v1", {"task": task}),
431
+ client.call("org/agent-c@v1", {"task": task}),
432
+ )
433
+
434
+ # Combine results
435
+ print(json.dumps({
436
+ "results": [agent_a, agent_b, agent_c],
437
+ "summary": f"Collected results from 3 agents",
438
+ "success": True,
439
+ }))
440
+
441
+
442
+ if __name__ == "__main__":
443
+ asyncio.run(run())
444
+ `;
445
+ const FANOUT_MAIN_JS = `/**
446
+ * orchagent fan-out orchestrator.
447
+ *
448
+ * Calls multiple agents in parallel, combines their results.
449
+ *
450
+ * Usage:
451
+ * echo '{"task": "analyze this"}' | node main.js
452
+ */
453
+
454
+ const fs = require('fs');
455
+ const { AgentClient } = require('orchagent-sdk');
456
+
457
+ async function main() {
458
+ const raw = fs.readFileSync('/dev/stdin', 'utf-8');
459
+ let data;
460
+ try {
461
+ data = raw.trim() ? JSON.parse(raw) : {};
462
+ } catch {
463
+ console.log(JSON.stringify({ error: 'Invalid JSON input' }));
464
+ process.exit(1);
465
+ }
466
+
467
+ const task = data.task || '';
468
+ const client = new AgentClient();
469
+
470
+ // Fan-out: call all agents in parallel
471
+ // Replace these with your actual dependencies (must match manifest.dependencies)
472
+ const [agentA, agentB, agentC] = await Promise.all([
473
+ client.call('org/agent-a@v1', { task }),
474
+ client.call('org/agent-b@v1', { task }),
475
+ client.call('org/agent-c@v1', { task }),
476
+ ]);
477
+
478
+ console.log(JSON.stringify({
479
+ results: [agentA, agentB, agentC],
480
+ summary: 'Collected results from 3 agents',
481
+ success: true,
482
+ }));
483
+ }
484
+
485
+ main().catch(err => {
486
+ console.error(err);
487
+ process.exit(1);
488
+ });
489
+ `;
490
+ const PIPELINE_MAIN_PY = `"""
491
+ orchagent pipeline orchestrator.
492
+
493
+ Calls agents sequentially — each step's output feeds into the next.
494
+
495
+ Usage:
496
+ echo '{"task": "process this data"}' | python main.py
497
+ """
498
+
499
+ import asyncio
500
+ import json
501
+ import sys
502
+
503
+ from orchagent import AgentClient
504
+
505
+
506
+ async def run():
507
+ raw = sys.stdin.read()
508
+ try:
509
+ data = json.loads(raw) if raw.strip() else {}
510
+ except json.JSONDecodeError:
511
+ print(json.dumps({"error": "Invalid JSON input"}))
512
+ sys.exit(1)
513
+
514
+ task = data.get("task", "")
515
+ client = AgentClient()
516
+
517
+ # Pipeline: each step feeds into the next
518
+ # Replace these with your actual dependencies (must match manifest.dependencies)
519
+ step1 = await client.call("org/parser@v1", {"input": task})
520
+ step2 = await client.call("org/analyzer@v1", {"input": step1})
521
+ step3 = await client.call("org/reporter@v1", {"input": step2})
522
+
523
+ print(json.dumps({
524
+ "result": step3,
525
+ "steps_completed": 3,
526
+ "success": True,
527
+ }))
528
+
529
+
530
+ if __name__ == "__main__":
531
+ asyncio.run(run())
532
+ `;
533
+ const PIPELINE_MAIN_JS = `/**
534
+ * orchagent pipeline orchestrator.
535
+ *
536
+ * Calls agents sequentially — each step's output feeds into the next.
537
+ *
538
+ * Usage:
539
+ * echo '{"task": "process this data"}' | node main.js
540
+ */
541
+
542
+ const fs = require('fs');
543
+ const { AgentClient } = require('orchagent-sdk');
544
+
545
+ async function main() {
546
+ const raw = fs.readFileSync('/dev/stdin', 'utf-8');
547
+ let data;
548
+ try {
549
+ data = raw.trim() ? JSON.parse(raw) : {};
550
+ } catch {
551
+ console.log(JSON.stringify({ error: 'Invalid JSON input' }));
552
+ process.exit(1);
553
+ }
554
+
555
+ const task = data.task || '';
556
+ const client = new AgentClient();
557
+
558
+ // Pipeline: each step feeds into the next
559
+ // Replace these with your actual dependencies (must match manifest.dependencies)
560
+ const step1 = await client.call('org/parser@v1', { input: task });
561
+ const step2 = await client.call('org/analyzer@v1', { input: step1 });
562
+ const step3 = await client.call('org/reporter@v1', { input: step2 });
563
+
564
+ console.log(JSON.stringify({
565
+ result: step3,
566
+ steps_completed: 3,
567
+ success: true,
568
+ }));
569
+ }
570
+
571
+ main().catch(err => {
572
+ console.error(err);
573
+ process.exit(1);
574
+ });
575
+ `;
576
+ const MAPREDUCE_MAIN_PY = `"""
577
+ orchagent map-reduce orchestrator.
578
+
579
+ Splits input into chunks, processes each in parallel (map), then aggregates (reduce).
580
+
581
+ Usage:
582
+ echo '{"items": ["item1", "item2", "item3"]}' | python main.py
583
+ """
584
+
585
+ import asyncio
586
+ import json
587
+ import sys
588
+
589
+ from orchagent import AgentClient
590
+
591
+
592
+ async def run():
593
+ raw = sys.stdin.read()
594
+ try:
595
+ data = json.loads(raw) if raw.strip() else {}
596
+ except json.JSONDecodeError:
597
+ print(json.dumps({"error": "Invalid JSON input"}))
598
+ sys.exit(1)
599
+
600
+ items = data.get("items", [])
601
+ if not items:
602
+ print(json.dumps({"error": "No items to process", "success": False}))
603
+ sys.exit(1)
604
+
605
+ client = AgentClient()
606
+
607
+ # Map: process each item in parallel
608
+ # Replace with your actual dependency (must match manifest.dependencies)
609
+ mapped = await asyncio.gather(
610
+ *[client.call("org/processor@v1", {"item": item}) for item in items]
611
+ )
612
+
613
+ # Reduce: aggregate results into a single output
614
+ # Replace with your actual dependency (must match manifest.dependencies)
615
+ reduced = await client.call("org/aggregator@v1", {"results": mapped})
616
+
617
+ print(json.dumps({
618
+ "result": reduced,
619
+ "items_processed": len(items),
620
+ "success": True,
621
+ }))
622
+
623
+
624
+ if __name__ == "__main__":
625
+ asyncio.run(run())
626
+ `;
627
+ const MAPREDUCE_MAIN_JS = `/**
628
+ * orchagent map-reduce orchestrator.
629
+ *
630
+ * Splits input into chunks, processes each in parallel (map), then aggregates (reduce).
631
+ *
632
+ * Usage:
633
+ * echo '{"items": ["item1", "item2", "item3"]}' | node main.js
634
+ */
635
+
636
+ const fs = require('fs');
637
+ const { AgentClient } = require('orchagent-sdk');
638
+
639
+ async function main() {
640
+ const raw = fs.readFileSync('/dev/stdin', 'utf-8');
641
+ let data;
642
+ try {
643
+ data = raw.trim() ? JSON.parse(raw) : {};
644
+ } catch {
645
+ console.log(JSON.stringify({ error: 'Invalid JSON input' }));
646
+ process.exit(1);
647
+ }
648
+
649
+ const items = data.items || [];
650
+ if (!items.length) {
651
+ console.log(JSON.stringify({ error: 'No items to process', success: false }));
652
+ process.exit(1);
653
+ }
654
+
655
+ const client = new AgentClient();
656
+
657
+ // Map: process each item in parallel
658
+ // Replace with your actual dependency (must match manifest.dependencies)
659
+ const mapped = await Promise.all(
660
+ items.map(item => client.call('org/processor@v1', { item }))
661
+ );
662
+
663
+ // Reduce: aggregate results into a single output
664
+ // Replace with your actual dependency (must match manifest.dependencies)
665
+ const reduced = await client.call('org/aggregator@v1', { results: mapped });
666
+
667
+ console.log(JSON.stringify({
668
+ result: reduced,
669
+ items_processed: items.length,
670
+ success: true,
671
+ }));
672
+ }
673
+
674
+ main().catch(err => {
675
+ console.error(err);
676
+ process.exit(1);
677
+ });
678
+ `;
679
+ const FANOUT_SCHEMA = `{
680
+ "input": {
681
+ "type": "object",
682
+ "properties": {
683
+ "task": {
684
+ "type": "string",
685
+ "description": "The task to fan out to all agents"
686
+ }
687
+ },
688
+ "required": ["task"]
689
+ },
690
+ "output": {
691
+ "type": "object",
692
+ "properties": {
693
+ "results": {
694
+ "type": "array",
695
+ "description": "Results from each agent"
696
+ },
697
+ "summary": {
698
+ "type": "string",
699
+ "description": "Summary of combined results"
700
+ },
701
+ "success": {
702
+ "type": "boolean",
703
+ "description": "Whether all agents completed successfully"
704
+ }
705
+ },
706
+ "required": ["results", "success"]
707
+ }
708
+ }
709
+ `;
710
+ const PIPELINE_SCHEMA = `{
711
+ "input": {
712
+ "type": "object",
713
+ "properties": {
714
+ "task": {
715
+ "type": "string",
716
+ "description": "The input to feed into the pipeline"
717
+ }
718
+ },
719
+ "required": ["task"]
720
+ },
721
+ "output": {
722
+ "type": "object",
723
+ "properties": {
724
+ "result": {
725
+ "type": "object",
726
+ "description": "Final output from the last pipeline step"
727
+ },
728
+ "steps_completed": {
729
+ "type": "integer",
730
+ "description": "Number of pipeline steps completed"
731
+ },
732
+ "success": {
733
+ "type": "boolean",
734
+ "description": "Whether the pipeline completed successfully"
735
+ }
736
+ },
737
+ "required": ["result", "success"]
738
+ }
739
+ }
740
+ `;
741
+ const MAPREDUCE_SCHEMA = `{
742
+ "input": {
743
+ "type": "object",
744
+ "properties": {
745
+ "items": {
746
+ "type": "array",
747
+ "items": { "type": "string" },
748
+ "description": "Items to process in parallel"
749
+ }
750
+ },
751
+ "required": ["items"]
752
+ },
753
+ "output": {
754
+ "type": "object",
755
+ "properties": {
756
+ "result": {
757
+ "type": "object",
758
+ "description": "Aggregated result from all processed items"
759
+ },
760
+ "items_processed": {
761
+ "type": "integer",
762
+ "description": "Number of items processed"
763
+ },
764
+ "success": {
765
+ "type": "boolean",
766
+ "description": "Whether all items were processed successfully"
767
+ }
768
+ },
769
+ "required": ["result", "success"]
770
+ }
771
+ }
772
+ `;
396
773
  const AGENT_BUILDER_HINT = `\n Tip: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`;
397
- function readmeTemplate(agentName, flavor) {
774
+ function readmeTemplate(agentName, flavor, type) {
398
775
  if (flavor === 'support_agent') {
399
776
  return `# ${agentName}
400
777
 
@@ -556,14 +933,11 @@ Edit \`main.js\` to customize:
556
933
  | \`MAX_TOKENS\` | No | Max response tokens (default: 1024) |
557
934
  `;
558
935
  }
559
- const inputField = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'task' : 'input';
560
- const inputDescription = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'The task to perform' : 'The input to process';
561
- const cloudExample = flavor === 'code_runtime'
562
- ? `orchagent run ${agentName} --data '{"input": "Hello world"}'`
563
- : `orchagent run ${agentName} --data '{"${inputField}": "Hello world"}'`;
564
- const localExample = flavor === 'code_runtime'
565
- ? `orchagent run ${agentName} --local --data '{"input": "Hello world"}'`
566
- : `orchagent run ${agentName} --local --data '{"${inputField}": "Hello world"}'`;
936
+ const usesTask = flavor === 'managed_loop' || flavor === 'orchestrator' || type === 'agent';
937
+ const inputField = usesTask ? 'task' : 'input';
938
+ const inputDescription = usesTask ? 'The task to perform' : 'The input to process';
939
+ const cloudExample = `orchagent run ${agentName} --data '{"${inputField}": "Hello world"}'`;
940
+ const localExample = `orchagent run ${agentName} --local --data '{"${inputField}": "Hello world"}'`;
567
941
  let readme = `# ${agentName}
568
942
 
569
943
  A brief description of what this agent does.
@@ -605,10 +979,185 @@ This orchestrator calls other agents. Update \`manifest.dependencies\` in \`orch
605
979
  | Dependency | Version | Description |
606
980
  |------------|---------|-------------|
607
981
  | \`org/agent-name\` | v1 | TODO: describe what this agent does |
982
+ `;
983
+ }
984
+ if (flavor === 'fan_out') {
985
+ readme += `
986
+ ## Pattern: Fan-Out
987
+
988
+ This orchestrator calls multiple agents **in parallel** and combines their results. Use this when you have independent tasks that can run concurrently.
989
+
990
+ \`\`\`
991
+ Input ──┬──> Agent A ──┐
992
+ ├──> Agent B ──┼──> Combined Results
993
+ └──> Agent C ──┘
994
+ \`\`\`
995
+
996
+ ## Dependencies
997
+
998
+ Update \`manifest.dependencies\` in \`orchagent.json\` with your actual agents.
999
+
1000
+ **Publish order:** Publish dependency agents first, then this orchestrator.
1001
+
1002
+ | Dependency | Version | Description |
1003
+ |------------|---------|-------------|
1004
+ | \`org/agent-a\` | v1 | TODO: describe |
1005
+ | \`org/agent-b\` | v1 | TODO: describe |
1006
+ | \`org/agent-c\` | v1 | TODO: describe |
1007
+ `;
1008
+ }
1009
+ if (flavor === 'pipeline') {
1010
+ readme += `
1011
+ ## Pattern: Pipeline
1012
+
1013
+ This orchestrator calls agents **sequentially** — each step's output feeds into the next. Use this when data must flow through ordered processing stages.
1014
+
1015
+ \`\`\`
1016
+ Input ──> Parser ──> Analyzer ──> Reporter ──> Output
1017
+ \`\`\`
1018
+
1019
+ ## Dependencies
1020
+
1021
+ Update \`manifest.dependencies\` in \`orchagent.json\` with your actual agents.
1022
+
1023
+ **Publish order:** Publish dependency agents first, then this orchestrator.
1024
+
1025
+ | Dependency | Version | Description |
1026
+ |------------|---------|-------------|
1027
+ | \`org/parser\` | v1 | TODO: describe |
1028
+ | \`org/analyzer\` | v1 | TODO: describe |
1029
+ | \`org/reporter\` | v1 | TODO: describe |
1030
+ `;
1031
+ }
1032
+ if (flavor === 'map_reduce') {
1033
+ readme += `
1034
+ ## Pattern: Map-Reduce
1035
+
1036
+ This orchestrator **splits input** into items, processes each in **parallel** (map), then **aggregates** the results (reduce).
1037
+
1038
+ \`\`\`
1039
+ Items ──┬──> Processor (item 1) ──┐
1040
+ ├──> Processor (item 2) ──┼──> Aggregator ──> Output
1041
+ └──> Processor (item N) ──┘
1042
+ \`\`\`
1043
+
1044
+ ## Dependencies
1045
+
1046
+ Update \`manifest.dependencies\` in \`orchagent.json\` with your actual agents.
1047
+
1048
+ **Publish order:** Publish dependency agents first, then this orchestrator.
1049
+
1050
+ | Dependency | Version | Description |
1051
+ |------------|---------|-------------|
1052
+ | \`org/processor\` | v1 | Processes individual items |
1053
+ | \`org/aggregator\` | v1 | Combines processed results |
608
1054
  `;
609
1055
  }
610
1056
  return readme;
611
1057
  }
1058
+ const AGENT_CODE_TEMPLATE_PY = `"""
1059
+ orchagent agent entrypoint.
1060
+
1061
+ Reads JSON input from stdin, processes the task, and writes JSON output to stdout.
1062
+ This is a code-runtime agent — you control the logic and can call any LLM provider.
1063
+
1064
+ Usage:
1065
+ echo '{"task": "summarize this text"}' | python main.py
1066
+ """
1067
+
1068
+ import json
1069
+ import sys
1070
+
1071
+
1072
+ def main():
1073
+ # Read JSON input from stdin
1074
+ raw = sys.stdin.read()
1075
+ try:
1076
+ data = json.loads(raw) if raw.strip() else {}
1077
+ except json.JSONDecodeError:
1078
+ print(json.dumps({"error": "Invalid JSON input", "success": False}))
1079
+ sys.exit(1)
1080
+
1081
+ task = data.get("task", "")
1082
+
1083
+ # --- Your agent logic here ---
1084
+ # This is a code-runtime agent. You write the logic — call any LLM provider,
1085
+ # use any library, chain multiple steps, etc.
1086
+ #
1087
+ # Example (Anthropic):
1088
+ # pip install anthropic
1089
+ # import anthropic
1090
+ # client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
1091
+ # response = client.messages.create(model="claude-sonnet-4-5-20250929", ...)
1092
+ #
1093
+ # Example (OpenAI):
1094
+ # pip install openai
1095
+ # import openai
1096
+ # client = openai.OpenAI() # reads OPENAI_API_KEY from env
1097
+ # response = client.chat.completions.create(model="gpt-4o", ...)
1098
+ #
1099
+ # To use workspace secrets, add them to "required_secrets" in orchagent.json:
1100
+ # "required_secrets": ["ANTHROPIC_API_KEY"]
1101
+ # Then access via: os.environ["ANTHROPIC_API_KEY"]
1102
+ result = f"Received task: {task}"
1103
+ # --- End your logic ---
1104
+
1105
+ # Write JSON output to stdout
1106
+ print(json.dumps({"result": result, "success": True}))
1107
+
1108
+
1109
+ if __name__ == "__main__":
1110
+ main()
1111
+ `;
1112
+ const AGENT_CODE_TEMPLATE_JS = `/**
1113
+ * orchagent agent entrypoint.
1114
+ *
1115
+ * Reads JSON input from stdin, processes the task, and writes JSON output to stdout.
1116
+ * This is a code-runtime agent — you control the logic and can call any LLM provider.
1117
+ *
1118
+ * Usage:
1119
+ * echo '{"task": "summarize this text"}' | node main.js
1120
+ */
1121
+
1122
+ const fs = require('fs');
1123
+
1124
+ function main() {
1125
+ const raw = fs.readFileSync('/dev/stdin', 'utf-8');
1126
+ let data;
1127
+ try {
1128
+ data = raw.trim() ? JSON.parse(raw) : {};
1129
+ } catch {
1130
+ console.log(JSON.stringify({ error: 'Invalid JSON input', success: false }));
1131
+ process.exit(1);
1132
+ }
1133
+
1134
+ const task = data.task || '';
1135
+
1136
+ // --- Your agent logic here ---
1137
+ // This is a code-runtime agent. You write the logic — call any LLM provider,
1138
+ // use any library, chain multiple steps, etc.
1139
+ //
1140
+ // Example (Anthropic):
1141
+ // npm install @anthropic-ai/sdk
1142
+ // const Anthropic = require('@anthropic-ai/sdk');
1143
+ // const client = new Anthropic(); // reads ANTHROPIC_API_KEY from env
1144
+ //
1145
+ // Example (OpenAI):
1146
+ // npm install openai
1147
+ // const OpenAI = require('openai');
1148
+ // const client = new OpenAI(); // reads OPENAI_API_KEY from env
1149
+ //
1150
+ // To use workspace secrets, add them to "required_secrets" in orchagent.json:
1151
+ // "required_secrets": ["ANTHROPIC_API_KEY"]
1152
+ // Then access via: process.env.ANTHROPIC_API_KEY
1153
+ const result = \`Received task: \${task}\`;
1154
+ // --- End your logic ---
1155
+
1156
+ console.log(JSON.stringify({ result, success: true }));
1157
+ }
1158
+
1159
+ main();
1160
+ `;
612
1161
  const AGENT_PROMPT_TEMPLATE = `You are a helpful AI agent.
613
1162
 
614
1163
  Given the input, complete the task step by step.
@@ -939,7 +1488,7 @@ function resolveInitFlavor(typeOption) {
939
1488
  return { type: 'prompt', flavor: 'direct_llm' };
940
1489
  }
941
1490
  if (normalized === 'agent' || normalized === 'agentic') {
942
- return { type: 'agent', flavor: 'managed_loop' };
1491
+ return { type: 'agent', flavor: 'code_runtime' };
943
1492
  }
944
1493
  if (normalized === 'tool' || normalized === 'code') {
945
1494
  return { type: 'tool', flavor: 'code_runtime' };
@@ -955,7 +1504,8 @@ function registerInitCommand(program) {
955
1504
  .option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
956
1505
  .option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
957
1506
  .option('--language <lang>', 'Language: python or javascript (default: python)', 'python')
958
- .option('--template <name>', 'Start from a template (available: support-agent, discord, discord-js, github-weekly-summary)')
1507
+ .option('--loop', 'Use platform-managed LLM loop instead of code runtime (requires --type agent)')
1508
+ .option('--template <name>', 'Start from a template (available: fan-out, pipeline, map-reduce, support-agent, discord, discord-js, github-weekly-summary)')
959
1509
  .action(async (name, options) => {
960
1510
  const cwd = process.cwd();
961
1511
  let runMode = (options.runMode || 'on_demand').trim().toLowerCase();
@@ -969,25 +1519,18 @@ function registerInitCommand(program) {
969
1519
  }
970
1520
  initMode = { type: 'agent', flavor: 'orchestrator' };
971
1521
  }
972
- // Validate --language option
973
- const language = (options.language || 'python').trim().toLowerCase();
974
- if (!['python', 'javascript', 'js', 'typescript', 'ts'].includes(language)) {
975
- throw new errors_1.CliError(`Invalid --language '${options.language}'. Use 'python' or 'javascript'.`);
976
- }
977
- const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language);
978
- // Block unsupported JS flavors
979
- if (isJavaScript && initMode.flavor === 'managed_loop') {
980
- throw new errors_1.CliError('JavaScript agent-type agents are not yet supported. Use --type tool for JavaScript agents.');
981
- }
982
- // JS orchestrators are now supported via the orchagent-sdk npm package
983
- // Block --language for types that don't create runtime files
984
- if (isJavaScript && (initMode.type === 'prompt' || initMode.type === 'skill')) {
985
- throw new errors_1.CliError(`The --language flag has no effect for ${initMode.type} types (no runtime files are created). ` +
986
- 'Use --type tool or --type agent to create a project with runtime scaffolding.');
1522
+ if (options.loop) {
1523
+ if (options.orchestrator) {
1524
+ throw new errors_1.CliError('Cannot use --loop with --orchestrator. Orchestrators use code runtime with SDK calls.');
1525
+ }
1526
+ if (initMode.type !== 'agent') {
1527
+ throw new errors_1.CliError('The --loop flag requires --type agent. It enables platform-managed LLM loop execution.');
1528
+ }
1529
+ initMode = { type: 'agent', flavor: 'managed_loop' };
987
1530
  }
988
1531
  if (options.template) {
989
1532
  const template = options.template.trim().toLowerCase();
990
- const validTemplates = ['support-agent', 'discord', 'discord-js', 'github-weekly-summary'];
1533
+ const validTemplates = ['fan-out', 'pipeline', 'map-reduce', 'support-agent', 'discord', 'discord-js', 'github-weekly-summary'];
991
1534
  if (!validTemplates.includes(template)) {
992
1535
  throw new errors_1.CliError(`Unknown --template '${template}'. Available templates: ${validTemplates.join(', ')}`);
993
1536
  }
@@ -997,7 +1540,16 @@ function registerInitCommand(program) {
997
1540
  if (initMode.type === 'skill') {
998
1541
  throw new errors_1.CliError('Cannot use --template with --type skill.');
999
1542
  }
1000
- if (template === 'support-agent') {
1543
+ if (template === 'fan-out') {
1544
+ initMode = { type: 'agent', flavor: 'fan_out' };
1545
+ }
1546
+ else if (template === 'pipeline') {
1547
+ initMode = { type: 'agent', flavor: 'pipeline' };
1548
+ }
1549
+ else if (template === 'map-reduce') {
1550
+ initMode = { type: 'agent', flavor: 'map_reduce' };
1551
+ }
1552
+ else if (template === 'support-agent') {
1001
1553
  initMode = { type: 'agent', flavor: 'support_agent' };
1002
1554
  runMode = 'always_on';
1003
1555
  }
@@ -1013,6 +1565,22 @@ function registerInitCommand(program) {
1013
1565
  initMode = { type: 'agent', flavor: 'github_weekly_summary' };
1014
1566
  }
1015
1567
  }
1568
+ // Validate --language option
1569
+ const language = (options.language || 'python').trim().toLowerCase();
1570
+ if (!['python', 'javascript', 'js', 'typescript', 'ts'].includes(language)) {
1571
+ throw new errors_1.CliError(`Invalid --language '${options.language}'. Use 'python' or 'javascript'.`);
1572
+ }
1573
+ const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language);
1574
+ // Block unsupported JS flavors
1575
+ if (isJavaScript && initMode.flavor === 'managed_loop') {
1576
+ throw new errors_1.CliError('JavaScript is not supported for --loop (managed loop). Use --type agent without --loop for a code-runtime agent.');
1577
+ }
1578
+ // JS orchestrators are now supported via the orchagent-sdk npm package
1579
+ // Block --language for types that don't create runtime files
1580
+ if (isJavaScript && (initMode.type === 'prompt' || initMode.type === 'skill')) {
1581
+ throw new errors_1.CliError(`The --language flag has no effect for ${initMode.type} types (no runtime files are created). ` +
1582
+ 'Use --type tool or --type agent to create a project with runtime scaffolding.');
1583
+ }
1016
1584
  // When a name is provided, create a subdirectory for the project
1017
1585
  const targetDir = name ? path_1.default.join(cwd, name) : cwd;
1018
1586
  const agentName = name || path_1.default.basename(cwd);
@@ -1230,6 +1798,121 @@ function registerInitCommand(program) {
1230
1798
  process.stdout.write(AGENT_BUILDER_HINT);
1231
1799
  return;
1232
1800
  }
1801
+ // Handle orchestration templates (fan-out, pipeline, map-reduce)
1802
+ if (initMode.flavor === 'fan_out' || initMode.flavor === 'pipeline' || initMode.flavor === 'map_reduce') {
1803
+ const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
1804
+ try {
1805
+ await promises_1.default.access(manifestPath);
1806
+ throw new errors_1.CliError(`Already initialized (orchagent.json exists in ${name ? name + '/' : 'current directory'})`);
1807
+ }
1808
+ catch (err) {
1809
+ if (err.code !== 'ENOENT') {
1810
+ throw err;
1811
+ }
1812
+ }
1813
+ // Build dependencies based on template
1814
+ let dependencies;
1815
+ let mainPy;
1816
+ let mainJs;
1817
+ let schema;
1818
+ let templateLabel;
1819
+ let maxHops;
1820
+ if (initMode.flavor === 'fan_out') {
1821
+ dependencies = [
1822
+ { id: 'org/agent-a', version: 'v1' },
1823
+ { id: 'org/agent-b', version: 'v1' },
1824
+ { id: 'org/agent-c', version: 'v1' },
1825
+ ];
1826
+ mainPy = FANOUT_MAIN_PY;
1827
+ mainJs = FANOUT_MAIN_JS;
1828
+ schema = FANOUT_SCHEMA;
1829
+ templateLabel = 'fan-out';
1830
+ maxHops = 2;
1831
+ }
1832
+ else if (initMode.flavor === 'pipeline') {
1833
+ dependencies = [
1834
+ { id: 'org/parser', version: 'v1' },
1835
+ { id: 'org/analyzer', version: 'v1' },
1836
+ { id: 'org/reporter', version: 'v1' },
1837
+ ];
1838
+ mainPy = PIPELINE_MAIN_PY;
1839
+ mainJs = PIPELINE_MAIN_JS;
1840
+ schema = PIPELINE_SCHEMA;
1841
+ templateLabel = 'pipeline';
1842
+ maxHops = 2;
1843
+ }
1844
+ else {
1845
+ dependencies = [
1846
+ { id: 'org/processor', version: 'v1' },
1847
+ { id: 'org/aggregator', version: 'v1' },
1848
+ ];
1849
+ mainPy = MAPREDUCE_MAIN_PY;
1850
+ mainJs = MAPREDUCE_MAIN_JS;
1851
+ schema = MAPREDUCE_SCHEMA;
1852
+ templateLabel = 'map-reduce';
1853
+ maxHops = 2;
1854
+ }
1855
+ const manifest = {
1856
+ name: agentName,
1857
+ type: 'agent',
1858
+ description: `A ${templateLabel} orchestrator agent`,
1859
+ run_mode: runMode,
1860
+ runtime: { command: isJavaScript ? 'node main.js' : 'python main.py' },
1861
+ manifest: {
1862
+ manifest_version: 1,
1863
+ dependencies,
1864
+ max_hops: maxHops,
1865
+ timeout_ms: 120000,
1866
+ per_call_downstream_cap: 50,
1867
+ },
1868
+ required_secrets: [],
1869
+ };
1870
+ if (isJavaScript) {
1871
+ manifest.entrypoint = 'main.js';
1872
+ }
1873
+ await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
1874
+ if (isJavaScript) {
1875
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), mainJs);
1876
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), ORCHESTRATOR_PACKAGE_JSON);
1877
+ }
1878
+ else {
1879
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), mainPy);
1880
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'requirements.txt'), ORCHESTRATOR_REQUIREMENTS);
1881
+ }
1882
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'schema.json'), schema);
1883
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), readmeTemplate(agentName, initMode.flavor));
1884
+ const prefix = name ? name + '/' : '';
1885
+ process.stdout.write(`Initialized ${templateLabel} orchestrator "${agentName}" in ${targetDir}\n`);
1886
+ process.stdout.write(`\nFiles created:\n`);
1887
+ process.stdout.write(` ${prefix}orchagent.json - Agent configuration (${templateLabel} pattern)\n`);
1888
+ if (isJavaScript) {
1889
+ process.stdout.write(` ${prefix}main.js - ${templateLabel} orchestrator entrypoint\n`);
1890
+ process.stdout.write(` ${prefix}package.json - npm dependencies (orchagent-sdk)\n`);
1891
+ }
1892
+ else {
1893
+ process.stdout.write(` ${prefix}main.py - ${templateLabel} orchestrator entrypoint\n`);
1894
+ process.stdout.write(` ${prefix}requirements.txt - Python dependencies (orchagent-sdk)\n`);
1895
+ }
1896
+ process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
1897
+ process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
1898
+ process.stdout.write(` Run mode: ${runMode}\n`);
1899
+ process.stdout.write(` Execution: code_runtime (${templateLabel})\n`);
1900
+ process.stdout.write(`\nNext steps:\n`);
1901
+ const stepNum = name ? 2 : 1;
1902
+ if (name) {
1903
+ process.stdout.write(` 1. cd ${name}\n`);
1904
+ }
1905
+ process.stdout.write(` ${stepNum}. Update manifest.dependencies in orchagent.json with your actual agents\n`);
1906
+ if (isJavaScript) {
1907
+ process.stdout.write(` ${stepNum + 1}. Edit main.js with your orchestration logic\n`);
1908
+ }
1909
+ else {
1910
+ process.stdout.write(` ${stepNum + 1}. Edit main.py with your orchestration logic\n`);
1911
+ }
1912
+ process.stdout.write(` ${stepNum + 2}. Publish dependency agents first, then: orchagent publish\n`);
1913
+ process.stdout.write(AGENT_BUILDER_HINT);
1914
+ return;
1915
+ }
1233
1916
  const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
1234
1917
  const promptPath = path_1.default.join(targetDir, 'prompt.md');
1235
1918
  const schemaPath = path_1.default.join(targetDir, 'schema.json');
@@ -1270,8 +1953,8 @@ function registerInitCommand(program) {
1270
1953
  manifest.required_secrets = [];
1271
1954
  }
1272
1955
  else if (initMode.flavor === 'managed_loop') {
1273
- manifest.description = 'An AI agent with tool use';
1274
- manifest.supported_providers = ['anthropic'];
1956
+ manifest.description = 'An AI agent with tool use (managed loop)';
1957
+ manifest.supported_providers = ['any'];
1275
1958
  manifest.loop = { max_turns: 25 };
1276
1959
  manifest.required_secrets = [];
1277
1960
  }
@@ -1283,7 +1966,7 @@ function registerInitCommand(program) {
1283
1966
  manifest.tags = ['discord', 'always-on'];
1284
1967
  }
1285
1968
  else if (initMode.flavor === 'code_runtime') {
1286
- manifest.description = 'A code-runtime agent';
1969
+ manifest.description = initMode.type === 'agent' ? 'An AI agent' : 'A code-runtime tool';
1287
1970
  if (isJavaScript) {
1288
1971
  manifest.runtime = { command: 'node main.js' };
1289
1972
  manifest.entrypoint = 'main.js';
@@ -1314,8 +1997,11 @@ function registerInitCommand(program) {
1314
1997
  await promises_1.default.writeFile(envExamplePath, DISCORD_ENV_EXAMPLE);
1315
1998
  }
1316
1999
  else if (initMode.flavor === 'code_runtime') {
2000
+ const isAgent = initMode.type === 'agent';
1317
2001
  if (isJavaScript) {
1318
- await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_JS : CODE_TEMPLATE_JS);
2002
+ const template = runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_JS
2003
+ : isAgent ? AGENT_CODE_TEMPLATE_JS : CODE_TEMPLATE_JS;
2004
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), template);
1319
2005
  await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), JSON.stringify({
1320
2006
  name: agentName,
1321
2007
  private: true,
@@ -1324,9 +2010,11 @@ function registerInitCommand(program) {
1324
2010
  }, null, 2) + '\n');
1325
2011
  }
1326
2012
  else {
1327
- await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_PY : CODE_TEMPLATE_PY);
2013
+ const template = runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_PY
2014
+ : isAgent ? AGENT_CODE_TEMPLATE_PY : CODE_TEMPLATE_PY;
2015
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), template);
1328
2016
  }
1329
- await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
2017
+ await promises_1.default.writeFile(schemaPath, isAgent ? AGENT_SCHEMA_TEMPLATE : SCHEMA_TEMPLATE);
1330
2018
  }
1331
2019
  else if (initMode.flavor === 'managed_loop') {
1332
2020
  await promises_1.default.writeFile(promptPath, AGENT_PROMPT_TEMPLATE);
@@ -1338,7 +2026,7 @@ function registerInitCommand(program) {
1338
2026
  }
1339
2027
  // Create README
1340
2028
  const readmePath = path_1.default.join(targetDir, 'README.md');
1341
- await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, initMode.flavor || 'direct_llm'));
2029
+ await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, initMode.flavor || 'direct_llm', initMode.type));
1342
2030
  process.stdout.write(`Initialized agent "${agentName}" in ${targetDir}\n`);
1343
2031
  process.stdout.write(`\nFiles created:\n`);
1344
2032
  const prefix = name ? name + '/' : '';
@@ -1359,7 +2047,8 @@ function registerInitCommand(program) {
1359
2047
  process.stdout.write(` ${prefix}.env.example - Environment variables template\n`);
1360
2048
  }
1361
2049
  else if (initMode.flavor === 'code_runtime') {
1362
- const entrypointDesc = runMode === 'always_on' ? 'Always-on HTTP server' : 'Agent entrypoint (stdin/stdout JSON)';
2050
+ const entrypointDesc = runMode === 'always_on' ? 'Always-on HTTP server'
2051
+ : initMode.type === 'agent' ? 'Agent entrypoint (your code)' : 'Tool entrypoint (stdin/stdout JSON)';
1363
2052
  if (isJavaScript) {
1364
2053
  process.stdout.write(` ${prefix}main.js - ${entrypointDesc}\n`);
1365
2054
  process.stdout.write(` ${prefix}package.json - npm dependencies\n`);
@@ -1417,15 +2106,17 @@ function registerInitCommand(program) {
1417
2106
  process.stdout.write(` ${stepNum + 3}. Deploy: orch service deploy\n`);
1418
2107
  }
1419
2108
  else if (isJavaScript) {
2109
+ const inputField = initMode.type === 'agent' ? 'task' : 'input';
1420
2110
  process.stdout.write(` ${stepNum}. Edit main.js with your agent logic\n`);
1421
2111
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
1422
- process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | node main.js\n`);
2112
+ process.stdout.write(` ${stepNum + 2}. Test: echo '{"${inputField}": "test"}' | node main.js\n`);
1423
2113
  process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
1424
2114
  }
1425
2115
  else {
2116
+ const inputField = initMode.type === 'agent' ? 'task' : 'input';
1426
2117
  process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
1427
2118
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
1428
- process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | python main.py\n`);
2119
+ process.stdout.write(` ${stepNum + 2}. Test: echo '{"${inputField}": "test"}' | python main.py\n`);
1429
2120
  process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
1430
2121
  }
1431
2122
  }