aoaoe 0.74.0 → 0.76.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.
package/dist/colors.d.ts CHANGED
@@ -14,6 +14,7 @@ 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
16
  export declare const BG_DARK = "\u001B[48;5;236m";
17
+ export declare const BG_HOVER = "\u001B[48;5;238m";
17
18
  export declare const BOX: {
18
19
  readonly tl: "┌";
19
20
  readonly tr: "┐";
package/dist/colors.js CHANGED
@@ -19,6 +19,7 @@ export const LIME = "\x1b[38;5;114m"; // fresh green for success/working
19
19
  export const SKY = "\x1b[38;5;117m"; // light blue for reasoning
20
20
  // background variants (256-color)
21
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
22
23
  // box-drawing characters — Unicode block elements
23
24
  export const BOX = {
24
25
  tl: "┌", tr: "┐", bl: "└", br: "┘",
package/dist/index.js CHANGED
@@ -325,6 +325,25 @@ async function main() {
325
325
  tui.log("system", `viewing session #${sessionIdx}`);
326
326
  }
327
327
  });
328
+ // wire quick-switch: bare digit 1-9 jumps to session (or switches in drilldown)
329
+ input.onQuickSwitch((num) => {
330
+ if (tui.getViewMode() === "drilldown") {
331
+ // in drilldown, switch to a different session
332
+ const ok = tui.enterDrilldown(num);
333
+ if (ok)
334
+ tui.log("system", `switched to session #${num}`);
335
+ else
336
+ tui.log("system", `session #${num} not found`);
337
+ }
338
+ else {
339
+ // in overview, drill into session
340
+ const ok = tui.enterDrilldown(num);
341
+ if (ok)
342
+ tui.log("system", `viewing session #${num}`);
343
+ else
344
+ tui.log("system", `session #${num} not found`);
345
+ }
346
+ });
328
347
  // wire /search command to TUI activity filter
329
348
  input.onSearch((pattern) => {
330
349
  tui.setSearch(pattern);
@@ -335,6 +354,13 @@ async function main() {
335
354
  tui.log("system", "search cleared");
336
355
  }
337
356
  });
357
+ // wire mouse move to hover highlight on session cards
358
+ input.onMouseMove((row, _col) => {
359
+ if (tui.getViewMode() === "overview") {
360
+ const sessionIdx = hitTestSession(row, 1, tui.getSessionCount());
361
+ tui.setHoverSession(sessionIdx);
362
+ }
363
+ });
338
364
  // wire mouse wheel to scroll (3 lines per tick for smooth scrolling)
339
365
  input.onMouseWheel((direction) => {
340
366
  if (tui.getViewMode() === "drilldown") {
package/dist/input.d.ts CHANGED
@@ -2,6 +2,7 @@ export type ScrollDirection = "up" | "down" | "top" | "bottom";
2
2
  export declare const INSIST_PREFIX = "__INSIST__";
3
3
  export type ViewHandler = (target: string | null) => void;
4
4
  export type SearchHandler = (pattern: string | null) => void;
5
+ export type QuickSwitchHandler = (sessionNum: number) => void;
5
6
  export interface MouseEvent {
6
7
  button: number;
7
8
  col: number;
@@ -10,6 +11,7 @@ export interface MouseEvent {
10
11
  }
11
12
  export type MouseClickHandler = (row: number, col: number) => void;
12
13
  export type MouseWheelHandler = (direction: "up" | "down") => void;
14
+ export type MouseMoveHandler = (row: number, col: number) => void;
13
15
  /** Parse an SGR extended mouse event from raw terminal data. Returns null if not a mouse event. */
14
16
  export declare function parseMouseEvent(data: string): MouseEvent | null;
15
17
  export declare class InputReader {
@@ -22,14 +24,19 @@ export declare class InputReader {
22
24
  private viewHandler;
23
25
  private mouseClickHandler;
24
26
  private mouseWheelHandler;
27
+ private mouseMoveHandler;
28
+ private lastMoveRow;
25
29
  private searchHandler;
30
+ private quickSwitchHandler;
26
31
  private mouseDataListener;
27
32
  onScroll(handler: (dir: ScrollDirection) => void): void;
28
33
  onQueueChange(handler: (count: number) => void): void;
29
34
  onView(handler: ViewHandler): void;
30
35
  onMouseClick(handler: MouseClickHandler): void;
31
36
  onMouseWheel(handler: MouseWheelHandler): void;
37
+ onMouseMove(handler: MouseMoveHandler): void;
32
38
  onSearch(handler: SearchHandler): void;
39
+ onQuickSwitch(handler: QuickSwitchHandler): void;
33
40
  private notifyQueueChange;
34
41
  start(): void;
35
42
  drain(): string[];
package/dist/input.js CHANGED
@@ -31,7 +31,10 @@ export class InputReader {
31
31
  viewHandler = null;
32
32
  mouseClickHandler = null;
33
33
  mouseWheelHandler = null;
34
+ mouseMoveHandler = null;
35
+ lastMoveRow = 0; // debounce: only fire move handler when row changes
34
36
  searchHandler = null;
37
+ quickSwitchHandler = null;
35
38
  mouseDataListener = null;
36
39
  // register a callback for scroll key events (PgUp/PgDn/Home/End)
37
40
  onScroll(handler) {
@@ -53,10 +56,18 @@ export class InputReader {
53
56
  onMouseWheel(handler) {
54
57
  this.mouseWheelHandler = handler;
55
58
  }
59
+ // register a callback for mouse move events (only fires on row change for efficiency)
60
+ onMouseMove(handler) {
61
+ this.mouseMoveHandler = handler;
62
+ }
56
63
  // register a callback for search commands (/search <pattern> or /search to clear)
57
64
  onSearch(handler) {
58
65
  this.searchHandler = handler;
59
66
  }
67
+ // register a callback for quick-switch (bare digit 1-9 on empty input line)
68
+ onQuickSwitch(handler) {
69
+ this.quickSwitchHandler = handler;
70
+ }
60
71
  notifyQueueChange() {
61
72
  this.queueChangeHandler?.(this.queue.length);
62
73
  }
@@ -91,6 +102,13 @@ export class InputReader {
91
102
  else if (evt.button === 65 && this.mouseWheelHandler) {
92
103
  this.mouseWheelHandler("down");
93
104
  }
105
+ // mouse motion: bit 5 set (button 32-35), only fire on row change
106
+ if (evt.button >= 32 && evt.button <= 35 && this.mouseMoveHandler) {
107
+ if (evt.row !== this.lastMoveRow) {
108
+ this.lastMoveRow = evt.row;
109
+ this.mouseMoveHandler(evt.row, evt.col);
110
+ }
111
+ }
94
112
  };
95
113
  process.stdin.on("data", this.mouseDataListener);
96
114
  process.stdin.on("keypress", (_ch, key) => {
@@ -178,6 +196,12 @@ export class InputReader {
178
196
  this.rl?.prompt();
179
197
  return;
180
198
  }
199
+ // quick-switch: bare digit 1-9 jumps to that session
200
+ if (/^[1-9]$/.test(line) && this.quickSwitchHandler) {
201
+ this.quickSwitchHandler(parseInt(line, 10));
202
+ this.rl?.prompt();
203
+ return;
204
+ }
181
205
  // built-in slash commands
182
206
  if (line.startsWith("/")) {
183
207
  this.handleCommand(line);
@@ -218,6 +242,7 @@ ${BOLD}controls:${RESET}
218
242
  ESC ESC same as /interrupt (shortcut)
219
243
 
220
244
  ${BOLD}navigation:${RESET}
245
+ 1-9 quick-switch: jump to session N (type digit + Enter)
221
246
  /view [N|name] drill into a session's live output (default: 1)
222
247
  /back return to overview from drill-down
223
248
  /search <pattern> filter activity entries by substring (case-insensitive)
package/dist/tui.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare class TUI {
24
24
  private newWhileScrolled;
25
25
  private pendingCount;
26
26
  private searchPattern;
27
+ private hoverSessionIdx;
27
28
  private viewMode;
28
29
  private drilldownSessionId;
29
30
  private sessionOutputs;
@@ -75,12 +76,18 @@ export declare class TUI {
75
76
  setSearch(pattern: string | null): void;
76
77
  /** Get the current search pattern (or null if no active search). */
77
78
  getSearchPattern(): string | null;
79
+ /** Set the hovered session index (1-indexed) or null to clear. Only repaints the affected cards. */
80
+ setHoverSession(idx: number | null): void;
81
+ /** Get the current hovered session index (1-indexed, null if none). */
82
+ getHoverSession(): number | null;
78
83
  private updateDimensions;
79
84
  private computeLayout;
80
85
  private onResize;
81
86
  private paintAll;
82
87
  private paintHeader;
83
88
  private paintSessions;
89
+ /** Repaint a single session card by 1-indexed position (for hover updates). */
90
+ private repaintSessionCard;
84
91
  private paintSeparator;
85
92
  private writeActivityLine;
86
93
  private repaintActivityRegion;
@@ -91,6 +98,7 @@ export declare class TUI {
91
98
  declare function formatSessionCard(s: DaemonSessionState, maxWidth: number): string;
92
99
  declare function formatActivity(entry: ActivityEntry, maxCols: number): string;
93
100
  declare function padBoxLine(line: string, totalWidth: number): string;
101
+ declare function padBoxLineHover(line: string, totalWidth: number, hovered: boolean): string;
94
102
  declare function padToWidth(line: string, totalWidth: number): string;
95
103
  declare function stripAnsiForLen(str: string): number;
96
104
  declare function truncateAnsi(str: string, maxCols: number): string;
@@ -120,5 +128,5 @@ declare function formatSearchIndicator(pattern: string, matchCount: number, tota
120
128
  * (row = headerHeight + 2 + i for 0-indexed session i)
121
129
  */
122
130
  export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
123
- export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
131
+ export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
124
132
  //# sourceMappingURL=tui.d.ts.map
package/dist/tui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BOLD, DIM, RESET, GREEN, CYAN, WHITE, BG_DARK, INDIGO, TEAL, AMBER, SLATE, ROSE, LIME, SKY, BOX, SPINNER, DOT, } from "./colors.js";
1
+ import { BOLD, DIM, RESET, GREEN, CYAN, WHITE, BG_DARK, BG_HOVER, INDIGO, TEAL, AMBER, SLATE, ROSE, LIME, SKY, BOX, SPINNER, DOT, } from "./colors.js";
2
2
  import { appendHistoryEntry } from "./tui-history.js";
3
3
  // ── ANSI helpers ────────────────────────────────────────────────────────────
4
4
  const ESC = "\x1b";
@@ -12,9 +12,11 @@ const CURSOR_HIDE = `${CSI}?25l`;
12
12
  const CURSOR_SHOW = `${CSI}?25h`;
13
13
  const SAVE_CURSOR = `${ESC}7`;
14
14
  const RESTORE_CURSOR = `${ESC}8`;
15
- // mouse tracking (SGR extended mode — button events + extended coordinates)
16
- const MOUSE_ON = `${CSI}?1000h${CSI}?1006h`;
17
- const MOUSE_OFF = `${CSI}?1000l${CSI}?1006l`;
15
+ // mouse tracking (SGR extended mode — any-event tracking + extended coordinates)
16
+ // ?1003h = report all mouse events including motion (needed for hover)
17
+ // ?1006h = SGR extended format (supports large coordinates)
18
+ const MOUSE_ON = `${CSI}?1003h${CSI}?1006h`;
19
+ const MOUSE_OFF = `${CSI}?1003l${CSI}?1006l`;
18
20
  // cursor movement
19
21
  const moveTo = (row, col) => `${CSI}${row};${col}H`;
20
22
  const setScrollRegion = (top, bottom) => `${CSI}${top};${bottom}r`;
@@ -62,6 +64,7 @@ export class TUI {
62
64
  newWhileScrolled = 0; // entries added while user is scrolled back
63
65
  pendingCount = 0; // queued user messages awaiting next tick
64
66
  searchPattern = null; // active search filter pattern
67
+ hoverSessionIdx = null; // 1-indexed session under mouse cursor (null = none)
65
68
  // drill-down mode: show a single session's full output
66
69
  viewMode = "overview";
67
70
  drilldownSessionId = null;
@@ -318,6 +321,7 @@ export class TUI {
318
321
  this.drilldownSessionId = sessionId;
319
322
  this.drilldownScrollOffset = 0;
320
323
  this.drilldownNewWhileScrolled = 0;
324
+ this.hoverSessionIdx = null;
321
325
  if (this.active) {
322
326
  this.computeLayout(this.sessions.length);
323
327
  this.paintAll();
@@ -332,6 +336,7 @@ export class TUI {
332
336
  this.drilldownSessionId = null;
333
337
  this.drilldownScrollOffset = 0;
334
338
  this.drilldownNewWhileScrolled = 0;
339
+ this.hoverSessionIdx = null;
335
340
  if (this.active) {
336
341
  this.computeLayout(this.sessions.length);
337
342
  this.paintAll();
@@ -360,6 +365,25 @@ export class TUI {
360
365
  getSearchPattern() {
361
366
  return this.searchPattern;
362
367
  }
368
+ // ── Hover ───────────────────────────────────────────────────────────────
369
+ /** Set the hovered session index (1-indexed) or null to clear. Only repaints the affected cards. */
370
+ setHoverSession(idx) {
371
+ if (idx === this.hoverSessionIdx)
372
+ return; // no change
373
+ const prev = this.hoverSessionIdx;
374
+ this.hoverSessionIdx = idx;
375
+ if (this.active && this.viewMode === "overview") {
376
+ // repaint only the affected session cards (previous and new hover)
377
+ if (prev !== null)
378
+ this.repaintSessionCard(prev);
379
+ if (idx !== null)
380
+ this.repaintSessionCard(idx);
381
+ }
382
+ }
383
+ /** Get the current hovered session index (1-indexed, null if none). */
384
+ getHoverSession() {
385
+ return this.hoverSessionIdx;
386
+ }
363
387
  // ── Layout computation ──────────────────────────────────────────────────
364
388
  updateDimensions() {
365
389
  this.cols = process.stderr.columns || 80;
@@ -451,8 +475,10 @@ export class TUI {
451
475
  else {
452
476
  for (let i = 0; i < this.sessions.length; i++) {
453
477
  const s = this.sessions[i];
454
- const line = `${SLATE}${BOX.v}${RESET} ${formatSessionCard(s, innerWidth - 1)}`;
455
- const padded = padBoxLine(line, this.cols);
478
+ const isHovered = this.hoverSessionIdx === i + 1; // 1-indexed
479
+ const bg = isHovered ? BG_HOVER : "";
480
+ const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${formatSessionCard(s, innerWidth - 1)}`;
481
+ const padded = padBoxLineHover(line, this.cols, isHovered);
456
482
  process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
457
483
  }
458
484
  }
@@ -467,6 +493,22 @@ export class TUI {
467
493
  }
468
494
  process.stderr.write(RESTORE_CURSOR);
469
495
  }
496
+ /** Repaint a single session card by 1-indexed position (for hover updates). */
497
+ repaintSessionCard(idx) {
498
+ if (!this.active || this.viewMode !== "overview")
499
+ return;
500
+ const i = idx - 1; // 0-indexed
501
+ if (i < 0 || i >= this.sessions.length)
502
+ return;
503
+ const startRow = this.headerHeight + 1;
504
+ const innerWidth = this.cols - 2;
505
+ const s = this.sessions[i];
506
+ const isHovered = this.hoverSessionIdx === idx;
507
+ const bg = isHovered ? BG_HOVER : "";
508
+ const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${formatSessionCard(s, innerWidth - 1)}`;
509
+ const padded = padBoxLineHover(line, this.cols, isHovered);
510
+ process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
511
+ }
470
512
  paintSeparator() {
471
513
  const prefix = `${BOX.h}${BOX.h} activity `;
472
514
  let hints;
@@ -644,6 +686,15 @@ function padBoxLine(line, totalWidth) {
644
686
  const pad = Math.max(0, totalWidth - visible - 1); // -1 for closing border
645
687
  return line + " ".repeat(pad) + `${SLATE}${BOX.v}${RESET}`;
646
688
  }
689
+ // pad a box line with optional hover background that extends through the padding
690
+ function padBoxLineHover(line, totalWidth, hovered) {
691
+ const visible = stripAnsiForLen(line);
692
+ const pad = Math.max(0, totalWidth - visible - 1);
693
+ if (hovered) {
694
+ return line + `${BG_HOVER}${" ".repeat(pad)}${RESET}${SLATE}${BOX.v}${RESET}`;
695
+ }
696
+ return line + " ".repeat(pad) + `${SLATE}${BOX.v}${RESET}`;
697
+ }
647
698
  // pad the header bar to fill the full width with background color
648
699
  function padToWidth(line, totalWidth) {
649
700
  const visible = stripAnsiForLen(line);
@@ -784,5 +835,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
784
835
  return row - firstSessionRow + 1; // 1-indexed
785
836
  }
786
837
  // ── Exported pure helpers (for testing) ─────────────────────────────────────
787
- export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
838
+ export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
788
839
  //# sourceMappingURL=tui.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aoaoe",
3
- "version": "0.74.0",
3
+ "version": "0.76.0",
4
4
  "description": "Autonomous supervisor for agent-of-empires sessions using OpenCode or Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",