@os-eco/overstory-cli 0.6.6 → 0.6.8
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/README.md +3 -2
- package/agents/coordinator.md +5 -5
- package/agents/lead.md +1 -6
- package/agents/merger.md +3 -3
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +3 -3
- package/agents/supervisor.md +16 -16
- package/package.json +1 -1
- package/src/commands/clean.ts +3 -2
- package/src/commands/coordinator.test.ts +7 -12
- package/src/commands/coordinator.ts +20 -20
- package/src/commands/group.ts +6 -5
- package/src/commands/hooks.test.ts +1 -1
- package/src/commands/hooks.ts +7 -8
- package/src/commands/init.test.ts +1 -5
- package/src/commands/init.ts +6 -5
- package/src/commands/mail.test.ts +6 -6
- package/src/commands/mail.ts +8 -7
- package/src/commands/merge.ts +2 -1
- package/src/commands/monitor.ts +6 -5
- package/src/commands/nudge.ts +2 -1
- package/src/commands/run.ts +8 -7
- package/src/commands/sling.ts +5 -4
- package/src/commands/spec.test.ts +10 -8
- package/src/commands/spec.ts +3 -2
- package/src/commands/status.ts +12 -9
- package/src/commands/stop.test.ts +8 -6
- package/src/commands/stop.ts +3 -2
- package/src/commands/supervisor.ts +8 -7
- package/src/commands/worktree.test.ts +6 -5
- package/src/commands/worktree.ts +19 -26
- package/src/doctor/version.ts +2 -2
- package/src/e2e/init-sling-lifecycle.test.ts +1 -5
- package/src/index.ts +97 -14
- package/src/json.test.ts +72 -0
- package/src/json.ts +24 -0
- package/src/logging/color.test.ts +127 -0
- package/src/logging/color.ts +28 -0
- package/src/mulch/client.test.ts +22 -22
- package/src/worktree/tmux.test.ts +123 -5
- package/src/worktree/tmux.ts +38 -8
- package/agents/issue-reviews.md +0 -71
- package/agents/pr-reviews.md +0 -60
- package/agents/prioritize.md +0 -110
- package/agents/release.md +0 -56
package/README.md
CHANGED
|
@@ -273,13 +273,13 @@ Global Flags:
|
|
|
273
273
|
- **Dependencies**: Minimal runtime — `chalk` (color output), `commander` (CLI framework), core I/O via Bun built-in APIs
|
|
274
274
|
- **Database**: SQLite via `bun:sqlite` (WAL mode for concurrent access)
|
|
275
275
|
- **Linting**: Biome (formatter + linter)
|
|
276
|
-
- **Testing**: `bun test` (
|
|
276
|
+
- **Testing**: `bun test` (2167 tests across 77 files, colocated with source)
|
|
277
277
|
- **External CLIs**: `bd` (beads) or `sd` (seeds), `mulch`, `git`, `tmux` — invoked as subprocesses
|
|
278
278
|
|
|
279
279
|
## Development
|
|
280
280
|
|
|
281
281
|
```bash
|
|
282
|
-
# Run tests (
|
|
282
|
+
# Run tests (2167 tests across 77 files)
|
|
283
283
|
bun test
|
|
284
284
|
|
|
285
285
|
# Run a single test
|
|
@@ -319,6 +319,7 @@ overstory/
|
|
|
319
319
|
types.ts Shared types and interfaces
|
|
320
320
|
config.ts Config loader + validation
|
|
321
321
|
errors.ts Custom error types
|
|
322
|
+
json.ts Standardized JSON envelope helpers
|
|
322
323
|
commands/ One file per CLI subcommand (30 commands)
|
|
323
324
|
agents.ts Agent discovery and querying
|
|
324
325
|
coordinator.ts Persistent orchestrator lifecycle
|
package/agents/coordinator.md
CHANGED
|
@@ -128,15 +128,15 @@ Coordinator (you, depth 0)
|
|
|
128
128
|
- **Your agent name** is `coordinator` (or as set by `$OVERSTORY_AGENT_NAME`)
|
|
129
129
|
|
|
130
130
|
#### Mail Types You Send
|
|
131
|
-
- `dispatch` -- assign a work stream to a lead (includes
|
|
131
|
+
- `dispatch` -- assign a work stream to a lead (includes beadId, objective, file area)
|
|
132
132
|
- `status` -- progress updates, clarifications, answers to questions
|
|
133
133
|
- `error` -- report unrecoverable failures to the human operator
|
|
134
134
|
|
|
135
135
|
#### Mail Types You Receive
|
|
136
|
-
- `merge_ready` -- lead confirms all builders are done, branch verified and ready to merge (branch,
|
|
137
|
-
- `merged` -- merger confirms successful merge (branch,
|
|
138
|
-
- `merge_failed` -- merger reports merge failure (branch,
|
|
139
|
-
- `escalation` -- any agent escalates an issue (severity: warning|error|critical,
|
|
136
|
+
- `merge_ready` -- lead confirms all builders are done, branch verified and ready to merge (branch, beadId, agentName, filesModified)
|
|
137
|
+
- `merged` -- merger confirms successful merge (branch, beadId, tier)
|
|
138
|
+
- `merge_failed` -- merger reports merge failure (branch, beadId, conflictFiles, errorMessage)
|
|
139
|
+
- `escalation` -- any agent escalates an issue (severity: warning|error|critical, beadId, context)
|
|
140
140
|
- `health_check` -- watchdog probes liveness (agentName, checkType)
|
|
141
141
|
- `status` -- leads report progress
|
|
142
142
|
- `result` -- leads report completed work streams
|
package/agents/lead.md
CHANGED
|
@@ -81,7 +81,6 @@ You are primarily a coordinator, but you can also be a doer for simple tasks. Yo
|
|
|
81
81
|
- `{{TRACKER_CLI}} sync` (sync {{TRACKER_NAME}} with git)
|
|
82
82
|
- `ml prime`, `ml record`, `ml query`, `ml search` (expertise)
|
|
83
83
|
- `ov sling` (spawn sub-workers)
|
|
84
|
-
- `ov spec write <id> --body "..." --agent $OVERSTORY_AGENT_NAME` (write spec files)
|
|
85
84
|
- `ov status` (monitor active agents)
|
|
86
85
|
- `ov mail send`, `ov mail check`, `ov mail list`, `ov mail read`, `ov mail reply` (communication)
|
|
87
86
|
- `ov nudge <agent> [message]` (poke stalled workers)
|
|
@@ -192,11 +191,7 @@ Delegate exploration to scouts so you can focus on decomposition and planning.
|
|
|
192
191
|
|
|
193
192
|
Write specs from scout findings and dispatch builders.
|
|
194
193
|
|
|
195
|
-
6. **Write spec files** for each subtask based on scout findings
|
|
196
|
-
```bash
|
|
197
|
-
ov spec write <subtask-id> --body "<spec content>" --agent $OVERSTORY_AGENT_NAME
|
|
198
|
-
```
|
|
199
|
-
Specs are written to `.overstory/specs/<subtask-id>.md` at the canonical root. Each spec should include:
|
|
194
|
+
6. **Write spec files** for each subtask based on scout findings. Each spec goes to `.overstory/specs/<bead-id>.md` and should include:
|
|
200
195
|
- Objective (what to build)
|
|
201
196
|
- Acceptance criteria (how to know it is done)
|
|
202
197
|
- File scope (which files the builder owns -- non-overlapping)
|
package/agents/merger.md
CHANGED
|
@@ -19,7 +19,7 @@ These are named failures. If you catch yourself doing any of these, stop and cor
|
|
|
19
19
|
|
|
20
20
|
## overlay
|
|
21
21
|
|
|
22
|
-
Your task-specific context (task ID, branches to merge, target branch, merge order, parent agent) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `
|
|
22
|
+
Your task-specific context (task ID, branches to merge, target branch, merge order, parent agent) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `overstory sling` and tells you WHAT to merge. This file tells you HOW to merge.
|
|
23
23
|
|
|
24
24
|
## constraints
|
|
25
25
|
|
|
@@ -85,7 +85,7 @@ You are a branch integration specialist. When workers complete their tasks on se
|
|
|
85
85
|
- `bun run typecheck` (verify no TypeScript errors)
|
|
86
86
|
- `{{TRACKER_CLI}} show`, `{{TRACKER_CLI}} close` ({{TRACKER_NAME}} task management)
|
|
87
87
|
- `ml prime`, `ml query` (load expertise for conflict understanding)
|
|
88
|
-
- `ov merge` (use
|
|
88
|
+
- `ov merge` (use overstory merge infrastructure)
|
|
89
89
|
- `ov mail send`, `ov mail check` (communication)
|
|
90
90
|
- `ov status` (check which branches are ready to merge)
|
|
91
91
|
|
|
@@ -146,7 +146,7 @@ If AI-resolve fails or produces broken code:
|
|
|
146
146
|
```
|
|
147
147
|
7. **Send detailed merge report** via mail:
|
|
148
148
|
```bash
|
|
149
|
-
ov mail send --to <parent-or-
|
|
149
|
+
ov mail send --to <parent-or-orchestrator> \
|
|
150
150
|
--subject "Merge complete: <branch>" \
|
|
151
151
|
--body "Tier: <tier-used>. Conflicts: <list or none>. Tests: passing." \
|
|
152
152
|
--type result
|
package/agents/reviewer.md
CHANGED
|
@@ -16,7 +16,7 @@ These are named failures. If you catch yourself doing any of these, stop and cor
|
|
|
16
16
|
|
|
17
17
|
## overlay
|
|
18
18
|
|
|
19
|
-
Your task-specific context (task ID, code to review, branch name, parent agent) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `
|
|
19
|
+
Your task-specific context (task ID, code to review, branch name, parent agent) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `overstory sling` and tells you WHAT to review. This file tells you HOW to review.
|
|
20
20
|
|
|
21
21
|
## constraints
|
|
22
22
|
|
|
@@ -53,7 +53,7 @@ The only write exception is `ov spec write` for persisting spec files (scout onl
|
|
|
53
53
|
|
|
54
54
|
1. Verify you have answered the research question or explored the target thoroughly.
|
|
55
55
|
2. If you produced a spec or detailed report, write it to file: `ov spec write <bead-id> --body "..." --agent <your-name>`.
|
|
56
|
-
3. **Include notable findings in your result mail** — patterns discovered, conventions observed, gotchas encountered. Your parent may record these via
|
|
56
|
+
3. **Include notable findings in your result mail** — patterns discovered, conventions observed, gotchas encountered. Your parent may record these via mulch.
|
|
57
57
|
4. Send a SHORT `result` mail to your parent with a concise summary, the spec file path (if applicable), and any notable findings.
|
|
58
58
|
5. Run `{{TRACKER_CLI}} close <task-id> --reason "<summary of findings>"`.
|
|
59
59
|
6. Stop. Do not continue exploring after closing.
|
package/agents/scout.md
CHANGED
|
@@ -16,7 +16,7 @@ These are named failures. If you catch yourself doing any of these, stop and cor
|
|
|
16
16
|
|
|
17
17
|
## overlay
|
|
18
18
|
|
|
19
|
-
Your task-specific context (what to explore, who spawned you, your agent name) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `
|
|
19
|
+
Your task-specific context (what to explore, who spawned you, your agent name) is in `.claude/CLAUDE.md` in your worktree. That file is generated by `overstory sling` and tells you WHAT to work on. This file tells you HOW to work.
|
|
20
20
|
|
|
21
21
|
## constraints
|
|
22
22
|
|
|
@@ -53,7 +53,7 @@ The only write exception is `ov spec write` for persisting spec files (scout onl
|
|
|
53
53
|
|
|
54
54
|
1. Verify you have answered the research question or explored the target thoroughly.
|
|
55
55
|
2. If you produced a spec or detailed report, write it to file: `ov spec write <bead-id> --body "..." --agent <your-name>`.
|
|
56
|
-
3. **Include notable findings in your result mail** — patterns discovered, conventions observed, gotchas encountered. Your parent may record these via
|
|
56
|
+
3. **Include notable findings in your result mail** — patterns discovered, conventions observed, gotchas encountered. Your parent may record these via mulch.
|
|
57
57
|
4. Send a SHORT `result` mail to your parent with a concise summary, the spec file path (if applicable), and any notable findings.
|
|
58
58
|
5. Run `{{TRACKER_CLI}} close <task-id> --reason "<summary of findings>"`.
|
|
59
59
|
6. Stop. Do not continue exploring after closing.
|
|
@@ -110,7 +110,7 @@ You perform reconnaissance. Given a research question, exploration target, or an
|
|
|
110
110
|
This writes the spec to `.overstory/specs/<bead-id>.md`. Do NOT send full specs via mail.
|
|
111
111
|
6. **Notify via short mail** after writing a spec file:
|
|
112
112
|
```bash
|
|
113
|
-
ov mail send --to <parent-or-
|
|
113
|
+
ov mail send --to <parent-or-orchestrator> \
|
|
114
114
|
--subject "Spec ready: <bead-id>" \
|
|
115
115
|
--body "Spec written to .overstory/specs/<bead-id>.md — <one-line summary>" \
|
|
116
116
|
--type result
|
package/agents/supervisor.md
CHANGED
|
@@ -133,18 +133,18 @@ Before spawning, check `ov status` to ensure non-overlapping file scope across a
|
|
|
133
133
|
- **Read message:** `ov mail read <id> --agent $OVERSTORY_AGENT_NAME`
|
|
134
134
|
|
|
135
135
|
#### Mail Types You Send
|
|
136
|
-
- `assign` -- assign work to a specific worker (
|
|
137
|
-
- `merge_ready` -- signal to coordinator that a branch is verified and ready for merge (branch,
|
|
136
|
+
- `assign` -- assign work to a specific worker (beadId, specPath, workerName, branch)
|
|
137
|
+
- `merge_ready` -- signal to coordinator that a branch is verified and ready for merge (branch, beadId, agentName, filesModified)
|
|
138
138
|
- `status` -- progress updates to coordinator
|
|
139
|
-
- `escalation` -- report unresolvable issues to coordinator (severity: warning|error|critical,
|
|
139
|
+
- `escalation` -- report unresolvable issues to coordinator (severity: warning|error|critical, beadId, context)
|
|
140
140
|
- `question` -- ask coordinator for clarification
|
|
141
141
|
- `result` -- report completed batch results to coordinator
|
|
142
142
|
|
|
143
143
|
#### Mail Types You Receive
|
|
144
|
-
- `dispatch` -- coordinator assigns a task batch (
|
|
145
|
-
- `worker_done` -- worker signals completion (
|
|
146
|
-
- `merged` -- merger confirms successful merge (branch,
|
|
147
|
-
- `merge_failed` -- merger reports merge failure (branch,
|
|
144
|
+
- `dispatch` -- coordinator assigns a task batch (beadId, specPath, capability, fileScope)
|
|
145
|
+
- `worker_done` -- worker signals completion (beadId, branch, exitCode, filesModified)
|
|
146
|
+
- `merged` -- merger confirms successful merge (branch, beadId, tier)
|
|
147
|
+
- `merge_failed` -- merger reports merge failure (branch, beadId, conflictFiles, errorMessage)
|
|
148
148
|
- `status` -- workers report progress
|
|
149
149
|
- `question` -- workers ask for clarification
|
|
150
150
|
- `error` -- workers report failures
|
|
@@ -218,7 +218,7 @@ This is your core responsibility. You manage the full worker lifecycle from spaw
|
|
|
218
218
|
|
|
219
219
|
### On `worker_done` Received
|
|
220
220
|
|
|
221
|
-
When a worker sends `worker_done` mail (
|
|
221
|
+
When a worker sends `worker_done` mail (beadId, branch, exitCode, filesModified):
|
|
222
222
|
|
|
223
223
|
1. **Verify the branch has commits:**
|
|
224
224
|
```bash
|
|
@@ -228,7 +228,7 @@ When a worker sends `worker_done` mail (taskId, branch, exitCode, filesModified)
|
|
|
228
228
|
|
|
229
229
|
2. **Check if the worker closed its bead issue:**
|
|
230
230
|
```bash
|
|
231
|
-
{{TRACKER_CLI}} show <
|
|
231
|
+
{{TRACKER_CLI}} show <bead-id>
|
|
232
232
|
```
|
|
233
233
|
Status should be `closed`. If still `open` or `in_progress`, send mail to worker to close it.
|
|
234
234
|
|
|
@@ -240,17 +240,17 @@ When a worker sends `worker_done` mail (taskId, branch, exitCode, filesModified)
|
|
|
240
240
|
--body "Branch <branch> verified for bead <bead-id>. Worker <worker-name> completed successfully." \
|
|
241
241
|
--type merge_ready --agent $OVERSTORY_AGENT_NAME
|
|
242
242
|
```
|
|
243
|
-
Include payload: `{"branch": "<branch>", "
|
|
243
|
+
Include payload: `{"branch": "<branch>", "beadId": "<bead-id>", "agentName": "<worker-name>", "filesModified": [...]}`
|
|
244
244
|
|
|
245
245
|
5. **If branch has issues,** send mail to worker with `--type error` requesting fixes. Track retry count. After 2 failed attempts, escalate to coordinator.
|
|
246
246
|
|
|
247
247
|
### On `merged` Received
|
|
248
248
|
|
|
249
|
-
When coordinator or merger sends `merged` mail (branch,
|
|
249
|
+
When coordinator or merger sends `merged` mail (branch, beadId, tier):
|
|
250
250
|
|
|
251
251
|
1. **Mark the corresponding bead issue as closed** (if not already):
|
|
252
252
|
```bash
|
|
253
|
-
{{TRACKER_CLI}} close <
|
|
253
|
+
{{TRACKER_CLI}} close <bead-id> --reason "Merged to main via tier <tier>"
|
|
254
254
|
```
|
|
255
255
|
|
|
256
256
|
2. **Clean up worktree:**
|
|
@@ -266,7 +266,7 @@ When coordinator or merger sends `merged` mail (branch, taskId, tier):
|
|
|
266
266
|
|
|
267
267
|
### On `merge_failed` Received
|
|
268
268
|
|
|
269
|
-
When merger sends `merge_failed` mail (branch,
|
|
269
|
+
When merger sends `merge_failed` mail (branch, beadId, conflictFiles, errorMessage):
|
|
270
270
|
|
|
271
271
|
1. **Assess the failure.** Read `conflictFiles` and `errorMessage` to understand root cause.
|
|
272
272
|
|
|
@@ -354,7 +354,7 @@ ov mail send --to coordinator --subject "Warning: <brief-description>" \
|
|
|
354
354
|
--body "<context and current state>" \
|
|
355
355
|
--type escalation --priority normal --agent $OVERSTORY_AGENT_NAME
|
|
356
356
|
```
|
|
357
|
-
Payload: `{"severity": "warning", "
|
|
357
|
+
Payload: `{"severity": "warning", "beadId": "<bead-id>", "context": "<details>"}`
|
|
358
358
|
|
|
359
359
|
#### Error
|
|
360
360
|
Use when the issue is blocking but recoverable with coordinator intervention:
|
|
@@ -368,7 +368,7 @@ ov mail send --to coordinator --subject "Error: <brief-description>" \
|
|
|
368
368
|
--body "<what failed, what was tried, what is needed>" \
|
|
369
369
|
--type escalation --priority high --agent $OVERSTORY_AGENT_NAME
|
|
370
370
|
```
|
|
371
|
-
Payload: `{"severity": "error", "
|
|
371
|
+
Payload: `{"severity": "error", "beadId": "<bead-id>", "context": "<detailed-context>"}`
|
|
372
372
|
|
|
373
373
|
#### Critical
|
|
374
374
|
Use when the automated system cannot self-heal and human intervention is required:
|
|
@@ -382,7 +382,7 @@ ov mail send --to coordinator --subject "CRITICAL: <brief-description>" \
|
|
|
382
382
|
--body "<what broke, impact scope, manual intervention needed>" \
|
|
383
383
|
--type escalation --priority urgent --agent $OVERSTORY_AGENT_NAME
|
|
384
384
|
```
|
|
385
|
-
Payload: `{"severity": "critical", "
|
|
385
|
+
Payload: `{"severity": "critical", "beadId": null, "context": "<full-details>"}`
|
|
386
386
|
|
|
387
387
|
After sending a critical escalation, **stop dispatching new work** for the affected area until the coordinator responds.
|
|
388
388
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@os-eco/overstory-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Multi-agent orchestration for Claude Code — spawn worker agents in git worktrees via tmux, coordinate through SQLite mail, merge with tiered conflict resolution",
|
|
5
5
|
"author": "Jaymin West",
|
|
6
6
|
"license": "MIT",
|
package/src/commands/clean.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { join } from "node:path";
|
|
|
25
25
|
import { loadConfig } from "../config.ts";
|
|
26
26
|
import { ValidationError } from "../errors.ts";
|
|
27
27
|
import { createEventStore } from "../events/store.ts";
|
|
28
|
+
import { printHint, printSuccess } from "../logging/color.ts";
|
|
28
29
|
import { createMulchClient } from "../mulch/client.ts";
|
|
29
30
|
import { openSessionStore } from "../sessions/compat.ts";
|
|
30
31
|
import type { AgentSession, MulchDoctorResult, MulchPruneResult, MulchStatus } from "../types.ts";
|
|
@@ -584,7 +585,7 @@ export async function cleanCommand(opts: CleanOptions): Promise<void> {
|
|
|
584
585
|
}
|
|
585
586
|
|
|
586
587
|
if (lines.length === 0) {
|
|
587
|
-
|
|
588
|
+
printHint("Nothing to clean");
|
|
588
589
|
} else {
|
|
589
590
|
if (result.mulchHealth?.checked) {
|
|
590
591
|
process.stdout.write("\n--- Cleanup Results ---\n");
|
|
@@ -592,6 +593,6 @@ export async function cleanCommand(opts: CleanOptions): Promise<void> {
|
|
|
592
593
|
for (const line of lines) {
|
|
593
594
|
process.stdout.write(`${line}\n`);
|
|
594
595
|
}
|
|
595
|
-
|
|
596
|
+
printSuccess("Clean complete");
|
|
596
597
|
}
|
|
597
598
|
}
|
|
@@ -1185,7 +1185,7 @@ describe("watchdog integration", () => {
|
|
|
1185
1185
|
Bun.sleep = originalSleep;
|
|
1186
1186
|
}
|
|
1187
1187
|
|
|
1188
|
-
expect(output).toContain("Watchdog
|
|
1188
|
+
expect(output).toContain("Watchdog started");
|
|
1189
1189
|
});
|
|
1190
1190
|
});
|
|
1191
1191
|
|
|
@@ -1414,7 +1414,7 @@ describe("monitor integration", () => {
|
|
|
1414
1414
|
Bun.sleep = originalSleep;
|
|
1415
1415
|
}
|
|
1416
1416
|
|
|
1417
|
-
expect(output).toContain("Monitor
|
|
1417
|
+
expect(output).toContain("Monitor started");
|
|
1418
1418
|
});
|
|
1419
1419
|
|
|
1420
1420
|
test("does NOT call monitor.start() when tier2Enabled is false", async () => {
|
|
@@ -1460,21 +1460,16 @@ describe("monitor integration", () => {
|
|
|
1460
1460
|
const originalSleep = Bun.sleep;
|
|
1461
1461
|
Bun.sleep = (() => Promise.resolve()) as typeof Bun.sleep;
|
|
1462
1462
|
|
|
1463
|
-
let
|
|
1464
|
-
const origStderrWrite = process.stderr.write.bind(process.stderr);
|
|
1465
|
-
process.stderr.write = (chunk: string | Uint8Array) => {
|
|
1466
|
-
stderrOutput += typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
1467
|
-
return true;
|
|
1468
|
-
};
|
|
1469
|
-
|
|
1463
|
+
let output: string;
|
|
1470
1464
|
try {
|
|
1471
|
-
await captureStdout(() =>
|
|
1465
|
+
output = await captureStdout(() =>
|
|
1466
|
+
coordinatorCommand(["start", "--monitor", "--no-attach"], deps),
|
|
1467
|
+
);
|
|
1472
1468
|
} finally {
|
|
1473
1469
|
Bun.sleep = originalSleep;
|
|
1474
|
-
process.stderr.write = origStderrWrite;
|
|
1475
1470
|
}
|
|
1476
1471
|
|
|
1477
|
-
expect(
|
|
1472
|
+
expect(output).toContain("skipped");
|
|
1478
1473
|
});
|
|
1479
1474
|
});
|
|
1480
1475
|
|
|
@@ -20,6 +20,7 @@ import { createIdentity, loadIdentity } from "../agents/identity.ts";
|
|
|
20
20
|
import { createManifestLoader, resolveModel } from "../agents/manifest.ts";
|
|
21
21
|
import { loadConfig } from "../config.ts";
|
|
22
22
|
import { AgentError, ValidationError } from "../errors.ts";
|
|
23
|
+
import { printHint, printSuccess, printWarning } from "../logging/color.ts";
|
|
23
24
|
import { openSessionStore } from "../sessions/compat.ts";
|
|
24
25
|
import { createRunStore } from "../sessions/store.ts";
|
|
25
26
|
import { resolveBackend, trackerCliName } from "../tracker/factory.ts";
|
|
@@ -282,7 +283,7 @@ async function startCoordinator(
|
|
|
282
283
|
|
|
283
284
|
if (isRunningAsRoot()) {
|
|
284
285
|
throw new AgentError(
|
|
285
|
-
"Cannot spawn agents as root (UID 0). The claude CLI rejects --
|
|
286
|
+
"Cannot spawn agents as root (UID 0). The claude CLI rejects --permission-mode bypassPermissions when run as root, causing the tmux session to die immediately. Run overstory as a non-root user.",
|
|
286
287
|
);
|
|
287
288
|
}
|
|
288
289
|
|
|
@@ -357,7 +358,7 @@ async function startCoordinator(
|
|
|
357
358
|
// (overstory-gaio, overstory-0kwf).
|
|
358
359
|
const agentDefPath = join(projectRoot, ".overstory", "agent-defs", "coordinator.md");
|
|
359
360
|
const agentDefFile = Bun.file(agentDefPath);
|
|
360
|
-
let claudeCmd = `claude --model ${model} --
|
|
361
|
+
let claudeCmd = `claude --model ${model} --permission-mode bypassPermissions`;
|
|
361
362
|
if (await agentDefFile.exists()) {
|
|
362
363
|
const agentDef = await agentDefFile.text();
|
|
363
364
|
// Single-quote the content for safe shell expansion (only escape single quotes)
|
|
@@ -417,7 +418,7 @@ async function startCoordinator(
|
|
|
417
418
|
await tmux.sendKeys(tmuxSession, beacon);
|
|
418
419
|
|
|
419
420
|
// Follow-up Enters with increasing delays to ensure submission
|
|
420
|
-
for (const delay of [1_000, 2_000]) {
|
|
421
|
+
for (const delay of [1_000, 2_000, 3_000, 5_000]) {
|
|
421
422
|
await Bun.sleep(delay);
|
|
422
423
|
await tmux.sendKeys(tmuxSession, "");
|
|
423
424
|
}
|
|
@@ -428,9 +429,9 @@ async function startCoordinator(
|
|
|
428
429
|
const watchdogResult = await watchdog.start();
|
|
429
430
|
if (watchdogResult) {
|
|
430
431
|
watchdogPid = watchdogResult.pid;
|
|
431
|
-
if (!json)
|
|
432
|
+
if (!json) printHint("Watchdog started");
|
|
432
433
|
} else {
|
|
433
|
-
if (!json)
|
|
434
|
+
if (!json) printWarning("Watchdog failed to start");
|
|
434
435
|
}
|
|
435
436
|
}
|
|
436
437
|
|
|
@@ -438,15 +439,14 @@ async function startCoordinator(
|
|
|
438
439
|
let monitorPid: number | undefined;
|
|
439
440
|
if (monitorFlag) {
|
|
440
441
|
if (!config.watchdog.tier2Enabled) {
|
|
441
|
-
if (!json)
|
|
442
|
-
process.stderr.write(" Monitor: skipped (watchdog.tier2Enabled is false in config)\n");
|
|
442
|
+
if (!json) printWarning("Monitor skipped", "watchdog.tier2Enabled is false");
|
|
443
443
|
} else {
|
|
444
444
|
const monitorResult = await monitor.start([]);
|
|
445
445
|
if (monitorResult) {
|
|
446
446
|
monitorPid = monitorResult.pid;
|
|
447
|
-
if (!json)
|
|
447
|
+
if (!json) printHint("Monitor started");
|
|
448
448
|
} else {
|
|
449
|
-
if (!json)
|
|
449
|
+
if (!json) printWarning("Monitor failed to start");
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
}
|
|
@@ -464,7 +464,7 @@ async function startCoordinator(
|
|
|
464
464
|
if (json) {
|
|
465
465
|
process.stdout.write(`${JSON.stringify(output)}\n`);
|
|
466
466
|
} else {
|
|
467
|
-
|
|
467
|
+
printSuccess("Coordinator started");
|
|
468
468
|
process.stdout.write(` Tmux: ${tmuxSession}\n`);
|
|
469
469
|
process.stdout.write(` Root: ${projectRoot}\n`);
|
|
470
470
|
process.stdout.write(` PID: ${pid}\n`);
|
|
@@ -568,21 +568,21 @@ async function stopCoordinator(opts: { json: boolean }, deps: CoordinatorDeps =
|
|
|
568
568
|
`${JSON.stringify({ stopped: true, sessionId: session.id, watchdogStopped, monitorStopped, runCompleted })}\n`,
|
|
569
569
|
);
|
|
570
570
|
} else {
|
|
571
|
-
|
|
571
|
+
printSuccess("Coordinator stopped", session.id);
|
|
572
572
|
if (watchdogStopped) {
|
|
573
|
-
|
|
573
|
+
printHint("Watchdog stopped");
|
|
574
574
|
} else {
|
|
575
|
-
|
|
575
|
+
printHint("No watchdog running");
|
|
576
576
|
}
|
|
577
577
|
if (monitorStopped) {
|
|
578
|
-
|
|
578
|
+
printHint("Monitor stopped");
|
|
579
579
|
} else {
|
|
580
|
-
|
|
580
|
+
printHint("No monitor running");
|
|
581
581
|
}
|
|
582
582
|
if (runCompleted) {
|
|
583
|
-
|
|
583
|
+
printHint("Run completed");
|
|
584
584
|
} else {
|
|
585
|
-
|
|
585
|
+
printHint("No active run");
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
588
|
} finally {
|
|
@@ -633,12 +633,12 @@ async function statusCoordinator(
|
|
|
633
633
|
`${JSON.stringify({ running: false, watchdogRunning, monitorRunning })}\n`,
|
|
634
634
|
);
|
|
635
635
|
} else {
|
|
636
|
-
|
|
636
|
+
printHint("Coordinator is not running");
|
|
637
637
|
if (watchdogRunning) {
|
|
638
|
-
|
|
638
|
+
printHint("Watchdog: running");
|
|
639
639
|
}
|
|
640
640
|
if (monitorRunning) {
|
|
641
|
-
|
|
641
|
+
printHint("Monitor: running");
|
|
642
642
|
}
|
|
643
643
|
}
|
|
644
644
|
return;
|
package/src/commands/group.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
import { loadConfig } from "../config.ts";
|
|
13
13
|
import { GroupError, ValidationError } from "../errors.ts";
|
|
14
|
+
import { printHint, printSuccess } from "../logging/color.ts";
|
|
14
15
|
import { createTrackerClient, resolveBackend, type TrackerClient } from "../tracker/factory.ts";
|
|
15
16
|
import type { TaskGroup, TaskGroupProgress } from "../types.ts";
|
|
16
17
|
|
|
@@ -326,7 +327,7 @@ export function createGroupCommand(): Command {
|
|
|
326
327
|
if (opts.json) {
|
|
327
328
|
process.stdout.write(`${JSON.stringify(group, null, "\t")}\n`);
|
|
328
329
|
} else {
|
|
329
|
-
|
|
330
|
+
printSuccess("Created group", group.name);
|
|
330
331
|
process.stdout.write(` Members: ${group.memberIssueIds.join(", ")}\n`);
|
|
331
332
|
}
|
|
332
333
|
},
|
|
@@ -365,7 +366,7 @@ export function createGroupCommand(): Command {
|
|
|
365
366
|
if (json) {
|
|
366
367
|
process.stdout.write("[]\n");
|
|
367
368
|
} else {
|
|
368
|
-
|
|
369
|
+
printHint("No active groups");
|
|
369
370
|
}
|
|
370
371
|
return;
|
|
371
372
|
}
|
|
@@ -414,7 +415,7 @@ export function createGroupCommand(): Command {
|
|
|
414
415
|
if (opts.json) {
|
|
415
416
|
process.stdout.write(`${JSON.stringify(group, null, "\t")}\n`);
|
|
416
417
|
} else {
|
|
417
|
-
|
|
418
|
+
printSuccess("Added to group", group.name);
|
|
418
419
|
process.stdout.write(` Members: ${group.memberIssueIds.join(", ")}\n`);
|
|
419
420
|
}
|
|
420
421
|
},
|
|
@@ -434,7 +435,7 @@ export function createGroupCommand(): Command {
|
|
|
434
435
|
if (opts.json) {
|
|
435
436
|
process.stdout.write(`${JSON.stringify(group, null, "\t")}\n`);
|
|
436
437
|
} else {
|
|
437
|
-
|
|
438
|
+
printSuccess("Removed from group", group.name);
|
|
438
439
|
process.stdout.write(` Members: ${group.memberIssueIds.join(", ")}\n`);
|
|
439
440
|
}
|
|
440
441
|
});
|
|
@@ -452,7 +453,7 @@ export function createGroupCommand(): Command {
|
|
|
452
453
|
if (opts.json) {
|
|
453
454
|
process.stdout.write("[]\n");
|
|
454
455
|
} else {
|
|
455
|
-
|
|
456
|
+
printHint("No groups");
|
|
456
457
|
}
|
|
457
458
|
return;
|
|
458
459
|
}
|
|
@@ -227,7 +227,7 @@ describe("hooks uninstall", () => {
|
|
|
227
227
|
);
|
|
228
228
|
|
|
229
229
|
const output = await captureStdout(() => hooksCommand(["uninstall"]));
|
|
230
|
-
expect(output).toContain("
|
|
230
|
+
expect(output).toContain("Removed");
|
|
231
231
|
|
|
232
232
|
const content = await Bun.file(join(claudeDir, "settings.local.json")).text();
|
|
233
233
|
const parsed = JSON.parse(content) as Record<string, unknown>;
|
package/src/commands/hooks.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { join } from "node:path";
|
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
import { loadConfig } from "../config.ts";
|
|
17
17
|
import { ValidationError } from "../errors.ts";
|
|
18
|
+
import { printHint, printSuccess, printWarning } from "../logging/color.ts";
|
|
18
19
|
|
|
19
20
|
interface HookEntry {
|
|
20
21
|
matcher: string;
|
|
@@ -120,8 +121,8 @@ async function installHooks(force: boolean): Promise<void> {
|
|
|
120
121
|
await mkdir(targetDir, { recursive: true });
|
|
121
122
|
await Bun.write(targetPath, `${JSON.stringify(targetConfig, null, "\t")}\n`);
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
printSuccess("Installed orchestrator hooks");
|
|
125
|
+
printHint("Source: .overstory/hooks.json");
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
/**
|
|
@@ -139,7 +140,7 @@ async function uninstallHooks(): Promise<void> {
|
|
|
139
140
|
const targetFile = Bun.file(targetPath);
|
|
140
141
|
|
|
141
142
|
if (!(await targetFile.exists())) {
|
|
142
|
-
|
|
143
|
+
printWarning("No .claude/settings.local.json found", "nothing to uninstall");
|
|
143
144
|
return;
|
|
144
145
|
}
|
|
145
146
|
|
|
@@ -159,12 +160,10 @@ async function uninstallHooks(): Promise<void> {
|
|
|
159
160
|
const remainingKeys = Object.keys(rest);
|
|
160
161
|
if (remainingKeys.length === 0) {
|
|
161
162
|
await unlink(targetPath);
|
|
162
|
-
|
|
163
|
+
printSuccess("Removed hooks from settings.local.json");
|
|
163
164
|
} else {
|
|
164
165
|
await Bun.write(targetPath, `${JSON.stringify(rest, null, "\t")}\n`);
|
|
165
|
-
|
|
166
|
-
"\u2713 Removed hooks from .claude/settings.local.json (preserved other settings)\n",
|
|
167
|
-
);
|
|
166
|
+
printSuccess("Removed hooks from settings.local.json");
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
169
|
|
|
@@ -199,7 +198,7 @@ async function statusHooks(json: boolean): Promise<void> {
|
|
|
199
198
|
`Hooks installed (.claude/settings.local.json): ${installed ? "yes" : "no"}\n`,
|
|
200
199
|
);
|
|
201
200
|
if (!installed && sourceExists) {
|
|
202
|
-
|
|
201
|
+
printHint("Run ov hooks install to install");
|
|
203
202
|
}
|
|
204
203
|
}
|
|
205
204
|
}
|
|
@@ -20,10 +20,6 @@ const AGENT_DEF_FILES = [
|
|
|
20
20
|
"supervisor.md",
|
|
21
21
|
"coordinator.md",
|
|
22
22
|
"monitor.md",
|
|
23
|
-
"issue-reviews.md",
|
|
24
|
-
"pr-reviews.md",
|
|
25
|
-
"prioritize.md",
|
|
26
|
-
"release.md",
|
|
27
23
|
];
|
|
28
24
|
|
|
29
25
|
/** Resolve the source agents directory (same logic as init.ts). */
|
|
@@ -50,7 +46,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
50
46
|
await cleanupTempDir(tempDir);
|
|
51
47
|
});
|
|
52
48
|
|
|
53
|
-
test("creates .overstory/agent-defs/ with all
|
|
49
|
+
test("creates .overstory/agent-defs/ with all 8 agent definition files", async () => {
|
|
54
50
|
await initCommand({});
|
|
55
51
|
|
|
56
52
|
const agentDefsDir = join(tempDir, ".overstory", "agent-defs");
|
package/src/commands/init.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { mkdir, readdir } from "node:fs/promises";
|
|
|
14
14
|
import { basename, join } from "node:path";
|
|
15
15
|
import { DEFAULT_CONFIG } from "../config.ts";
|
|
16
16
|
import { ValidationError } from "../errors.ts";
|
|
17
|
+
import { printHint, printSuccess } from "../logging/color.ts";
|
|
17
18
|
import type { AgentManifest, OverstoryConfig } from "../types.ts";
|
|
18
19
|
|
|
19
20
|
const OVERSTORY_DIR = ".overstory";
|
|
@@ -522,7 +523,7 @@ export interface InitOptions {
|
|
|
522
523
|
* Print a success status line.
|
|
523
524
|
*/
|
|
524
525
|
function printCreated(relativePath: string): void {
|
|
525
|
-
|
|
526
|
+
printSuccess("Created", relativePath);
|
|
526
527
|
}
|
|
527
528
|
|
|
528
529
|
/**
|
|
@@ -631,11 +632,11 @@ export async function initCommand(opts: InitOptions): Promise<void> {
|
|
|
631
632
|
if (force) {
|
|
632
633
|
const migrated = await migrateExistingDatabases(overstoryPath);
|
|
633
634
|
for (const dbName of migrated) {
|
|
634
|
-
|
|
635
|
+
printSuccess("Migrated", dbName);
|
|
635
636
|
}
|
|
636
637
|
}
|
|
637
638
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
639
|
+
printSuccess("Initialized");
|
|
640
|
+
printHint("Next: run `ov hooks install` to enable Claude Code hooks.");
|
|
641
|
+
printHint("Then: run `ov status` to see the current state.");
|
|
641
642
|
}
|