@os-eco/overstory-cli 0.6.1

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/agents/builder.md +137 -0
  4. package/agents/coordinator.md +263 -0
  5. package/agents/lead.md +301 -0
  6. package/agents/merger.md +160 -0
  7. package/agents/monitor.md +214 -0
  8. package/agents/reviewer.md +140 -0
  9. package/agents/scout.md +119 -0
  10. package/agents/supervisor.md +423 -0
  11. package/package.json +47 -0
  12. package/src/agents/checkpoint.test.ts +88 -0
  13. package/src/agents/checkpoint.ts +101 -0
  14. package/src/agents/hooks-deployer.test.ts +2040 -0
  15. package/src/agents/hooks-deployer.ts +607 -0
  16. package/src/agents/identity.test.ts +603 -0
  17. package/src/agents/identity.ts +384 -0
  18. package/src/agents/lifecycle.test.ts +196 -0
  19. package/src/agents/lifecycle.ts +183 -0
  20. package/src/agents/manifest.test.ts +746 -0
  21. package/src/agents/manifest.ts +354 -0
  22. package/src/agents/overlay.test.ts +676 -0
  23. package/src/agents/overlay.ts +308 -0
  24. package/src/beads/client.test.ts +217 -0
  25. package/src/beads/client.ts +202 -0
  26. package/src/beads/molecules.test.ts +338 -0
  27. package/src/beads/molecules.ts +198 -0
  28. package/src/commands/agents.test.ts +322 -0
  29. package/src/commands/agents.ts +287 -0
  30. package/src/commands/clean.test.ts +670 -0
  31. package/src/commands/clean.ts +618 -0
  32. package/src/commands/completions.test.ts +342 -0
  33. package/src/commands/completions.ts +887 -0
  34. package/src/commands/coordinator.test.ts +1530 -0
  35. package/src/commands/coordinator.ts +733 -0
  36. package/src/commands/costs.test.ts +1119 -0
  37. package/src/commands/costs.ts +564 -0
  38. package/src/commands/dashboard.test.ts +308 -0
  39. package/src/commands/dashboard.ts +838 -0
  40. package/src/commands/doctor.test.ts +294 -0
  41. package/src/commands/doctor.ts +213 -0
  42. package/src/commands/errors.test.ts +647 -0
  43. package/src/commands/errors.ts +248 -0
  44. package/src/commands/feed.test.ts +578 -0
  45. package/src/commands/feed.ts +361 -0
  46. package/src/commands/group.test.ts +262 -0
  47. package/src/commands/group.ts +511 -0
  48. package/src/commands/hooks.test.ts +458 -0
  49. package/src/commands/hooks.ts +253 -0
  50. package/src/commands/init.test.ts +347 -0
  51. package/src/commands/init.ts +650 -0
  52. package/src/commands/inspect.test.ts +670 -0
  53. package/src/commands/inspect.ts +431 -0
  54. package/src/commands/log.test.ts +1454 -0
  55. package/src/commands/log.ts +724 -0
  56. package/src/commands/logs.test.ts +379 -0
  57. package/src/commands/logs.ts +546 -0
  58. package/src/commands/mail.test.ts +1270 -0
  59. package/src/commands/mail.ts +771 -0
  60. package/src/commands/merge.test.ts +670 -0
  61. package/src/commands/merge.ts +355 -0
  62. package/src/commands/metrics.test.ts +444 -0
  63. package/src/commands/metrics.ts +143 -0
  64. package/src/commands/monitor.test.ts +191 -0
  65. package/src/commands/monitor.ts +390 -0
  66. package/src/commands/nudge.test.ts +230 -0
  67. package/src/commands/nudge.ts +372 -0
  68. package/src/commands/prime.test.ts +470 -0
  69. package/src/commands/prime.ts +381 -0
  70. package/src/commands/replay.test.ts +741 -0
  71. package/src/commands/replay.ts +360 -0
  72. package/src/commands/run.test.ts +431 -0
  73. package/src/commands/run.ts +351 -0
  74. package/src/commands/sling.test.ts +657 -0
  75. package/src/commands/sling.ts +661 -0
  76. package/src/commands/spec.test.ts +203 -0
  77. package/src/commands/spec.ts +168 -0
  78. package/src/commands/status.test.ts +430 -0
  79. package/src/commands/status.ts +398 -0
  80. package/src/commands/stop.test.ts +420 -0
  81. package/src/commands/stop.ts +151 -0
  82. package/src/commands/supervisor.test.ts +187 -0
  83. package/src/commands/supervisor.ts +535 -0
  84. package/src/commands/trace.test.ts +745 -0
  85. package/src/commands/trace.ts +325 -0
  86. package/src/commands/watch.test.ts +145 -0
  87. package/src/commands/watch.ts +247 -0
  88. package/src/commands/worktree.test.ts +786 -0
  89. package/src/commands/worktree.ts +311 -0
  90. package/src/config.test.ts +822 -0
  91. package/src/config.ts +829 -0
  92. package/src/doctor/agents.test.ts +454 -0
  93. package/src/doctor/agents.ts +396 -0
  94. package/src/doctor/config-check.test.ts +190 -0
  95. package/src/doctor/config-check.ts +183 -0
  96. package/src/doctor/consistency.test.ts +651 -0
  97. package/src/doctor/consistency.ts +294 -0
  98. package/src/doctor/databases.test.ts +290 -0
  99. package/src/doctor/databases.ts +218 -0
  100. package/src/doctor/dependencies.test.ts +184 -0
  101. package/src/doctor/dependencies.ts +175 -0
  102. package/src/doctor/logs.test.ts +251 -0
  103. package/src/doctor/logs.ts +295 -0
  104. package/src/doctor/merge-queue.test.ts +216 -0
  105. package/src/doctor/merge-queue.ts +144 -0
  106. package/src/doctor/structure.test.ts +291 -0
  107. package/src/doctor/structure.ts +198 -0
  108. package/src/doctor/types.ts +37 -0
  109. package/src/doctor/version.test.ts +136 -0
  110. package/src/doctor/version.ts +129 -0
  111. package/src/e2e/init-sling-lifecycle.test.ts +277 -0
  112. package/src/errors.ts +217 -0
  113. package/src/events/store.test.ts +660 -0
  114. package/src/events/store.ts +369 -0
  115. package/src/events/tool-filter.test.ts +330 -0
  116. package/src/events/tool-filter.ts +126 -0
  117. package/src/index.ts +316 -0
  118. package/src/insights/analyzer.test.ts +466 -0
  119. package/src/insights/analyzer.ts +203 -0
  120. package/src/logging/color.test.ts +142 -0
  121. package/src/logging/color.ts +71 -0
  122. package/src/logging/logger.test.ts +813 -0
  123. package/src/logging/logger.ts +266 -0
  124. package/src/logging/reporter.test.ts +259 -0
  125. package/src/logging/reporter.ts +109 -0
  126. package/src/logging/sanitizer.test.ts +190 -0
  127. package/src/logging/sanitizer.ts +57 -0
  128. package/src/mail/broadcast.test.ts +203 -0
  129. package/src/mail/broadcast.ts +92 -0
  130. package/src/mail/client.test.ts +773 -0
  131. package/src/mail/client.ts +223 -0
  132. package/src/mail/store.test.ts +705 -0
  133. package/src/mail/store.ts +387 -0
  134. package/src/merge/queue.test.ts +359 -0
  135. package/src/merge/queue.ts +231 -0
  136. package/src/merge/resolver.test.ts +1345 -0
  137. package/src/merge/resolver.ts +645 -0
  138. package/src/metrics/store.test.ts +667 -0
  139. package/src/metrics/store.ts +445 -0
  140. package/src/metrics/summary.test.ts +398 -0
  141. package/src/metrics/summary.ts +178 -0
  142. package/src/metrics/transcript.test.ts +356 -0
  143. package/src/metrics/transcript.ts +175 -0
  144. package/src/mulch/client.test.ts +671 -0
  145. package/src/mulch/client.ts +332 -0
  146. package/src/sessions/compat.test.ts +280 -0
  147. package/src/sessions/compat.ts +104 -0
  148. package/src/sessions/store.test.ts +873 -0
  149. package/src/sessions/store.ts +494 -0
  150. package/src/test-helpers.test.ts +124 -0
  151. package/src/test-helpers.ts +126 -0
  152. package/src/tracker/beads.ts +56 -0
  153. package/src/tracker/factory.test.ts +80 -0
  154. package/src/tracker/factory.ts +64 -0
  155. package/src/tracker/seeds.ts +182 -0
  156. package/src/tracker/types.ts +52 -0
  157. package/src/types.ts +724 -0
  158. package/src/watchdog/daemon.test.ts +1975 -0
  159. package/src/watchdog/daemon.ts +671 -0
  160. package/src/watchdog/health.test.ts +431 -0
  161. package/src/watchdog/health.ts +264 -0
  162. package/src/watchdog/triage.test.ts +164 -0
  163. package/src/watchdog/triage.ts +179 -0
  164. package/src/worktree/manager.test.ts +439 -0
  165. package/src/worktree/manager.ts +198 -0
  166. package/src/worktree/tmux.test.ts +1009 -0
  167. package/src/worktree/tmux.ts +509 -0
  168. package/templates/CLAUDE.md.tmpl +89 -0
  169. package/templates/hooks.json.tmpl +105 -0
  170. package/templates/overlay.md.tmpl +81 -0
@@ -0,0 +1,183 @@
1
+ import { mkdir } 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 file = Bun.file(filePath);
16
+ const exists = await file.exists();
17
+
18
+ if (!exists) {
19
+ return [];
20
+ }
21
+
22
+ try {
23
+ const text = await file.text();
24
+ return JSON.parse(text) as SessionHandoff[];
25
+ } catch (err) {
26
+ throw new LifecycleError(`Failed to read handoffs: ${filePath}`, {
27
+ agentName,
28
+ cause: err instanceof Error ? err : undefined,
29
+ });
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Write handoffs array to disk.
35
+ */
36
+ async function writeHandoffs(
37
+ agentsDir: string,
38
+ agentName: string,
39
+ handoffs: SessionHandoff[],
40
+ ): Promise<void> {
41
+ const filePath = join(agentsDir, agentName, HANDOFFS_FILENAME);
42
+ const dir = dirname(filePath);
43
+
44
+ try {
45
+ await mkdir(dir, { recursive: true });
46
+ } catch (err) {
47
+ throw new LifecycleError(`Failed to create handoffs directory: ${dir}`, {
48
+ agentName,
49
+ cause: err instanceof Error ? err : undefined,
50
+ });
51
+ }
52
+
53
+ try {
54
+ await Bun.write(filePath, `${JSON.stringify(handoffs, null, "\t")}\n`);
55
+ } catch (err) {
56
+ throw new LifecycleError(`Failed to write handoffs: ${filePath}`, {
57
+ agentName,
58
+ cause: err instanceof Error ? err : undefined,
59
+ });
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Initiate a session handoff.
65
+ *
66
+ * 1. Builds a SessionCheckpoint from options
67
+ * 2. Saves the checkpoint to disk
68
+ * 3. Builds a SessionHandoff record
69
+ * 4. Appends to handoffs.json
70
+ * 5. Returns the handoff
71
+ */
72
+ export async function initiateHandoff(options: {
73
+ agentsDir: string;
74
+ agentName: string;
75
+ sessionId: string;
76
+ beadId: string;
77
+ reason: SessionHandoff["reason"];
78
+ progressSummary: string;
79
+ pendingWork: string;
80
+ currentBranch: string;
81
+ filesModified: string[];
82
+ mulchDomains: string[];
83
+ }): Promise<SessionHandoff> {
84
+ const checkpoint: SessionCheckpoint = {
85
+ agentName: options.agentName,
86
+ beadId: options.beadId,
87
+ sessionId: options.sessionId,
88
+ timestamp: new Date().toISOString(),
89
+ progressSummary: options.progressSummary,
90
+ filesModified: options.filesModified,
91
+ currentBranch: options.currentBranch,
92
+ pendingWork: options.pendingWork,
93
+ mulchDomains: options.mulchDomains,
94
+ };
95
+
96
+ await saveCheckpoint(options.agentsDir, checkpoint);
97
+
98
+ const handoff: SessionHandoff = {
99
+ fromSessionId: options.sessionId,
100
+ toSessionId: null,
101
+ checkpoint,
102
+ reason: options.reason,
103
+ handoffAt: new Date().toISOString(),
104
+ };
105
+
106
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
107
+ handoffs.push(handoff);
108
+ await writeHandoffs(options.agentsDir, options.agentName, handoffs);
109
+
110
+ return handoff;
111
+ }
112
+
113
+ /**
114
+ * Resume from a pending handoff.
115
+ *
116
+ * Finds the most recent handoff where `toSessionId` is null,
117
+ * loads the associated checkpoint, and returns both.
118
+ * Returns null if no pending handoff exists.
119
+ */
120
+ export async function resumeFromHandoff(options: {
121
+ agentsDir: string;
122
+ agentName: string;
123
+ }): Promise<{ checkpoint: SessionCheckpoint; handoff: SessionHandoff } | null> {
124
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
125
+
126
+ // Find most recent pending handoff (search from end)
127
+ let pendingHandoff: SessionHandoff | undefined;
128
+ for (let i = handoffs.length - 1; i >= 0; i--) {
129
+ const h = handoffs[i];
130
+ if (h !== undefined && h.toSessionId === null) {
131
+ pendingHandoff = h;
132
+ break;
133
+ }
134
+ }
135
+
136
+ if (pendingHandoff === undefined) {
137
+ return null;
138
+ }
139
+
140
+ const checkpoint = await loadCheckpoint(options.agentsDir, options.agentName);
141
+ if (checkpoint === null) {
142
+ return null;
143
+ }
144
+
145
+ return { checkpoint, handoff: pendingHandoff };
146
+ }
147
+
148
+ /**
149
+ * Complete a pending handoff.
150
+ *
151
+ * 1. Loads handoffs.json
152
+ * 2. Finds the most recent handoff with toSessionId === null
153
+ * 3. Sets toSessionId to the new session ID
154
+ * 4. Writes back handoffs.json
155
+ * 5. Clears the checkpoint
156
+ */
157
+ export async function completeHandoff(options: {
158
+ agentsDir: string;
159
+ agentName: string;
160
+ newSessionId: string;
161
+ }): Promise<void> {
162
+ const handoffs = await loadHandoffs(options.agentsDir, options.agentName);
163
+
164
+ // Find most recent pending handoff (search from end)
165
+ let found = false;
166
+ for (let i = handoffs.length - 1; i >= 0; i--) {
167
+ const h = handoffs[i];
168
+ if (h !== undefined && h.toSessionId === null) {
169
+ h.toSessionId = options.newSessionId;
170
+ found = true;
171
+ break;
172
+ }
173
+ }
174
+
175
+ if (!found) {
176
+ throw new LifecycleError("No pending handoff to complete", {
177
+ agentName: options.agentName,
178
+ });
179
+ }
180
+
181
+ await writeHandoffs(options.agentsDir, options.agentName, handoffs);
182
+ await clearCheckpoint(options.agentsDir, options.agentName);
183
+ }