aoaoe 0.92.0 → 0.94.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/input.js CHANGED
@@ -337,7 +337,7 @@ ${BOLD}navigation:${RESET}
337
337
  /focus toggle focus mode (show only pinned sessions)
338
338
  /mute [N|name] mute/unmute a session's activity entries (toggle)
339
339
  /unmute-all unmute all sessions at once
340
- /filter [tag] filter activity by tag (error, system, etc. no arg = clear)
340
+ /filter [tag] filter activity by tag — presets: errors, actions, system (no arg = clear)
341
341
  /uptime show session uptimes (time since first observed)
342
342
  /auto-pin toggle auto-pin on error (pin sessions that emit errors)
343
343
  /note N|name text attach a note to a session (no text = clear)
package/dist/stats.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface ActionStats {
15
15
  export interface HistoryStats {
16
16
  total: number;
17
17
  byTag: Map<string, number>;
18
+ byHour: number[];
18
19
  firstTs: number;
19
20
  lastTs: number;
20
21
  }
@@ -51,4 +52,6 @@ export declare function formatRate(count: number, spanMs: number): string;
51
52
  * Format the full stats display for terminal output.
52
53
  */
53
54
  export declare function formatStats(stats: CombinedStats, windowLabel?: string): string;
55
+ /** Format a 24-element heatmap as a colored block string. */
56
+ export declare function formatHeatmap(counts: number[]): string;
54
57
  //# sourceMappingURL=stats.d.ts.map
package/dist/stats.js CHANGED
@@ -59,6 +59,7 @@ export function parseActionStats(lines, maxAgeMs, now) {
59
59
  export function parseHistoryStats(entries, maxAgeMs, now) {
60
60
  const cutoff = maxAgeMs ? (now ?? Date.now()) - maxAgeMs : 0;
61
61
  const byTag = new Map();
62
+ const byHour = new Array(24).fill(0);
62
63
  let total = 0;
63
64
  let firstTs = Infinity;
64
65
  let lastTs = 0;
@@ -71,10 +72,11 @@ export function parseHistoryStats(entries, maxAgeMs, now) {
71
72
  if (entry.ts > lastTs)
72
73
  lastTs = entry.ts;
73
74
  byTag.set(entry.tag, (byTag.get(entry.tag) ?? 0) + 1);
75
+ byHour[new Date(entry.ts).getHours()]++;
74
76
  }
75
77
  if (total === 0)
76
78
  return null;
77
- return { total, byTag, firstTs, lastTs };
79
+ return { total, byTag, byHour, firstTs, lastTs };
78
80
  }
79
81
  /**
80
82
  * Combine action and history stats into a unified stats object.
@@ -214,8 +216,30 @@ export function formatStats(stats, windowLabel) {
214
216
  lines.push(` ${color}${tag.padEnd(18)}${RESET} ${String(count).padStart(4)} ${SLATE}${bar}${RESET} ${DIM}${pct}%${RESET}`);
215
217
  }
216
218
  }
219
+ // hourly heatmap
220
+ if (stats.history && stats.history.total > 0) {
221
+ lines.push("");
222
+ lines.push(` ${BOLD}hourly activity${RESET}`);
223
+ lines.push(` ${formatHeatmap(stats.history.byHour)}`);
224
+ lines.push(` ${DIM}${"0".padEnd(6)}${"6".padEnd(6)}${"12".padEnd(6)}${"18".padEnd(5)}23${RESET}`);
225
+ }
217
226
  lines.push(` ${hr}`);
218
227
  lines.push("");
219
228
  return lines.join("\n");
220
229
  }
230
+ // ── Heatmap ──────────────────────────────────────────────────────────────────
231
+ const HEAT_BLOCKS = [" ", "░", "▒", "▓", "█"];
232
+ /** Format a 24-element heatmap as a colored block string. */
233
+ export function formatHeatmap(counts) {
234
+ const max = Math.max(...counts);
235
+ if (max === 0)
236
+ return DIM + HEAT_BLOCKS[0].repeat(24) + RESET;
237
+ return counts.map((c) => {
238
+ if (c === 0)
239
+ return `${SLATE}${HEAT_BLOCKS[0]}${RESET}`;
240
+ const level = Math.min(HEAT_BLOCKS.length - 1, Math.ceil((c / max) * (HEAT_BLOCKS.length - 1)));
241
+ const color = level <= 1 ? SLATE : level <= 2 ? SKY : level <= 3 ? AMBER : LIME;
242
+ return `${color}${HEAT_BLOCKS[level]}${RESET}`;
243
+ }).join("");
244
+ }
221
245
  //# sourceMappingURL=stats.js.map
package/dist/tui.d.ts CHANGED
@@ -53,8 +53,12 @@ export declare function truncateNote(text: string): string;
53
53
  export declare function shouldMuteEntry(entry: ActivityEntry, mutedIds: Set<string>): boolean;
54
54
  /** Format a suppressed entry count badge for muted sessions. Returns empty string for 0. */
55
55
  export declare function formatMuteBadge(count: number): string;
56
- /** Check if an activity entry matches a tag filter (case-insensitive exact match on tag). */
56
+ /** Check if an activity entry matches a tag filter (case-insensitive, supports pipe-separated multi-tag). */
57
57
  export declare function matchesTagFilter(entry: ActivityEntry, tag: string): boolean;
58
+ /** Built-in filter presets: name → pipe-separated tag pattern. */
59
+ export declare const FILTER_PRESETS: Record<string, string>;
60
+ /** Resolve a filter string — expands preset names, returns raw tag otherwise. */
61
+ export declare function resolveFilterPreset(input: string): string;
58
62
  /** Format the tag filter indicator text for the separator bar. */
59
63
  export declare function formatTagFilterIndicator(tag: string, matchCount: number, totalCount: number): string;
60
64
  /** Default number of entries for /clip when no count specified. */
@@ -241,7 +245,7 @@ export declare class TUI {
241
245
  setSearch(pattern: string | null): void;
242
246
  /** Get the current search pattern (or null if no active search). */
243
247
  getSearchPattern(): string | null;
244
- /** Set or clear the tag filter. Resets scroll and repaints. */
248
+ /** Set or clear the tag filter. Resolves presets (e.g. "errors"). Resets scroll and repaints. */
245
249
  setTagFilter(tag: string | null): void;
246
250
  /** Get the current tag filter (or null if none active). */
247
251
  getTagFilter(): string | null;
package/dist/tui.js CHANGED
@@ -194,11 +194,24 @@ export function formatMuteBadge(count) {
194
194
  const label = count > 999 ? "999+" : String(count);
195
195
  return `${DIM}(${label})${RESET}`;
196
196
  }
197
- /** Check if an activity entry matches a tag filter (case-insensitive exact match on tag). */
197
+ /** Check if an activity entry matches a tag filter (case-insensitive, supports pipe-separated multi-tag). */
198
198
  export function matchesTagFilter(entry, tag) {
199
199
  if (!tag)
200
200
  return true;
201
- return entry.tag.toLowerCase() === tag.toLowerCase();
201
+ const lower = entry.tag.toLowerCase();
202
+ if (tag.includes("|"))
203
+ return tag.toLowerCase().split("|").some((t) => lower === t.trim());
204
+ return lower === tag.toLowerCase();
205
+ }
206
+ /** Built-in filter presets: name → pipe-separated tag pattern. */
207
+ export const FILTER_PRESETS = {
208
+ errors: "error|! action",
209
+ actions: "+ action|! action",
210
+ system: "system|status",
211
+ };
212
+ /** Resolve a filter string — expands preset names, returns raw tag otherwise. */
213
+ export function resolveFilterPreset(input) {
214
+ return FILTER_PRESETS[input.toLowerCase()] ?? input;
202
215
  }
203
216
  /** Format the tag filter indicator text for the separator bar. */
204
217
  export function formatTagFilterIndicator(tag, matchCount, totalCount) {
@@ -915,9 +928,9 @@ export class TUI {
915
928
  return this.searchPattern;
916
929
  }
917
930
  // ── Tag filter ─────────────────────────────────────────────────────────
918
- /** Set or clear the tag filter. Resets scroll and repaints. */
931
+ /** Set or clear the tag filter. Resolves presets (e.g. "errors"). Resets scroll and repaints. */
919
932
  setTagFilter(tag) {
920
- this.filterTag = tag && tag.length > 0 ? tag : null;
933
+ this.filterTag = tag && tag.length > 0 ? resolveFilterPreset(tag) : null;
921
934
  this.scrollOffset = 0;
922
935
  this.newWhileScrolled = 0;
923
936
  if (this.active && this.viewMode === "overview") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aoaoe",
3
- "version": "0.92.0",
3
+ "version": "0.94.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",