aoaoe 0.146.0 → 0.149.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 +39 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.js +47 -0
- package/dist/tui.d.ts +18 -1
- package/dist/tui.js +71 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1023,6 +1023,45 @@ async function main() {
|
|
|
1023
1023
|
tui.log("system", `quiet hours: ${specs.join(", ")} — watchdog+burn alerts suppressed`);
|
|
1024
1024
|
persistPrefs();
|
|
1025
1025
|
});
|
|
1026
|
+
// wire /budget cost alerts
|
|
1027
|
+
input.onBudget((target, budgetUSD) => {
|
|
1028
|
+
if (budgetUSD === null) {
|
|
1029
|
+
// clear global budget
|
|
1030
|
+
tui.setGlobalBudget(null);
|
|
1031
|
+
tui.log("system", "budget: global budget cleared");
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (target === null) {
|
|
1035
|
+
tui.setGlobalBudget(budgetUSD);
|
|
1036
|
+
tui.log("system", `budget: global budget set to $${budgetUSD.toFixed(2)}`);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
|
1040
|
+
const ok = tui.setSessionBudget(num ?? target, budgetUSD);
|
|
1041
|
+
if (ok)
|
|
1042
|
+
tui.log("system", `budget: $${budgetUSD.toFixed(2)} set for ${target}`);
|
|
1043
|
+
else
|
|
1044
|
+
tui.log("system", `session not found: ${target}`);
|
|
1045
|
+
});
|
|
1046
|
+
// wire /pause-all and /resume-all
|
|
1047
|
+
input.onBulkControl((action) => {
|
|
1048
|
+
const sessions = tui.getSessions();
|
|
1049
|
+
if (sessions.length === 0) {
|
|
1050
|
+
tui.log("system", `${action}-all: no sessions`);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
// ESC for pause, Enter for resume (nudge sessions out of waiting)
|
|
1054
|
+
const keys = action === "pause" ? "Escape" : "Enter";
|
|
1055
|
+
let count = 0;
|
|
1056
|
+
for (const s of sessions) {
|
|
1057
|
+
const tmuxName = computeTmuxName(s.title, s.id);
|
|
1058
|
+
shellExec("tmux", ["send-keys", "-t", tmuxName, "", keys])
|
|
1059
|
+
.then(() => { })
|
|
1060
|
+
.catch((_e) => { });
|
|
1061
|
+
count++;
|
|
1062
|
+
}
|
|
1063
|
+
tui.log("system", `${action}-all: sent to ${count} session${count !== 1 ? "s" : ""}`);
|
|
1064
|
+
});
|
|
1026
1065
|
// wire /quiet-status
|
|
1027
1066
|
input.onQuietStatus(() => {
|
|
1028
1067
|
const { active, message } = formatQuietStatus(tui.getQuietHours());
|
package/dist/input.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export type CostSummaryHandler = () => void;
|
|
|
56
56
|
export type SessionReportHandler = (target: string) => void;
|
|
57
57
|
export type QuietStatusHandler = () => void;
|
|
58
58
|
export type AlertLogHandler = (count: number) => void;
|
|
59
|
+
export type BudgetHandler = (target: string | null, budgetUSD: number | null) => void;
|
|
60
|
+
export type BulkControlHandler = (action: "pause" | "resume") => void;
|
|
59
61
|
export interface MouseEvent {
|
|
60
62
|
button: number;
|
|
61
63
|
col: number;
|
|
@@ -134,6 +136,8 @@ export declare class InputReader {
|
|
|
134
136
|
private sessionReportHandler;
|
|
135
137
|
private quietStatusHandler;
|
|
136
138
|
private alertLogHandler;
|
|
139
|
+
private budgetHandler;
|
|
140
|
+
private bulkControlHandler;
|
|
137
141
|
private aliases;
|
|
138
142
|
private mouseDataListener;
|
|
139
143
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
@@ -195,6 +199,8 @@ export declare class InputReader {
|
|
|
195
199
|
onSessionReport(handler: SessionReportHandler): void;
|
|
196
200
|
onQuietStatus(handler: QuietStatusHandler): void;
|
|
197
201
|
onAlertLog(handler: AlertLogHandler): void;
|
|
202
|
+
onBudget(handler: BudgetHandler): void;
|
|
203
|
+
onBulkControl(handler: BulkControlHandler): void;
|
|
198
204
|
/** Set aliases from persisted prefs. */
|
|
199
205
|
setAliases(aliases: Record<string, string>): void;
|
|
200
206
|
/** Get current aliases as a plain object. */
|
package/dist/input.js
CHANGED
|
@@ -89,6 +89,8 @@ export class InputReader {
|
|
|
89
89
|
sessionReportHandler = null;
|
|
90
90
|
quietStatusHandler = null;
|
|
91
91
|
alertLogHandler = null;
|
|
92
|
+
budgetHandler = null;
|
|
93
|
+
bulkControlHandler = null;
|
|
92
94
|
aliases = new Map(); // /shortcut → /full command
|
|
93
95
|
mouseDataListener = null;
|
|
94
96
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
@@ -303,6 +305,8 @@ export class InputReader {
|
|
|
303
305
|
onSessionReport(handler) { this.sessionReportHandler = handler; }
|
|
304
306
|
onQuietStatus(handler) { this.quietStatusHandler = handler; }
|
|
305
307
|
onAlertLog(handler) { this.alertLogHandler = handler; }
|
|
308
|
+
onBudget(handler) { this.budgetHandler = handler; }
|
|
309
|
+
onBulkControl(handler) { this.bulkControlHandler = handler; }
|
|
306
310
|
/** Set aliases from persisted prefs. */
|
|
307
311
|
setAliases(aliases) {
|
|
308
312
|
this.aliases.clear();
|
|
@@ -565,6 +569,9 @@ ${BOLD}navigation:${RESET}
|
|
|
565
569
|
/color-all [c] set accent color for all sessions (no color = clear all)
|
|
566
570
|
/quiet-hours [H-H] suppress watchdog+burn alerts during hours (e.g. 22-06; no arg = clear)
|
|
567
571
|
/quiet-status show whether quiet hours are currently active
|
|
572
|
+
/budget [N] [$] set cost budget: /budget 1 2.50 (session), /budget 2.50 (global), /budget clear
|
|
573
|
+
/pause-all send interrupt to all sessions
|
|
574
|
+
/resume-all send resume to all sessions
|
|
568
575
|
/alert-log [N] show last N auto-generated alerts (burn-rate/watchdog/ceiling; default 20)
|
|
569
576
|
/history-stats show aggregate statistics from persisted activity history
|
|
570
577
|
/cost-summary show total estimated spend across all sessions
|
|
@@ -1121,6 +1128,46 @@ ${BOLD}other:${RESET}
|
|
|
1121
1128
|
console.error(`${DIM}history-stats not available (no TUI)${RESET}`);
|
|
1122
1129
|
}
|
|
1123
1130
|
break;
|
|
1131
|
+
case "/budget": {
|
|
1132
|
+
const bArgs = line.slice("/budget".length).trim().split(/\s+/).filter(Boolean);
|
|
1133
|
+
if (bArgs.length === 0 || bArgs[0] === "clear") {
|
|
1134
|
+
// clear global budget
|
|
1135
|
+
if (this.budgetHandler)
|
|
1136
|
+
this.budgetHandler(null, null);
|
|
1137
|
+
else
|
|
1138
|
+
console.error(`${DIM}budget not available${RESET}`);
|
|
1139
|
+
break;
|
|
1140
|
+
}
|
|
1141
|
+
if (this.budgetHandler) {
|
|
1142
|
+
// /budget <$N> → global, /budget <N|name> <$N> → per-session
|
|
1143
|
+
const maybeUSD = parseFloat(bArgs[bArgs.length - 1].replace("$", ""));
|
|
1144
|
+
if (!isNaN(maybeUSD) && bArgs.length === 1) {
|
|
1145
|
+
this.budgetHandler(null, maybeUSD); // global
|
|
1146
|
+
}
|
|
1147
|
+
else if (!isNaN(maybeUSD) && bArgs.length >= 2) {
|
|
1148
|
+
this.budgetHandler(bArgs[0], maybeUSD); // per-session
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
console.error(`${DIM}usage: /budget [$N.NN] — global, /budget <N|name> $N.NN — per-session, /budget clear — remove${RESET}`);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
console.error(`${DIM}budget not available${RESET}`);
|
|
1156
|
+
}
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
case "/pause-all":
|
|
1160
|
+
if (this.bulkControlHandler)
|
|
1161
|
+
this.bulkControlHandler("pause");
|
|
1162
|
+
else
|
|
1163
|
+
console.error(`${DIM}pause-all not available${RESET}`);
|
|
1164
|
+
break;
|
|
1165
|
+
case "/resume-all":
|
|
1166
|
+
if (this.bulkControlHandler)
|
|
1167
|
+
this.bulkControlHandler("resume");
|
|
1168
|
+
else
|
|
1169
|
+
console.error(`${DIM}resume-all not available${RESET}`);
|
|
1170
|
+
break;
|
|
1124
1171
|
case "/quiet-status":
|
|
1125
1172
|
if (this.quietStatusHandler)
|
|
1126
1173
|
this.quietStatusHandler();
|
package/dist/tui.d.ts
CHANGED
|
@@ -180,6 +180,10 @@ export interface SessionReportData {
|
|
|
180
180
|
}
|
|
181
181
|
/** Format a session report as a Markdown document. */
|
|
182
182
|
export declare function formatSessionReport(data: SessionReportData): string;
|
|
183
|
+
/** Check if a cost string exceeds a budget value. Returns true when over budget. */
|
|
184
|
+
export declare function isOverBudget(costStr: string | undefined, budgetUSD: number): boolean;
|
|
185
|
+
/** Format a budget-exceeded alert message. */
|
|
186
|
+
export declare function formatBudgetAlert(title: string, costStr: string, budgetUSD: number): string;
|
|
183
187
|
/**
|
|
184
188
|
* Build args for duplicating a session: given a source session, return
|
|
185
189
|
* { path, tool, title } for use in a create_agent action.
|
|
@@ -408,6 +412,9 @@ export declare class TUI {
|
|
|
408
412
|
private quietHoursRanges;
|
|
409
413
|
private sessionHealthHistory;
|
|
410
414
|
private alertLog;
|
|
415
|
+
private sessionBudgets;
|
|
416
|
+
private globalBudget;
|
|
417
|
+
private budgetAlerted;
|
|
411
418
|
private viewMode;
|
|
412
419
|
private drilldownSessionId;
|
|
413
420
|
private sessionOutputs;
|
|
@@ -539,6 +546,16 @@ export declare class TUI {
|
|
|
539
546
|
tool: string;
|
|
540
547
|
title: string;
|
|
541
548
|
} | null;
|
|
549
|
+
/** Set a per-session budget in USD. Pass null to clear. */
|
|
550
|
+
setSessionBudget(sessionIdOrIndex: string | number, budgetUSD: number | null): boolean;
|
|
551
|
+
/** Set global fallback budget (applies to all sessions without per-session budget). */
|
|
552
|
+
setGlobalBudget(budgetUSD: number | null): void;
|
|
553
|
+
/** Get the per-session budget (or null). */
|
|
554
|
+
getSessionBudget(id: string): number | null;
|
|
555
|
+
/** Get the global budget (or null if not set). */
|
|
556
|
+
getGlobalBudget(): number | null;
|
|
557
|
+
/** Return all per-session budgets. */
|
|
558
|
+
getAllSessionBudgets(): ReadonlyMap<string, number>;
|
|
542
559
|
/** Return health history for a session (for sparkline). */
|
|
543
560
|
getSessionHealthHistory(id: string): readonly HealthSnapshot[];
|
|
544
561
|
/** Return all alert log entries (last 100 "status" tag entries). */
|
|
@@ -691,7 +708,7 @@ export declare class TUI {
|
|
|
691
708
|
private repaintDrilldownContent;
|
|
692
709
|
private paintInputLine;
|
|
693
710
|
}
|
|
694
|
-
declare function formatSessionCard(s: DaemonSessionState, maxWidth: number, errorSparkline?: string, idleSinceMs?: number, healthBadge?: string, displayName?: string): string;
|
|
711
|
+
declare function formatSessionCard(s: DaemonSessionState, maxWidth: number, errorSparkline?: string, idleSinceMs?: number, healthBadge?: string, displayName?: string, ageStr?: string): string;
|
|
695
712
|
declare function formatActivity(entry: ActivityEntry, maxCols: number): string;
|
|
696
713
|
declare function padBoxLine(line: string, totalWidth: number): string;
|
|
697
714
|
declare function padBoxLineHover(line: string, totalWidth: number, hovered: boolean): string;
|
package/dist/tui.js
CHANGED
|
@@ -518,6 +518,18 @@ export function formatSessionReport(data) {
|
|
|
518
518
|
}
|
|
519
519
|
return lines.join("\n");
|
|
520
520
|
}
|
|
521
|
+
// ── Session cost budget (pure, exported for testing) ─────────────────────────
|
|
522
|
+
/** Check if a cost string exceeds a budget value. Returns true when over budget. */
|
|
523
|
+
export function isOverBudget(costStr, budgetUSD) {
|
|
524
|
+
if (!costStr)
|
|
525
|
+
return false;
|
|
526
|
+
const val = parseCostValue(costStr);
|
|
527
|
+
return val !== null && val > budgetUSD;
|
|
528
|
+
}
|
|
529
|
+
/** Format a budget-exceeded alert message. */
|
|
530
|
+
export function formatBudgetAlert(title, costStr, budgetUSD) {
|
|
531
|
+
return `${title}: cost ${costStr} exceeded budget $${budgetUSD.toFixed(2)}`;
|
|
532
|
+
}
|
|
521
533
|
// ── Duplicate session helpers (pure, exported for testing) ───────────────────
|
|
522
534
|
/**
|
|
523
535
|
* Build args for duplicating a session: given a source session, return
|
|
@@ -726,6 +738,7 @@ export const BUILTIN_COMMANDS = new Set([
|
|
|
726
738
|
"/group", "/groups", "/group-filter", "/burn-rate", "/snapshot", "/broadcast", "/watchdog", "/top", "/ceiling", "/rename", "/copy", "/stats", "/recall", "/pin-all-errors", "/export-stats",
|
|
727
739
|
"/mute-errors", "/prev-goal", "/tag", "/tags", "/tag-filter", "/find", "/reset-health", "/timeline", "/color", "/clear-history",
|
|
728
740
|
"/duplicate", "/color-all", "/quiet-hours", "/quiet-status", "/history-stats", "/cost-summary", "/session-report", "/alert-log",
|
|
741
|
+
"/budget", "/pause-all", "/resume-all",
|
|
729
742
|
]);
|
|
730
743
|
/** Resolve a slash command through the alias map. Returns the expanded command or the original. */
|
|
731
744
|
export function resolveAlias(line, aliases) {
|
|
@@ -999,6 +1012,9 @@ export class TUI {
|
|
|
999
1012
|
quietHoursRanges = []; // quiet-hour start/end pairs
|
|
1000
1013
|
sessionHealthHistory = new Map(); // session ID → health snapshots
|
|
1001
1014
|
alertLog = []; // recent auto-generated status alerts (ring buffer, max 100)
|
|
1015
|
+
sessionBudgets = new Map(); // session ID → USD budget
|
|
1016
|
+
globalBudget = null; // global fallback budget in USD
|
|
1017
|
+
budgetAlerted = new Map(); // session ID → epoch ms of last budget alert
|
|
1002
1018
|
// drill-down mode: show a single session's full output
|
|
1003
1019
|
viewMode = "overview";
|
|
1004
1020
|
drilldownSessionId = null;
|
|
@@ -1471,6 +1487,42 @@ export class TUI {
|
|
|
1471
1487
|
return buildDuplicateArgs(this.sessions, sessionIdOrIndex, newTitle);
|
|
1472
1488
|
}
|
|
1473
1489
|
// ── Session timeline ─────────────────────────────────────────────────────
|
|
1490
|
+
// ── Session cost budget ──────────────────────────────────────────────────
|
|
1491
|
+
/** Set a per-session budget in USD. Pass null to clear. */
|
|
1492
|
+
setSessionBudget(sessionIdOrIndex, budgetUSD) {
|
|
1493
|
+
let sessionId;
|
|
1494
|
+
if (typeof sessionIdOrIndex === "number") {
|
|
1495
|
+
sessionId = this.sessions[sessionIdOrIndex - 1]?.id;
|
|
1496
|
+
}
|
|
1497
|
+
else {
|
|
1498
|
+
const needle = sessionIdOrIndex.toLowerCase();
|
|
1499
|
+
const match = this.sessions.find((s) => s.id === sessionIdOrIndex || s.id.startsWith(needle) || s.title.toLowerCase() === needle);
|
|
1500
|
+
sessionId = match?.id;
|
|
1501
|
+
}
|
|
1502
|
+
if (!sessionId)
|
|
1503
|
+
return false;
|
|
1504
|
+
if (budgetUSD === null)
|
|
1505
|
+
this.sessionBudgets.delete(sessionId);
|
|
1506
|
+
else
|
|
1507
|
+
this.sessionBudgets.set(sessionId, budgetUSD);
|
|
1508
|
+
return true;
|
|
1509
|
+
}
|
|
1510
|
+
/** Set global fallback budget (applies to all sessions without per-session budget). */
|
|
1511
|
+
setGlobalBudget(budgetUSD) {
|
|
1512
|
+
this.globalBudget = budgetUSD;
|
|
1513
|
+
}
|
|
1514
|
+
/** Get the per-session budget (or null). */
|
|
1515
|
+
getSessionBudget(id) {
|
|
1516
|
+
return this.sessionBudgets.get(id) ?? null;
|
|
1517
|
+
}
|
|
1518
|
+
/** Get the global budget (or null if not set). */
|
|
1519
|
+
getGlobalBudget() {
|
|
1520
|
+
return this.globalBudget;
|
|
1521
|
+
}
|
|
1522
|
+
/** Return all per-session budgets. */
|
|
1523
|
+
getAllSessionBudgets() {
|
|
1524
|
+
return this.sessionBudgets;
|
|
1525
|
+
}
|
|
1474
1526
|
/** Return health history for a session (for sparkline). */
|
|
1475
1527
|
getSessionHealthHistory(id) {
|
|
1476
1528
|
return this.sessionHealthHistory.get(id) ?? [];
|
|
@@ -1803,9 +1855,18 @@ export class TUI {
|
|
|
1803
1855
|
}
|
|
1804
1856
|
if (s.lastActivity !== undefined)
|
|
1805
1857
|
this.prevLastActivity.set(s.id, s.lastActivity);
|
|
1806
|
-
// track cost string
|
|
1807
|
-
if (s.costStr)
|
|
1858
|
+
// track cost string + check budget
|
|
1859
|
+
if (s.costStr) {
|
|
1808
1860
|
this.sessionCosts.set(s.id, s.costStr);
|
|
1861
|
+
const budget = this.sessionBudgets.get(s.id) ?? this.globalBudget;
|
|
1862
|
+
if (budget !== null && isOverBudget(s.costStr, budget) && !quietNow) {
|
|
1863
|
+
const lastAlert = this.budgetAlerted.get(s.id) ?? 0;
|
|
1864
|
+
if (now - lastAlert >= 5 * 60_000) {
|
|
1865
|
+
this.budgetAlerted.set(s.id, now);
|
|
1866
|
+
this.log("status", formatBudgetAlert(s.title, s.costStr, budget), s.id);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1809
1870
|
// track context token history for burn-rate alerts
|
|
1810
1871
|
const tokens = parseContextTokenNumber(s.contextTokens);
|
|
1811
1872
|
if (tokens !== null) {
|
|
@@ -2394,7 +2455,8 @@ export class TUI {
|
|
|
2394
2455
|
const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
|
|
2395
2456
|
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth + groupBadgeWidth + tagsBadgeWidth + colorDotWidth;
|
|
2396
2457
|
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
2397
|
-
const
|
|
2458
|
+
const cardAge = s.createdAt ? formatSessionAge(s.createdAt, nowMs) : undefined;
|
|
2459
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${groupBadge}${tagsBadge}${colorDot}${formatSessionCard(s, cardWidth, errSparkline || undefined, idleSinceMs, healthBadge || undefined, displayName, cardAge || undefined)}`;
|
|
2398
2460
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
2399
2461
|
process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
|
|
2400
2462
|
}
|
|
@@ -2464,7 +2526,8 @@ export class TUI {
|
|
|
2464
2526
|
const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
|
|
2465
2527
|
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth + groupBadgeWidth + tagsBadgeWidth2 + colorDotWidth2;
|
|
2466
2528
|
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
2467
|
-
const
|
|
2529
|
+
const cardAge2 = s.createdAt ? formatSessionAge(s.createdAt, nowMs2) : undefined;
|
|
2530
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${groupBadge}${tagsBadge2}${colorDot2}${formatSessionCard(s, cardWidth, errSparkline || undefined, idleSinceMs, healthBadge2 || undefined, displayName2, cardAge2 || undefined)}`;
|
|
2468
2531
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
2469
2532
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
|
|
2470
2533
|
}
|
|
@@ -2582,7 +2645,8 @@ export class TUI {
|
|
|
2582
2645
|
// idleSinceMs: optional ms since last activity change (shown when idle/stopped)
|
|
2583
2646
|
// healthBadge: optional pre-formatted health score badge ("⬡83" colored)
|
|
2584
2647
|
// displayName: optional custom name override (from /rename)
|
|
2585
|
-
|
|
2648
|
+
// ageStr: optional session age string (from createdAt)
|
|
2649
|
+
function formatSessionCard(s, maxWidth, errorSparkline, idleSinceMs, healthBadge, displayName, ageStr) {
|
|
2586
2650
|
const dot = STATUS_DOT[s.status] ?? `${AMBER}${DOT.filled}${RESET}`;
|
|
2587
2651
|
const title = displayName ?? s.title;
|
|
2588
2652
|
const name = displayName ? `${BOLD}${displayName}${DIM} (${s.title})${RESET}` : `${BOLD}${s.title}${RESET}`;
|
|
@@ -2622,7 +2686,8 @@ function formatSessionCard(s, maxWidth, errorSparkline, idleSinceMs, healthBadge
|
|
|
2622
2686
|
else {
|
|
2623
2687
|
desc = `${SLATE}${s.status}${RESET}`;
|
|
2624
2688
|
}
|
|
2625
|
-
|
|
2689
|
+
const ageSuffix = ageStr ? ` ${DIM}age:${ageStr}${RESET}` : "";
|
|
2690
|
+
return truncateAnsi(`${dot} ${healthPrefix}${name} ${toolBadge}${contextBadge} ${SLATE}${BOX.h}${RESET} ${desc}${ageSuffix}${sparkSuffix}`, maxWidth);
|
|
2626
2691
|
}
|
|
2627
2692
|
// colorize an activity entry based on its tag
|
|
2628
2693
|
function formatActivity(entry, maxCols) {
|