@katyella/legio 0.1.3 → 0.2.2
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 +61 -3
- package/README.md +21 -10
- package/agents/builder.md +11 -10
- package/agents/coordinator.md +36 -27
- package/agents/cto.md +9 -8
- package/agents/gateway.md +28 -12
- package/agents/lead.md +45 -30
- package/agents/merger.md +4 -4
- package/agents/monitor.md +10 -9
- package/agents/reviewer.md +8 -8
- package/agents/scout.md +10 -10
- package/agents/supervisor.md +60 -45
- package/package.json +2 -2
- package/src/agents/hooks-deployer.test.ts +46 -41
- package/src/agents/hooks-deployer.ts +10 -9
- package/src/agents/manifest.test.ts +6 -2
- package/src/agents/overlay.test.ts +9 -7
- package/src/agents/overlay.ts +29 -7
- package/src/commands/agents.test.ts +1 -5
- package/src/commands/clean.test.ts +2 -5
- package/src/commands/clean.ts +25 -1
- package/src/commands/completions.test.ts +1 -1
- package/src/commands/completions.ts +26 -7
- package/src/commands/coordinator.test.ts +87 -82
- package/src/commands/coordinator.ts +94 -48
- package/src/commands/costs.test.ts +2 -6
- package/src/commands/dashboard.test.ts +2 -5
- package/src/commands/doctor.test.ts +2 -6
- package/src/commands/down.ts +3 -3
- package/src/commands/errors.test.ts +2 -6
- package/src/commands/feed.test.ts +2 -6
- package/src/commands/gateway.test.ts +43 -17
- package/src/commands/gateway.ts +101 -11
- package/src/commands/hooks.test.ts +2 -5
- package/src/commands/init.test.ts +4 -13
- package/src/commands/inspect.test.ts +2 -6
- package/src/commands/log.test.ts +2 -6
- package/src/commands/logs.test.ts +2 -9
- package/src/commands/mail.test.ts +76 -215
- package/src/commands/mail.ts +43 -187
- package/src/commands/metrics.test.ts +3 -10
- package/src/commands/nudge.ts +15 -0
- package/src/commands/prime.test.ts +4 -11
- package/src/commands/replay.test.ts +2 -6
- package/src/commands/server.test.ts +1 -5
- package/src/commands/server.ts +1 -1
- package/src/commands/sling.test.ts +6 -1
- package/src/commands/sling.ts +42 -17
- package/src/commands/spec.test.ts +2 -5
- package/src/commands/status.test.ts +2 -4
- package/src/commands/stop.test.ts +2 -5
- package/src/commands/supervisor.ts +6 -6
- package/src/commands/trace.test.ts +2 -6
- package/src/commands/up.test.ts +43 -9
- package/src/commands/up.ts +15 -11
- package/src/commands/watchman.ts +327 -0
- package/src/commands/worktree.test.ts +2 -6
- package/src/config.test.ts +34 -104
- package/src/config.ts +120 -32
- package/src/doctor/agents.test.ts +52 -2
- package/src/doctor/agents.ts +4 -2
- package/src/doctor/config-check.test.ts +7 -2
- package/src/doctor/consistency.test.ts +7 -2
- package/src/doctor/databases.test.ts +6 -2
- package/src/doctor/dependencies.test.ts +18 -13
- package/src/doctor/dependencies.ts +23 -94
- package/src/doctor/logs.test.ts +7 -2
- package/src/doctor/merge-queue.test.ts +6 -2
- package/src/doctor/structure.test.ts +7 -2
- package/src/doctor/version.test.ts +7 -2
- package/src/e2e/init-sling-lifecycle.test.ts +2 -5
- package/src/index.ts +7 -7
- package/src/mail/pending.ts +120 -0
- package/src/mail/store.test.ts +89 -0
- package/src/mail/store.ts +11 -0
- package/src/merge/resolver.test.ts +518 -489
- package/src/server/index.ts +33 -2
- package/src/server/public/app.js +3 -3
- package/src/server/public/components/message-bubble.js +11 -1
- package/src/server/public/components/terminal-panel.js +66 -74
- package/src/server/public/views/chat.js +18 -2
- package/src/server/public/views/costs.js +5 -5
- package/src/server/public/views/dashboard.js +80 -51
- package/src/server/public/views/gateway-chat.js +37 -131
- package/src/server/public/views/inspect.js +16 -4
- package/src/server/public/views/issues.js +16 -12
- package/src/server/routes.test.ts +55 -39
- package/src/server/routes.ts +38 -26
- package/src/test-helpers.ts +6 -3
- package/src/tracker/beads.ts +159 -0
- package/src/tracker/exec.ts +44 -0
- package/src/tracker/factory.test.ts +283 -0
- package/src/tracker/factory.ts +59 -0
- package/src/tracker/seeds.ts +156 -0
- package/src/tracker/types.ts +46 -0
- package/src/types.ts +11 -2
- package/src/{watchdog → watchman}/daemon.test.ts +421 -515
- package/src/watchman/daemon.ts +940 -0
- package/src/worktree/tmux.test.ts +2 -1
- package/src/worktree/tmux.ts +4 -4
- package/templates/hooks.json.tmpl +17 -17
- package/src/beads/client.test.ts +0 -210
- package/src/commands/merge.test.ts +0 -676
- package/src/commands/watch.test.ts +0 -152
- package/src/commands/watch.ts +0 -238
- package/src/test-helpers.test.ts +0 -97
- package/src/watchdog/daemon.ts +0 -533
- package/src/watchdog/health.test.ts +0 -371
- package/src/watchdog/triage.test.ts +0 -162
- package/src/worktree/manager.test.ts +0 -444
- /package/src/{watchdog → watchman}/health.ts +0 -0
- /package/src/{watchdog → watchman}/triage.ts +0 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { statSync } from "node:fs";
|
|
15
15
|
import { access, mkdir, readFile, realpath, writeFile } from "node:fs/promises";
|
|
16
16
|
import { join } from "node:path";
|
|
17
|
-
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
18
18
|
import { AgentError, ValidationError } from "../errors.ts";
|
|
19
19
|
import { openSessionStore } from "../sessions/compat.ts";
|
|
20
20
|
import { createRunStore } from "../sessions/store.ts";
|
|
@@ -42,10 +42,10 @@ interface TmuxCallTracker {
|
|
|
42
42
|
sendKeys: Array<{ name: string; keys: string }>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// --- Fake
|
|
45
|
+
// --- Fake Watchman ---
|
|
46
46
|
|
|
47
|
-
/** Track calls to fake
|
|
48
|
-
interface
|
|
47
|
+
/** Track calls to fake watchman for assertions. */
|
|
48
|
+
interface WatchmanCallTracker {
|
|
49
49
|
start: number;
|
|
50
50
|
stop: number;
|
|
51
51
|
isRunning: number;
|
|
@@ -99,26 +99,26 @@ function makeFakeTmux(sessionAliveMap: Record<string, boolean> = {}): {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Build a fake
|
|
103
|
-
* @param running - Whether the
|
|
102
|
+
* Build a fake watchman DI object with configurable behavior.
|
|
103
|
+
* @param running - Whether the watchman should report as running
|
|
104
104
|
* @param startSuccess - Whether start() should succeed (return a PID)
|
|
105
105
|
* @param stopSuccess - Whether stop() should succeed (return true)
|
|
106
106
|
*/
|
|
107
|
-
function
|
|
107
|
+
function makeFakeWatchman(
|
|
108
108
|
running = false,
|
|
109
109
|
startSuccess = true,
|
|
110
110
|
stopSuccess = true,
|
|
111
111
|
): {
|
|
112
|
-
|
|
113
|
-
calls:
|
|
112
|
+
watchman: NonNullable<CoordinatorDeps["_watchman"]>;
|
|
113
|
+
calls: WatchmanCallTracker;
|
|
114
114
|
} {
|
|
115
|
-
const calls:
|
|
115
|
+
const calls: WatchmanCallTracker = {
|
|
116
116
|
start: 0,
|
|
117
117
|
stop: 0,
|
|
118
118
|
isRunning: 0,
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
-
const
|
|
121
|
+
const watchman: NonNullable<CoordinatorDeps["_watchman"]> = {
|
|
122
122
|
async start(): Promise<{ pid: number } | null> {
|
|
123
123
|
calls.start++;
|
|
124
124
|
return startSuccess ? { pid: 88888 } : null;
|
|
@@ -133,7 +133,7 @@ function makeFakeWatchdog(
|
|
|
133
133
|
},
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
-
return {
|
|
136
|
+
return { watchman, calls };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -178,7 +178,6 @@ function makeFakeMonitor(
|
|
|
178
178
|
|
|
179
179
|
let tempDir: string;
|
|
180
180
|
let legioDir: string;
|
|
181
|
-
const originalCwd = process.cwd();
|
|
182
181
|
|
|
183
182
|
/** Save sessions to the SessionStore (sessions.db) for test setup. */
|
|
184
183
|
function saveSessionsToDb(sessions: AgentSession[]): void {
|
|
@@ -203,10 +202,6 @@ function loadSessionsFromDb(): AgentSession[] {
|
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
beforeEach(async () => {
|
|
206
|
-
// Restore cwd FIRST so createTempGitRepo's git operations don't fail
|
|
207
|
-
// if a prior test's tempDir was already cleaned up.
|
|
208
|
-
process.chdir(originalCwd);
|
|
209
|
-
|
|
210
205
|
tempDir = await realpath(await createTempGitRepo());
|
|
211
206
|
legioDir = join(tempDir, ".legio");
|
|
212
207
|
await mkdir(legioDir, { recursive: true });
|
|
@@ -242,12 +237,10 @@ beforeEach(async () => {
|
|
|
242
237
|
);
|
|
243
238
|
await writeFile(join(agentDefsDir, "coordinator.md"), "# Coordinator\n");
|
|
244
239
|
|
|
245
|
-
|
|
246
|
-
process.chdir(tempDir);
|
|
240
|
+
vi.spyOn(process, "cwd").mockReturnValue(tempDir);
|
|
247
241
|
});
|
|
248
242
|
|
|
249
243
|
afterEach(async () => {
|
|
250
|
-
process.chdir(originalCwd);
|
|
251
244
|
await cleanupTempDir(tempDir);
|
|
252
245
|
});
|
|
253
246
|
|
|
@@ -291,24 +284,24 @@ async function captureStdout(fn: () => Promise<void>): Promise<string> {
|
|
|
291
284
|
return chunks.join("");
|
|
292
285
|
}
|
|
293
286
|
|
|
294
|
-
/** Build default CoordinatorDeps with fake tmux,
|
|
287
|
+
/** Build default CoordinatorDeps with fake tmux, watchman, and monitor.
|
|
295
288
|
* Always injects fakes for all three to prevent real child_process.spawn(["legio", ...])
|
|
296
289
|
* calls in tests (legio CLI is not available in CI). */
|
|
297
290
|
function makeDeps(
|
|
298
291
|
sessionAliveMap: Record<string, boolean> = {},
|
|
299
|
-
|
|
292
|
+
watchmanConfig?: { running?: boolean; startSuccess?: boolean; stopSuccess?: boolean },
|
|
300
293
|
monitorConfig?: { running?: boolean; startSuccess?: boolean; stopSuccess?: boolean },
|
|
301
294
|
): {
|
|
302
295
|
deps: CoordinatorDeps;
|
|
303
296
|
calls: TmuxCallTracker;
|
|
304
|
-
|
|
297
|
+
watchmanCalls: WatchmanCallTracker;
|
|
305
298
|
monitorCalls: MonitorCallTracker;
|
|
306
299
|
} {
|
|
307
300
|
const { tmux, calls } = makeFakeTmux(sessionAliveMap);
|
|
308
|
-
const {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
301
|
+
const { watchman, calls: watchmanCalls } = makeFakeWatchman(
|
|
302
|
+
watchmanConfig?.running,
|
|
303
|
+
watchmanConfig?.startSuccess,
|
|
304
|
+
watchmanConfig?.stopSuccess,
|
|
312
305
|
);
|
|
313
306
|
const { monitor, calls: monitorCalls } = makeFakeMonitor(
|
|
314
307
|
monitorConfig?.running,
|
|
@@ -318,7 +311,7 @@ function makeDeps(
|
|
|
318
311
|
|
|
319
312
|
const deps: CoordinatorDeps = {
|
|
320
313
|
_tmux: tmux,
|
|
321
|
-
|
|
314
|
+
_watchman: watchman,
|
|
322
315
|
_monitor: monitor,
|
|
323
316
|
_sleep: () => Promise.resolve(),
|
|
324
317
|
};
|
|
@@ -326,7 +319,7 @@ function makeDeps(
|
|
|
326
319
|
return {
|
|
327
320
|
deps,
|
|
328
321
|
calls,
|
|
329
|
-
|
|
322
|
+
watchmanCalls,
|
|
330
323
|
monitorCalls,
|
|
331
324
|
};
|
|
332
325
|
}
|
|
@@ -442,7 +435,7 @@ describe("startCoordinator", () => {
|
|
|
442
435
|
expect(config.hooks.Stop).toBeDefined();
|
|
443
436
|
});
|
|
444
437
|
|
|
445
|
-
test("hooks use
|
|
438
|
+
test("hooks use LEGIO_AGENT_NAME env var for event logging", async () => {
|
|
446
439
|
const { deps } = makeDeps();
|
|
447
440
|
|
|
448
441
|
await captureStdout(() => coordinatorCommand(["start", "--no-attach"], deps));
|
|
@@ -450,8 +443,8 @@ describe("startCoordinator", () => {
|
|
|
450
443
|
const settingsPath = join(tempDir, ".claude", "settings.local.json");
|
|
451
444
|
const content = await readFile(settingsPath, "utf-8");
|
|
452
445
|
|
|
453
|
-
// The hooks should
|
|
454
|
-
expect(content).toContain("--agent
|
|
446
|
+
// The hooks should use the env var, not a hardcoded agent name
|
|
447
|
+
expect(content).toContain("--agent $LEGIO_AGENT_NAME");
|
|
455
448
|
});
|
|
456
449
|
|
|
457
450
|
test("hooks include ENV_GUARD to avoid affecting user's Claude Code session", async () => {
|
|
@@ -882,8 +875,7 @@ describe("buildCoordinatorBeacon", () => {
|
|
|
882
875
|
const beacon = buildCoordinatorBeacon();
|
|
883
876
|
expect(beacon).toContain("mulch prime");
|
|
884
877
|
expect(beacon).toContain("legio mail check --agent coordinator");
|
|
885
|
-
expect(beacon).toContain("
|
|
886
|
-
expect(beacon).toContain("legio group status");
|
|
878
|
+
expect(beacon).toContain("legio status");
|
|
887
879
|
});
|
|
888
880
|
|
|
889
881
|
test("includes hierarchy enforcement instruction", () => {
|
|
@@ -898,11 +890,17 @@ describe("buildCoordinatorBeacon", () => {
|
|
|
898
890
|
expect(beacon).toContain("spawn a lead who will spawn scouts");
|
|
899
891
|
});
|
|
900
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
|
+
|
|
901
899
|
test("parts are joined with em-dash separator", () => {
|
|
902
900
|
const beacon = buildCoordinatorBeacon();
|
|
903
|
-
// Should have exactly
|
|
901
|
+
// Should have exactly 5 " — " separators (6 parts)
|
|
904
902
|
const dashes = beacon.split(" — ");
|
|
905
|
-
expect(dashes).toHaveLength(
|
|
903
|
+
expect(dashes).toHaveLength(6);
|
|
906
904
|
});
|
|
907
905
|
});
|
|
908
906
|
|
|
@@ -934,167 +932,174 @@ describe("resolveAttach", () => {
|
|
|
934
932
|
});
|
|
935
933
|
});
|
|
936
934
|
|
|
937
|
-
describe("
|
|
938
|
-
describe("startCoordinator with --
|
|
939
|
-
test("calls
|
|
940
|
-
const { deps,
|
|
941
|
-
await captureStdout(() => coordinatorCommand(["start", "--
|
|
935
|
+
describe("watchman integration", () => {
|
|
936
|
+
describe("startCoordinator with --watchman", () => {
|
|
937
|
+
test("calls watchman.start() when --watchman flag is present", async () => {
|
|
938
|
+
const { deps, watchmanCalls } = makeDeps({}, { startSuccess: true });
|
|
939
|
+
await captureStdout(() => coordinatorCommand(["start", "--watchman", "--json"], deps));
|
|
942
940
|
|
|
943
|
-
expect(
|
|
941
|
+
expect(watchmanCalls?.start).toBe(1);
|
|
944
942
|
});
|
|
945
943
|
|
|
946
|
-
test("does NOT call
|
|
947
|
-
const { deps,
|
|
944
|
+
test("does NOT call watchman.start() when --watchman flag is absent", async () => {
|
|
945
|
+
const { deps, watchmanCalls } = makeDeps({}, { startSuccess: true });
|
|
948
946
|
await captureStdout(() => coordinatorCommand(["start", "--json"], deps));
|
|
949
947
|
|
|
950
|
-
expect(
|
|
948
|
+
expect(watchmanCalls?.start).toBe(0);
|
|
951
949
|
});
|
|
952
950
|
|
|
953
|
-
test("--json output includes
|
|
951
|
+
test("--json output includes watchman:false (output written before watchman starts)", async () => {
|
|
954
952
|
const { deps } = makeDeps({}, { startSuccess: true });
|
|
955
953
|
const output = await captureStdout(() =>
|
|
956
|
-
coordinatorCommand(["start", "--
|
|
954
|
+
coordinatorCommand(["start", "--watchman", "--json"], deps),
|
|
957
955
|
);
|
|
958
956
|
|
|
959
957
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
960
|
-
expect(parsed.
|
|
958
|
+
expect(parsed.watchman).toBe(false);
|
|
961
959
|
});
|
|
962
960
|
|
|
963
|
-
test("--json output includes
|
|
961
|
+
test("--json output includes watchman:false when --watchman is present but start fails", async () => {
|
|
964
962
|
const { deps } = makeDeps({}, { startSuccess: false });
|
|
965
963
|
const output = await captureStdout(() =>
|
|
966
|
-
coordinatorCommand(["start", "--
|
|
964
|
+
coordinatorCommand(["start", "--watchman", "--json"], deps),
|
|
967
965
|
);
|
|
968
966
|
|
|
969
967
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
970
|
-
expect(parsed.
|
|
968
|
+
expect(parsed.watchman).toBe(false);
|
|
971
969
|
});
|
|
972
970
|
|
|
973
|
-
test("--json output includes
|
|
971
|
+
test("--json output includes watchman:false when --watchman is absent", async () => {
|
|
974
972
|
const { deps } = makeDeps({}, { startSuccess: true });
|
|
975
973
|
const output = await captureStdout(() => coordinatorCommand(["start", "--json"], deps));
|
|
976
974
|
|
|
977
975
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
978
|
-
expect(parsed.
|
|
976
|
+
expect(parsed.watchman).toBe(false);
|
|
979
977
|
});
|
|
980
978
|
|
|
981
|
-
test("text output includes
|
|
979
|
+
test("text output includes watchman PID when --watchman succeeds", async () => {
|
|
982
980
|
const { deps } = makeDeps({}, { startSuccess: true });
|
|
983
981
|
const output = await captureStdout(() =>
|
|
984
|
-
coordinatorCommand(["start", "--
|
|
982
|
+
coordinatorCommand(["start", "--watchman", "--no-attach"], deps),
|
|
985
983
|
);
|
|
986
984
|
|
|
987
|
-
expect(output).toContain("
|
|
985
|
+
expect(output).toContain("Watchman: started (PID 88888)");
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
test("--watchdog flag is accepted as alias for --watchman", async () => {
|
|
989
|
+
const { deps, watchmanCalls } = makeDeps({}, { startSuccess: true });
|
|
990
|
+
await captureStdout(() => coordinatorCommand(["start", "--watchdog", "--json"], deps));
|
|
991
|
+
|
|
992
|
+
expect(watchmanCalls?.start).toBe(1);
|
|
988
993
|
});
|
|
989
994
|
});
|
|
990
995
|
|
|
991
|
-
describe("stopCoordinator
|
|
992
|
-
test("always calls
|
|
996
|
+
describe("stopCoordinator watchman cleanup", () => {
|
|
997
|
+
test("always calls watchman.stop() when stopping coordinator", async () => {
|
|
993
998
|
const session = makeCoordinatorSession({ state: "working" });
|
|
994
999
|
saveSessionsToDb([session]);
|
|
995
|
-
const { deps,
|
|
1000
|
+
const { deps, watchmanCalls } = makeDeps(
|
|
996
1001
|
{ "legio-test-project-coordinator": true },
|
|
997
1002
|
{ stopSuccess: true },
|
|
998
1003
|
);
|
|
999
1004
|
|
|
1000
1005
|
await captureStdout(() => coordinatorCommand(["stop"], deps));
|
|
1001
1006
|
|
|
1002
|
-
expect(
|
|
1007
|
+
expect(watchmanCalls?.stop).toBe(1);
|
|
1003
1008
|
});
|
|
1004
1009
|
|
|
1005
|
-
test("--json output includes
|
|
1010
|
+
test("--json output includes watchmanStopped:true when watchman was running", async () => {
|
|
1006
1011
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1007
1012
|
saveSessionsToDb([session]);
|
|
1008
1013
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { stopSuccess: true });
|
|
1009
1014
|
|
|
1010
1015
|
const output = await captureStdout(() => coordinatorCommand(["stop", "--json"], deps));
|
|
1011
1016
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
1012
|
-
expect(parsed.
|
|
1017
|
+
expect(parsed.watchmanStopped).toBe(true);
|
|
1013
1018
|
});
|
|
1014
1019
|
|
|
1015
|
-
test("--json output includes
|
|
1020
|
+
test("--json output includes watchmanStopped:false when no watchman was running", async () => {
|
|
1016
1021
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1017
1022
|
saveSessionsToDb([session]);
|
|
1018
1023
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { stopSuccess: false });
|
|
1019
1024
|
|
|
1020
1025
|
const output = await captureStdout(() => coordinatorCommand(["stop", "--json"], deps));
|
|
1021
1026
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
1022
|
-
expect(parsed.
|
|
1027
|
+
expect(parsed.watchmanStopped).toBe(false);
|
|
1023
1028
|
});
|
|
1024
1029
|
|
|
1025
|
-
test("text output shows '
|
|
1030
|
+
test("text output shows 'Watchman stopped' when watchman was running", async () => {
|
|
1026
1031
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1027
1032
|
saveSessionsToDb([session]);
|
|
1028
1033
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { stopSuccess: true });
|
|
1029
1034
|
|
|
1030
1035
|
const output = await captureStdout(() => coordinatorCommand(["stop"], deps));
|
|
1031
|
-
expect(output).toContain("
|
|
1036
|
+
expect(output).toContain("Watchman stopped");
|
|
1032
1037
|
});
|
|
1033
1038
|
|
|
1034
|
-
test("text output shows 'No
|
|
1039
|
+
test("text output shows 'No watchman running' when no watchman was running", async () => {
|
|
1035
1040
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1036
1041
|
saveSessionsToDb([session]);
|
|
1037
1042
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { stopSuccess: false });
|
|
1038
1043
|
|
|
1039
1044
|
const output = await captureStdout(() => coordinatorCommand(["stop"], deps));
|
|
1040
|
-
expect(output).toContain("No
|
|
1045
|
+
expect(output).toContain("No watchman running");
|
|
1041
1046
|
});
|
|
1042
1047
|
});
|
|
1043
1048
|
|
|
1044
|
-
describe("statusCoordinator
|
|
1045
|
-
test("includes
|
|
1049
|
+
describe("statusCoordinator watchman state", () => {
|
|
1050
|
+
test("includes watchmanRunning in JSON output when coordinator is running", async () => {
|
|
1046
1051
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1047
1052
|
saveSessionsToDb([session]);
|
|
1048
1053
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { running: true });
|
|
1049
1054
|
|
|
1050
1055
|
const output = await captureStdout(() => coordinatorCommand(["status", "--json"], deps));
|
|
1051
1056
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
1052
|
-
expect(parsed.
|
|
1057
|
+
expect(parsed.watchmanRunning).toBe(true);
|
|
1053
1058
|
});
|
|
1054
1059
|
|
|
1055
|
-
test("includes
|
|
1060
|
+
test("includes watchmanRunning:false in JSON output when watchman is not running", async () => {
|
|
1056
1061
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1057
1062
|
saveSessionsToDb([session]);
|
|
1058
1063
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { running: false });
|
|
1059
1064
|
|
|
1060
1065
|
const output = await captureStdout(() => coordinatorCommand(["status", "--json"], deps));
|
|
1061
1066
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
1062
|
-
expect(parsed.
|
|
1067
|
+
expect(parsed.watchmanRunning).toBe(false);
|
|
1063
1068
|
});
|
|
1064
1069
|
|
|
1065
|
-
test("text output shows
|
|
1070
|
+
test("text output shows watchman status when coordinator is running", async () => {
|
|
1066
1071
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1067
1072
|
saveSessionsToDb([session]);
|
|
1068
1073
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { running: true });
|
|
1069
1074
|
|
|
1070
1075
|
const output = await captureStdout(() => coordinatorCommand(["status"], deps));
|
|
1071
|
-
expect(output).toContain("
|
|
1076
|
+
expect(output).toContain("Watchman: running");
|
|
1072
1077
|
});
|
|
1073
1078
|
|
|
1074
|
-
test("text output shows 'not running' when
|
|
1079
|
+
test("text output shows 'not running' when watchman is not running", async () => {
|
|
1075
1080
|
const session = makeCoordinatorSession({ state: "working" });
|
|
1076
1081
|
saveSessionsToDb([session]);
|
|
1077
1082
|
const { deps } = makeDeps({ "legio-test-project-coordinator": true }, { running: false });
|
|
1078
1083
|
|
|
1079
1084
|
const output = await captureStdout(() => coordinatorCommand(["status"], deps));
|
|
1080
|
-
expect(output).toContain("
|
|
1085
|
+
expect(output).toContain("Watchman: not running");
|
|
1081
1086
|
});
|
|
1082
1087
|
|
|
1083
|
-
test("includes
|
|
1088
|
+
test("includes watchmanRunning in JSON output when coordinator is not running", async () => {
|
|
1084
1089
|
const { deps } = makeDeps({}, { running: true });
|
|
1085
1090
|
|
|
1086
1091
|
const output = await captureStdout(() => coordinatorCommand(["status", "--json"], deps));
|
|
1087
1092
|
const parsed = JSON.parse(output) as Record<string, unknown>;
|
|
1088
1093
|
expect(parsed.running).toBe(false);
|
|
1089
|
-
expect(parsed.
|
|
1094
|
+
expect(parsed.watchmanRunning).toBe(true);
|
|
1090
1095
|
});
|
|
1091
1096
|
});
|
|
1092
1097
|
|
|
1093
1098
|
describe("COORDINATOR_HELP", () => {
|
|
1094
|
-
test("help text includes --
|
|
1099
|
+
test("help text includes --watchman flag", async () => {
|
|
1095
1100
|
const output = await captureStdout(() => coordinatorCommand(["--help"]));
|
|
1096
|
-
expect(output).toContain("--
|
|
1097
|
-
expect(output).toContain("Auto-start
|
|
1101
|
+
expect(output).toContain("--watchman");
|
|
1102
|
+
expect(output).toContain("Auto-start watchman daemon with coordinator");
|
|
1098
1103
|
});
|
|
1099
1104
|
});
|
|
1100
1105
|
});
|