@katyella/legio 0.1.0
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 +422 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/agents/builder.md +141 -0
- package/agents/coordinator.md +351 -0
- package/agents/cto.md +196 -0
- package/agents/gateway.md +276 -0
- package/agents/lead.md +281 -0
- package/agents/merger.md +156 -0
- package/agents/monitor.md +212 -0
- package/agents/reviewer.md +142 -0
- package/agents/scout.md +131 -0
- package/agents/supervisor.md +416 -0
- package/bin/legio.mjs +38 -0
- package/package.json +77 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +102 -0
- package/src/agents/hooks-deployer.test.ts +1820 -0
- package/src/agents/hooks-deployer.ts +574 -0
- package/src/agents/identity.test.ts +614 -0
- package/src/agents/identity.ts +385 -0
- package/src/agents/lifecycle.test.ts +202 -0
- package/src/agents/lifecycle.ts +184 -0
- package/src/agents/manifest.test.ts +558 -0
- package/src/agents/manifest.ts +297 -0
- package/src/agents/overlay.test.ts +592 -0
- package/src/agents/overlay.ts +316 -0
- package/src/beads/client.test.ts +210 -0
- package/src/beads/client.ts +227 -0
- package/src/beads/molecules.test.ts +320 -0
- package/src/beads/molecules.ts +209 -0
- package/src/commands/agents.test.ts +325 -0
- package/src/commands/agents.ts +286 -0
- package/src/commands/clean.test.ts +730 -0
- package/src/commands/clean.ts +653 -0
- package/src/commands/completions.test.ts +346 -0
- package/src/commands/completions.ts +950 -0
- package/src/commands/coordinator.test.ts +1524 -0
- package/src/commands/coordinator.ts +880 -0
- package/src/commands/costs.test.ts +1015 -0
- package/src/commands/costs.ts +473 -0
- package/src/commands/dashboard.test.ts +94 -0
- package/src/commands/dashboard.ts +607 -0
- package/src/commands/doctor.test.ts +295 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/down.test.ts +308 -0
- package/src/commands/down.ts +124 -0
- package/src/commands/errors.test.ts +648 -0
- package/src/commands/errors.ts +255 -0
- package/src/commands/feed.test.ts +579 -0
- package/src/commands/feed.ts +368 -0
- package/src/commands/gateway.test.ts +698 -0
- package/src/commands/gateway.ts +419 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +539 -0
- package/src/commands/hooks.test.ts +292 -0
- package/src/commands/hooks.ts +210 -0
- package/src/commands/init.test.ts +211 -0
- package/src/commands/init.ts +622 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +455 -0
- package/src/commands/log.test.ts +1556 -0
- package/src/commands/log.ts +752 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +544 -0
- package/src/commands/mail.test.ts +1726 -0
- package/src/commands/mail.ts +926 -0
- package/src/commands/merge.test.ts +676 -0
- package/src/commands/merge.ts +374 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +150 -0
- package/src/commands/monitor.test.ts +151 -0
- package/src/commands/monitor.ts +394 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +373 -0
- package/src/commands/prime.test.ts +467 -0
- package/src/commands/prime.ts +386 -0
- package/src/commands/replay.test.ts +742 -0
- package/src/commands/replay.ts +367 -0
- package/src/commands/run.test.ts +443 -0
- package/src/commands/run.ts +365 -0
- package/src/commands/server.test.ts +626 -0
- package/src/commands/server.ts +298 -0
- package/src/commands/sling.test.ts +810 -0
- package/src/commands/sling.ts +700 -0
- package/src/commands/spec.test.ts +206 -0
- package/src/commands/spec.ts +171 -0
- package/src/commands/status.test.ts +276 -0
- package/src/commands/status.ts +339 -0
- package/src/commands/stop.test.ts +357 -0
- package/src/commands/stop.ts +119 -0
- package/src/commands/supervisor.test.ts +186 -0
- package/src/commands/supervisor.ts +544 -0
- package/src/commands/trace.test.ts +746 -0
- package/src/commands/trace.ts +332 -0
- package/src/commands/up.test.ts +597 -0
- package/src/commands/up.ts +275 -0
- package/src/commands/watch.test.ts +152 -0
- package/src/commands/watch.ts +238 -0
- package/src/commands/worktree.test.ts +648 -0
- package/src/commands/worktree.ts +266 -0
- package/src/config.test.ts +496 -0
- package/src/config.ts +616 -0
- package/src/doctor/agents.test.ts +448 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +184 -0
- package/src/doctor/config-check.ts +185 -0
- package/src/doctor/consistency.test.ts +645 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +284 -0
- package/src/doctor/databases.ts +211 -0
- package/src/doctor/dependencies.test.ts +150 -0
- package/src/doctor/dependencies.ts +179 -0
- package/src/doctor/logs.test.ts +244 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +210 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +285 -0
- package/src/doctor/structure.ts +195 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +130 -0
- package/src/doctor/version.ts +131 -0
- package/src/e2e/chat-flow.test.ts +346 -0
- package/src/e2e/init-sling-lifecycle.test.ts +288 -0
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +246 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +344 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/global-setup.ts +14 -0
- package/src/index.ts +339 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +118 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +812 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +258 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +873 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +815 -0
- package/src/mail/store.ts +402 -0
- package/src/merge/queue.test.ts +449 -0
- package/src/merge/queue.ts +262 -0
- package/src/merge/resolver.test.ts +1453 -0
- package/src/merge/resolver.ts +759 -0
- package/src/metrics/store.test.ts +1167 -0
- package/src/metrics/store.ts +511 -0
- package/src/metrics/summary.test.ts +397 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +643 -0
- package/src/metrics/transcript.ts +351 -0
- package/src/mulch/client.test.ts +547 -0
- package/src/mulch/client.ts +416 -0
- package/src/server/audit-store.test.ts +384 -0
- package/src/server/audit-store.ts +257 -0
- package/src/server/headless.test.ts +180 -0
- package/src/server/headless.ts +151 -0
- package/src/server/index.test.ts +241 -0
- package/src/server/index.ts +317 -0
- package/src/server/public/app.js +187 -0
- package/src/server/public/apple-touch-icon.png +0 -0
- package/src/server/public/components/agent-badge.js +37 -0
- package/src/server/public/components/data-table.js +114 -0
- package/src/server/public/components/gateway-chat.js +256 -0
- package/src/server/public/components/issue-card.js +96 -0
- package/src/server/public/components/layout.js +88 -0
- package/src/server/public/components/message-bubble.js +120 -0
- package/src/server/public/components/stat-card.js +26 -0
- package/src/server/public/components/terminal-panel.js +140 -0
- package/src/server/public/favicon-16.png +0 -0
- package/src/server/public/favicon-32.png +0 -0
- package/src/server/public/favicon.ico +0 -0
- package/src/server/public/favicon.png +0 -0
- package/src/server/public/index.html +64 -0
- package/src/server/public/lib/api.js +35 -0
- package/src/server/public/lib/markdown.js +8 -0
- package/src/server/public/lib/preact-setup.js +8 -0
- package/src/server/public/lib/state.js +99 -0
- package/src/server/public/lib/utils.js +309 -0
- package/src/server/public/lib/ws.js +79 -0
- package/src/server/public/views/chat.js +983 -0
- package/src/server/public/views/costs.js +692 -0
- package/src/server/public/views/dashboard.js +781 -0
- package/src/server/public/views/gateway-chat.js +622 -0
- package/src/server/public/views/inspect.js +399 -0
- package/src/server/public/views/issues.js +470 -0
- package/src/server/public/views/setup.js +94 -0
- package/src/server/public/views/task-detail.js +422 -0
- package/src/server/routes.test.ts +3816 -0
- package/src/server/routes.ts +1964 -0
- package/src/server/websocket.test.ts +288 -0
- package/src/server/websocket.ts +196 -0
- package/src/sessions/compat.test.ts +109 -0
- package/src/sessions/compat.ts +17 -0
- package/src/sessions/store.test.ts +969 -0
- package/src/sessions/store.ts +480 -0
- package/src/test-helpers.test.ts +97 -0
- package/src/test-helpers.ts +143 -0
- package/src/types.ts +708 -0
- package/src/watchdog/daemon.test.ts +1233 -0
- package/src/watchdog/daemon.ts +533 -0
- package/src/watchdog/health.test.ts +371 -0
- package/src/watchdog/health.ts +248 -0
- package/src/watchdog/triage.test.ts +162 -0
- package/src/watchdog/triage.ts +193 -0
- package/src/worktree/manager.test.ts +444 -0
- package/src/worktree/manager.ts +224 -0
- package/src/worktree/tmux.test.ts +1238 -0
- package/src/worktree/tmux.ts +644 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +132 -0
- package/templates/overlay.md.tmpl +79 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { access, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
4
|
+
import { cleanupTempDir, createTempGitRepo } from "../test-helpers.ts";
|
|
5
|
+
import { initCommand, LEGIO_GITIGNORE } from "./init.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tests for `legio init` -- agent definition deployment.
|
|
9
|
+
*
|
|
10
|
+
* Uses real temp git repos. Suppresses stdout to keep test output clean.
|
|
11
|
+
* process.cwd() is saved/restored because initCommand uses it to find the project root.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const AGENT_DEF_FILES = [
|
|
15
|
+
"scout.md",
|
|
16
|
+
"builder.md",
|
|
17
|
+
"reviewer.md",
|
|
18
|
+
"lead.md",
|
|
19
|
+
"merger.md",
|
|
20
|
+
"supervisor.md",
|
|
21
|
+
"coordinator.md",
|
|
22
|
+
"monitor.md",
|
|
23
|
+
"cto.md",
|
|
24
|
+
"gateway.md",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/** Resolve the source agents directory (same logic as init.ts). */
|
|
28
|
+
const SOURCE_AGENTS_DIR = join(import.meta.dirname, "..", "..", "agents");
|
|
29
|
+
|
|
30
|
+
describe("initCommand: agent-defs deployment", () => {
|
|
31
|
+
let tempDir: string;
|
|
32
|
+
let originalCwd: string;
|
|
33
|
+
let originalWrite: typeof process.stdout.write;
|
|
34
|
+
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
tempDir = await createTempGitRepo();
|
|
37
|
+
originalCwd = process.cwd();
|
|
38
|
+
process.chdir(tempDir);
|
|
39
|
+
|
|
40
|
+
// Suppress stdout noise from initCommand
|
|
41
|
+
originalWrite = process.stdout.write;
|
|
42
|
+
process.stdout.write = (() => true) as typeof process.stdout.write;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
process.chdir(originalCwd);
|
|
47
|
+
process.stdout.write = originalWrite;
|
|
48
|
+
await cleanupTempDir(tempDir);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("creates .legio/agent-defs/ with all 10 agent definition files", async () => {
|
|
52
|
+
await initCommand([]);
|
|
53
|
+
|
|
54
|
+
const agentDefsDir = join(tempDir, ".legio", "agent-defs");
|
|
55
|
+
const files = await readdir(agentDefsDir);
|
|
56
|
+
const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
|
|
57
|
+
|
|
58
|
+
expect(mdFiles).toEqual(AGENT_DEF_FILES.slice().sort());
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("copied files match source content", async () => {
|
|
62
|
+
await initCommand([]);
|
|
63
|
+
|
|
64
|
+
for (const fileName of AGENT_DEF_FILES) {
|
|
65
|
+
const sourcePath = join(SOURCE_AGENTS_DIR, fileName);
|
|
66
|
+
const targetPath = join(tempDir, ".legio", "agent-defs", fileName);
|
|
67
|
+
|
|
68
|
+
const sourceContent = await readFile(sourcePath, "utf-8");
|
|
69
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
70
|
+
|
|
71
|
+
expect(targetContent).toBe(sourceContent);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("--force reinit overwrites existing agent def files", async () => {
|
|
76
|
+
// First init
|
|
77
|
+
await initCommand([]);
|
|
78
|
+
|
|
79
|
+
// Tamper with one of the deployed files
|
|
80
|
+
const tamperPath = join(tempDir, ".legio", "agent-defs", "scout.md");
|
|
81
|
+
await writeFile(tamperPath, "# tampered content\n");
|
|
82
|
+
|
|
83
|
+
// Verify tamper worked
|
|
84
|
+
const tampered = await readFile(tamperPath, "utf-8");
|
|
85
|
+
expect(tampered).toBe("# tampered content\n");
|
|
86
|
+
|
|
87
|
+
// Reinit with --force
|
|
88
|
+
await initCommand(["--force"]);
|
|
89
|
+
|
|
90
|
+
// Verify the file was overwritten with the original source
|
|
91
|
+
const sourceContent = await readFile(join(SOURCE_AGENTS_DIR, "scout.md"), "utf-8");
|
|
92
|
+
const restored = await readFile(tamperPath, "utf-8");
|
|
93
|
+
expect(restored).toBe(sourceContent);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("Stop hook includes mulch learn command", async () => {
|
|
97
|
+
await initCommand([]);
|
|
98
|
+
|
|
99
|
+
const hooksPath = join(tempDir, ".legio", "hooks.json");
|
|
100
|
+
const content = await readFile(hooksPath, "utf-8");
|
|
101
|
+
const parsed = JSON.parse(content);
|
|
102
|
+
const stopHooks = parsed.hooks.Stop[0].hooks;
|
|
103
|
+
|
|
104
|
+
expect(stopHooks.length).toBe(2);
|
|
105
|
+
expect(stopHooks[0].command).toContain("legio log session-end");
|
|
106
|
+
expect(stopHooks[1].command).toBe("mulch learn");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("initCommand: .legio/.gitignore", () => {
|
|
111
|
+
let tempDir: string;
|
|
112
|
+
let originalCwd: string;
|
|
113
|
+
let originalWrite: typeof process.stdout.write;
|
|
114
|
+
|
|
115
|
+
beforeEach(async () => {
|
|
116
|
+
tempDir = await createTempGitRepo();
|
|
117
|
+
originalCwd = process.cwd();
|
|
118
|
+
process.chdir(tempDir);
|
|
119
|
+
|
|
120
|
+
// Suppress stdout noise from initCommand
|
|
121
|
+
originalWrite = process.stdout.write;
|
|
122
|
+
process.stdout.write = (() => true) as typeof process.stdout.write;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
afterEach(async () => {
|
|
126
|
+
process.chdir(originalCwd);
|
|
127
|
+
process.stdout.write = originalWrite;
|
|
128
|
+
await cleanupTempDir(tempDir);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("creates .legio/.gitignore with wildcard+whitelist model", async () => {
|
|
132
|
+
await initCommand([]);
|
|
133
|
+
|
|
134
|
+
const gitignorePath = join(tempDir, ".legio", ".gitignore");
|
|
135
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
136
|
+
|
|
137
|
+
// Verify wildcard+whitelist pattern
|
|
138
|
+
expect(content).toContain("*\n");
|
|
139
|
+
expect(content).toContain("!.gitignore\n");
|
|
140
|
+
expect(content).toContain("!config.yaml\n");
|
|
141
|
+
expect(content).toContain("!agent-manifest.json\n");
|
|
142
|
+
expect(content).toContain("!hooks.json\n");
|
|
143
|
+
expect(content).toContain("!groups.json\n");
|
|
144
|
+
expect(content).toContain("!agent-defs/\n");
|
|
145
|
+
|
|
146
|
+
// Verify it matches the exported constant
|
|
147
|
+
expect(content).toBe(LEGIO_GITIGNORE);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("gitignore is always written when init completes", async () => {
|
|
151
|
+
// Init should write gitignore
|
|
152
|
+
await initCommand([]);
|
|
153
|
+
|
|
154
|
+
const gitignorePath = join(tempDir, ".legio", ".gitignore");
|
|
155
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
156
|
+
|
|
157
|
+
// Verify gitignore was written with correct content
|
|
158
|
+
expect(content).toBe(LEGIO_GITIGNORE);
|
|
159
|
+
|
|
160
|
+
// Verify the file exists
|
|
161
|
+
const exists = await access(gitignorePath)
|
|
162
|
+
.then(() => true)
|
|
163
|
+
.catch(() => false);
|
|
164
|
+
expect(exists).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("--force reinit overwrites stale .legio/.gitignore", async () => {
|
|
168
|
+
// First init
|
|
169
|
+
await initCommand([]);
|
|
170
|
+
|
|
171
|
+
const gitignorePath = join(tempDir, ".legio", ".gitignore");
|
|
172
|
+
|
|
173
|
+
// Tamper with the gitignore file (simulate old deny-list format)
|
|
174
|
+
await writeFile(gitignorePath, "# old format\nworktrees/\nlogs/\nmail.db\n");
|
|
175
|
+
|
|
176
|
+
// Verify tamper worked
|
|
177
|
+
const tampered = await readFile(gitignorePath, "utf-8");
|
|
178
|
+
expect(tampered).not.toContain("*\n");
|
|
179
|
+
expect(tampered).not.toContain("!.gitignore\n");
|
|
180
|
+
|
|
181
|
+
// Reinit with --force
|
|
182
|
+
await initCommand(["--force"]);
|
|
183
|
+
|
|
184
|
+
// Verify the file was overwritten with the new wildcard+whitelist format
|
|
185
|
+
const restored = await readFile(gitignorePath, "utf-8");
|
|
186
|
+
expect(restored).toBe(LEGIO_GITIGNORE);
|
|
187
|
+
expect(restored).toContain("*\n");
|
|
188
|
+
expect(restored).toContain("!.gitignore\n");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("subsequent init without --force does not overwrite gitignore", async () => {
|
|
192
|
+
// First init
|
|
193
|
+
await initCommand([]);
|
|
194
|
+
|
|
195
|
+
const gitignorePath = join(tempDir, ".legio", ".gitignore");
|
|
196
|
+
|
|
197
|
+
// Tamper with the gitignore file
|
|
198
|
+
await writeFile(gitignorePath, "# custom content\n");
|
|
199
|
+
|
|
200
|
+
// Verify tamper worked
|
|
201
|
+
const tampered = await readFile(gitignorePath, "utf-8");
|
|
202
|
+
expect(tampered).toBe("# custom content\n");
|
|
203
|
+
|
|
204
|
+
// Second init without --force should return early (not overwrite)
|
|
205
|
+
await initCommand([]);
|
|
206
|
+
|
|
207
|
+
// Verify the file was NOT overwritten (early return prevented it)
|
|
208
|
+
const afterSecondInit = await readFile(gitignorePath, "utf-8");
|
|
209
|
+
expect(afterSecondInit).toBe("# custom content\n");
|
|
210
|
+
});
|
|
211
|
+
});
|