@katyella/legio 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.3] - 2026-03-02
11
+
12
+ ### Fixed
13
+ - Coordinator and sling beacons now explain what legio is — previously Claude Code on fresh machines rejected thin beacons as "unrecognized" foreign content (same fix as gateway in v0.2.1)
14
+ - Coordinator beacon startup instructions use `legio status` instead of `bd ready` / `legio group status` (doesn't assume beads is installed)
15
+ - `legio doctor` no longer flags persistent agent identity files (coordinator, gateway, monitor) as stale — they legitimately exist outside the agent manifest
16
+ - Gateway greeting mail ("Gateway is online and ready") is only sent after confirmed beacon delivery — previously it was sent even when the beacon was rejected
17
+ - 2399 tests across 79 test files (up from 2397 across 79)
18
+
19
+ ## [0.2.2] - 2026-03-02
20
+
21
+ ### Fixed
22
+ - Coordinator and sling beacons now explain what legio is — previously Claude Code on fresh machines rejected thin beacons as "unrecognized" foreign content (same fix as gateway in v0.2.1)
23
+ - Coordinator beacon startup instructions use `legio status` instead of `bd ready` / `legio group status` (doesn't assume beads is installed)
24
+ - `legio doctor` no longer flags persistent agent identity files (coordinator, gateway, monitor) as stale — they legitimately exist outside the agent manifest
25
+ - 2398 tests across 79 test files (up from 2397 across 79)
26
+
27
+ ## [0.2.1] - 2026-03-02
28
+
29
+ ### Fixed
30
+ - Gateway beacon now explains what legio is — previously Claude Code on fresh machines rejected the thin beacon as "unrecognized" foreign content
31
+ - Gateway tmux session now receives provider env vars (`collectProviderEnv()`) — was the only agent type missing them
32
+ - Removed hardcoded `bd create` from gateway beacon (not all users have beads)
33
+ - `legio doctor` checks for bun availability with install instructions (required runtime for seeds and mulch)
34
+ - `legio doctor` marks sd, mulch, and bd as optional dependencies (warn instead of fail)
35
+ - Doctor install hints explain what each tool does and note bun requirement
36
+ - README requirements section separates required (Node, Claude Code, git, tmux) from optional (sd, mulch, bd) dependencies
37
+
10
38
  ## [0.2.0] - 2026-03-02
11
39
 
12
40
  ### Added
@@ -182,7 +210,10 @@ Initial public release on npm as [`@katyella/legio`](https://www.npmjs.com/packa
182
210
  - E2E lifecycle tests via Playwright
183
211
  - Vitest test runner with forks pool for CI compatibility
184
212
 
185
- [Unreleased]: https://github.com/katyella/legio/compare/v0.2.0...HEAD
213
+ [Unreleased]: https://github.com/katyella/legio/compare/v0.2.3...HEAD
214
+ [0.2.3]: https://github.com/katyella/legio/compare/v0.2.2...v0.2.3
215
+ [0.2.2]: https://github.com/katyella/legio/compare/v0.2.1...v0.2.2
216
+ [0.2.1]: https://github.com/katyella/legio/compare/v0.2.0...v0.2.1
186
217
  [0.2.0]: https://github.com/katyella/legio/compare/v0.1.3...v0.2.0
187
218
  [0.1.3]: https://github.com/katyella/legio/compare/v0.1.2...v0.1.3
188
219
  [0.1.2]: https://github.com/katyella/legio/compare/v0.1.1...v0.1.2
package/README.md CHANGED
@@ -116,8 +116,12 @@ npm link
116
116
  - [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
117
117
  - git
118
118
  - tmux
119
- - [bd (beads)](https://github.com/steveyegge/beads) — issue tracking CLI
120
- - [mulch](https://github.com/jayminwest/mulch) — structured expertise management CLI (`npm install -g @os-eco/mulch-cli`)
119
+
120
+ ### Optional
121
+
122
+ - [sd (seeds)](https://github.com/jayminwest/seeds) — issue tracking CLI (`bun install -g @os-eco/seeds-cli`) — requires [Bun](https://bun.sh)
123
+ - [mulch](https://github.com/jayminwest/mulch) — structured expertise management CLI (`bun install -g @os-eco/mulch-cli`) — requires [Bun](https://bun.sh)
124
+ - [bd (beads)](https://github.com/steveyegge/beads) — legacy issue tracker (alternative to seeds)
121
125
 
122
126
  ## Documentation
123
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@katyella/legio",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "Multi-agent orchestration for Claude Code — spawn worker agents in git worktrees via tmux, coordinate through SQLite mail, merge with tiered conflict resolution",
5
5
  "author": "Matthew Wojtowicz",
6
6
  "license": "MIT",
@@ -875,8 +875,7 @@ describe("buildCoordinatorBeacon", () => {
875
875
  const beacon = buildCoordinatorBeacon();
876
876
  expect(beacon).toContain("mulch prime");
877
877
  expect(beacon).toContain("legio mail check --agent coordinator");
878
- expect(beacon).toContain("bd ready");
879
- expect(beacon).toContain("legio group status");
878
+ expect(beacon).toContain("legio status");
880
879
  });
881
880
 
882
881
  test("includes hierarchy enforcement instruction", () => {
@@ -891,11 +890,17 @@ describe("buildCoordinatorBeacon", () => {
891
890
  expect(beacon).toContain("spawn a lead who will spawn scouts");
892
891
  });
893
892
 
893
+ test("explains what legio is", () => {
894
+ const beacon = buildCoordinatorBeacon();
895
+ expect(beacon).toContain("legio multi-agent orchestration system");
896
+ expect(beacon).toContain("CLI tool installed on this machine");
897
+ });
898
+
894
899
  test("parts are joined with em-dash separator", () => {
895
900
  const beacon = buildCoordinatorBeacon();
896
- // Should have exactly 4 " — " separators (5 parts)
901
+ // Should have exactly 5 " — " separators (6 parts)
897
902
  const dashes = beacon.split(" — ");
898
- expect(dashes).toHaveLength(5);
903
+ expect(dashes).toHaveLength(6);
899
904
  });
900
905
  });
901
906
 
@@ -281,10 +281,11 @@ export function buildCoordinatorBeacon(): string {
281
281
  const timestamp = new Date().toISOString();
282
282
  const parts = [
283
283
  `[LEGIO] ${COORDINATOR_NAME} (coordinator) ${timestamp}`,
284
+ "You are a coordinator agent in the legio multi-agent orchestration system. legio is a CLI tool installed on this machine that coordinates multiple Claude Code agents via tmux, SQLite mail, and git worktrees.",
284
285
  "Depth: 0 | Parent: none | Role: persistent orchestrator",
285
286
  "HIERARCHY: You ONLY spawn leads (legio sling --capability lead). Leads spawn scouts, builders, reviewers. NEVER spawn non-lead agents directly.",
286
287
  "DELEGATION: For any exploration/scouting, spawn a lead who will spawn scouts. Do NOT explore the codebase yourself beyond initial planning.",
287
- `Startup: run mulch prime, check mail (legio mail check --agent ${COORDINATOR_NAME}), check bd ready, check legio group status, then begin work`,
288
+ `Startup: run mulch prime, check mail (legio mail check --agent ${COORDINATOR_NAME}), check legio status, then begin work`,
288
289
  ];
289
290
  return parts.join(" — ");
290
291
  }
@@ -518,8 +518,26 @@ describe("startGateway", () => {
518
518
  expect(enterCalls).toHaveLength(2); // follow-up Enter + retry Enter
519
519
  });
520
520
 
521
- test("sends greeting mail to human after beacon delivery", async () => {
522
- const { deps } = makeDeps();
521
+ test("sends greeting mail to human after confirmed beacon delivery", async () => {
522
+ const { tmux } = makeFakeTmux();
523
+ // Simulate hooks transitioning session out of "booting" after a few sleep calls
524
+ let sleepCount = 0;
525
+ const deps: GatewayDeps = {
526
+ _tmux: tmux,
527
+ _sleep: async () => {
528
+ sleepCount++;
529
+ // After 3 sleeps (waitForTuiReady + follow-up Enter delay + first poll),
530
+ // transition session to "working" like hooks would
531
+ if (sleepCount === 3) {
532
+ const { store } = openSessionStore(legioDir);
533
+ try {
534
+ store.updateState("gateway", "working");
535
+ } finally {
536
+ store.close();
537
+ }
538
+ }
539
+ },
540
+ };
523
541
  await captureStdout(() => gatewayCommand(["start", "--no-attach"], deps));
524
542
  // Verify mail.db has the greeting
525
543
  const { createMailStore } = await import("../mail/store.ts");
@@ -535,6 +553,20 @@ describe("startGateway", () => {
535
553
  mailDb.close();
536
554
  }
537
555
  });
556
+
557
+ test("does not send greeting mail when beacon delivery fails", async () => {
558
+ const { deps } = makeDeps();
559
+ await captureStdout(() => gatewayCommand(["start", "--no-attach"], deps));
560
+ // verifyBeaconDelivery returns false (session stays in "booting"), so no greeting mail
561
+ const { createMailStore } = await import("../mail/store.ts");
562
+ const mailDb = createMailStore(join(legioDir, "mail.db"));
563
+ try {
564
+ const msgs = mailDb.getAll({ from: "gateway", to: "human" });
565
+ expect(msgs).toHaveLength(0);
566
+ } finally {
567
+ mailDb.close();
568
+ }
569
+ });
538
570
  });
539
571
 
540
572
  describe("stopGateway", () => {
@@ -713,21 +745,21 @@ describe("buildGatewayBeacon", () => {
713
745
 
714
746
  test("includes ISSUES notice", () => {
715
747
  const beacon = buildGatewayBeacon();
716
- expect(beacon).toContain("ISSUES: Use bd create");
748
+ expect(beacon).toContain("ISSUES:");
749
+ expect(beacon).toContain("legio status");
717
750
  });
718
751
 
719
752
  test("includes startup instructions", () => {
720
753
  const beacon = buildGatewayBeacon();
721
- expect(beacon).toContain("mulch prime");
722
754
  expect(beacon).toContain("legio mail check --agent gateway");
723
755
  expect(beacon).toContain("respond to user via BOTH terminal AND mail");
724
756
  });
725
757
 
726
758
  test("parts are joined with em-dash separator", () => {
727
759
  const beacon = buildGatewayBeacon();
728
- // Should have exactly 4 " — " separators (5 parts)
760
+ // Should have exactly 5 " — " separators (6 parts)
729
761
  const dashes = beacon.split(" — ");
730
- expect(dashes).toHaveLength(5);
762
+ expect(dashes).toHaveLength(6);
731
763
  });
732
764
 
733
765
  test("default (no args) does not include FIRST_RUN", () => {
@@ -18,7 +18,7 @@ import { join } from "node:path";
18
18
  import { deployHooks } from "../agents/hooks-deployer.ts";
19
19
  import { createIdentity, loadIdentity } from "../agents/identity.ts";
20
20
  import { createManifestLoader, resolveModel } from "../agents/manifest.ts";
21
- import { loadConfig } from "../config.ts";
21
+ import { collectProviderEnv, loadConfig } from "../config.ts";
22
22
  import { AgentError, ValidationError } from "../errors.ts";
23
23
  import { openSessionStore } from "../sessions/compat.ts";
24
24
  import type { SessionStore } from "../sessions/store.ts";
@@ -82,10 +82,11 @@ export function buildGatewayBeacon(isFirstRun = false): string {
82
82
  const timestamp = new Date().toISOString();
83
83
  const parts = [
84
84
  `[LEGIO] ${GATEWAY_NAME} (gateway) ${timestamp}`,
85
- "Depth: 0 | Role: planning companion",
86
- "READONLY: No Write/Edit",
87
- "ISSUES: Use bd create",
88
- `Startup: run mulch prime, check mail (legio mail check --agent ${GATEWAY_NAME}), respond to user via BOTH terminal AND mail (legio mail send --to human --subject "chat" --body "..." --type status --agent ${GATEWAY_NAME}) so replies appear in dashboard chat`,
85
+ "You are a gateway agent in the legio multi-agent orchestration system. legio is a CLI tool installed on this machine that coordinates multiple Claude Code agents via tmux, SQLite mail, and git worktrees.",
86
+ "Depth: 0 | Role: planning companion | READONLY: No Write/Edit tool access",
87
+ 'COMMUNICATION: Use legio mail for all inter-agent messaging. Check your inbox with: legio mail check --agent gateway. Send replies via: legio mail send --to human --subject "chat" --body "..." --type status --agent gateway',
88
+ "ISSUES: If a task tracker is configured, use it to create issues for work decomposition. Check legio status for current agent fleet.",
89
+ `Startup: check mail (legio mail check --agent ${GATEWAY_NAME}), respond to user via BOTH terminal AND mail so replies appear in dashboard chat`,
89
90
  ];
90
91
  if (isFirstRun) {
91
92
  parts.push("FIRST_RUN: true — Follow the First Run workflow in your agent definition");
@@ -117,7 +118,7 @@ async function verifyBeaconDelivery(
117
118
  tmuxSession: string,
118
119
  beacon: string,
119
120
  sleep: (ms: number) => Promise<void>,
120
- ): Promise<void> {
121
+ ): Promise<boolean> {
121
122
  const MAX_CHECKS = 10;
122
123
  const INTERVAL_MS = 2_000;
123
124
 
@@ -126,7 +127,7 @@ async function verifyBeaconDelivery(
126
127
  const session = store.getByName(GATEWAY_NAME);
127
128
  if (session && session.state !== "booting") {
128
129
  // Beacon confirmed — gateway transitioned out of booting
129
- return;
130
+ return true;
130
131
  }
131
132
  }
132
133
 
@@ -141,7 +142,9 @@ async function verifyBeaconDelivery(
141
142
  const finalSession = store.getByName(GATEWAY_NAME);
142
143
  if (!finalSession || finalSession.state === "booting") {
143
144
  process.stderr.write("[legio] Warning: gateway beacon delivery could not be confirmed\n");
145
+ return false;
144
146
  }
147
+ return true;
145
148
  }
146
149
 
147
150
  /**
@@ -238,6 +241,7 @@ async function startGateway(args: string[], deps: GatewayDeps = {}): Promise<voi
238
241
  await writeFile(settingsPath, JSON.stringify(settings), "utf-8");
239
242
  const claudeCmd = `claude --model ${model} --dangerously-skip-permissions --settings ${settingsPath}`;
240
243
  const pid = await tmux.createSession(tmuxSession, projectRoot, claudeCmd, {
244
+ ...collectProviderEnv(),
241
245
  LEGIO_AGENT_NAME: GATEWAY_NAME,
242
246
  });
243
247
 
@@ -303,25 +307,27 @@ async function startGateway(args: string[], deps: GatewayDeps = {}): Promise<voi
303
307
 
304
308
  // Verify delivery: poll store until state transitions out of booting.
305
309
  // Must complete before the finally block closes the store.
306
- await verifyBeaconDelivery(store, tmux, tmuxSession, beacon, sleep);
307
-
308
- // Send greeting mail to human
309
- const { createMailStore } = await import("../mail/store.ts");
310
- const mailDb = createMailStore(join(legioDir, "mail.db"));
311
- try {
312
- mailDb.insert({
313
- id: "",
314
- from: GATEWAY_NAME,
315
- to: "human",
316
- subject: "Gateway online",
317
- body: "Gateway is online and ready. Send a message to start chatting.",
318
- type: "status",
319
- priority: "normal",
320
- threadId: null,
321
- audience: "human",
322
- });
323
- } finally {
324
- mailDb.close();
310
+ const confirmed = await verifyBeaconDelivery(store, tmux, tmuxSession, beacon, sleep);
311
+
312
+ // Send greeting mail to human only after confirmed beacon delivery
313
+ if (confirmed) {
314
+ const { createMailStore } = await import("../mail/store.ts");
315
+ const mailDb = createMailStore(join(legioDir, "mail.db"));
316
+ try {
317
+ mailDb.insert({
318
+ id: "",
319
+ from: GATEWAY_NAME,
320
+ to: "human",
321
+ subject: "Gateway online",
322
+ body: "Gateway is online and ready. Send a message to start chatting.",
323
+ type: "status",
324
+ priority: "normal",
325
+ threadId: null,
326
+ audience: "human",
327
+ });
328
+ } finally {
329
+ mailDb.close();
330
+ }
325
331
  }
326
332
 
327
333
  if (shouldAttach) {
@@ -399,12 +399,17 @@ describe("buildBeacon", () => {
399
399
  expect(beacon).toContain("Depth: 0 | Parent: none");
400
400
  });
401
401
 
402
+ test("explains what legio is", () => {
403
+ const beacon = buildBeacon(makeBeaconOpts());
404
+ expect(beacon).toContain("legio multi-agent orchestration system");
405
+ expect(beacon).toContain("CLI tool installed on this machine");
406
+ });
407
+
402
408
  test("includes startup instructions with agent name and task ID", () => {
403
409
  const opts = makeBeaconOpts({ agentName: "scout-1", taskId: "legio-xyz" });
404
410
  const beacon = buildBeacon(opts);
405
411
 
406
412
  expect(beacon).toContain("read .claude/CLAUDE.md");
407
- expect(beacon).toContain("mulch prime");
408
413
  expect(beacon).toContain("legio mail check --agent scout-1");
409
414
  expect(beacon).toContain("begin task legio-xyz");
410
415
  });
@@ -115,8 +115,9 @@ export function buildBeacon(opts: BeaconOptions): string {
115
115
  const parent = opts.parentAgent ?? "none";
116
116
  const parts = [
117
117
  `[LEGIO] ${opts.agentName} (${opts.capability}) ${timestamp} task:${opts.taskId}`,
118
+ "You are an agent in the legio multi-agent orchestration system. legio is a CLI tool installed on this machine that coordinates multiple Claude Code agents via tmux, SQLite mail, and git worktrees.",
118
119
  `Depth: ${opts.depth} | Parent: ${parent}`,
119
- `Startup: read .claude/CLAUDE.md, run mulch prime, check mail (legio mail check --agent ${opts.agentName}), then begin task ${opts.taskId}`,
120
+ `Startup: read .claude/CLAUDE.md, check mail (legio mail check --agent ${opts.agentName}), then begin task ${opts.taskId}`,
120
121
  ];
121
122
  return parts.join(" — ");
122
123
  }
@@ -375,6 +375,51 @@ sessionsCompleted: -5
375
375
  expect(identityCheck?.details?.some((d) => d.includes("sessionsCompleted"))).toBe(true);
376
376
  });
377
377
 
378
+ test("does not flag persistent agent identities as stale", async () => {
379
+ const manifest = {
380
+ version: "1.0",
381
+ agents: {
382
+ scout: {
383
+ file: "scout.md",
384
+ model: "haiku",
385
+ tools: ["Read"],
386
+ capabilities: ["explore"],
387
+ canSpawn: false,
388
+ constraints: [],
389
+ },
390
+ },
391
+ capabilityIndex: {
392
+ explore: ["scout"],
393
+ },
394
+ };
395
+
396
+ await mkdir(join(legioDir, "agent-defs"), { recursive: true });
397
+ await mkdir(join(legioDir, "agents", "gateway"), { recursive: true });
398
+ await mkdir(join(legioDir, "agents", "coordinator"), { recursive: true });
399
+ await writeFile(join(legioDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
400
+ await writeFile(join(legioDir, "agent-defs", "scout.md"), "# Scout");
401
+
402
+ const identity = `name: gateway
403
+ capability: gateway
404
+ created: "2024-01-01T00:00:00Z"
405
+ sessionsCompleted: 3
406
+ `;
407
+ await writeFile(join(legioDir, "agents", "gateway", "identity.yaml"), identity);
408
+
409
+ const coordIdentity = `name: coordinator
410
+ capability: coordinator
411
+ created: "2024-01-01T00:00:00Z"
412
+ sessionsCompleted: 10
413
+ `;
414
+ await writeFile(join(legioDir, "agents", "coordinator", "identity.yaml"), coordIdentity);
415
+
416
+ const checks = await checkAgents(mockConfig, legioDir);
417
+
418
+ const staleCheck = checks.find((c) => c.name === "Stale identities");
419
+ // Should not warn — persistent agents are expected to have identity files
420
+ expect(staleCheck?.status).not.toBe("warn");
421
+ });
422
+
378
423
  test("warns about stale identity files", async () => {
379
424
  const manifest = {
380
425
  version: "1.0",
@@ -5,6 +5,8 @@ import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
5
5
 
6
6
  const VALID_MODELS = new Set(["sonnet", "opus", "haiku"]);
7
7
  const VALID_NAME_REGEX = /^[a-zA-Z0-9_-]+$/;
8
+ /** Persistent agents create identity files but are not registered in the agent manifest. */
9
+ const PERSISTENT_AGENTS = new Set(["coordinator", "gateway", "monitor"]);
8
10
 
9
11
  /**
10
12
  * Check if a path exists.
@@ -313,8 +315,8 @@ export const checkAgents: DoctorCheckFn = async (_config, legioDir): Promise<Doc
313
315
 
314
316
  identityFileCount++;
315
317
 
316
- // Check if agent still exists in manifest
317
- if (!manifest.agents[agentName]) {
318
+ // Check if agent still exists in manifest (persistent agents are not manifest-registered)
319
+ if (!manifest.agents[agentName] && !PERSISTENT_AGENTS.has(agentName)) {
318
320
  staleIdentities.push(agentName);
319
321
  continue;
320
322
  }
@@ -56,46 +56,26 @@ describe("checkDependencies", () => {
56
56
  const checks = await checkDependencies(mockConfig, "/tmp/.legio");
57
57
 
58
58
  expect(Array.isArray(checks)).toBe(true);
59
- expect(checks.length).toBeGreaterThanOrEqual(5);
59
+ expect(checks.length).toBeGreaterThanOrEqual(6);
60
60
 
61
61
  // Verify we have checks for each tool
62
62
  const toolNames = checks.map((c) => c.name);
63
63
  expect(toolNames).toContain("git availability");
64
64
  expect(toolNames).toContain("node availability");
65
65
  expect(toolNames).toContain("tmux availability");
66
+ expect(toolNames).toContain("bun availability");
66
67
  expect(toolNames).toContain("sd availability");
67
68
  expect(toolNames).toContain("mulch availability");
68
69
  expect(toolNames).toContain("bd availability");
69
70
  });
70
71
 
71
- test("sd is required when backend is auto", async () => {
72
+ test("bun, sd, mulch, and bd are all optional (warn if missing)", async () => {
72
73
  const checks = await checkDependencies(mockConfig, "/tmp/.legio");
73
- const sdCheck = checks.find((c) => c.name === "sd availability");
74
- // sd should be required (fail if missing) when backend is "auto"
75
- if (sdCheck?.status !== "pass") {
76
- expect(sdCheck?.status).toBe("fail");
77
- }
78
- });
79
-
80
- test("bd is optional when backend is auto", async () => {
81
- const checks = await checkDependencies(mockConfig, "/tmp/.legio");
82
- const bdCheck = checks.find((c) => c.name === "bd availability");
83
- // bd should be optional (warn if missing) when backend is "auto"
84
- if (bdCheck?.status !== "pass") {
85
- expect(bdCheck?.status).toBe("warn");
86
- }
87
- });
88
-
89
- test("bd is required when backend is beads", async () => {
90
- const beadsConfig = {
91
- ...mockConfig,
92
- taskTracker: { backend: "beads" as const, enabled: true },
93
- };
94
- const checks = await checkDependencies(beadsConfig, "/tmp/.legio");
95
- const bdCheck = checks.find((c) => c.name === "bd availability");
96
- // bd should be required (fail if missing) when backend is "beads"
97
- if (bdCheck?.status !== "pass") {
98
- expect(bdCheck?.status).toBe("fail");
74
+ for (const name of ["bun", "sd", "mulch", "bd"]) {
75
+ const check = checks.find((c) => c.name === `${name} availability`);
76
+ if (check?.status !== "pass") {
77
+ expect(check?.status).toBe("warn");
78
+ }
99
79
  }
100
80
  });
101
81
 
@@ -8,33 +8,38 @@ import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
8
8
  * bd is checked as optional (legacy tracker backend).
9
9
  */
10
10
  export const checkDependencies: DoctorCheckFn = async (
11
- config: LegioConfig,
11
+ _config: LegioConfig,
12
12
  _legioDir,
13
13
  ): Promise<DoctorCheck[]> => {
14
- const backend = config.taskTracker.backend;
15
-
16
14
  const requiredTools = [
17
15
  { name: "git", versionFlag: "--version", required: true },
18
16
  { name: "node", versionFlag: "--version", required: true },
19
17
  { name: "tmux", versionFlag: "-V", required: true },
18
+ {
19
+ name: "bun",
20
+ versionFlag: "--version",
21
+ required: false,
22
+ installHint: "curl -fsSL https://bun.sh/install | bash — required runtime for sd and mulch",
23
+ },
20
24
  {
21
25
  name: "sd",
22
26
  versionFlag: "--version",
23
- required: backend === "auto" || backend === "seeds",
24
- installHint: "npm install -g @os-eco/seeds-cli — https://github.com/jayminwest/seeds",
27
+ required: false,
28
+ installHint:
29
+ "bun install -g @os-eco/seeds-cli — issue tracker for agent task dispatch (requires Bun)",
25
30
  },
26
31
  {
27
32
  name: "mulch",
28
33
  versionFlag: "--version",
29
- required: true,
30
- installHint: "npm install -g @os-eco/mulch-cli — https://github.com/jayminwest/mulch",
34
+ required: false,
35
+ installHint:
36
+ "bun install -g @os-eco/mulch-cli — structured expertise/memory across sessions (requires Bun)",
31
37
  },
32
38
  {
33
39
  name: "bd",
34
40
  versionFlag: "--version",
35
- required: backend === "beads",
36
- installHint:
37
- "https://github.com/steveyegge/beads (legacy — consider migrating to seeds: sd migrate-from-beads)",
41
+ required: false,
42
+ installHint: "https://github.com/steveyegge/beads — legacy issue tracker (alternative to sd)",
38
43
  },
39
44
  ];
40
45
 
package/src/index.ts CHANGED
@@ -45,7 +45,7 @@ import { worktreeCommand } from "./commands/worktree.ts";
45
45
  import { LegioError, WorktreeError } from "./errors.ts";
46
46
  import { setQuiet } from "./logging/color.ts";
47
47
 
48
- const VERSION = "0.2.0";
48
+ const VERSION = "0.2.3";
49
49
 
50
50
  const HELP = `legio v${VERSION} — Multi-agent orchestration for Claude Code
51
51