@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.
Files changed (219) hide show
  1. package/CHANGELOG.md +422 -0
  2. package/LICENSE +21 -0
  3. package/README.md +555 -0
  4. package/agents/builder.md +141 -0
  5. package/agents/coordinator.md +351 -0
  6. package/agents/cto.md +196 -0
  7. package/agents/gateway.md +276 -0
  8. package/agents/lead.md +281 -0
  9. package/agents/merger.md +156 -0
  10. package/agents/monitor.md +212 -0
  11. package/agents/reviewer.md +142 -0
  12. package/agents/scout.md +131 -0
  13. package/agents/supervisor.md +416 -0
  14. package/bin/legio.mjs +38 -0
  15. package/package.json +77 -0
  16. package/src/agents/checkpoint.test.ts +88 -0
  17. package/src/agents/checkpoint.ts +102 -0
  18. package/src/agents/hooks-deployer.test.ts +1820 -0
  19. package/src/agents/hooks-deployer.ts +574 -0
  20. package/src/agents/identity.test.ts +614 -0
  21. package/src/agents/identity.ts +385 -0
  22. package/src/agents/lifecycle.test.ts +202 -0
  23. package/src/agents/lifecycle.ts +184 -0
  24. package/src/agents/manifest.test.ts +558 -0
  25. package/src/agents/manifest.ts +297 -0
  26. package/src/agents/overlay.test.ts +592 -0
  27. package/src/agents/overlay.ts +316 -0
  28. package/src/beads/client.test.ts +210 -0
  29. package/src/beads/client.ts +227 -0
  30. package/src/beads/molecules.test.ts +320 -0
  31. package/src/beads/molecules.ts +209 -0
  32. package/src/commands/agents.test.ts +325 -0
  33. package/src/commands/agents.ts +286 -0
  34. package/src/commands/clean.test.ts +730 -0
  35. package/src/commands/clean.ts +653 -0
  36. package/src/commands/completions.test.ts +346 -0
  37. package/src/commands/completions.ts +950 -0
  38. package/src/commands/coordinator.test.ts +1524 -0
  39. package/src/commands/coordinator.ts +880 -0
  40. package/src/commands/costs.test.ts +1015 -0
  41. package/src/commands/costs.ts +473 -0
  42. package/src/commands/dashboard.test.ts +94 -0
  43. package/src/commands/dashboard.ts +607 -0
  44. package/src/commands/doctor.test.ts +295 -0
  45. package/src/commands/doctor.ts +213 -0
  46. package/src/commands/down.test.ts +308 -0
  47. package/src/commands/down.ts +124 -0
  48. package/src/commands/errors.test.ts +648 -0
  49. package/src/commands/errors.ts +255 -0
  50. package/src/commands/feed.test.ts +579 -0
  51. package/src/commands/feed.ts +368 -0
  52. package/src/commands/gateway.test.ts +698 -0
  53. package/src/commands/gateway.ts +419 -0
  54. package/src/commands/group.test.ts +262 -0
  55. package/src/commands/group.ts +539 -0
  56. package/src/commands/hooks.test.ts +292 -0
  57. package/src/commands/hooks.ts +210 -0
  58. package/src/commands/init.test.ts +211 -0
  59. package/src/commands/init.ts +622 -0
  60. package/src/commands/inspect.test.ts +670 -0
  61. package/src/commands/inspect.ts +455 -0
  62. package/src/commands/log.test.ts +1556 -0
  63. package/src/commands/log.ts +752 -0
  64. package/src/commands/logs.test.ts +379 -0
  65. package/src/commands/logs.ts +544 -0
  66. package/src/commands/mail.test.ts +1726 -0
  67. package/src/commands/mail.ts +926 -0
  68. package/src/commands/merge.test.ts +676 -0
  69. package/src/commands/merge.ts +374 -0
  70. package/src/commands/metrics.test.ts +444 -0
  71. package/src/commands/metrics.ts +150 -0
  72. package/src/commands/monitor.test.ts +151 -0
  73. package/src/commands/monitor.ts +394 -0
  74. package/src/commands/nudge.test.ts +230 -0
  75. package/src/commands/nudge.ts +373 -0
  76. package/src/commands/prime.test.ts +467 -0
  77. package/src/commands/prime.ts +386 -0
  78. package/src/commands/replay.test.ts +742 -0
  79. package/src/commands/replay.ts +367 -0
  80. package/src/commands/run.test.ts +443 -0
  81. package/src/commands/run.ts +365 -0
  82. package/src/commands/server.test.ts +626 -0
  83. package/src/commands/server.ts +298 -0
  84. package/src/commands/sling.test.ts +810 -0
  85. package/src/commands/sling.ts +700 -0
  86. package/src/commands/spec.test.ts +206 -0
  87. package/src/commands/spec.ts +171 -0
  88. package/src/commands/status.test.ts +276 -0
  89. package/src/commands/status.ts +339 -0
  90. package/src/commands/stop.test.ts +357 -0
  91. package/src/commands/stop.ts +119 -0
  92. package/src/commands/supervisor.test.ts +186 -0
  93. package/src/commands/supervisor.ts +544 -0
  94. package/src/commands/trace.test.ts +746 -0
  95. package/src/commands/trace.ts +332 -0
  96. package/src/commands/up.test.ts +597 -0
  97. package/src/commands/up.ts +275 -0
  98. package/src/commands/watch.test.ts +152 -0
  99. package/src/commands/watch.ts +238 -0
  100. package/src/commands/worktree.test.ts +648 -0
  101. package/src/commands/worktree.ts +266 -0
  102. package/src/config.test.ts +496 -0
  103. package/src/config.ts +616 -0
  104. package/src/doctor/agents.test.ts +448 -0
  105. package/src/doctor/agents.ts +396 -0
  106. package/src/doctor/config-check.test.ts +184 -0
  107. package/src/doctor/config-check.ts +185 -0
  108. package/src/doctor/consistency.test.ts +645 -0
  109. package/src/doctor/consistency.ts +294 -0
  110. package/src/doctor/databases.test.ts +284 -0
  111. package/src/doctor/databases.ts +211 -0
  112. package/src/doctor/dependencies.test.ts +150 -0
  113. package/src/doctor/dependencies.ts +179 -0
  114. package/src/doctor/logs.test.ts +244 -0
  115. package/src/doctor/logs.ts +295 -0
  116. package/src/doctor/merge-queue.test.ts +210 -0
  117. package/src/doctor/merge-queue.ts +144 -0
  118. package/src/doctor/structure.test.ts +285 -0
  119. package/src/doctor/structure.ts +195 -0
  120. package/src/doctor/types.ts +37 -0
  121. package/src/doctor/version.test.ts +130 -0
  122. package/src/doctor/version.ts +131 -0
  123. package/src/e2e/chat-flow.test.ts +346 -0
  124. package/src/e2e/init-sling-lifecycle.test.ts +288 -0
  125. package/src/errors.test.ts +21 -0
  126. package/src/errors.ts +246 -0
  127. package/src/events/store.test.ts +660 -0
  128. package/src/events/store.ts +344 -0
  129. package/src/events/tool-filter.test.ts +330 -0
  130. package/src/events/tool-filter.ts +126 -0
  131. package/src/global-setup.ts +14 -0
  132. package/src/index.ts +339 -0
  133. package/src/insights/analyzer.test.ts +466 -0
  134. package/src/insights/analyzer.ts +203 -0
  135. package/src/logging/color.test.ts +118 -0
  136. package/src/logging/color.ts +71 -0
  137. package/src/logging/logger.test.ts +812 -0
  138. package/src/logging/logger.ts +266 -0
  139. package/src/logging/reporter.test.ts +258 -0
  140. package/src/logging/reporter.ts +109 -0
  141. package/src/logging/sanitizer.test.ts +190 -0
  142. package/src/logging/sanitizer.ts +57 -0
  143. package/src/mail/broadcast.test.ts +203 -0
  144. package/src/mail/broadcast.ts +92 -0
  145. package/src/mail/client.test.ts +873 -0
  146. package/src/mail/client.ts +236 -0
  147. package/src/mail/store.test.ts +815 -0
  148. package/src/mail/store.ts +402 -0
  149. package/src/merge/queue.test.ts +449 -0
  150. package/src/merge/queue.ts +262 -0
  151. package/src/merge/resolver.test.ts +1453 -0
  152. package/src/merge/resolver.ts +759 -0
  153. package/src/metrics/store.test.ts +1167 -0
  154. package/src/metrics/store.ts +511 -0
  155. package/src/metrics/summary.test.ts +397 -0
  156. package/src/metrics/summary.ts +178 -0
  157. package/src/metrics/transcript.test.ts +643 -0
  158. package/src/metrics/transcript.ts +351 -0
  159. package/src/mulch/client.test.ts +547 -0
  160. package/src/mulch/client.ts +416 -0
  161. package/src/server/audit-store.test.ts +384 -0
  162. package/src/server/audit-store.ts +257 -0
  163. package/src/server/headless.test.ts +180 -0
  164. package/src/server/headless.ts +151 -0
  165. package/src/server/index.test.ts +241 -0
  166. package/src/server/index.ts +317 -0
  167. package/src/server/public/app.js +187 -0
  168. package/src/server/public/apple-touch-icon.png +0 -0
  169. package/src/server/public/components/agent-badge.js +37 -0
  170. package/src/server/public/components/data-table.js +114 -0
  171. package/src/server/public/components/gateway-chat.js +256 -0
  172. package/src/server/public/components/issue-card.js +96 -0
  173. package/src/server/public/components/layout.js +88 -0
  174. package/src/server/public/components/message-bubble.js +120 -0
  175. package/src/server/public/components/stat-card.js +26 -0
  176. package/src/server/public/components/terminal-panel.js +140 -0
  177. package/src/server/public/favicon-16.png +0 -0
  178. package/src/server/public/favicon-32.png +0 -0
  179. package/src/server/public/favicon.ico +0 -0
  180. package/src/server/public/favicon.png +0 -0
  181. package/src/server/public/index.html +64 -0
  182. package/src/server/public/lib/api.js +35 -0
  183. package/src/server/public/lib/markdown.js +8 -0
  184. package/src/server/public/lib/preact-setup.js +8 -0
  185. package/src/server/public/lib/state.js +99 -0
  186. package/src/server/public/lib/utils.js +309 -0
  187. package/src/server/public/lib/ws.js +79 -0
  188. package/src/server/public/views/chat.js +983 -0
  189. package/src/server/public/views/costs.js +692 -0
  190. package/src/server/public/views/dashboard.js +781 -0
  191. package/src/server/public/views/gateway-chat.js +622 -0
  192. package/src/server/public/views/inspect.js +399 -0
  193. package/src/server/public/views/issues.js +470 -0
  194. package/src/server/public/views/setup.js +94 -0
  195. package/src/server/public/views/task-detail.js +422 -0
  196. package/src/server/routes.test.ts +3816 -0
  197. package/src/server/routes.ts +1964 -0
  198. package/src/server/websocket.test.ts +288 -0
  199. package/src/server/websocket.ts +196 -0
  200. package/src/sessions/compat.test.ts +109 -0
  201. package/src/sessions/compat.ts +17 -0
  202. package/src/sessions/store.test.ts +969 -0
  203. package/src/sessions/store.ts +480 -0
  204. package/src/test-helpers.test.ts +97 -0
  205. package/src/test-helpers.ts +143 -0
  206. package/src/types.ts +708 -0
  207. package/src/watchdog/daemon.test.ts +1233 -0
  208. package/src/watchdog/daemon.ts +533 -0
  209. package/src/watchdog/health.test.ts +371 -0
  210. package/src/watchdog/health.ts +248 -0
  211. package/src/watchdog/triage.test.ts +162 -0
  212. package/src/watchdog/triage.ts +193 -0
  213. package/src/worktree/manager.test.ts +444 -0
  214. package/src/worktree/manager.ts +224 -0
  215. package/src/worktree/tmux.test.ts +1238 -0
  216. package/src/worktree/tmux.ts +644 -0
  217. package/templates/CLAUDE.md.tmpl +89 -0
  218. package/templates/hooks.json.tmpl +132 -0
  219. package/templates/overlay.md.tmpl +79 -0
@@ -0,0 +1,184 @@
1
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { LifecycleError } from "../errors.ts";
4
+ import type { SessionCheckpoint, SessionHandoff } from "../types.ts";
5
+ import { clearCheckpoint, loadCheckpoint, saveCheckpoint } from "./checkpoint.ts";
6
+
7
+ const HANDOFFS_FILENAME = "handoffs.json";
8
+
9
+ /**
10
+ * Load handoffs array from disk.
11
+ * Returns an empty array if the file doesn't exist.
12
+ */
13
+ async function loadHandoffs(agentsDir: string, agentName: string): Promise<SessionHandoff[]> {
14
+ const filePath = join(agentsDir, agentName, HANDOFFS_FILENAME);
15
+ const exists = await access(filePath)
16
+ .then(() => true)
17
+ .catch(() => false);
18
+
19
+ if (!exists) {
20
+ return [];
21
+ }
22
+
23
+ try {
24
+ const text = await readFile(filePath, "utf-8");
25
+ return JSON.parse(text) as SessionHandoff[];
26
+ } catch (err) {
27
+ throw new LifecycleError(`Failed to read handoffs: ${filePath}`, {
28
+ agentName,
29
+ cause: err instanceof Error ? err : undefined,
30
+ });
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Write handoffs array to disk.
36
+ */
37
+ async function writeHandoffs(
38
+ agentsDir: string,
39
+ agentName: string,
40
+ handoffs: SessionHandoff[],
41
+ ): Promise<void> {
42
+ const filePath = join(agentsDir, agentName, HANDOFFS_FILENAME);
43
+ const dir = dirname(filePath);
44
+
45
+ try {
46
+ await mkdir(dir, { recursive: true });
47
+ } catch (err) {
48
+ throw new LifecycleError(`Failed to create handoffs directory: ${dir}`, {
49
+ agentName,
50
+ cause: err instanceof Error ? err : undefined,
51
+ });
52
+ }
53
+
54
+ try {
55
+ await writeFile(filePath, `${JSON.stringify(handoffs, null, "\t")}\n`);
56
+ } catch (err) {
57
+ throw new LifecycleError(`Failed to write handoffs: ${filePath}`, {
58
+ agentName,
59
+ cause: err instanceof Error ? err : undefined,
60
+ });
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Initiate a session handoff.
66
+ *
67
+ * 1. Builds a SessionCheckpoint from options
68
+ * 2. Saves the checkpoint to disk
69
+ * 3. Builds a SessionHandoff record
70
+ * 4. Appends to handoffs.json
71
+ * 5. Returns the handoff
72
+ */
73
+ export async function initiateHandoff(options: {
74
+ agentsDir: string;
75
+ agentName: string;
76
+ sessionId: string;
77
+ beadId: string;
78
+ reason: SessionHandoff["reason"];
79
+ progressSummary: string;
80
+ pendingWork: string;
81
+ currentBranch: string;
82
+ filesModified: string[];
83
+ mulchDomains: string[];
84
+ }): Promise<SessionHandoff> {
85
+ const checkpoint: SessionCheckpoint = {
86
+ agentName: options.agentName,
87
+ beadId: options.beadId,
88
+ sessionId: options.sessionId,
89
+ timestamp: new Date().toISOString(),
90
+ progressSummary: options.progressSummary,
91
+ filesModified: options.filesModified,
92
+ currentBranch: options.currentBranch,
93
+ pendingWork: options.pendingWork,
94
+ mulchDomains: options.mulchDomains,
95
+ };
96
+
97
+ await saveCheckpoint(options.agentsDir, checkpoint);
98
+
99
+ const handoff: SessionHandoff = {
100
+ fromSessionId: options.sessionId,
101
+ toSessionId: null,
102
+ checkpoint,
103
+ reason: options.reason,
104
+ handoffAt: new Date().toISOString(),
105
+ };
106
+
107
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
108
+ handoffs.push(handoff);
109
+ await writeHandoffs(options.agentsDir, options.agentName, handoffs);
110
+
111
+ return handoff;
112
+ }
113
+
114
+ /**
115
+ * Resume from a pending handoff.
116
+ *
117
+ * Finds the most recent handoff where `toSessionId` is null,
118
+ * loads the associated checkpoint, and returns both.
119
+ * Returns null if no pending handoff exists.
120
+ */
121
+ export async function resumeFromHandoff(options: {
122
+ agentsDir: string;
123
+ agentName: string;
124
+ }): Promise<{ checkpoint: SessionCheckpoint; handoff: SessionHandoff } | null> {
125
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
126
+
127
+ // Find most recent pending handoff (search from end)
128
+ let pendingHandoff: SessionHandoff | undefined;
129
+ for (let i = handoffs.length - 1; i >= 0; i--) {
130
+ const h = handoffs[i];
131
+ if (h !== undefined && h.toSessionId === null) {
132
+ pendingHandoff = h;
133
+ break;
134
+ }
135
+ }
136
+
137
+ if (pendingHandoff === undefined) {
138
+ return null;
139
+ }
140
+
141
+ const checkpoint = await loadCheckpoint(options.agentsDir, options.agentName);
142
+ if (checkpoint === null) {
143
+ return null;
144
+ }
145
+
146
+ return { checkpoint, handoff: pendingHandoff };
147
+ }
148
+
149
+ /**
150
+ * Complete a pending handoff.
151
+ *
152
+ * 1. Loads handoffs.json
153
+ * 2. Finds the most recent handoff with toSessionId === null
154
+ * 3. Sets toSessionId to the new session ID
155
+ * 4. Writes back handoffs.json
156
+ * 5. Clears the checkpoint
157
+ */
158
+ export async function completeHandoff(options: {
159
+ agentsDir: string;
160
+ agentName: string;
161
+ newSessionId: string;
162
+ }): Promise<void> {
163
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
164
+
165
+ // Find most recent pending handoff (search from end)
166
+ let found = false;
167
+ for (let i = handoffs.length - 1; i >= 0; i--) {
168
+ const h = handoffs[i];
169
+ if (h !== undefined && h.toSessionId === null) {
170
+ h.toSessionId = options.newSessionId;
171
+ found = true;
172
+ break;
173
+ }
174
+ }
175
+
176
+ if (!found) {
177
+ throw new LifecycleError("No pending handoff to complete", {
178
+ agentName: options.agentName,
179
+ });
180
+ }
181
+
182
+ await writeHandoffs(options.agentsDir, options.agentName, handoffs);
183
+ await clearCheckpoint(options.agentsDir, options.agentName);
184
+ }