aoaoe 0.160.0 → 0.187.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 (44) hide show
  1. package/README.md +110 -3
  2. package/dist/colors.d.ts +39 -1
  3. package/dist/colors.js +40 -3
  4. package/dist/config.d.ts +26 -0
  5. package/dist/config.js +207 -7
  6. package/dist/executor.d.ts +1 -0
  7. package/dist/executor.js +19 -4
  8. package/dist/health-score.d.ts +11 -0
  9. package/dist/health-score.js +100 -0
  10. package/dist/index.js +2041 -108
  11. package/dist/init.js +4 -2
  12. package/dist/input.d.ts +83 -0
  13. package/dist/input.js +455 -23
  14. package/dist/notify.d.ts +19 -0
  15. package/dist/notify.js +54 -1
  16. package/dist/pin-presets.d.ts +10 -0
  17. package/dist/pin-presets.js +72 -0
  18. package/dist/poller.d.ts +5 -0
  19. package/dist/poller.js +40 -15
  20. package/dist/reasoner/claude-code.js +1 -1
  21. package/dist/reasoner/opencode.js +1 -1
  22. package/dist/reasoner/parse.d.ts +6 -1
  23. package/dist/reasoner/parse.js +40 -1
  24. package/dist/reasoner/prompt-templates.d.ts +10 -0
  25. package/dist/reasoner/prompt-templates.js +111 -0
  26. package/dist/reasoner/prompt.d.ts +2 -2
  27. package/dist/reasoner/prompt.js +39 -7
  28. package/dist/supervisor-history.d.ts +9 -0
  29. package/dist/supervisor-history.js +62 -0
  30. package/dist/task-cli.d.ts +36 -2
  31. package/dist/task-cli.js +326 -61
  32. package/dist/task-manager.d.ts +19 -2
  33. package/dist/task-manager.js +335 -70
  34. package/dist/task-parser.d.ts +12 -0
  35. package/dist/task-parser.js +69 -0
  36. package/dist/task-templates.d.ts +11 -0
  37. package/dist/task-templates.js +96 -0
  38. package/dist/tui.d.ts +458 -5
  39. package/dist/tui.js +1691 -171
  40. package/dist/types.d.ts +26 -1
  41. package/dist/types.js +30 -0
  42. package/dist/vibe.d.ts +33 -0
  43. package/dist/vibe.js +179 -0
  44. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <a href="https://github.com/Talador12/agent-of-agent-of-empires/actions/workflows/ci.yml"><img src="https://github.com/Talador12/agent-of-agent-of-empires/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
5
5
  <a href="https://www.npmjs.com/package/aoaoe"><img src="https://img.shields.io/npm/v/aoaoe" alt="npm version"></a>
6
6
  <a href="https://github.com/Talador12/agent-of-agent-of-empires/releases"><img src="https://img.shields.io/github/v/release/Talador12/agent-of-agent-of-empires" alt="GitHub release"></a>
7
- <img src="https://img.shields.io/badge/tests-2005-brightgreen" alt="tests">
7
+ <img src="https://img.shields.io/badge/tests-2427-brightgreen" alt="tests">
8
8
  <img src="https://img.shields.io/badge/node-%3E%3D20-blue" alt="Node.js >= 20">
9
9
  <img src="https://img.shields.io/badge/runtime%20deps-0-brightgreen" alt="zero runtime dependencies">
10
10
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
@@ -15,6 +15,8 @@ An autonomous supervisor for [Agent of Empires](https://github.com/njbrake/agent
15
15
 
16
16
  **This project is a companion to [Agent of Empires (AoE)](https://github.com/njbrake/agent-of-empires) by [Nate Brake](https://x.com/natebrake).** AoE is the foundation -- it manages multiple AI coding agents in tmux sessions with git worktrees. aoaoe adds an autonomous supervisor layer on top. You need AoE running first; aoaoe plugs into it.
17
17
 
18
+ > **Self-improvement mode**: `make self` starts aoaoe supervising its own AoE session — reading the roadmap from `aoaoe.tasks.json`, implementing features, committing, and pushing. It updates itself in real time.
19
+
18
20
  ## What is this?
19
21
 
20
22
  [AoE](https://github.com/njbrake/agent-of-empires) is great at spawning and organizing agents, but someone still needs to watch the tmux panes and intervene when agents get stuck, ask questions, or finish their work.
@@ -103,6 +105,18 @@ The real deal. Polls, reasons, and executes -- sending keystrokes to agents, res
103
105
  | `--confirm` | Yes | Yes | You approve each action |
104
106
  | `aoaoe` | Yes | Yes | Yes |
105
107
 
108
+ ## Self-improvement
109
+
110
+ aoaoe can supervise its own development. With an `aoaoe` AoE session open on this repo:
111
+
112
+ ```bash
113
+ make setup # install deps, build, create AoE session if missing (one-time)
114
+ make self # aoaoe supervises itself: reads roadmap, implements, commits, pushes
115
+ make self-dry # watch-only — see what it would do without letting it act
116
+ ```
117
+
118
+ The goal in `aoaoe.tasks.json` drives the session: pick backlog items, implement with tests, commit atomically, push, tag releases. The daemon watches its own tmux pane and nudges the agent when it stalls or needs direction.
119
+
106
120
  ## Quick Start
107
121
 
108
122
  aoaoe has two parts: a **daemon** (the brain) and a **chat UI** (your window into it). Here's how to get both running.
@@ -248,7 +262,7 @@ The daemon runs an interactive TUI with a rich command set. These commands are a
248
262
  | `1`-`9` | Quick-switch: jump to session N |
249
263
  | `/view [N\|name]` | Drill into a session's live output (default: 1) |
250
264
  | `/back` | Return to overview from drill-down |
251
- | `/sort [mode]` | Sort sessions: `status`, `name`, `activity`, `default` (no arg = cycle) |
265
+ | `/sort [mode]` | Sort sessions: `status`, `name`, `activity`, `health`, `default` (no arg = cycle) |
252
266
  | `/compact` | Toggle compact mode (dense session panel) |
253
267
  | `/pin [N\|name]` | Pin/unpin a session to the top |
254
268
  | `/bell` | Toggle terminal bell on errors/completions |
@@ -276,6 +290,10 @@ The daemon runs an interactive TUI with a rich command set. These commands are a
276
290
  | `/color-all [c]` | Set accent color for all sessions at once |
277
291
  | `/mute-errors` | Toggle suppression of `error`/`! action` entries in activity log |
278
292
  | `/pin-all-errors` | Pin every session currently in error state |
293
+ | `/pin-draining` | Pin all draining sessions to the top |
294
+ | `/labels` | List all active session labels |
295
+ | `/sort-by-health` | Sort sessions by health score (worst first) |
296
+ | `/icon N [emoji]` | Set or clear a single emoji shown in the session row |
279
297
  | `/timeline N [n]` | Show last n activity entries for a session (default 30) |
280
298
  | `/find <text>` | Search all session pane outputs for text |
281
299
  | `/reset-health N` | Clear error counts + context history to reset a session's health score |
@@ -318,6 +336,10 @@ The daemon runs an interactive TUI with a rich command set. These commands are a
318
336
  | `/clear-history` | Truncate `~/.aoaoe/tui-history.jsonl` |
319
337
  | `/copy [N]` | Copy session's current pane output to clipboard (default: current drill-down) |
320
338
  | `/alias /x /cmd` | Create command alias (`/x` expands to `/cmd`); no args = list |
339
+ | `/ctx-budget` | Show context budget allocation across sessions (activity-weighted) |
340
+ | `/profile [name]` | Show active AoE profiles; optionally switch focus to a named profile |
341
+ | `/replay <N\|name> [lps]` | Play back a session's stored pane output line by line (default 10 lps; run again to stop) |
342
+ | `/notify-filter <session> <events...>` | Set per-session webhook event filter (`session_error`, `session_done`, etc.); no args = list; `clear` = remove all |
321
343
 
322
344
  ### Other
323
345
 
@@ -358,6 +380,9 @@ The daemon runs an interactive TUI with a rich command set. These commands are a
358
380
  - **Activity heatmap** -- 24-hour colored block chart via `aoaoe stats`
359
381
  - **Bookmarks** -- mark positions, jump back, diff since a bookmark
360
382
  - **Clipboard export** -- `/clip` and `/copy` copy activity or session pane output to clipboard
383
+ - **Automatic context compaction** -- when a session hits 80% context usage, daemon sends a compaction nudge via tmux; 10-min cooldown per session
384
+ - **Multi-profile polling** -- daemon polls all configured AoE profiles simultaneously; sessions deduped by ID across profiles
385
+ - **Session replay** -- `/replay <N|name> [lps]` plays back stored pane output in the activity log at configurable speed
361
386
 
362
387
  ## Chat UI Commands
363
388
 
@@ -368,6 +393,9 @@ The chat UI (`aoaoe-chat`) runs inside an AoE tmux pane. Register it with `aoaoe
368
393
  | `/overview` | Show all AoE sessions with tasks, model, tokens, cost. **Works without the daemon.** |
369
394
  | `/tasks` | Alias for `/overview` |
370
395
  | `/status` | Daemon connection status + countdown to next reasoning cycle |
396
+ | `/incident [opts]` | Incident quick view: response-flow runbook + recent supervisor activity (`--since`, `--limit`, `--json`, `--ndjson`, `--follow`) |
397
+ | `/runbook [section] [--json]` | Print operator playbook slice (`quickstart`, `response-flow`/`incident`, `all`) |
398
+ | `/supervisor [opts]` | Judge/orchestrator status across tasks/sessions (`--all`, `--since`, `--limit`, `--json`) |
371
399
  | `/interrupt` | Interrupt the current reasoner call |
372
400
  | `/dashboard` | Request full dashboard output from daemon |
373
401
  | `/pause` | Pause the daemon (stops reasoning) |
@@ -403,6 +431,29 @@ commands:
403
431
  (none) start the supervisor daemon (interactive TUI)
404
432
  init detect tools + sessions, import history, generate config
405
433
  status quick daemon health check (is it running? what's it doing?)
434
+ runbook print operator quickstart for day-2 supervision
435
+ runbook --json machine-readable runbook output for tooling
436
+ runbook --section <quickstart|response-flow|incident|all> print only one runbook section
437
+ incident one-shot incident quick view (response-flow + recent activity)
438
+ incident --since <duration> filter incident event window (30m, 2h, 1d)
439
+ incident --limit <N> cap incident events shown (default: 5)
440
+ incident --json machine-readable incident output
441
+ incident --ndjson emit compact one-line JSON snapshots
442
+ incident --watch stream incident snapshots continuously
443
+ incident --follow shortcut for --watch --changes-only --heartbeat 30
444
+ incident --changes-only emit only when incident state changes (implies --watch)
445
+ incident --heartbeat <sec> keepalive interval (implies --changes-only + --watch)
446
+ incident --interval <ms> watch refresh interval (default: 5000, min: 500)
447
+ supervisor one-shot supervisor/task/session orchestration status
448
+ supervisor --all show full recent supervisor event buffer
449
+ supervisor --since <duration> filter events to a time window (30m, 2h, 7d)
450
+ supervisor --limit <N> cap number of events shown (default: 5)
451
+ supervisor --json machine-readable output for automation
452
+ supervisor --ndjson emit compact one-line JSON snapshots
453
+ supervisor --watch stream supervisor snapshots continuously
454
+ supervisor --changes-only emit only when state changes (implies --watch)
455
+ supervisor --heartbeat <sec> keepalive interval (implies --changes-only + --watch)
456
+ supervisor --interval <ms> watch refresh interval (default: 5000, min: 500)
406
457
  config show the effective resolved config (defaults + file)
407
458
  config --validate validate config + check tool availability
408
459
  config --diff show only fields that differ from defaults
@@ -416,7 +467,7 @@ commands:
416
467
  export --format <json|markdown> output format (default: json)
417
468
  export --output <file> write to file (default: stdout)
418
469
  export --last <duration> time window: 1h, 6h, 24h, 7d (default: 24h)
419
- task manage tasks and sessions (list, start, stop, new, rm, edit)
470
+ task manage tasks and sessions (list, reconcile, start, stop, new, rm, edit, help)
420
471
  tasks show task progress (from aoaoe.tasks.json)
421
472
  history review recent actions (from ~/.aoaoe/actions.log)
422
473
  test-context scan sessions + context files (read-only, no LLM, safe)
@@ -447,6 +498,62 @@ register options:
447
498
  --title, -t <name> session title in AoE (default: aoaoe)
448
499
  ```
449
500
 
501
+ ### Supervisor Streaming Examples
502
+
503
+ ```bash
504
+ # Human-readable watch output (change-only)
505
+ aoaoe supervisor --watch --changes-only --heartbeat 60
506
+
507
+ # NDJSON stream for pipes/collectors
508
+ aoaoe supervisor --watch --ndjson --changes-only --heartbeat 30
509
+
510
+ # Filter to the last 2 hours and show only 20 events
511
+ aoaoe supervisor --since 2h --limit 20
512
+ ```
513
+
514
+ JSON/NDJSON payloads include `emitReason` with one of:
515
+ - `snapshot` (one-shot invocation)
516
+ - `interval` (periodic watch tick)
517
+ - `change` (watch emission caused by state change)
518
+ - `heartbeat` (keepalive emission from `--heartbeat`)
519
+
520
+ ### Incident Streaming Examples
521
+
522
+ ```bash
523
+ # One-shot incident snapshot (human readable)
524
+ aoaoe incident --since 30m --limit 10
525
+
526
+ # Fast follow mode (alias for --watch --changes-only)
527
+ aoaoe incident --follow --heartbeat 30 --ndjson
528
+
529
+ # NDJSON incident stream for monitors (change-only + keepalive)
530
+ aoaoe incident --watch --ndjson --changes-only --heartbeat 30
531
+
532
+ # JSON watch stream (compact one object per tick in watch mode)
533
+ aoaoe incident --watch --json --interval 5000
534
+ ```
535
+
536
+ ### Operator Playbook
537
+
538
+ Use this when running aoaoe as a long-lived judge over many AoE sessions.
539
+
540
+ ```bash
541
+ # 1) Start low-noise supervision stream in a side pane
542
+ aoaoe supervisor --watch --ndjson --changes-only --heartbeat 30
543
+
544
+ # 2) If sessions/tasks drift, force immediate reconciliation
545
+ aoaoe task reconcile
546
+
547
+ # 3) If a specific session needs new direction, inject a goal quickly
548
+ aoaoe-chat
549
+ # then type: /task <session> :: <new goal>
550
+ ```
551
+
552
+ Recommended response flow:
553
+ - `emitReason=change` spikes: inspect `/supervisor --since 30m --limit 20`
554
+ - stalled task (pending/paused too long): run `aoaoe task reconcile`, then nudge via `/task ... :: ...`
555
+ - noisy but unchanged systems: keep `--changes-only --heartbeat 30` so monitors still get liveness
556
+
450
557
  ## Configuration
451
558
 
452
559
  Config lives at `~/.aoaoe/aoaoe.config.json` (canonical, written by `aoaoe init`). A local `aoaoe.config.json` in cwd overrides for development. Defaults work fine without a config file:
package/dist/colors.d.ts CHANGED
@@ -13,8 +13,23 @@ export declare const SLATE = "\u001B[38;5;245m";
13
13
  export declare const ROSE = "\u001B[38;5;204m";
14
14
  export declare const LIME = "\u001B[38;5;114m";
15
15
  export declare const SKY = "\u001B[38;5;117m";
16
- export declare const BG_DARK = "\u001B[48;5;236m";
16
+ export declare const PURPLE = "\u001B[38;5;141m";
17
+ export declare const ORANGE = "\u001B[38;5;208m";
18
+ export declare const PINK = "\u001B[38;5;213m";
19
+ export declare const GOLD = "\u001B[38;5;220m";
20
+ export declare const SILVER = "\u001B[38;5;250m";
21
+ export declare const STEEL = "\u001B[38;5;240m";
22
+ export declare const BG_DARK = "\u001B[48;5;235m";
23
+ export declare const BG_HEADER2 = "\u001B[48;5;237m";
17
24
  export declare const BG_HOVER = "\u001B[48;5;238m";
25
+ export declare const BG_INPUT = "\u001B[48;5;234m";
26
+ export declare const BG_SECTION = "\u001B[48;5;236m";
27
+ export declare const BG_INDIGO = "\u001B[48;5;62m";
28
+ export declare const BG_SKY = "\u001B[48;5;67m";
29
+ export declare const BG_LIME = "\u001B[48;5;71m";
30
+ export declare const BG_ROSE = "\u001B[48;5;160m";
31
+ export declare const BG_AMBER = "\u001B[48;5;172m";
32
+ export declare const BG_TEAL = "\u001B[48;5;30m";
18
33
  export declare const BOX: {
19
34
  readonly tl: "┌";
20
35
  readonly tr: "┐";
@@ -31,11 +46,34 @@ export declare const BOX: {
31
46
  readonly rtr: "╮";
32
47
  readonly rbl: "╰";
33
48
  readonly rbr: "╯";
49
+ readonly dh: "═";
50
+ readonly dv: "║";
51
+ readonly dtl: "╔";
52
+ readonly dtr: "╗";
53
+ readonly dbl: "╚";
54
+ readonly dbr: "╝";
55
+ readonly dltee: "╠";
56
+ readonly drtee: "╣";
34
57
  };
35
58
  export declare const SPINNER: readonly ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
59
+ export declare const PROGRESS_BLOCKS = 12;
60
+ export declare const PROGRESS_IDLE = "\u001B[38;5;240m";
61
+ export declare const PROGRESS_TIP = "\u001B[38;5;75m";
36
62
  export declare const DOT: {
37
63
  readonly filled: "●";
38
64
  readonly hollow: "○";
39
65
  readonly half: "◐";
40
66
  };
67
+ export declare const GLYPH: {
68
+ readonly agent: "◈";
69
+ readonly activity: "◉";
70
+ readonly input: "▸";
71
+ readonly thinking: "⟳";
72
+ readonly clock: "◷";
73
+ readonly check: "✓";
74
+ readonly cross: "✗";
75
+ readonly arrow: "→";
76
+ readonly bullet: "•";
77
+ readonly pipe: "┃";
78
+ };
41
79
  //# sourceMappingURL=colors.d.ts.map
package/dist/colors.js CHANGED
@@ -9,7 +9,6 @@ export const YELLOW = "\x1b[33m";
9
9
  export const CYAN = "\x1b[36m";
10
10
  export const WHITE = "\x1b[37m";
11
11
  // 256-color palette — tasteful accents for the TUI
12
- // use sparingly: these are highlights, not defaults
13
12
  export const INDIGO = "\x1b[38;5;105m"; // muted purple-blue for branding
14
13
  export const TEAL = "\x1b[38;5;73m"; // cool blue-green for info
15
14
  export const AMBER = "\x1b[38;5;214m"; // warm orange for warnings/active
@@ -17,9 +16,25 @@ export const SLATE = "\x1b[38;5;245m"; // neutral gray for secondary text
17
16
  export const ROSE = "\x1b[38;5;204m"; // soft red for errors
18
17
  export const LIME = "\x1b[38;5;114m"; // fresh green for success/working
19
18
  export const SKY = "\x1b[38;5;117m"; // light blue for reasoning
19
+ export const PURPLE = "\x1b[38;5;141m"; // violet for special highlights
20
+ export const ORANGE = "\x1b[38;5;208m"; // bright orange for warnings
21
+ export const PINK = "\x1b[38;5;213m"; // hot pink for high-signal events
22
+ export const GOLD = "\x1b[38;5;220m"; // gold for completed/done
23
+ export const SILVER = "\x1b[38;5;250m"; // light gray for borders/structural
24
+ export const STEEL = "\x1b[38;5;240m"; // dark gray for dim structural elements
20
25
  // background variants (256-color)
21
- export const BG_DARK = "\x1b[48;5;236m"; // dark gray for header bar
22
- export const BG_HOVER = "\x1b[48;5;238m"; // slightly brighter gray for hover highlight
26
+ export const BG_DARK = "\x1b[48;5;235m"; // header bar (very dark)
27
+ export const BG_HEADER2 = "\x1b[48;5;237m"; // secondary header sections
28
+ export const BG_HOVER = "\x1b[48;5;238m"; // hover highlight
29
+ export const BG_INPUT = "\x1b[48;5;234m"; // input box background
30
+ export const BG_SECTION = "\x1b[48;5;236m"; // section panel background
31
+ // bright foreground on dark background (for section labels in colored boxes)
32
+ export const BG_INDIGO = "\x1b[48;5;62m"; // indigo bg for brand pill
33
+ export const BG_SKY = "\x1b[48;5;67m"; // sky bg for reasoning pill
34
+ export const BG_LIME = "\x1b[48;5;71m"; // lime bg for working pill
35
+ export const BG_ROSE = "\x1b[48;5;160m"; // rose bg for error pill
36
+ export const BG_AMBER = "\x1b[48;5;172m"; // amber bg for warning pill
37
+ export const BG_TEAL = "\x1b[48;5;30m"; // teal bg for info pill
23
38
  // box-drawing characters — Unicode block elements
24
39
  export const BOX = {
25
40
  tl: "┌", tr: "┐", bl: "└", br: "┘",
@@ -27,9 +42,31 @@ export const BOX = {
27
42
  ltee: "├", rtee: "┤", ttee: "┬", btee: "┴", cross: "┼",
28
43
  // rounded corners (softer look)
29
44
  rtl: "╭", rtr: "╮", rbl: "╰", rbr: "╯",
45
+ // double-line (for section headers)
46
+ dh: "═", dv: "║",
47
+ dtl: "╔", dtr: "╗", dbl: "╚", dbr: "╝",
48
+ // mixed (double-horizontal, single-vertical tees)
49
+ dltee: "╠", drtee: "╣",
30
50
  };
31
51
  // braille spinner frames for phase animation
32
52
  export const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
53
+ // bouncing progress bar frames (blue tip sweeping over grey blocks)
54
+ export const PROGRESS_BLOCKS = 12; // width of the progress bar in chars
55
+ export const PROGRESS_IDLE = "\x1b[38;5;240m"; // grey for idle blocks ░
56
+ export const PROGRESS_TIP = "\x1b[38;5;75m"; // bright blue for active tip ▓
33
57
  // status dots — filled circle variants
34
58
  export const DOT = { filled: "●", hollow: "○", half: "◐" };
59
+ // section label glyphs
60
+ export const GLYPH = {
61
+ agent: "◈", // agents panel
62
+ activity: "◉", // activity log
63
+ input: "▸", // input prompt
64
+ thinking: "⟳", // reasoning in progress
65
+ clock: "◷", // countdown
66
+ check: "✓", // done/ok
67
+ cross: "✗", // error
68
+ arrow: "→", // action
69
+ bullet: "•", // generic
70
+ pipe: "┃", // vertical gutter bar (colored per tag)
71
+ };
35
72
  //# sourceMappingURL=colors.js.map
package/dist/config.d.ts CHANGED
@@ -23,8 +23,34 @@ export declare function parseCliArgs(argv: string[]): {
23
23
  testContext: boolean;
24
24
  runTest: boolean;
25
25
  showTasks: boolean;
26
+ showTasksJson: boolean;
27
+ runProgress: boolean;
28
+ progressSince?: string;
29
+ progressJson: boolean;
26
30
  showHistory: boolean;
27
31
  showStatus: boolean;
32
+ runRunbook: boolean;
33
+ runbookJson: boolean;
34
+ runbookSection?: string;
35
+ runIncident: boolean;
36
+ incidentSince?: string;
37
+ incidentLimit?: number;
38
+ incidentJson: boolean;
39
+ incidentNdjson: boolean;
40
+ incidentWatch: boolean;
41
+ incidentChangesOnly: boolean;
42
+ incidentHeartbeatSec?: number;
43
+ incidentIntervalMs?: number;
44
+ runSupervisor: boolean;
45
+ supervisorAll: boolean;
46
+ supervisorSince?: string;
47
+ supervisorLimit?: number;
48
+ supervisorJson: boolean;
49
+ supervisorNdjson: boolean;
50
+ supervisorWatch: boolean;
51
+ supervisorChangesOnly: boolean;
52
+ supervisorHeartbeatSec?: number;
53
+ supervisorIntervalMs?: number;
28
54
  showConfig: boolean;
29
55
  configValidate: boolean;
30
56
  configDiff: boolean;
package/dist/config.js CHANGED
@@ -12,6 +12,7 @@ const CONFIG_SEARCH_DIRS = [AOAOE_DIR, process.cwd()];
12
12
  export const DEFAULTS = {
13
13
  reasoner: "opencode",
14
14
  pollIntervalMs: 10_000,
15
+ reasonIntervalMs: 60_000,
15
16
  opencode: {
16
17
  port: 4097,
17
18
  },
@@ -81,7 +82,7 @@ export function loadConfig(overrides) {
81
82
  }
82
83
  // known top-level and nested config keys — used to warn on typos
83
84
  const KNOWN_KEYS = {
84
- reasoner: true, pollIntervalMs: true, captureLinesCount: true,
85
+ reasoner: true, pollIntervalMs: true, reasonIntervalMs: true, captureLinesCount: true,
85
86
  verbose: true, dryRun: true, observe: true, confirm: true,
86
87
  contextFiles: true, sessionDirs: true, protectedSessions: true, healthPort: true, tuiHistoryRetentionDays: true,
87
88
  opencode: new Set(["port", "model"]),
@@ -122,6 +123,9 @@ export function validateConfig(config) {
122
123
  if (typeof config.pollIntervalMs !== "number" || config.pollIntervalMs < 1000 || !isFinite(config.pollIntervalMs)) {
123
124
  errors.push(`pollIntervalMs must be a number >= 1000, got ${config.pollIntervalMs}`);
124
125
  }
126
+ if (typeof config.reasonIntervalMs !== "number" || config.reasonIntervalMs < config.pollIntervalMs || !isFinite(config.reasonIntervalMs)) {
127
+ errors.push(`reasonIntervalMs must be a number >= pollIntervalMs (${config.pollIntervalMs}), got ${config.reasonIntervalMs}`);
128
+ }
125
129
  if (typeof config.captureLinesCount !== "number" || config.captureLinesCount < 1 || !isFinite(config.captureLinesCount)) {
126
130
  errors.push(`captureLinesCount must be a positive number, got ${config.captureLinesCount}`);
127
131
  }
@@ -327,7 +331,7 @@ export function parseCliArgs(argv) {
327
331
  let initForce = false;
328
332
  let runTaskCli = false;
329
333
  let registerTitle;
330
- const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
334
+ const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
331
335
  // check for subcommand as first non-flag arg
332
336
  if (argv[2] === "test-context") {
333
337
  return { ...defaults, testContext: true };
@@ -339,7 +343,17 @@ export function parseCliArgs(argv) {
339
343
  return { ...defaults, runTaskCli: true };
340
344
  }
341
345
  if (argv[2] === "tasks") {
342
- return { ...defaults, showTasks: true };
346
+ const json = argv.includes("--json");
347
+ return { ...defaults, showTasks: true, showTasksJson: json };
348
+ }
349
+ if (argv[2] === "progress") {
350
+ let since;
351
+ const json = argv.includes("--json");
352
+ for (let i = 3; i < argv.length; i++) {
353
+ if (argv[i] === "--since" && argv[i + 1])
354
+ since = argv[++i];
355
+ }
356
+ return { ...defaults, runProgress: true, progressSince: since, progressJson: json };
343
357
  }
344
358
  if (argv[2] === "history") {
345
359
  return { ...defaults, showHistory: true };
@@ -347,6 +361,148 @@ export function parseCliArgs(argv) {
347
361
  if (argv[2] === "status") {
348
362
  return { ...defaults, showStatus: true };
349
363
  }
364
+ if (argv[2] === "runbook") {
365
+ const json = argv.includes("--json");
366
+ let section;
367
+ for (let i = 3; i < argv.length; i++) {
368
+ if ((argv[i] === "--section" || argv[i] === "-s") && argv[i + 1]) {
369
+ section = argv[++i];
370
+ }
371
+ else if (!argv[i].startsWith("-") && !section) {
372
+ section = argv[i];
373
+ }
374
+ }
375
+ return { ...defaults, runRunbook: true, runbookJson: json, runbookSection: section };
376
+ }
377
+ if (argv[2] === "incident") {
378
+ let since;
379
+ let limit;
380
+ let json = false;
381
+ let ndjson = false;
382
+ let watch = false;
383
+ let follow = false;
384
+ let changesOnly = false;
385
+ let heartbeatSec;
386
+ let intervalMs;
387
+ for (let i = 3; i < argv.length; i++) {
388
+ if (argv[i] === "--json") {
389
+ json = true;
390
+ }
391
+ else if (argv[i] === "--ndjson") {
392
+ ndjson = true;
393
+ }
394
+ else if (argv[i] === "--follow" || argv[i] === "-f") {
395
+ follow = true;
396
+ }
397
+ else if (argv[i] === "--watch" || argv[i] === "-w") {
398
+ watch = true;
399
+ }
400
+ else if (argv[i] === "--changes-only") {
401
+ changesOnly = true;
402
+ }
403
+ else if ((argv[i] === "--heartbeat" || argv[i] === "-H") && argv[i + 1]) {
404
+ const val = parseInt(argv[++i], 10);
405
+ if (!isNaN(val) && val >= 1)
406
+ heartbeatSec = val;
407
+ }
408
+ else if ((argv[i] === "--interval" || argv[i] === "-i") && argv[i + 1]) {
409
+ const val = parseInt(argv[++i], 10);
410
+ if (!isNaN(val) && val >= 500)
411
+ intervalMs = val;
412
+ }
413
+ else if (argv[i] === "--since" && argv[i + 1]) {
414
+ since = argv[++i];
415
+ }
416
+ else if (argv[i] === "--limit" && argv[i + 1]) {
417
+ const val = parseInt(argv[++i], 10);
418
+ if (!isNaN(val) && val > 0)
419
+ limit = val;
420
+ }
421
+ }
422
+ if (follow) {
423
+ if (!watch)
424
+ watch = true;
425
+ if (!changesOnly)
426
+ changesOnly = true;
427
+ if (heartbeatSec === undefined)
428
+ heartbeatSec = 30;
429
+ }
430
+ if (changesOnly && !watch)
431
+ watch = true;
432
+ if (heartbeatSec !== undefined) {
433
+ if (!changesOnly)
434
+ changesOnly = true;
435
+ if (!watch)
436
+ watch = true;
437
+ }
438
+ return {
439
+ ...defaults,
440
+ runIncident: true,
441
+ incidentSince: since,
442
+ incidentLimit: limit,
443
+ incidentJson: json,
444
+ incidentNdjson: ndjson,
445
+ incidentWatch: watch,
446
+ incidentChangesOnly: changesOnly,
447
+ incidentHeartbeatSec: heartbeatSec,
448
+ incidentIntervalMs: intervalMs,
449
+ };
450
+ }
451
+ if (argv[2] === "supervisor") {
452
+ let all = false;
453
+ let since;
454
+ let limit;
455
+ let json = false;
456
+ let ndjson = false;
457
+ let watch = false;
458
+ let changesOnly = false;
459
+ let heartbeatSec;
460
+ let intervalMs;
461
+ for (let i = 3; i < argv.length; i++) {
462
+ if (argv[i] === "--all") {
463
+ all = true;
464
+ }
465
+ else if (argv[i] === "--json") {
466
+ json = true;
467
+ }
468
+ else if (argv[i] === "--ndjson") {
469
+ ndjson = true;
470
+ }
471
+ else if (argv[i] === "--watch" || argv[i] === "-w") {
472
+ watch = true;
473
+ }
474
+ else if (argv[i] === "--changes-only") {
475
+ changesOnly = true;
476
+ }
477
+ else if ((argv[i] === "--heartbeat" || argv[i] === "-H") && argv[i + 1]) {
478
+ const val = parseInt(argv[++i], 10);
479
+ if (!isNaN(val) && val >= 1)
480
+ heartbeatSec = val;
481
+ }
482
+ else if (argv[i] === "--since" && argv[i + 1]) {
483
+ since = argv[++i];
484
+ }
485
+ else if (argv[i] === "--limit" && argv[i + 1]) {
486
+ const val = parseInt(argv[++i], 10);
487
+ if (!isNaN(val) && val > 0)
488
+ limit = val;
489
+ }
490
+ else if ((argv[i] === "--interval" || argv[i] === "-i") && argv[i + 1]) {
491
+ const val = parseInt(argv[++i], 10);
492
+ if (!isNaN(val) && val >= 500)
493
+ intervalMs = val;
494
+ }
495
+ }
496
+ if (changesOnly && !watch)
497
+ watch = true;
498
+ if (heartbeatSec !== undefined) {
499
+ if (!changesOnly)
500
+ changesOnly = true;
501
+ if (!watch)
502
+ watch = true;
503
+ }
504
+ return { ...defaults, runSupervisor: true, supervisorAll: all, supervisorSince: since, supervisorLimit: limit, supervisorJson: json, supervisorNdjson: ndjson, supervisorWatch: watch, supervisorChangesOnly: changesOnly, supervisorHeartbeatSec: heartbeatSec, supervisorIntervalMs: intervalMs };
505
+ }
350
506
  if (argv[2] === "config") {
351
507
  const validate = argv.includes("--validate") || argv.includes("-V");
352
508
  const diff = argv.includes("--diff");
@@ -455,7 +611,7 @@ export function parseCliArgs(argv) {
455
611
  return argv[i + 1];
456
612
  };
457
613
  const knownFlags = new Set([
458
- "--reasoner", "--poll-interval", "--port", "--model", "--profile", "--health-port",
614
+ "--reasoner", "--opencode", "--claude-code", "--poll-interval", "--reason-interval", "--port", "--model", "--profile", "--health-port",
459
615
  "--verbose", "-v", "--dry-run", "--observe", "--confirm", "--help", "-h", "--version",
460
616
  ]);
461
617
  for (let i = 2; i < argv.length; i++) {
@@ -465,6 +621,12 @@ export function parseCliArgs(argv) {
465
621
  overrides.reasoner = toReasonerBackend(nextArg(i, arg));
466
622
  i++;
467
623
  break;
624
+ case "--opencode":
625
+ overrides.reasoner = "opencode";
626
+ break;
627
+ case "--claude-code":
628
+ overrides.reasoner = "claude-code";
629
+ break;
468
630
  case "--poll-interval": {
469
631
  const val = parseInt(nextArg(i, arg), 10);
470
632
  if (isNaN(val))
@@ -473,6 +635,14 @@ export function parseCliArgs(argv) {
473
635
  i++;
474
636
  break;
475
637
  }
638
+ case "--reason-interval": {
639
+ const val = parseInt(nextArg(i, arg), 10);
640
+ if (isNaN(val))
641
+ throw new Error(`--reason-interval value '${argv[i + 1]}' is not a valid number`);
642
+ overrides.reasonIntervalMs = val;
643
+ i++;
644
+ break;
645
+ }
476
646
  case "--port": {
477
647
  const val = parseInt(nextArg(i, arg), 10);
478
648
  if (isNaN(val))
@@ -530,7 +700,7 @@ export function parseCliArgs(argv) {
530
700
  break;
531
701
  }
532
702
  }
533
- return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
703
+ return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
534
704
  }
535
705
  export function printHelp() {
536
706
  console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
@@ -547,6 +717,29 @@ commands:
547
717
  init detect tools + sessions, import history, generate config
548
718
  (none) start the supervisor daemon (interactive TUI)
549
719
  status quick daemon health check (is it running? what's it doing?)
720
+ runbook print operator playbook for day-2 supervision
721
+ runbook --json machine-readable runbook output
722
+ runbook --section <quickstart|response-flow|incident|all> print only one runbook section
723
+ incident one-shot incident quick view (response-flow + recent activity)
724
+ incident --since <duration> filter incident events window (30m, 2h, 1d)
725
+ incident --limit <N> cap incident events shown (default: 5)
726
+ incident --json machine-readable incident output
727
+ incident --ndjson emit compact one-line JSON snapshots
728
+ incident --watch stream incident snapshots continuously
729
+ incident --follow shortcut for --watch --changes-only --heartbeat 30
730
+ incident --changes-only emit only when incident state changes (implies --watch)
731
+ incident --heartbeat <sec> keepalive interval (implies --changes-only + --watch)
732
+ incident --interval <ms> watch refresh interval (default: 5000, min: 500)
733
+ supervisor one-shot supervisor/task/session orchestration status
734
+ supervisor --all show full recent supervisor event buffer
735
+ supervisor --since <duration> filter events to a time window (30m, 2h, 7d)
736
+ supervisor --limit <N> cap number of events shown (default: 5)
737
+ supervisor --json machine-readable output for automation
738
+ supervisor --ndjson emit one compact JSON object per snapshot
739
+ supervisor --watch stream supervisor snapshot continuously
740
+ supervisor --changes-only emit only when state changes (implies --watch)
741
+ supervisor --heartbeat <sec> keepalive interval (implies --changes-only + --watch)
742
+ supervisor --interval <ms> watch refresh interval (default: 5000, min: 500)
550
743
  config show the effective resolved config (defaults + file)
551
744
  config --validate validate config + check tool availability
552
745
  config --diff show only fields that differ from defaults
@@ -569,8 +762,12 @@ commands:
569
762
  tail live-stream daemon activity to a separate terminal
570
763
  tail -f follow mode — keep watching for new entries (Ctrl+C to stop)
571
764
  tail -n <N> number of entries to show (default: 50)
572
- task manage tasks and sessions (list, start, stop, new, rm, edit)
765
+ task manage tasks and sessions (list, reconcile, start, stop, new, rm, edit, help)
573
766
  tasks show task progress (from aoaoe.tasks.json)
767
+ tasks --json machine-readable task state output
768
+ progress per-session accomplishment digest (last 24h)
769
+ progress --since <duration> filter progress window (1h, 8h, 7d)
770
+ progress --json machine-readable progress output
574
771
  history review recent actions (from ~/.aoaoe/actions.log)
575
772
  test run integration test (creates sessions, tests, cleans up)
576
773
  test-context scan sessions + context files (read-only, no LLM, safe)
@@ -578,7 +775,10 @@ commands:
578
775
 
579
776
  options:
580
777
  --reasoner <opencode|claude-code> reasoning backend (default: opencode)
581
- --poll-interval <ms> poll interval in ms (default: 10000)
778
+ --opencode shorthand for --reasoner opencode
779
+ --claude-code shorthand for --reasoner claude-code
780
+ --poll-interval <ms> tmux observation poll interval in ms (default: 10000)
781
+ --reason-interval <ms> minimum ms between LLM reasoning calls (default: 60000)
582
782
  --port <number> opencode server port (default: 4097)
583
783
  --health-port <number> start HTTP health check server on this port
584
784
  --model <model> model to use
@@ -24,6 +24,7 @@ export declare class Executor {
24
24
  private resolveSession;
25
25
  private resolveTmuxName;
26
26
  private resolveSessionId;
27
+ private buildProfileAwareAoeArgs;
27
28
  private isProtected;
28
29
  private get cooldownMs();
29
30
  private isRateLimited;