aoaoe 0.81.0 → 0.82.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/index.js CHANGED
@@ -378,6 +378,12 @@ async function main() {
378
378
  tui.setCompact(enabled);
379
379
  tui.log("system", `compact mode: ${enabled ? "on" : "off"}`);
380
380
  });
381
+ // wire /focus toggle
382
+ input.onFocus(() => {
383
+ const enabled = !tui.isFocused();
384
+ tui.setFocus(enabled);
385
+ tui.log("system", `focus mode: ${enabled ? "on (pinned only)" : "off (all sessions)"}`);
386
+ });
381
387
  // wire /bell toggle
382
388
  input.onBell(() => {
383
389
  const enabled = !tui.isBellEnabled();
package/dist/input.d.ts CHANGED
@@ -7,6 +7,7 @@ export type SortHandler = (mode: string | null) => void;
7
7
  export type CompactHandler = () => void;
8
8
  export type PinHandler = (target: string) => void;
9
9
  export type BellHandler = () => void;
10
+ export type FocusHandler = () => void;
10
11
  export interface MouseEvent {
11
12
  button: number;
12
13
  col: number;
@@ -36,6 +37,7 @@ export declare class InputReader {
36
37
  private compactHandler;
37
38
  private pinHandler;
38
39
  private bellHandler;
40
+ private focusHandler;
39
41
  private mouseDataListener;
40
42
  onScroll(handler: (dir: ScrollDirection) => void): void;
41
43
  onQueueChange(handler: (count: number) => void): void;
@@ -49,6 +51,7 @@ export declare class InputReader {
49
51
  onCompact(handler: CompactHandler): void;
50
52
  onPin(handler: PinHandler): void;
51
53
  onBell(handler: BellHandler): void;
54
+ onFocus(handler: FocusHandler): void;
52
55
  private notifyQueueChange;
53
56
  start(): void;
54
57
  drain(): string[];
package/dist/input.js CHANGED
@@ -39,6 +39,7 @@ export class InputReader {
39
39
  compactHandler = null;
40
40
  pinHandler = null;
41
41
  bellHandler = null;
42
+ focusHandler = null;
42
43
  mouseDataListener = null;
43
44
  // register a callback for scroll key events (PgUp/PgDn/Home/End)
44
45
  onScroll(handler) {
@@ -88,6 +89,10 @@ export class InputReader {
88
89
  onBell(handler) {
89
90
  this.bellHandler = handler;
90
91
  }
92
+ // register a callback for focus mode toggle (/focus)
93
+ onFocus(handler) {
94
+ this.focusHandler = handler;
95
+ }
91
96
  notifyQueueChange() {
92
97
  this.queueChangeHandler?.(this.queue.length);
93
98
  }
@@ -269,6 +274,7 @@ ${BOLD}navigation:${RESET}
269
274
  /compact toggle compact mode (dense session panel)
270
275
  /pin [N|name] pin/unpin a session to the top (toggle)
271
276
  /bell toggle terminal bell on errors/completions
277
+ /focus toggle focus mode (show only pinned sessions)
272
278
  /search <pattern> filter activity entries by substring (case-insensitive)
273
279
  /search clear active search filter
274
280
  click session click an agent card to drill down (click again to go back)
@@ -389,6 +395,14 @@ ${BOLD}other:${RESET}
389
395
  console.error(`${DIM}bell not available (no TUI)${RESET}`);
390
396
  }
391
397
  break;
398
+ case "/focus":
399
+ if (this.focusHandler) {
400
+ this.focusHandler();
401
+ }
402
+ else {
403
+ console.error(`${DIM}focus not available (no TUI)${RESET}`);
404
+ }
405
+ break;
392
406
  case "/search": {
393
407
  const searchArg = line.slice("/search".length).trim();
394
408
  if (this.searchHandler) {
package/dist/tui.d.ts CHANGED
@@ -53,6 +53,7 @@ export declare class TUI {
53
53
  private prevLastActivity;
54
54
  private compactMode;
55
55
  private pinnedIds;
56
+ private focusMode;
56
57
  private bellEnabled;
57
58
  private lastBellAt;
58
59
  private viewMode;
@@ -70,7 +71,7 @@ export declare class TUI {
70
71
  start(version: string): void;
71
72
  stop(): void;
72
73
  isActive(): boolean;
73
- /** Return the current number of sessions (for mouse hit testing) */
74
+ /** Return the current number of visible sessions (for mouse hit testing) */
74
75
  getSessionCount(): number;
75
76
  /** Set the session sort mode and repaint. */
76
77
  setSortMode(mode: SortMode): void;
@@ -89,6 +90,12 @@ export declare class TUI {
89
90
  isPinned(id: string): boolean;
90
91
  /** Return count of pinned sessions. */
91
92
  getPinnedCount(): number;
93
+ /** Enable or disable focus mode. When focused, only pinned sessions are visible. */
94
+ setFocus(enabled: boolean): void;
95
+ /** Return whether focus mode is enabled. */
96
+ isFocused(): boolean;
97
+ /** Return count of visible sessions (all in normal mode, pinned-only in focus mode). */
98
+ private getVisibleCount;
92
99
  /** Enable or disable terminal bell notifications. */
93
100
  setBell(enabled: boolean): void;
94
101
  /** Return whether terminal bell is enabled. */
package/dist/tui.js CHANGED
@@ -161,6 +161,7 @@ export class TUI {
161
161
  prevLastActivity = new Map(); // session ID → previous lastActivity string
162
162
  compactMode = false;
163
163
  pinnedIds = new Set(); // pinned session IDs (always sort to top)
164
+ focusMode = false; // focus mode: hide all sessions except pinned
164
165
  bellEnabled = false;
165
166
  lastBellAt = 0;
166
167
  // drill-down mode: show a single session's full output
@@ -215,9 +216,9 @@ export class TUI {
215
216
  isActive() {
216
217
  return this.active;
217
218
  }
218
- /** Return the current number of sessions (for mouse hit testing) */
219
+ /** Return the current number of visible sessions (for mouse hit testing) */
219
220
  getSessionCount() {
220
- return this.sessions.length;
221
+ return this.getVisibleCount();
221
222
  }
222
223
  /** Set the session sort mode and repaint. */
223
224
  setSortMode(mode) {
@@ -240,7 +241,7 @@ export class TUI {
240
241
  return;
241
242
  this.compactMode = enabled;
242
243
  if (this.active) {
243
- this.computeLayout(this.sessions.length);
244
+ this.computeLayout(this.getVisibleCount());
244
245
  this.paintAll();
245
246
  }
246
247
  }
@@ -285,6 +286,31 @@ export class TUI {
285
286
  getPinnedCount() {
286
287
  return this.pinnedIds.size;
287
288
  }
289
+ /** Enable or disable focus mode. When focused, only pinned sessions are visible. */
290
+ setFocus(enabled) {
291
+ if (enabled === this.focusMode)
292
+ return;
293
+ this.focusMode = enabled;
294
+ if (this.active) {
295
+ this.computeLayout(this.getVisibleCount());
296
+ this.paintAll();
297
+ }
298
+ }
299
+ /** Return whether focus mode is enabled. */
300
+ isFocused() {
301
+ return this.focusMode;
302
+ }
303
+ /** Return count of visible sessions (all in normal mode, pinned-only in focus mode). */
304
+ getVisibleCount() {
305
+ if (!this.focusMode)
306
+ return this.sessions.length;
307
+ let count = 0;
308
+ for (const s of this.sessions) {
309
+ if (this.pinnedIds.has(s.id))
310
+ count++;
311
+ }
312
+ return count;
313
+ }
288
314
  /** Enable or disable terminal bell notifications. */
289
315
  setBell(enabled) {
290
316
  this.bellEnabled = enabled;
@@ -319,10 +345,11 @@ export class TUI {
319
345
  this.prevLastActivity.set(s.id, s.lastActivity);
320
346
  }
321
347
  const sorted = sortSessions(opts.sessions, this.sortMode, this.lastChangeAt, this.pinnedIds);
322
- const sessionCountChanged = sorted.length !== this.sessions.length;
348
+ const prevVisibleCount = this.getVisibleCount();
323
349
  this.sessions = sorted;
324
- if (sessionCountChanged) {
325
- this.computeLayout(this.sessions.length);
350
+ const newVisibleCount = this.getVisibleCount();
351
+ if (newVisibleCount !== prevVisibleCount) {
352
+ this.computeLayout(newVisibleCount);
326
353
  this.paintAll();
327
354
  return;
328
355
  }
@@ -516,7 +543,7 @@ export class TUI {
516
543
  this.drilldownNewWhileScrolled = 0;
517
544
  this.hoverSessionIdx = null;
518
545
  if (this.active) {
519
- this.computeLayout(this.sessions.length);
546
+ this.computeLayout(this.getVisibleCount());
520
547
  this.paintAll();
521
548
  }
522
549
  return true;
@@ -531,7 +558,7 @@ export class TUI {
531
558
  this.drilldownNewWhileScrolled = 0;
532
559
  this.hoverSessionIdx = null;
533
560
  if (this.active) {
534
- this.computeLayout(this.sessions.length);
561
+ this.computeLayout(this.getVisibleCount());
535
562
  this.paintAll();
536
563
  }
537
564
  }
@@ -594,8 +621,9 @@ export class TUI {
594
621
  }
595
622
  else {
596
623
  // overview: header (1) + sessions box + separator + activity + input
624
+ const visibleSessions = this.sessions.slice(0, this.getVisibleCount());
597
625
  const sessBodyRows = this.compactMode
598
- ? computeCompactRowCount(this.sessions, this.cols - 2)
626
+ ? computeCompactRowCount(visibleSessions, this.cols - 2)
599
627
  : Math.max(sessionCount, 1);
600
628
  this.sessionRows = sessBodyRows + 2; // + top/bottom borders
601
629
  this.separatorRow = this.headerHeight + this.sessionRows + 1;
@@ -608,7 +636,7 @@ export class TUI {
608
636
  }
609
637
  }
610
638
  onResize() {
611
- this.computeLayout(this.sessions.length);
639
+ this.computeLayout(this.getVisibleCount());
612
640
  this.paintAll();
613
641
  }
614
642
  // ── Painting ────────────────────────────────────────────────────────────
@@ -636,7 +664,10 @@ export class TUI {
636
664
  }
637
665
  else {
638
666
  const phaseText = phaseDisplay(this.phase, this.paused, this.spinnerFrame);
639
- const sessCount = `${this.sessions.length} agent${this.sessions.length !== 1 ? "s" : ""}`;
667
+ const visCount = this.getVisibleCount();
668
+ const sessCount = this.focusMode
669
+ ? `${visCount}/${this.sessions.length} agent${this.sessions.length !== 1 ? "s" : ""}`
670
+ : `${this.sessions.length} agent${this.sessions.length !== 1 ? "s" : ""}`;
640
671
  const activeCount = this.sessions.filter((s) => s.userActive).length;
641
672
  const activeTag = activeCount > 0 ? ` ${SLATE}│${RESET} ${AMBER}${activeCount} user${RESET}` : "";
642
673
  // countdown to next tick (only in sleeping phase)
@@ -656,23 +687,29 @@ export class TUI {
656
687
  paintSessions() {
657
688
  const startRow = this.headerHeight + 1;
658
689
  const innerWidth = this.cols - 2; // inside the box borders
659
- // top border with label (includes compact/sort mode tags)
690
+ const visibleCount = this.getVisibleCount();
691
+ const visibleSessions = this.sessions.slice(0, visibleCount); // pinned sort to top
692
+ // top border with label (includes focus/compact/sort mode tags)
693
+ const focusTag = this.focusMode ? "focus" : "";
660
694
  const sortTag = this.sortMode !== "default" ? this.sortMode : "";
661
695
  const compactTag = this.compactMode ? "compact" : "";
662
- const tags = [compactTag, sortTag].filter(Boolean).join(", ");
696
+ const tags = [focusTag, compactTag, sortTag].filter(Boolean).join(", ");
663
697
  const label = tags ? ` agents (${tags}) ` : " agents ";
664
698
  const borderAfterLabel = Math.max(0, innerWidth - label.length);
665
699
  const topBorder = `${SLATE}${BOX.rtl}${BOX.h}${RESET}${SLATE}${label}${RESET}${SLATE}${BOX.h.repeat(borderAfterLabel)}${BOX.rtr}${RESET}`;
666
700
  process.stderr.write(SAVE_CURSOR + moveTo(startRow, 1) + CLEAR_LINE + truncateAnsi(topBorder, this.cols));
667
- if (this.sessions.length === 0) {
701
+ if (visibleSessions.length === 0) {
668
702
  // empty state
669
- const empty = `${SLATE}${BOX.v}${RESET} ${DIM}no agents connected${RESET}`;
703
+ const msg = this.focusMode && this.sessions.length > 0
704
+ ? `${DIM}no pinned agents — /pin to add, /focus to exit${RESET}`
705
+ : `${DIM}no agents connected${RESET}`;
706
+ const empty = `${SLATE}${BOX.v}${RESET} ${msg}`;
670
707
  const padded = padBoxLine(empty, this.cols);
671
708
  process.stderr.write(moveTo(startRow + 1, 1) + CLEAR_LINE + padded);
672
709
  }
673
710
  else if (this.compactMode) {
674
711
  // compact: inline tokens, multiple per row (with pin indicators)
675
- const compactRows = formatCompactRows(this.sessions, innerWidth - 1, this.pinnedIds);
712
+ const compactRows = formatCompactRows(visibleSessions, innerWidth - 1, this.pinnedIds);
676
713
  for (let r = 0; r < compactRows.length; r++) {
677
714
  const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
678
715
  const padded = padBoxLine(line, this.cols);
@@ -680,8 +717,8 @@ export class TUI {
680
717
  }
681
718
  }
682
719
  else {
683
- for (let i = 0; i < this.sessions.length; i++) {
684
- const s = this.sessions[i];
720
+ for (let i = 0; i < visibleSessions.length; i++) {
721
+ const s = visibleSessions[i];
685
722
  const isHovered = this.hoverSessionIdx === i + 1; // 1-indexed
686
723
  const bg = isHovered ? BG_HOVER : "";
687
724
  const pinned = this.pinnedIds.has(s.id);
@@ -694,8 +731,8 @@ export class TUI {
694
731
  }
695
732
  // bottom border
696
733
  const bodyRows = this.compactMode
697
- ? computeCompactRowCount(this.sessions, innerWidth)
698
- : Math.max(this.sessions.length, 1);
734
+ ? computeCompactRowCount(visibleSessions, innerWidth)
735
+ : Math.max(visibleCount, 1);
699
736
  const bottomRow = startRow + 1 + bodyRows;
700
737
  const bottomBorder = `${SLATE}${BOX.rbl}${BOX.h.repeat(Math.max(0, this.cols - 2))}${BOX.rbr}${RESET}`;
701
738
  process.stderr.write(moveTo(bottomRow, 1) + CLEAR_LINE + truncateAnsi(bottomBorder, this.cols));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aoaoe",
3
- "version": "0.81.0",
3
+ "version": "0.82.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",