aoaoe 0.85.0 → 0.86.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
@@ -444,6 +444,16 @@ async function main() {
444
444
  tui.log("system", `session not found: ${target}`);
445
445
  }
446
446
  });
447
+ // wire /unmute-all
448
+ input.onUnmuteAll(() => {
449
+ const count = tui.unmuteAll();
450
+ if (count > 0) {
451
+ tui.log("system", `unmuted ${count} session${count === 1 ? "" : "s"}`);
452
+ }
453
+ else {
454
+ tui.log("system", "no sessions are muted");
455
+ }
456
+ });
447
457
  // wire /note set/clear
448
458
  input.onNote((target, text) => {
449
459
  const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
package/dist/input.d.ts CHANGED
@@ -12,6 +12,7 @@ export type MarkHandler = () => void;
12
12
  export type JumpHandler = (num: number) => void;
13
13
  export type MarksHandler = () => void;
14
14
  export type MuteHandler = (target: string) => void;
15
+ export type UnmuteAllHandler = () => void;
15
16
  export type NoteHandler = (target: string, text: string) => void;
16
17
  export type NotesHandler = () => void;
17
18
  export interface MouseEvent {
@@ -48,6 +49,7 @@ export declare class InputReader {
48
49
  private jumpHandler;
49
50
  private marksHandler;
50
51
  private muteHandler;
52
+ private unmuteAllHandler;
51
53
  private noteHandler;
52
54
  private notesHandler;
53
55
  private mouseDataListener;
@@ -68,6 +70,7 @@ export declare class InputReader {
68
70
  onJump(handler: JumpHandler): void;
69
71
  onMarks(handler: MarksHandler): void;
70
72
  onMute(handler: MuteHandler): void;
73
+ onUnmuteAll(handler: UnmuteAllHandler): void;
71
74
  onNote(handler: NoteHandler): void;
72
75
  onNotes(handler: NotesHandler): void;
73
76
  private notifyQueueChange;
package/dist/input.js CHANGED
@@ -44,6 +44,7 @@ export class InputReader {
44
44
  jumpHandler = null;
45
45
  marksHandler = null;
46
46
  muteHandler = null;
47
+ unmuteAllHandler = null;
47
48
  noteHandler = null;
48
49
  notesHandler = null;
49
50
  mouseDataListener = null;
@@ -115,6 +116,10 @@ export class InputReader {
115
116
  onMute(handler) {
116
117
  this.muteHandler = handler;
117
118
  }
119
+ // register a callback for unmuting all sessions (/unmute-all)
120
+ onUnmuteAll(handler) {
121
+ this.unmuteAllHandler = handler;
122
+ }
118
123
  // register a callback for note commands (/note <target> <text>)
119
124
  onNote(handler) {
120
125
  this.noteHandler = handler;
@@ -306,6 +311,7 @@ ${BOLD}navigation:${RESET}
306
311
  /bell toggle terminal bell on errors/completions
307
312
  /focus toggle focus mode (show only pinned sessions)
308
313
  /mute [N|name] mute/unmute a session's activity entries (toggle)
314
+ /unmute-all unmute all sessions at once
309
315
  /note N|name text attach a note to a session (no text = clear)
310
316
  /notes list all session notes
311
317
  /mark bookmark current activity position
@@ -454,6 +460,14 @@ ${BOLD}other:${RESET}
454
460
  }
455
461
  break;
456
462
  }
463
+ case "/unmute-all":
464
+ if (this.unmuteAllHandler) {
465
+ this.unmuteAllHandler();
466
+ }
467
+ else {
468
+ console.error(`${DIM}unmute-all not available (no TUI)${RESET}`);
469
+ }
470
+ break;
457
471
  case "/note": {
458
472
  const noteArg = line.slice("/note".length).trim();
459
473
  if (this.noteHandler) {
package/dist/tui.d.ts CHANGED
@@ -51,6 +51,8 @@ export declare const MAX_NOTE_LEN = 80;
51
51
  export declare function truncateNote(text: string): string;
52
52
  /** Determine if an activity entry should be hidden due to muting. */
53
53
  export declare function shouldMuteEntry(entry: ActivityEntry, mutedIds: Set<string>): boolean;
54
+ /** Format a suppressed entry count badge for muted sessions. Returns empty string for 0. */
55
+ export declare function formatMuteBadge(count: number): string;
54
56
  export declare class TUI {
55
57
  private active;
56
58
  private countdownTimer;
@@ -81,6 +83,7 @@ export declare class TUI {
81
83
  private bellEnabled;
82
84
  private lastBellAt;
83
85
  private mutedIds;
86
+ private mutedEntryCounts;
84
87
  private sessionNotes;
85
88
  private viewMode;
86
89
  private drilldownSessionId;
@@ -132,10 +135,14 @@ export declare class TUI {
132
135
  * Returns true if session found.
133
136
  */
134
137
  toggleMute(sessionIdOrIndex: string | number): boolean;
138
+ /** Unmute all sessions at once. Returns count of sessions unmuted. */
139
+ unmuteAll(): number;
135
140
  /** Check if a session ID is muted. */
136
141
  isMuted(id: string): boolean;
137
142
  /** Return count of muted sessions. */
138
143
  getMutedCount(): number;
144
+ /** Return count of suppressed entries for a muted session (0 if not muted). */
145
+ getMutedEntryCount(id: string): number;
139
146
  /**
140
147
  * Set a note on a session (by 1-indexed number, ID, ID prefix, or title).
141
148
  * Returns true if session found. Pass empty text to clear.
package/dist/tui.js CHANGED
@@ -174,6 +174,13 @@ export function shouldMuteEntry(entry, mutedIds) {
174
174
  return false;
175
175
  return mutedIds.has(entry.sessionId);
176
176
  }
177
+ /** Format a suppressed entry count badge for muted sessions. Returns empty string for 0. */
178
+ export function formatMuteBadge(count) {
179
+ if (count <= 0)
180
+ return "";
181
+ const label = count > 999 ? "999+" : String(count);
182
+ return `${DIM}(${label})${RESET}`;
183
+ }
177
184
  // ── TUI class ───────────────────────────────────────────────────────────────
178
185
  export class TUI {
179
186
  active = false;
@@ -205,6 +212,7 @@ export class TUI {
205
212
  bellEnabled = false;
206
213
  lastBellAt = 0;
207
214
  mutedIds = new Set(); // muted session IDs (activity entries hidden)
215
+ mutedEntryCounts = new Map(); // session ID → suppressed entry count since mute
208
216
  sessionNotes = new Map(); // session ID → note text
209
217
  // drill-down mode: show a single session's full output
210
218
  viewMode = "overview";
@@ -380,9 +388,11 @@ export class TUI {
380
388
  return false;
381
389
  if (this.mutedIds.has(sessionId)) {
382
390
  this.mutedIds.delete(sessionId);
391
+ this.mutedEntryCounts.delete(sessionId);
383
392
  }
384
393
  else {
385
394
  this.mutedIds.add(sessionId);
395
+ this.mutedEntryCounts.set(sessionId, 0);
386
396
  }
387
397
  // repaint sessions (mute icon) and activity (filter changes)
388
398
  if (this.active) {
@@ -391,6 +401,19 @@ export class TUI {
391
401
  }
392
402
  return true;
393
403
  }
404
+ /** Unmute all sessions at once. Returns count of sessions unmuted. */
405
+ unmuteAll() {
406
+ const count = this.mutedIds.size;
407
+ if (count === 0)
408
+ return 0;
409
+ this.mutedIds.clear();
410
+ this.mutedEntryCounts.clear();
411
+ if (this.active) {
412
+ this.paintSessions();
413
+ this.repaintActivityRegion();
414
+ }
415
+ return count;
416
+ }
394
417
  /** Check if a session ID is muted. */
395
418
  isMuted(id) {
396
419
  return this.mutedIds.has(id);
@@ -399,6 +422,10 @@ export class TUI {
399
422
  getMutedCount() {
400
423
  return this.mutedIds.size;
401
424
  }
425
+ /** Return count of suppressed entries for a muted session (0 if not muted). */
426
+ getMutedEntryCount(id) {
427
+ return this.mutedEntryCounts.get(id) ?? 0;
428
+ }
402
429
  /**
403
430
  * Set a note on a session (by 1-indexed number, ID, ID prefix, or title).
404
431
  * Returns true if session found. Pass empty text to clear.
@@ -557,6 +584,10 @@ export class TUI {
557
584
  process.stderr.write("\x07");
558
585
  }
559
586
  }
587
+ // track suppressed entry counts regardless of active state (for badge accuracy)
588
+ if (shouldMuteEntry(entry, this.mutedIds) && entry.sessionId) {
589
+ this.mutedEntryCounts.set(entry.sessionId, (this.mutedEntryCounts.get(entry.sessionId) ?? 0) + 1);
590
+ }
560
591
  if (this.active) {
561
592
  // muted entries are still buffered + persisted but hidden from display
562
593
  if (shouldMuteEntry(entry, this.mutedIds)) {
@@ -911,12 +942,16 @@ export class TUI {
911
942
  const pinned = this.pinnedIds.has(s.id);
912
943
  const muted = this.mutedIds.has(s.id);
913
944
  const noted = this.sessionNotes.has(s.id);
945
+ const muteBadge = muted ? formatMuteBadge(this.mutedEntryCounts.get(s.id) ?? 0) : "";
946
+ const muteBadgeWidth = muted ? String(Math.min(this.mutedEntryCounts.get(s.id) ?? 0, 9999)).length + 2 : 0; // "(N)" visible chars, 0 when count is 0
947
+ const actualBadgeWidth = (this.mutedEntryCounts.get(s.id) ?? 0) > 0 ? muteBadgeWidth + 1 : 0; // +1 for trailing space
914
948
  const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
915
949
  const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
916
950
  const note = noted ? `${TEAL}${NOTE_ICON}${RESET} ` : "";
917
- const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0);
951
+ const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
952
+ const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth;
918
953
  const cardWidth = innerWidth - 1 - iconsWidth;
919
- const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${note}${formatSessionCard(s, cardWidth)}`;
954
+ const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${formatSessionCard(s, cardWidth)}`;
920
955
  const padded = padBoxLineHover(line, this.cols, isHovered);
921
956
  process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
922
957
  }
@@ -949,12 +984,16 @@ export class TUI {
949
984
  const pinned = this.pinnedIds.has(s.id);
950
985
  const muted = this.mutedIds.has(s.id);
951
986
  const noted = this.sessionNotes.has(s.id);
987
+ const muteBadge = muted ? formatMuteBadge(this.mutedEntryCounts.get(s.id) ?? 0) : "";
988
+ const actualBadgeWidth = (this.mutedEntryCounts.get(s.id) ?? 0) > 0
989
+ ? String(Math.min(this.mutedEntryCounts.get(s.id) ?? 0, 9999)).length + 3 : 0; // "(N) " visible chars
952
990
  const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
953
991
  const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
954
992
  const note = noted ? `${TEAL}${NOTE_ICON}${RESET} ` : "";
955
- const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0);
993
+ const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
994
+ const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth;
956
995
  const cardWidth = innerWidth - 1 - iconsWidth;
957
- const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${note}${formatSessionCard(s, cardWidth)}`;
996
+ const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${formatSessionCard(s, cardWidth)}`;
958
997
  const padded = padBoxLineHover(line, this.cols, isHovered);
959
998
  process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
960
999
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aoaoe",
3
- "version": "0.85.0",
3
+ "version": "0.86.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",