swarm-code 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/interactive.js +283 -70
- package/package.json +1 -1
package/dist/interactive.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - Browse previous trajectories
|
|
9
9
|
*/
|
|
10
10
|
import "./env.js";
|
|
11
|
+
import { execFileSync, execSync, spawn as spawnChild } from "node:child_process";
|
|
11
12
|
import * as fs from "node:fs";
|
|
12
13
|
import * as os from "node:os";
|
|
13
14
|
import * as path from "node:path";
|
|
@@ -306,6 +307,219 @@ function handleMultiLineAsContext(input) {
|
|
|
306
307
|
return null;
|
|
307
308
|
}
|
|
308
309
|
// ── Banner ──────────────────────────────────────────────────────────────────
|
|
310
|
+
// ── Ollama helpers ──────────────────────────────────────────────────────────
|
|
311
|
+
function isOllamaInstalled() {
|
|
312
|
+
try {
|
|
313
|
+
execFileSync("ollama", ["--version"], { stdio: ["ignore", "pipe", "pipe"], timeout: 5000 });
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function isOllamaModelAvailable(model) {
|
|
321
|
+
try {
|
|
322
|
+
const output = execFileSync("ollama", ["list"], {
|
|
323
|
+
encoding: "utf-8",
|
|
324
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
325
|
+
timeout: 10000,
|
|
326
|
+
});
|
|
327
|
+
return output.includes(model);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function installOllama() {
|
|
334
|
+
console.log(`\n ${c.bold}Installing Ollama...${c.reset}\n`);
|
|
335
|
+
if (process.platform === "darwin") {
|
|
336
|
+
// macOS: check for brew first, otherwise use the install script
|
|
337
|
+
try {
|
|
338
|
+
execFileSync("brew", ["--version"], { stdio: "ignore", timeout: 5000 });
|
|
339
|
+
console.log(` ${c.dim}Using Homebrew...${c.reset}`);
|
|
340
|
+
try {
|
|
341
|
+
execSync("brew install ollama", { stdio: "inherit", timeout: 120000 });
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
console.log(` ${c.dim}Homebrew install failed, trying curl installer...${c.reset}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
// No brew — fall through to curl
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Linux / macOS fallback: official install script
|
|
353
|
+
if (process.platform === "linux" || process.platform === "darwin") {
|
|
354
|
+
try {
|
|
355
|
+
execSync("curl -fsSL https://ollama.com/install.sh | sh", { stdio: "inherit", timeout: 180000 });
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Windows: direct user to download page
|
|
363
|
+
console.log(` ${c.dim}Download Ollama from: https://ollama.com/download${c.reset}`);
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
function isOllamaServing() {
|
|
367
|
+
try {
|
|
368
|
+
execFileSync("curl", ["-sf", "http://127.0.0.1:11434/api/tags"], {
|
|
369
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
370
|
+
timeout: 3000,
|
|
371
|
+
});
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function startOllamaServe() {
|
|
379
|
+
// Start ollama serve in the background (detached)
|
|
380
|
+
const child = spawnChild("ollama", ["serve"], {
|
|
381
|
+
stdio: "ignore",
|
|
382
|
+
detached: true,
|
|
383
|
+
});
|
|
384
|
+
child.unref();
|
|
385
|
+
}
|
|
386
|
+
async function pullOllamaModel(model) {
|
|
387
|
+
console.log(`\n ${c.bold}Pulling ${model}...${c.reset} ${c.dim}(this may take a few minutes)${c.reset}\n`);
|
|
388
|
+
return new Promise((resolve) => {
|
|
389
|
+
const child = spawnChild("ollama", ["pull", model], { stdio: "inherit" });
|
|
390
|
+
child.on("close", (code) => resolve(code === 0));
|
|
391
|
+
child.on("error", () => resolve(false));
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
async function ensureOllamaSetup(rl, model) {
|
|
395
|
+
const shortModel = model.replace("ollama/", "");
|
|
396
|
+
// 1. Check if Ollama is installed
|
|
397
|
+
if (!isOllamaInstalled()) {
|
|
398
|
+
console.log(`\n ${c.dim}Ollama is not installed. It's needed to run open-source models locally.${c.reset}`);
|
|
399
|
+
const install = await questionWithEsc(rl, ` ${c.cyan}Install Ollama now? [Y/n]:${c.reset} `);
|
|
400
|
+
if (install !== null && install.toLowerCase() !== "n" && install.toLowerCase() !== "no") {
|
|
401
|
+
const ok = await installOllama();
|
|
402
|
+
if (!ok) {
|
|
403
|
+
console.log(`\n ${c.red}Failed to install Ollama.${c.reset}`);
|
|
404
|
+
console.log(` ${c.dim}Install manually from https://ollama.com/download${c.reset}\n`);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
console.log(` ${c.green}✓${c.reset} Ollama installed`);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
console.log(`\n ${c.dim}Install later from https://ollama.com/download${c.reset}\n`);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
console.log(` ${c.green}✓${c.reset} Ollama installed`);
|
|
416
|
+
}
|
|
417
|
+
// 2. Ensure ollama serve is running
|
|
418
|
+
if (!isOllamaServing()) {
|
|
419
|
+
console.log(` ${c.dim}Starting Ollama server...${c.reset}`);
|
|
420
|
+
startOllamaServe();
|
|
421
|
+
// Give it a moment to start
|
|
422
|
+
let retries = 10;
|
|
423
|
+
while (retries > 0 && !isOllamaServing()) {
|
|
424
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
425
|
+
retries--;
|
|
426
|
+
}
|
|
427
|
+
if (isOllamaServing()) {
|
|
428
|
+
console.log(` ${c.green}✓${c.reset} Ollama server running`);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
console.log(` ${c.yellow}⚠${c.reset} Could not start Ollama server. Run ${c.bold}ollama serve${c.reset} manually.`);
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
console.log(` ${c.green}✓${c.reset} Ollama server running`);
|
|
437
|
+
}
|
|
438
|
+
// 3. Check if model is already pulled
|
|
439
|
+
if (isOllamaModelAvailable(shortModel)) {
|
|
440
|
+
console.log(` ${c.green}✓${c.reset} Model ${c.bold}${shortModel}${c.reset} ready\n`);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
// 4. Pull the model
|
|
444
|
+
console.log(` ${c.dim}Model ${shortModel} not found locally.${c.reset}`);
|
|
445
|
+
const pull = await questionWithEsc(rl, `\n ${c.cyan}Pull ${shortModel} now? [Y/n]:${c.reset} `);
|
|
446
|
+
if (pull !== null && pull.toLowerCase() !== "n" && pull.toLowerCase() !== "no") {
|
|
447
|
+
const ok = await pullOllamaModel(shortModel);
|
|
448
|
+
if (!ok) {
|
|
449
|
+
console.log(`\n ${c.red}Failed to pull ${shortModel}.${c.reset}`);
|
|
450
|
+
console.log(` ${c.dim}Try manually: ollama pull ${shortModel}${c.reset}\n`);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
console.log(` ${c.green}✓${c.reset} Model ${c.bold}${shortModel}${c.reset} ready\n`);
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
console.log(`\n ${c.dim}Run later: ollama pull ${shortModel}${c.reset}\n`);
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Interactive checkbox selector — arrow keys to navigate, space to toggle, enter to confirm.
|
|
461
|
+
* Returns indices of selected items.
|
|
462
|
+
*/
|
|
463
|
+
function checkboxSelect(items) {
|
|
464
|
+
return new Promise((resolve) => {
|
|
465
|
+
let cursor = 0;
|
|
466
|
+
const render = () => {
|
|
467
|
+
// Move cursor up to overwrite previous render (except first time)
|
|
468
|
+
if (rendered) {
|
|
469
|
+
process.stdout.write(`\x1b[${items.length}A`);
|
|
470
|
+
}
|
|
471
|
+
for (let i = 0; i < items.length; i++) {
|
|
472
|
+
const item = items[i];
|
|
473
|
+
const pointer = i === cursor ? `${c.cyan}❯${c.reset}` : " ";
|
|
474
|
+
const box = item.checked ? `${c.green}◼${c.reset}` : `${c.dim}◻${c.reset}`;
|
|
475
|
+
const label = i === cursor ? `${c.bold}${item.label}${c.reset}` : item.label;
|
|
476
|
+
const desc = `${c.dim}${item.desc}${c.reset}`;
|
|
477
|
+
process.stdout.write(`\x1b[2K ${pointer} ${box} ${label} ${desc}\n`);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
let rendered = false;
|
|
481
|
+
render();
|
|
482
|
+
rendered = true;
|
|
483
|
+
const wasRaw = stdin.isRaw;
|
|
484
|
+
if (stdin.isTTY)
|
|
485
|
+
stdin.setRawMode(true);
|
|
486
|
+
const onData = (data) => {
|
|
487
|
+
const key = data.toString();
|
|
488
|
+
if (key === "\x1b[A" || key === "k") {
|
|
489
|
+
// Up
|
|
490
|
+
cursor = (cursor - 1 + items.length) % items.length;
|
|
491
|
+
render();
|
|
492
|
+
}
|
|
493
|
+
else if (key === "\x1b[B" || key === "j") {
|
|
494
|
+
// Down
|
|
495
|
+
cursor = (cursor + 1) % items.length;
|
|
496
|
+
render();
|
|
497
|
+
}
|
|
498
|
+
else if (key === " ") {
|
|
499
|
+
// Toggle
|
|
500
|
+
items[cursor].checked = !items[cursor].checked;
|
|
501
|
+
render();
|
|
502
|
+
}
|
|
503
|
+
else if (key === "\r" || key === "\n") {
|
|
504
|
+
// Confirm
|
|
505
|
+
stdin.removeListener("data", onData);
|
|
506
|
+
if (stdin.isTTY)
|
|
507
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
508
|
+
const selected = items.map((item, i) => (item.checked ? i : -1)).filter((i) => i >= 0);
|
|
509
|
+
resolve(selected);
|
|
510
|
+
}
|
|
511
|
+
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);
|
|
516
|
+
resolve([]);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
stdin.on("data", onData);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
// ── Banner ──────────────────────────────────────────────────────────────────
|
|
309
523
|
function printBanner() {
|
|
310
524
|
console.log(`
|
|
311
525
|
${c.cyan}${c.bold}
|
|
@@ -316,7 +530,7 @@ ${c.cyan}${c.bold}
|
|
|
316
530
|
███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║
|
|
317
531
|
╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
318
532
|
${c.reset}
|
|
319
|
-
${c.dim}
|
|
533
|
+
${c.dim} Swarm-native coding agent orchestrator${c.reset}
|
|
320
534
|
`);
|
|
321
535
|
}
|
|
322
536
|
// ── Status line ─────────────────────────────────────────────────────────────
|
|
@@ -1323,90 +1537,78 @@ async function interactive() {
|
|
|
1323
1537
|
requiresKey: true,
|
|
1324
1538
|
},
|
|
1325
1539
|
];
|
|
1540
|
+
// ── Step 1: Checkbox agent selection ─────────────────────────
|
|
1541
|
+
console.log(` ${c.bold}Select your coding agent(s):${c.reset}`);
|
|
1542
|
+
console.log(` ${c.dim}↑/↓ navigate · space toggle · enter confirm${c.reset}\n`);
|
|
1543
|
+
const checkboxItems = AGENT_CHOICES.map((a) => ({
|
|
1544
|
+
label: a.name,
|
|
1545
|
+
desc: a.desc,
|
|
1546
|
+
checked: false,
|
|
1547
|
+
}));
|
|
1548
|
+
const selectedIndices = await checkboxSelect(checkboxItems);
|
|
1549
|
+
if (selectedIndices.length === 0) {
|
|
1550
|
+
console.log(`\n ${c.dim}No agents selected. Exiting.${c.reset}\n`);
|
|
1551
|
+
process.exit(0);
|
|
1552
|
+
}
|
|
1553
|
+
const selectedAgents = selectedIndices.map((i) => AGENT_CHOICES[i]);
|
|
1554
|
+
console.log();
|
|
1555
|
+
for (const agent of selectedAgents) {
|
|
1556
|
+
console.log(` ${c.green}✓${c.reset} ${c.bold}${agent.name}${c.reset}`);
|
|
1557
|
+
}
|
|
1558
|
+
console.log();
|
|
1559
|
+
// ── Step 2: Configure each agent ─────────────────────────────
|
|
1326
1560
|
const setupRl = readline.createInterface({ input: stdin, output: stdout, terminal: true });
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
for (let i = 0; i < AGENT_CHOICES.length; i++) {
|
|
1331
|
-
const a = AGENT_CHOICES[i];
|
|
1332
|
-
console.log(` ${c.dim}${i + 1}${c.reset} ${a.name} ${c.dim}${a.desc}${c.reset}`);
|
|
1333
|
-
}
|
|
1334
|
-
console.log();
|
|
1335
|
-
const choice = await questionWithEsc(setupRl, ` ${c.cyan}Agent [1-${AGENT_CHOICES.length}]:${c.reset} `);
|
|
1336
|
-
if (choice === null) {
|
|
1337
|
-
console.log(`\n ${c.dim}Exiting.${c.reset}\n`);
|
|
1338
|
-
setupRl.close();
|
|
1339
|
-
process.exit(0);
|
|
1340
|
-
}
|
|
1341
|
-
const idx = parseInt(choice, 10) - 1;
|
|
1342
|
-
if (Number.isNaN(idx) || idx < 0 || idx >= AGENT_CHOICES.length) {
|
|
1343
|
-
console.log(`\n ${c.dim}Invalid choice.${c.reset}\n`);
|
|
1344
|
-
continue;
|
|
1345
|
-
}
|
|
1346
|
-
const agent = AGENT_CHOICES[idx];
|
|
1347
|
-
console.log(`\n ${c.green}✓${c.reset} Agent: ${c.bold}${agent.name}${c.reset}\n`);
|
|
1348
|
-
// Prompt for API key(s) one by one based on the selected agent
|
|
1349
|
-
let gotAnyKey = false;
|
|
1561
|
+
const configuredProviders = new Set();
|
|
1562
|
+
let usesOllama = false;
|
|
1563
|
+
for (const agent of selectedAgents) {
|
|
1350
1564
|
if (agent.requiresKey) {
|
|
1351
|
-
// Agent
|
|
1352
|
-
console.log(` ${c.
|
|
1565
|
+
// Agent needs at least one API key
|
|
1566
|
+
console.log(` ${c.bold}${agent.name}${c.reset} ${c.dim}— configure API key:${c.reset}\n`);
|
|
1353
1567
|
for (const provider of agent.keys) {
|
|
1568
|
+
if (configuredProviders.has(provider.env) || process.env[provider.env]) {
|
|
1569
|
+
console.log(` ${c.green}✓${c.reset} ${provider.name} ${c.dim}(already configured)${c.reset}`);
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1354
1572
|
const gotKey = await promptForProviderKey(setupRl, provider);
|
|
1355
1573
|
if (gotKey === null)
|
|
1356
|
-
break;
|
|
1574
|
+
break;
|
|
1357
1575
|
if (gotKey) {
|
|
1358
|
-
|
|
1359
|
-
break;
|
|
1576
|
+
configuredProviders.add(provider.env);
|
|
1577
|
+
break;
|
|
1360
1578
|
}
|
|
1361
1579
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1580
|
+
const agentHasKey = agent.keys.some((p) => configuredProviders.has(p.env) || process.env[p.env]);
|
|
1581
|
+
if (!agentHasKey) {
|
|
1582
|
+
console.log(` ${c.dim}No key for ${agent.name} — set one in .env later${c.reset}`);
|
|
1365
1583
|
}
|
|
1584
|
+
console.log();
|
|
1366
1585
|
}
|
|
1367
1586
|
else {
|
|
1368
|
-
// Agent works without keys (e.g. OpenCode
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
console.log(` ${c.dim}-${c.reset} ${provider.name} ${c.dim}(${provider.env})${c.reset}`);
|
|
1374
|
-
}
|
|
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;
|
|
1375
1592
|
console.log();
|
|
1376
|
-
const addKey = await questionWithEsc(setupRl, ` ${c.cyan}Add an API key? [y/N]:${c.reset} `);
|
|
1377
|
-
if (addKey !== null && (addKey.toLowerCase() === "y" || addKey.toLowerCase() === "yes")) {
|
|
1378
|
-
for (const provider of agent.keys) {
|
|
1379
|
-
const gotKey = await promptForProviderKey(setupRl, provider);
|
|
1380
|
-
if (gotKey === null)
|
|
1381
|
-
break;
|
|
1382
|
-
if (gotKey) {
|
|
1383
|
-
gotAnyKey = true;
|
|
1384
|
-
break;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
1593
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
saveModelPreference(currentModelId);
|
|
1398
|
-
console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset}`);
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
else {
|
|
1402
|
-
// No API key — default to open-source model via OpenCode
|
|
1403
|
-
currentModelId = "ollama/deepseek-coder-v2";
|
|
1594
|
+
}
|
|
1595
|
+
// ── 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;
|
|
1404
1602
|
saveModelPreference(currentModelId);
|
|
1405
|
-
console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset}
|
|
1603
|
+
console.log(` ${c.green}✓${c.reset} Default model: ${c.bold}${currentModelId}${c.reset}`);
|
|
1406
1604
|
}
|
|
1407
|
-
console.log();
|
|
1408
|
-
setupDone = true;
|
|
1409
1605
|
}
|
|
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
|
+
console.log();
|
|
1410
1612
|
setupRl.close();
|
|
1411
1613
|
}
|
|
1412
1614
|
// Resolve model — ensure the resolved provider actually has an API key
|
|
@@ -1437,8 +1639,19 @@ async function interactive() {
|
|
|
1437
1639
|
}
|
|
1438
1640
|
}
|
|
1439
1641
|
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}`);
|
|
1646
|
+
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`);
|
|
1648
|
+
console.log(` ${c.bold}swarm --dir ./your-project "your task"${c.reset}\n`);
|
|
1649
|
+
process.exit(0);
|
|
1650
|
+
}
|
|
1440
1651
|
console.log(`\n ${c.red}Model "${currentModelId}" not found.${c.reset}`);
|
|
1441
|
-
console.log(`
|
|
1652
|
+
console.log(` Set ${c.bold}ANTHROPIC_API_KEY${c.reset}, ${c.bold}OPENAI_API_KEY${c.reset}, or ${c.bold}GEMINI_API_KEY${c.reset} in your .env file.`);
|
|
1653
|
+
console.log(`\n ${c.dim}For agent-based mode (works with open-source models):${c.reset}`);
|
|
1654
|
+
console.log(` ${c.bold}swarm --dir ./your-project "your task"${c.reset}\n`);
|
|
1442
1655
|
process.exit(1);
|
|
1443
1656
|
}
|
|
1444
1657
|
// Auto-load cwd context so the LLM knows the project structure
|
package/package.json
CHANGED