aoaoe 0.88.0 → 0.90.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 +32 -1
- package/dist/input.d.ts +6 -0
- package/dist/input.js +34 -0
- package/dist/tui.d.ts +13 -0
- package/dist/tui.js +34 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
2
3
|
import { loadConfig, validateEnvironment, parseCliArgs, printHelp, configFileExists, findConfigFile, DEFAULTS, computeConfigDiff } from "./config.js";
|
|
3
4
|
import { Poller, computeTmuxName } from "./poller.js";
|
|
4
5
|
import { createReasoner } from "./reasoner/index.js";
|
|
@@ -15,7 +16,7 @@ import { wakeableSleep } from "./wake.js";
|
|
|
15
16
|
import { classifyMessages, formatUserMessages, buildReceipts, shouldSkipSleep, hasPendingFile, isInsistMessage, stripInsistPrefix } from "./message.js";
|
|
16
17
|
import { TaskManager, loadTaskDefinitions, loadTaskState, formatTaskTable } from "./task-manager.js";
|
|
17
18
|
import { runTaskCli, handleTaskSlashCommand } from "./task-cli.js";
|
|
18
|
-
import { TUI, hitTestSession, nextSortMode, SORT_MODES, formatUptime } from "./tui.js";
|
|
19
|
+
import { TUI, hitTestSession, nextSortMode, SORT_MODES, formatUptime, formatClipText } from "./tui.js";
|
|
19
20
|
import { isDaemonRunningFromState } from "./chat.js";
|
|
20
21
|
import { sendNotification, sendTestNotification } from "./notify.js";
|
|
21
22
|
import { startHealthServer } from "./health.js";
|
|
@@ -480,6 +481,12 @@ async function main() {
|
|
|
480
481
|
}
|
|
481
482
|
}
|
|
482
483
|
});
|
|
484
|
+
// wire /auto-pin toggle
|
|
485
|
+
input.onAutoPin(() => {
|
|
486
|
+
const enabled = !tui.isAutoPinEnabled();
|
|
487
|
+
tui.setAutoPin(enabled);
|
|
488
|
+
tui.log("system", `auto-pin on error: ${enabled ? "on" : "off"}`);
|
|
489
|
+
});
|
|
483
490
|
// wire /note set/clear
|
|
484
491
|
input.onNote((target, text) => {
|
|
485
492
|
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
|
@@ -511,6 +518,30 @@ async function main() {
|
|
|
511
518
|
}
|
|
512
519
|
}
|
|
513
520
|
});
|
|
521
|
+
// wire /clip to export activity entries to clipboard or file
|
|
522
|
+
input.onClip((count) => {
|
|
523
|
+
const buffer = tui.getActivityBuffer();
|
|
524
|
+
if (buffer.length === 0) {
|
|
525
|
+
tui.log("system", "no activity to clip");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const text = formatClipText(buffer, count);
|
|
529
|
+
const entryCount = Math.min(count, buffer.length);
|
|
530
|
+
try {
|
|
531
|
+
execSync("pbcopy", { input: text, timeout: 5000 });
|
|
532
|
+
tui.log("system", `copied ${entryCount} entries to clipboard`);
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
try {
|
|
536
|
+
const clipPath = join(homedir(), ".aoaoe", "clip.txt");
|
|
537
|
+
writeFileSync(clipPath, text, "utf-8");
|
|
538
|
+
tui.log("system", `saved ${entryCount} entries to ~/.aoaoe/clip.txt`);
|
|
539
|
+
}
|
|
540
|
+
catch (writeErr) {
|
|
541
|
+
tui.log("error", `clip failed: ${writeErr}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
514
545
|
// wire mouse move to hover highlight on session cards (disabled in compact)
|
|
515
546
|
input.onMouseMove((row, _col) => {
|
|
516
547
|
if (tui.getViewMode() === "overview" && !tui.isCompact()) {
|
package/dist/input.d.ts
CHANGED
|
@@ -15,8 +15,10 @@ export type MuteHandler = (target: string) => void;
|
|
|
15
15
|
export type UnmuteAllHandler = () => void;
|
|
16
16
|
export type TagFilterHandler = (tag: string | null) => void;
|
|
17
17
|
export type UptimeHandler = () => void;
|
|
18
|
+
export type AutoPinHandler = () => void;
|
|
18
19
|
export type NoteHandler = (target: string, text: string) => void;
|
|
19
20
|
export type NotesHandler = () => void;
|
|
21
|
+
export type ClipHandler = (count: number) => void;
|
|
20
22
|
export interface MouseEvent {
|
|
21
23
|
button: number;
|
|
22
24
|
col: number;
|
|
@@ -54,8 +56,10 @@ export declare class InputReader {
|
|
|
54
56
|
private unmuteAllHandler;
|
|
55
57
|
private tagFilterHandler;
|
|
56
58
|
private uptimeHandler;
|
|
59
|
+
private autoPinHandler;
|
|
57
60
|
private noteHandler;
|
|
58
61
|
private notesHandler;
|
|
62
|
+
private clipHandler;
|
|
59
63
|
private mouseDataListener;
|
|
60
64
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
61
65
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -77,8 +81,10 @@ export declare class InputReader {
|
|
|
77
81
|
onUnmuteAll(handler: UnmuteAllHandler): void;
|
|
78
82
|
onTagFilter(handler: TagFilterHandler): void;
|
|
79
83
|
onUptime(handler: UptimeHandler): void;
|
|
84
|
+
onAutoPin(handler: AutoPinHandler): void;
|
|
80
85
|
onNote(handler: NoteHandler): void;
|
|
81
86
|
onNotes(handler: NotesHandler): void;
|
|
87
|
+
onClip(handler: ClipHandler): void;
|
|
82
88
|
private notifyQueueChange;
|
|
83
89
|
start(): void;
|
|
84
90
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -47,8 +47,10 @@ export class InputReader {
|
|
|
47
47
|
unmuteAllHandler = null;
|
|
48
48
|
tagFilterHandler = null;
|
|
49
49
|
uptimeHandler = null;
|
|
50
|
+
autoPinHandler = null;
|
|
50
51
|
noteHandler = null;
|
|
51
52
|
notesHandler = null;
|
|
53
|
+
clipHandler = null;
|
|
52
54
|
mouseDataListener = null;
|
|
53
55
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
54
56
|
onScroll(handler) {
|
|
@@ -130,6 +132,10 @@ export class InputReader {
|
|
|
130
132
|
onUptime(handler) {
|
|
131
133
|
this.uptimeHandler = handler;
|
|
132
134
|
}
|
|
135
|
+
// register a callback for auto-pin toggle (/auto-pin)
|
|
136
|
+
onAutoPin(handler) {
|
|
137
|
+
this.autoPinHandler = handler;
|
|
138
|
+
}
|
|
133
139
|
// register a callback for note commands (/note <target> <text>)
|
|
134
140
|
onNote(handler) {
|
|
135
141
|
this.noteHandler = handler;
|
|
@@ -138,6 +144,10 @@ export class InputReader {
|
|
|
138
144
|
onNotes(handler) {
|
|
139
145
|
this.notesHandler = handler;
|
|
140
146
|
}
|
|
147
|
+
// register a callback for clipboard export (/clip [N])
|
|
148
|
+
onClip(handler) {
|
|
149
|
+
this.clipHandler = handler;
|
|
150
|
+
}
|
|
141
151
|
notifyQueueChange() {
|
|
142
152
|
this.queueChangeHandler?.(this.queue.length);
|
|
143
153
|
}
|
|
@@ -324,8 +334,10 @@ ${BOLD}navigation:${RESET}
|
|
|
324
334
|
/unmute-all unmute all sessions at once
|
|
325
335
|
/filter [tag] filter activity by tag (error, system, etc. — no arg = clear)
|
|
326
336
|
/uptime show session uptimes (time since first observed)
|
|
337
|
+
/auto-pin toggle auto-pin on error (pin sessions that emit errors)
|
|
327
338
|
/note N|name text attach a note to a session (no text = clear)
|
|
328
339
|
/notes list all session notes
|
|
340
|
+
/clip [N] copy last N activity entries to clipboard (default 20)
|
|
329
341
|
/mark bookmark current activity position
|
|
330
342
|
/jump N jump to bookmark N
|
|
331
343
|
/marks list all bookmarks
|
|
@@ -498,6 +510,14 @@ ${BOLD}other:${RESET}
|
|
|
498
510
|
console.error(`${DIM}uptime not available (no TUI)${RESET}`);
|
|
499
511
|
}
|
|
500
512
|
break;
|
|
513
|
+
case "/auto-pin":
|
|
514
|
+
if (this.autoPinHandler) {
|
|
515
|
+
this.autoPinHandler();
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
console.error(`${DIM}auto-pin not available (no TUI)${RESET}`);
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
501
521
|
case "/note": {
|
|
502
522
|
const noteArg = line.slice("/note".length).trim();
|
|
503
523
|
if (this.noteHandler) {
|
|
@@ -529,6 +549,20 @@ ${BOLD}other:${RESET}
|
|
|
529
549
|
console.error(`${DIM}notes not available (no TUI)${RESET}`);
|
|
530
550
|
}
|
|
531
551
|
break;
|
|
552
|
+
case "/clip": {
|
|
553
|
+
const clipArg = line.slice("/clip".length).trim();
|
|
554
|
+
const clipCount = clipArg ? parseInt(clipArg, 10) : 20;
|
|
555
|
+
if (this.clipHandler && !isNaN(clipCount) && clipCount > 0) {
|
|
556
|
+
this.clipHandler(clipCount);
|
|
557
|
+
}
|
|
558
|
+
else if (!this.clipHandler) {
|
|
559
|
+
console.error(`${DIM}clip not available (no TUI)${RESET}`);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
console.error(`${DIM}usage: /clip [N] — copy last N activity entries to clipboard${RESET}`);
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
532
566
|
case "/mark":
|
|
533
567
|
if (this.markHandler) {
|
|
534
568
|
this.markHandler();
|
package/dist/tui.d.ts
CHANGED
|
@@ -57,6 +57,12 @@ export declare function formatMuteBadge(count: number): string;
|
|
|
57
57
|
export declare function matchesTagFilter(entry: ActivityEntry, tag: string): boolean;
|
|
58
58
|
/** Format the tag filter indicator text for the separator bar. */
|
|
59
59
|
export declare function formatTagFilterIndicator(tag: string, matchCount: number, totalCount: number): string;
|
|
60
|
+
/** Default number of entries for /clip when no count specified. */
|
|
61
|
+
export declare const CLIP_DEFAULT_COUNT = 20;
|
|
62
|
+
/** Format activity entries as plain text for clipboard/export. One line per entry. */
|
|
63
|
+
export declare function formatClipText(entries: readonly ActivityEntry[], n?: number): string;
|
|
64
|
+
/** Determine if a log entry should trigger auto-pin (error-like tags). */
|
|
65
|
+
export declare function shouldAutoPin(tag: string): boolean;
|
|
60
66
|
/** Format milliseconds as human-readable uptime: "2h 15m", "45m", "3d 2h", "< 1m". */
|
|
61
67
|
export declare function formatUptime(ms: number): string;
|
|
62
68
|
export declare class TUI {
|
|
@@ -93,6 +99,7 @@ export declare class TUI {
|
|
|
93
99
|
private mutedEntryCounts;
|
|
94
100
|
private sessionNotes;
|
|
95
101
|
private sessionFirstSeen;
|
|
102
|
+
private autoPinOnError;
|
|
96
103
|
private viewMode;
|
|
97
104
|
private drilldownSessionId;
|
|
98
105
|
private sessionOutputs;
|
|
@@ -137,6 +144,10 @@ export declare class TUI {
|
|
|
137
144
|
setBell(enabled: boolean): void;
|
|
138
145
|
/** Return whether terminal bell is enabled. */
|
|
139
146
|
isBellEnabled(): boolean;
|
|
147
|
+
/** Enable or disable auto-pin on error. */
|
|
148
|
+
setAutoPin(enabled: boolean): void;
|
|
149
|
+
/** Return whether auto-pin on error is enabled. */
|
|
150
|
+
isAutoPinEnabled(): boolean;
|
|
140
151
|
/**
|
|
141
152
|
* Toggle mute for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
142
153
|
* Muted sessions' activity entries are hidden from the log (still buffered + persisted).
|
|
@@ -168,6 +179,8 @@ export declare class TUI {
|
|
|
168
179
|
getUptime(id: string): number;
|
|
169
180
|
/** Return all session first-seen timestamps (for /uptime listing). */
|
|
170
181
|
getAllFirstSeen(): ReadonlyMap<string, number>;
|
|
182
|
+
/** Return the activity buffer (for /clip export). */
|
|
183
|
+
getActivityBuffer(): readonly ActivityEntry[];
|
|
171
184
|
/**
|
|
172
185
|
* Add a bookmark at the current activity position.
|
|
173
186
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
package/dist/tui.js
CHANGED
|
@@ -191,6 +191,21 @@ export function matchesTagFilter(entry, tag) {
|
|
|
191
191
|
export function formatTagFilterIndicator(tag, matchCount, totalCount) {
|
|
192
192
|
return `${SLATE}filter:${RESET} ${AMBER}${tag}${RESET} ${DIM}(${matchCount}/${totalCount})${RESET}`;
|
|
193
193
|
}
|
|
194
|
+
// ── Clip ─────────────────────────────────────────────────────────────────────
|
|
195
|
+
/** Default number of entries for /clip when no count specified. */
|
|
196
|
+
export const CLIP_DEFAULT_COUNT = 20;
|
|
197
|
+
/** Format activity entries as plain text for clipboard/export. One line per entry. */
|
|
198
|
+
export function formatClipText(entries, n) {
|
|
199
|
+
const count = n ?? CLIP_DEFAULT_COUNT;
|
|
200
|
+
const slice = entries.slice(-Math.max(1, count));
|
|
201
|
+
return slice.map((e) => `[${e.time}] ${e.tag}: ${e.text}`).join("\n") + "\n";
|
|
202
|
+
}
|
|
203
|
+
// ── Auto-pin ─────────────────────────────────────────────────────────────────
|
|
204
|
+
/** Determine if a log entry should trigger auto-pin (error-like tags). */
|
|
205
|
+
export function shouldAutoPin(tag) {
|
|
206
|
+
const lower = tag.toLowerCase();
|
|
207
|
+
return lower === "! action" || lower === "error";
|
|
208
|
+
}
|
|
194
209
|
// ── Uptime ───────────────────────────────────────────────────────────────────
|
|
195
210
|
/** Format milliseconds as human-readable uptime: "2h 15m", "45m", "3d 2h", "< 1m". */
|
|
196
211
|
export function formatUptime(ms) {
|
|
@@ -243,6 +258,7 @@ export class TUI {
|
|
|
243
258
|
mutedEntryCounts = new Map(); // session ID → suppressed entry count since mute
|
|
244
259
|
sessionNotes = new Map(); // session ID → note text
|
|
245
260
|
sessionFirstSeen = new Map(); // session ID → epoch ms when first observed
|
|
261
|
+
autoPinOnError = false; // auto-pin sessions that emit errors
|
|
246
262
|
// drill-down mode: show a single session's full output
|
|
247
263
|
viewMode = "overview";
|
|
248
264
|
drilldownSessionId = null;
|
|
@@ -398,6 +414,14 @@ export class TUI {
|
|
|
398
414
|
isBellEnabled() {
|
|
399
415
|
return this.bellEnabled;
|
|
400
416
|
}
|
|
417
|
+
/** Enable or disable auto-pin on error. */
|
|
418
|
+
setAutoPin(enabled) {
|
|
419
|
+
this.autoPinOnError = enabled;
|
|
420
|
+
}
|
|
421
|
+
/** Return whether auto-pin on error is enabled. */
|
|
422
|
+
isAutoPinEnabled() {
|
|
423
|
+
return this.autoPinOnError;
|
|
424
|
+
}
|
|
401
425
|
/**
|
|
402
426
|
* Toggle mute for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
403
427
|
* Muted sessions' activity entries are hidden from the log (still buffered + persisted).
|
|
@@ -512,6 +536,10 @@ export class TUI {
|
|
|
512
536
|
getAllFirstSeen() {
|
|
513
537
|
return this.sessionFirstSeen;
|
|
514
538
|
}
|
|
539
|
+
/** Return the activity buffer (for /clip export). */
|
|
540
|
+
getActivityBuffer() {
|
|
541
|
+
return this.activityBuffer;
|
|
542
|
+
}
|
|
515
543
|
/**
|
|
516
544
|
* Add a bookmark at the current activity position.
|
|
517
545
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
|
@@ -626,6 +654,12 @@ export class TUI {
|
|
|
626
654
|
process.stderr.write("\x07");
|
|
627
655
|
}
|
|
628
656
|
}
|
|
657
|
+
// auto-pin sessions that emit errors (when enabled)
|
|
658
|
+
if (this.autoPinOnError && sessionId && shouldAutoPin(tag) && !this.pinnedIds.has(sessionId)) {
|
|
659
|
+
this.pinnedIds.add(sessionId);
|
|
660
|
+
if (this.active)
|
|
661
|
+
this.paintSessions();
|
|
662
|
+
}
|
|
629
663
|
// track suppressed entry counts regardless of active state (for badge accuracy)
|
|
630
664
|
if (shouldMuteEntry(entry, this.mutedIds) && entry.sessionId) {
|
|
631
665
|
this.mutedEntryCounts.set(entry.sessionId, (this.mutedEntryCounts.get(entry.sessionId) ?? 0) + 1);
|