@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,248 @@
1
+ /**
2
+ * CLI command: overstory errors [--agent <name>] [--run <id>] [--json] [--since <ts>] [--until <ts>] [--limit <n>]
3
+ *
4
+ * Shows aggregated error-level events across all agents.
5
+ * Errors can be filtered by agent name, run ID, or time range.
6
+ * Human output groups errors by agent; JSON output returns a flat array.
7
+ */
8
+
9
+ import { join } from "node:path";
10
+ import { loadConfig } from "../config.ts";
11
+ import { ValidationError } from "../errors.ts";
12
+ import { createEventStore } from "../events/store.ts";
13
+ import { color } from "../logging/color.ts";
14
+ import type { StoredEvent } from "../types.ts";
15
+
16
+ /**
17
+ * Parse a named flag value from args.
18
+ */
19
+ function getFlag(args: string[], flag: string): string | undefined {
20
+ const idx = args.indexOf(flag);
21
+ if (idx === -1 || idx + 1 >= args.length) {
22
+ return undefined;
23
+ }
24
+ return args[idx + 1];
25
+ }
26
+
27
+ function hasFlag(args: string[], flag: string): boolean {
28
+ return args.includes(flag);
29
+ }
30
+
31
+ /**
32
+ * Format an absolute time from an ISO timestamp.
33
+ * Returns "HH:MM:SS" portion.
34
+ */
35
+ function formatAbsoluteTime(timestamp: string): string {
36
+ const match = /T(\d{2}:\d{2}:\d{2})/.exec(timestamp);
37
+ if (match?.[1]) {
38
+ return match[1];
39
+ }
40
+ return timestamp;
41
+ }
42
+
43
+ /**
44
+ * Format the date portion of an ISO timestamp.
45
+ * Returns "YYYY-MM-DD".
46
+ */
47
+ function formatDate(timestamp: string): string {
48
+ const match = /^(\d{4}-\d{2}-\d{2})/.exec(timestamp);
49
+ if (match?.[1]) {
50
+ return match[1];
51
+ }
52
+ return "";
53
+ }
54
+
55
+ /**
56
+ * Build a detail string for an error event based on its fields.
57
+ */
58
+ function buildErrorDetail(event: StoredEvent): string {
59
+ const parts: string[] = [];
60
+
61
+ if (event.toolName) {
62
+ parts.push(`tool=${event.toolName}`);
63
+ }
64
+
65
+ if (event.data) {
66
+ try {
67
+ const parsed: unknown = JSON.parse(event.data);
68
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
69
+ const data = parsed as Record<string, unknown>;
70
+ for (const [key, value] of Object.entries(data)) {
71
+ if (value !== null && value !== undefined) {
72
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
73
+ // Truncate long values
74
+ const truncated = strValue.length > 80 ? `${strValue.slice(0, 77)}...` : strValue;
75
+ parts.push(`${key}=${truncated}`);
76
+ }
77
+ }
78
+ }
79
+ } catch {
80
+ // data is not valid JSON; show it raw if short enough
81
+ if (event.data.length <= 80) {
82
+ parts.push(event.data);
83
+ }
84
+ }
85
+ }
86
+
87
+ return parts.join(" ");
88
+ }
89
+
90
+ /**
91
+ * Group errors by agent name, preserving insertion order.
92
+ */
93
+ function groupByAgent(events: StoredEvent[]): Map<string, StoredEvent[]> {
94
+ const groups = new Map<string, StoredEvent[]>();
95
+ for (const event of events) {
96
+ const existing = groups.get(event.agentName);
97
+ if (existing) {
98
+ existing.push(event);
99
+ } else {
100
+ groups.set(event.agentName, [event]);
101
+ }
102
+ }
103
+ return groups;
104
+ }
105
+
106
+ /**
107
+ * Print errors grouped by agent with ANSI colors.
108
+ */
109
+ function printErrors(events: StoredEvent[]): void {
110
+ const w = process.stdout.write.bind(process.stdout);
111
+
112
+ w(`${color.bold}${color.red}Errors${color.reset}\n`);
113
+ w(`${"=".repeat(70)}\n`);
114
+
115
+ if (events.length === 0) {
116
+ w(`${color.dim}No errors found.${color.reset}\n`);
117
+ return;
118
+ }
119
+
120
+ w(`${color.dim}${events.length} error${events.length === 1 ? "" : "s"}${color.reset}\n\n`);
121
+
122
+ const grouped = groupByAgent(events);
123
+
124
+ let firstGroup = true;
125
+ for (const [agentName, agentEvents] of grouped) {
126
+ if (!firstGroup) {
127
+ w("\n");
128
+ }
129
+ firstGroup = false;
130
+
131
+ w(
132
+ `${color.bold}${agentName}${color.reset} ${color.dim}(${agentEvents.length} error${agentEvents.length === 1 ? "" : "s"})${color.reset}\n`,
133
+ );
134
+
135
+ for (const event of agentEvents) {
136
+ const date = formatDate(event.createdAt);
137
+ const time = formatAbsoluteTime(event.createdAt);
138
+ const timestamp = date ? `${date} ${time}` : time;
139
+
140
+ const detail = buildErrorDetail(event);
141
+ const detailSuffix = detail ? ` ${color.dim}${detail}${color.reset}` : "";
142
+
143
+ w(
144
+ ` ${color.dim}${timestamp}${color.reset} ${color.red}${color.bold}ERROR${color.reset}${detailSuffix}\n`,
145
+ );
146
+ }
147
+ }
148
+ }
149
+
150
+ const ERRORS_HELP = `overstory errors -- Aggregated error view across agents
151
+
152
+ Usage: overstory errors [options]
153
+
154
+ Options:
155
+ --agent <name> Filter errors by agent name
156
+ --run <id> Filter errors by run ID
157
+ --since <timestamp> Start time filter (ISO 8601)
158
+ --until <timestamp> End time filter (ISO 8601)
159
+ --limit <n> Max errors to show (default: 100)
160
+ --json Output as JSON array of StoredEvent objects
161
+ --help, -h Show this help`;
162
+
163
+ /**
164
+ * Entry point for `overstory errors [--agent <name>] [--run <id>] [--json] [--since] [--until] [--limit]`.
165
+ */
166
+ export async function errorsCommand(args: string[]): Promise<void> {
167
+ if (args.includes("--help") || args.includes("-h")) {
168
+ process.stdout.write(`${ERRORS_HELP}\n`);
169
+ return;
170
+ }
171
+
172
+ const json = hasFlag(args, "--json");
173
+ const agentName = getFlag(args, "--agent");
174
+ const runId = getFlag(args, "--run");
175
+ const sinceStr = getFlag(args, "--since");
176
+ const untilStr = getFlag(args, "--until");
177
+ const limitStr = getFlag(args, "--limit");
178
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 100;
179
+
180
+ if (Number.isNaN(limit) || limit < 1) {
181
+ throw new ValidationError("--limit must be a positive integer", {
182
+ field: "limit",
183
+ value: limitStr,
184
+ });
185
+ }
186
+
187
+ // Validate timestamps if provided
188
+ if (sinceStr !== undefined && Number.isNaN(new Date(sinceStr).getTime())) {
189
+ throw new ValidationError("--since must be a valid ISO 8601 timestamp", {
190
+ field: "since",
191
+ value: sinceStr,
192
+ });
193
+ }
194
+ if (untilStr !== undefined && Number.isNaN(new Date(untilStr).getTime())) {
195
+ throw new ValidationError("--until must be a valid ISO 8601 timestamp", {
196
+ field: "until",
197
+ value: untilStr,
198
+ });
199
+ }
200
+
201
+ const cwd = process.cwd();
202
+ const config = await loadConfig(cwd);
203
+ const overstoryDir = join(config.project.root, ".overstory");
204
+
205
+ // Open event store
206
+ const eventsDbPath = join(overstoryDir, "events.db");
207
+ const eventsFile = Bun.file(eventsDbPath);
208
+ if (!(await eventsFile.exists())) {
209
+ if (json) {
210
+ process.stdout.write("[]\n");
211
+ } else {
212
+ process.stdout.write("No events data yet.\n");
213
+ }
214
+ return;
215
+ }
216
+
217
+ const eventStore = createEventStore(eventsDbPath);
218
+
219
+ try {
220
+ const queryOpts = {
221
+ since: sinceStr,
222
+ until: untilStr,
223
+ limit,
224
+ };
225
+
226
+ let events: StoredEvent[];
227
+
228
+ if (agentName !== undefined) {
229
+ // Filter by agent: use getByAgent with level filter
230
+ events = eventStore.getByAgent(agentName, { ...queryOpts, level: "error" });
231
+ } else if (runId !== undefined) {
232
+ // Filter by run: use getByRun with level filter
233
+ events = eventStore.getByRun(runId, { ...queryOpts, level: "error" });
234
+ } else {
235
+ // Global errors: use getErrors (already filters level='error')
236
+ events = eventStore.getErrors(queryOpts);
237
+ }
238
+
239
+ if (json) {
240
+ process.stdout.write(`${JSON.stringify(events)}\n`);
241
+ return;
242
+ }
243
+
244
+ printErrors(events);
245
+ } finally {
246
+ eventStore.close();
247
+ }
248
+ }