swarm-code 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/dist/interactive.js +74 -33
  2. package/package.json +1 -1
@@ -478,41 +478,39 @@ function checkboxSelect(items) {
478
478
  }
479
479
  };
480
480
  let rendered = false;
481
+ process.stdout.write("\x1b[?25l"); // Hide cursor
481
482
  render();
482
483
  rendered = true;
483
484
  const wasRaw = stdin.isRaw;
484
485
  if (stdin.isTTY)
485
486
  stdin.setRawMode(true);
487
+ const cleanup = () => {
488
+ stdin.removeListener("data", onData);
489
+ if (stdin.isTTY)
490
+ stdin.setRawMode(wasRaw ?? false);
491
+ process.stdout.write("\x1b[?25h"); // Show cursor
492
+ };
486
493
  const onData = (data) => {
487
494
  const key = data.toString();
488
495
  if (key === "\x1b[A" || key === "k") {
489
- // Up
490
496
  cursor = (cursor - 1 + items.length) % items.length;
491
497
  render();
492
498
  }
493
499
  else if (key === "\x1b[B" || key === "j") {
494
- // Down
495
500
  cursor = (cursor + 1) % items.length;
496
501
  render();
497
502
  }
498
503
  else if (key === " ") {
499
- // Toggle
500
504
  items[cursor].checked = !items[cursor].checked;
501
505
  render();
502
506
  }
503
507
  else if (key === "\r" || key === "\n") {
504
- // Confirm
505
- stdin.removeListener("data", onData);
506
- if (stdin.isTTY)
507
- stdin.setRawMode(wasRaw ?? false);
508
+ cleanup();
508
509
  const selected = items.map((item, i) => (item.checked ? i : -1)).filter((i) => i >= 0);
509
510
  resolve(selected);
510
511
  }
511
512
  else if (key === "\x1b" || key === "\x03") {
512
- // Escape or Ctrl+C
513
- stdin.removeListener("data", onData);
514
- if (stdin.isTTY)
515
- stdin.setRawMode(wasRaw ?? false);
513
+ cleanup();
516
514
  resolve([]);
517
515
  }
518
516
  };
@@ -1584,30 +1582,73 @@ async function interactive() {
1584
1582
  console.log();
1585
1583
  }
1586
1584
  else {
1587
- // Agent works without keys (e.g. OpenCode) — set up Ollama directly
1588
- console.log(` ${c.bold}${agent.name}${c.reset} ${c.dim}— setting up Ollama for local models:${c.reset}\n`);
1589
- const ok = await ensureOllamaSetup(setupRl, "ollama/deepseek-coder-v2");
1590
- if (ok)
1591
- usesOllama = true;
1585
+ // Agent works without keys (e.g. OpenCode) — choose backend
1586
+ console.log(` ${c.bold}${agent.name}${c.reset} ${c.dim}— choose your backend:${c.reset}\n`);
1587
+ console.log(` ${c.dim}1${c.reset} Ollama ${c.dim}Run models locally (free, requires download)${c.reset}`);
1588
+ console.log(` ${c.dim}2${c.reset} OpenRouter ${c.dim}Cloud API for 200+ models (requires API key)${c.reset}`);
1589
+ console.log();
1590
+ const backendChoice = await questionWithEsc(setupRl, ` ${c.cyan}Backend [1-2]:${c.reset} `);
1591
+ const pickedOpenRouter = backendChoice !== null && backendChoice.trim() === "2";
1592
+ if (pickedOpenRouter) {
1593
+ // OpenRouter setup
1594
+ console.log();
1595
+ const orKey = await questionWithEsc(setupRl, ` ${c.cyan}OPENROUTER_API_KEY:${c.reset} `);
1596
+ if (orKey?.trim()) {
1597
+ process.env.OPENROUTER_API_KEY = orKey.trim();
1598
+ try {
1599
+ const envPath = path.join(process.cwd(), ".env");
1600
+ let envContent = "";
1601
+ try {
1602
+ envContent = fs.readFileSync(envPath, "utf-8");
1603
+ }
1604
+ catch { }
1605
+ if (!envContent.includes("OPENROUTER_API_KEY")) {
1606
+ fs.appendFileSync(envPath, `\nOPENROUTER_API_KEY=${orKey.trim()}\n`);
1607
+ }
1608
+ console.log(` ${c.green}✓${c.reset} OpenRouter configured`);
1609
+ }
1610
+ catch {
1611
+ console.log(` ${c.green}✓${c.reset} OpenRouter key set for this session`);
1612
+ }
1613
+ currentModelId = "openrouter/auto";
1614
+ saveModelPreference(currentModelId);
1615
+ }
1616
+ else {
1617
+ console.log(` ${c.dim}No key provided — you can set OPENROUTER_API_KEY in .env later${c.reset}`);
1618
+ }
1619
+ }
1620
+ else {
1621
+ // Ollama setup
1622
+ console.log();
1623
+ const ok = await ensureOllamaSetup(setupRl, "ollama/deepseek-coder-v2");
1624
+ if (ok)
1625
+ usesOllama = true;
1626
+ }
1592
1627
  console.log();
1593
1628
  }
1594
1629
  }
1595
1630
  // ── Step 3: Set default model ────────────────────────────────
1596
- const activeProvider = Object.keys(PROVIDER_KEYS).find((p) => process.env[providerEnvKey(p)]);
1597
- if (activeProvider) {
1598
- currentProviderName = activeProvider;
1599
- const defaultModel = getDefaultModelForProvider(activeProvider);
1600
- if (defaultModel) {
1601
- currentModelId = defaultModel;
1631
+ if (currentModelId.startsWith("openrouter/")) {
1632
+ // Already set during OpenRouter setup
1633
+ console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset} ${c.dim}(OpenRouter)${c.reset}`);
1634
+ }
1635
+ else {
1636
+ const activeProvider = Object.keys(PROVIDER_KEYS).find((p) => process.env[providerEnvKey(p)]);
1637
+ if (activeProvider) {
1638
+ currentProviderName = activeProvider;
1639
+ const defaultModel = getDefaultModelForProvider(activeProvider);
1640
+ if (defaultModel) {
1641
+ currentModelId = defaultModel;
1642
+ saveModelPreference(currentModelId);
1643
+ console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset}`);
1644
+ }
1645
+ }
1646
+ else if (usesOllama) {
1647
+ currentModelId = "ollama/deepseek-coder-v2";
1602
1648
  saveModelPreference(currentModelId);
1603
- console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset}`);
1649
+ console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset} ${c.dim}(local)${c.reset}`);
1604
1650
  }
1605
1651
  }
1606
- else if (usesOllama) {
1607
- currentModelId = "ollama/deepseek-coder-v2";
1608
- saveModelPreference(currentModelId);
1609
- console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset} ${c.dim}(local)${c.reset}`);
1610
- }
1611
1652
  console.log();
1612
1653
  setupRl.close();
1613
1654
  }
@@ -1639,12 +1680,12 @@ async function interactive() {
1639
1680
  }
1640
1681
  }
1641
1682
  if (!currentModel) {
1642
- if (currentModelId.startsWith("ollama/")) {
1643
- // Ollama model selected this interactive REPL mode needs a cloud API.
1644
- // Redirect to swarm mode which works with OpenCode + Ollama.
1645
- console.log(`\n ${c.green}✓${c.reset} Ollama model selected: ${c.bold}${currentModelId}${c.reset}`);
1683
+ if (currentModelId.startsWith("ollama/") || currentModelId.startsWith("openrouter/")) {
1684
+ // Non-pi-ai model — redirect to swarm mode which uses OpenCode natively.
1685
+ const backend = currentModelId.startsWith("ollama/") ? "Ollama" : "OpenRouter";
1686
+ console.log(`\n ${c.green}✓${c.reset} ${backend} model selected: ${c.bold}${currentModelId}${c.reset}`);
1646
1687
  console.log(`\n ${c.dim}This interactive REPL uses direct LLM API calls.${c.reset}`);
1647
- console.log(` ${c.dim}To use Ollama models with OpenCode, run:${c.reset}\n`);
1688
+ console.log(` ${c.dim}To use ${backend} with OpenCode, run:${c.reset}\n`);
1648
1689
  console.log(` ${c.bold}swarm --dir ./your-project "your task"${c.reset}\n`);
1649
1690
  process.exit(0);
1650
1691
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swarm-code",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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": {