swarm-code 0.1.9 → 0.1.11

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.
@@ -244,6 +244,7 @@ function cmdHelp() {
244
244
  out.write(` ${cyan("/dag")} ${dim("Show thread DAG with status indicators")}\n`);
245
245
  out.write(` ${cyan("/budget")} ${dim("Show budget state")}\n`);
246
246
  out.write(` ${cyan("/status")} ${dim("Overall session status")}\n`);
247
+ out.write(` ${cyan("/configure")} ${dim("(/c)")} ${dim("Change agent, model, or backend")}\n`);
247
248
  out.write(` ${cyan("/help")} ${dim("Show this help")}\n`);
248
249
  out.write(` ${cyan("/quit")} ${dim("(/exit)")} ${dim("Cleanup and exit")}\n`);
249
250
  out.write("\n");
@@ -536,6 +537,138 @@ function cmdStatus(threadManager, sessionStartTime, taskCount) {
536
537
  }
537
538
  out.write("\n");
538
539
  }
540
+ // ── Configure command ───────────────────────────────────────────────────────
541
+ async function cmdConfigure(config, resolved, rl) {
542
+ const out = process.stderr;
543
+ const ask = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
544
+ out.write("\n");
545
+ out.write(` ${bold(cyan("Configuration"))}\n`);
546
+ out.write(` ${dim(symbols.horizontal.repeat(40))}\n\n`);
547
+ out.write(` ${dim("Current settings:")}\n`);
548
+ out.write(` ${cyan("1")} Agent ${bold(config.default_agent)}\n`);
549
+ const displayModel = resolved.provider === "ollama"
550
+ ? `ollama/${resolved.model.id}`
551
+ : resolved.provider === "openrouter"
552
+ ? `openrouter/${resolved.model.id}`
553
+ : resolved.model.id;
554
+ out.write(` ${cyan("2")} Model ${bold(displayModel)}${displayModel !== config.default_model ? dim(` (config: ${config.default_model})`) : ""}\n`);
555
+ out.write(` ${cyan("3")} Max threads ${bold(String(config.max_threads))}\n`);
556
+ out.write(` ${cyan("4")} Auto routing ${bold(config.auto_model_selection ? "on" : "off")}\n`);
557
+ out.write(` ${cyan("5")} Session budget ${bold(`$${config.max_session_budget_usd.toFixed(2)}`)}\n`);
558
+ out.write(` ${cyan("6")} Thread budget ${bold(`$${config.max_thread_budget_usd.toFixed(2)}`)}\n`);
559
+ out.write(` ${cyan("7")} Compression ${bold(config.compression_strategy)}\n`);
560
+ out.write("\n");
561
+ const choice = await ask(` ${coral(symbols.arrow)} Setting to change [1-7, or enter to cancel]: `);
562
+ if (!choice) {
563
+ out.write(` ${dim("No changes made.")}\n\n`);
564
+ return;
565
+ }
566
+ switch (choice) {
567
+ case "1": {
568
+ const agents = (await import("./agents/provider.js")).listAgents();
569
+ out.write(`\n ${dim("Available agents:")} ${agents.join(", ")}\n`);
570
+ const val = await ask(` ${coral(symbols.arrow)} New agent [${config.default_agent}]: `);
571
+ if (val && agents.includes(val)) {
572
+ config.default_agent = val;
573
+ logSuccess(`Agent set to ${bold(val)}`);
574
+ }
575
+ else if (val) {
576
+ logWarn(`Unknown agent "${val}"`);
577
+ }
578
+ break;
579
+ }
580
+ case "2": {
581
+ out.write(`\n ${dim("Enter model ID (e.g. ollama/deepseek-coder-v2, anthropic/claude-sonnet-4-6, openrouter/auto)")}\n`);
582
+ const val = await ask(` ${coral(symbols.arrow)} New model [${displayModel}]: `);
583
+ if (val) {
584
+ // Check for OpenRouter API key
585
+ if (val.startsWith("openrouter/") && !process.env.OPENROUTER_API_KEY) {
586
+ out.write(`\n ${dim("OpenRouter requires an API key (https://openrouter.ai/keys)")}\n`);
587
+ const key = await ask(` ${coral(symbols.arrow)} OPENROUTER_API_KEY: `);
588
+ if (key) {
589
+ process.env.OPENROUTER_API_KEY = key;
590
+ logSuccess("OpenRouter API key set for this session");
591
+ }
592
+ else {
593
+ logWarn("No API key provided — cannot use OpenRouter");
594
+ break;
595
+ }
596
+ }
597
+ const lookupId = val.startsWith("ollama/") || val.startsWith("openrouter/")
598
+ ? val
599
+ : val.replace(/^(anthropic|openai|google)\//, "");
600
+ const newResolved = resolveModel(lookupId, logWarn);
601
+ if (newResolved) {
602
+ config.default_model = val;
603
+ resolved.model = newResolved.model;
604
+ resolved.provider = newResolved.provider;
605
+ logSuccess(`Model set to ${bold(val)}`);
606
+ }
607
+ else {
608
+ logWarn(`Could not resolve model "${val}"`);
609
+ }
610
+ }
611
+ break;
612
+ }
613
+ case "3": {
614
+ const val = await ask(` ${coral(symbols.arrow)} Max concurrent threads [${config.max_threads}]: `);
615
+ const n = parseInt(val, 10);
616
+ if (n >= 1 && n <= 20) {
617
+ config.max_threads = n;
618
+ logSuccess(`Max threads set to ${bold(String(n))}`);
619
+ }
620
+ else if (val) {
621
+ logWarn("Must be between 1 and 20");
622
+ }
623
+ break;
624
+ }
625
+ case "4": {
626
+ config.auto_model_selection = !config.auto_model_selection;
627
+ logSuccess(`Auto routing ${bold(config.auto_model_selection ? "enabled" : "disabled")}`);
628
+ break;
629
+ }
630
+ case "5": {
631
+ const val = await ask(` ${coral(symbols.arrow)} Session budget USD [${config.max_session_budget_usd}]: `);
632
+ const n = parseFloat(val);
633
+ if (Number.isFinite(n) && n > 0) {
634
+ config.max_session_budget_usd = n;
635
+ logSuccess(`Session budget set to ${bold(`$${n.toFixed(2)}`)}`);
636
+ }
637
+ else if (val) {
638
+ logWarn("Must be a positive number");
639
+ }
640
+ break;
641
+ }
642
+ case "6": {
643
+ const val = await ask(` ${coral(symbols.arrow)} Per-thread budget USD [${config.max_thread_budget_usd}]: `);
644
+ const n = parseFloat(val);
645
+ if (Number.isFinite(n) && n > 0) {
646
+ config.max_thread_budget_usd = n;
647
+ logSuccess(`Thread budget set to ${bold(`$${n.toFixed(2)}`)}`);
648
+ }
649
+ else if (val) {
650
+ logWarn("Must be a positive number");
651
+ }
652
+ break;
653
+ }
654
+ case "7": {
655
+ const strategies = ["structured", "llm-summary", "diff-only", "truncate"];
656
+ out.write(`\n ${dim("Options:")} ${strategies.join(", ")}\n`);
657
+ const val = await ask(` ${coral(symbols.arrow)} Compression [${config.compression_strategy}]: `);
658
+ if (val && strategies.includes(val)) {
659
+ config.compression_strategy = val;
660
+ logSuccess(`Compression set to ${bold(val)}`);
661
+ }
662
+ else if (val) {
663
+ logWarn(`Unknown strategy "${val}"`);
664
+ }
665
+ break;
666
+ }
667
+ default:
668
+ logWarn("Invalid option");
669
+ }
670
+ out.write("\n");
671
+ }
539
672
  // ── Interactive banner ──────────────────────────────────────────────────────
540
673
  function renderInteractiveBanner(config) {
541
674
  const w = Math.max(Math.min(termWidth(), 60), 24);
@@ -932,6 +1065,11 @@ export async function runInteractiveSwarm(rawArgs) {
932
1065
  case "/s":
933
1066
  cmdStatus(threadManager, sessionStartTime, taskCount);
934
1067
  break;
1068
+ case "/configure":
1069
+ case "/config":
1070
+ case "/c":
1071
+ await cmdConfigure(config, resolved, rl);
1072
+ break;
935
1073
  case "/quit":
936
1074
  case "/exit":
937
1075
  case "/q":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swarm-code",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Open-source swarm-native coding agent orchestrator — spawns parallel coding agents in isolated git worktrees, built on RLM (arXiv:2512.24601)",
5
5
  "type": "module",
6
6
  "bin": {