@yusukeshib/pi-babysit 0.6.6 → 0.6.8
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/README.md +13 -7
- package/index.ts +128 -77
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,13 +80,19 @@ backgrounding (`… &`, `nohup`, `setsid`, `disown`). Set
|
|
|
80
80
|
|
|
81
81
|
A widget below the editor separates session **kind** from task **state** at a glance:
|
|
82
82
|
summary counts use `RUNNING` / `IDLE`, and every row is labeled
|
|
83
|
-
`PROCESS RUNNING`, `AGENT RUNNING`, or `AGENT IDLE`. In fullscreen mode,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
`PROCESS RUNNING`, `AGENT RUNNING`, or `AGENT IDLE`. In fullscreen mode, the
|
|
84
|
+
`babysits(n running)` list shows live sessions (including idle agents) by default.
|
|
85
|
+
Click the header to toggle to `babysits(n all)`, which also shows finished and failed
|
|
86
|
+
sessions, and click again to return to running-only. Both modes show newest first,
|
|
87
|
+
ten per page; `< 1/4 >` shows the current/total page when needed. Click the
|
|
88
|
+
arrows to page. Collapsed rows occupy one line: processes show the command,
|
|
89
|
+
while agents show a task preview. Click an agent row for the full wrapped task,
|
|
90
|
+
its RPC worker launch argv (bold, readable text rather than a copy-paste shell
|
|
91
|
+
command), and up to 20 output lines. Opening another row closes the previous
|
|
92
|
+
one. A session with no available output shows an explicit placeholder when
|
|
93
|
+
expanded. Mouse clicks
|
|
94
|
+
require fullscreen mode; `/babysit` works in regular mode. The displayed `age`
|
|
95
|
+
is total session age, not time spent
|
|
90
96
|
in the current state. Transcript details
|
|
91
97
|
from tool results, snapshots, and completion notifications collapse or expand
|
|
92
98
|
with Pi's tool-output toggle (`Ctrl+O` by default).
|
package/index.ts
CHANGED
|
@@ -43,7 +43,7 @@ import type {
|
|
|
43
43
|
ToolDefinition,
|
|
44
44
|
} from "@earendil-works/pi-coding-agent";
|
|
45
45
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
46
|
-
import { Box, Markdown, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
46
|
+
import { Box, Markdown, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
47
47
|
import { Type, type TSchema } from "typebox";
|
|
48
48
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
49
49
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents";
|
|
@@ -463,6 +463,7 @@ interface Meta {
|
|
|
463
463
|
startedAt?: number;
|
|
464
464
|
// subagent
|
|
465
465
|
task?: string;
|
|
466
|
+
launchCommand?: string;
|
|
466
467
|
promptOffset?: number;
|
|
467
468
|
model?: string;
|
|
468
469
|
tools?: string[];
|
|
@@ -1229,16 +1230,24 @@ async function selectedProcessOutput(
|
|
|
1229
1230
|
return tail ? `\n\nSelected tail (${lines} lines max):\n${clip(tail, maxBytes)}` : "";
|
|
1230
1231
|
}
|
|
1231
1232
|
|
|
1233
|
+
function escapeCommandForDisplay(command: string): string {
|
|
1234
|
+
return command
|
|
1235
|
+
.replace(/\r/g, "\\r")
|
|
1236
|
+
.replace(/\n/g, "\\n")
|
|
1237
|
+
.replace(/\t/g, "\\t")
|
|
1238
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, (char) =>
|
|
1239
|
+
`\\x${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`,
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
export function readableLaunchCommand(command: string): string {
|
|
1244
|
+
// Older sessions quoted every argv element, including safe paths and flags.
|
|
1245
|
+
if (!command.startsWith("'") || !command.endsWith("'") || !command.includes("' '")) return command;
|
|
1246
|
+
return command.slice(1, -1).split("' '").map((arg) => arg.replace(/'\\''/g, "'")).join(" ");
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1232
1249
|
export function summarizeNotificationCommand(command: string | undefined): string {
|
|
1233
|
-
const preview =
|
|
1234
|
-
(command ?? "?")
|
|
1235
|
-
.trim()
|
|
1236
|
-
.replace(/\r/g, "\\r")
|
|
1237
|
-
.replace(/\n/g, "\\n")
|
|
1238
|
-
.replace(/\t/g, "\\t")
|
|
1239
|
-
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, (char) =>
|
|
1240
|
-
`\\x${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`,
|
|
1241
|
-
) || "?";
|
|
1250
|
+
const preview = escapeCommandForDisplay((command ?? "?").trim()) || "?";
|
|
1242
1251
|
const bytes = Buffer.from(preview, "utf8");
|
|
1243
1252
|
if (bytes.length <= NOTIFY_COMMAND_MAX_BYTES) return preview;
|
|
1244
1253
|
if (NOTIFY_COMMAND_MAX_BYTES === 0) return "";
|
|
@@ -2152,6 +2161,7 @@ async function spawnSubagent(
|
|
|
2152
2161
|
PI_BIN,
|
|
2153
2162
|
...piArgs,
|
|
2154
2163
|
);
|
|
2164
|
+
const launchCommand = bsArgs.slice(bsArgs.indexOf("--") + 1).join(" ");
|
|
2155
2165
|
|
|
2156
2166
|
let r: Awaited<ReturnType<typeof bs>>;
|
|
2157
2167
|
try {
|
|
@@ -2185,6 +2195,7 @@ async function spawnSubagent(
|
|
|
2185
2195
|
kind: "subagent",
|
|
2186
2196
|
name: opts.name ?? id,
|
|
2187
2197
|
task: opts.task,
|
|
2198
|
+
launchCommand,
|
|
2188
2199
|
notified: true,
|
|
2189
2200
|
depth: opts.depth,
|
|
2190
2201
|
maxDepth: opts.maxDepth,
|
|
@@ -2272,6 +2283,7 @@ async function spawnSubagent(
|
|
|
2272
2283
|
kind: "subagent",
|
|
2273
2284
|
name: opts.name ?? id,
|
|
2274
2285
|
task: opts.task,
|
|
2286
|
+
launchCommand,
|
|
2275
2287
|
promptOffset: resp.offset,
|
|
2276
2288
|
model: resolvedModel,
|
|
2277
2289
|
tools,
|
|
@@ -2287,16 +2299,16 @@ async function spawnSubagent(
|
|
|
2287
2299
|
}
|
|
2288
2300
|
|
|
2289
2301
|
// ---------------------------------------------------------------------------
|
|
2290
|
-
// widget (live counts and browsable
|
|
2302
|
+
// widget (live counts and browsable sessions)
|
|
2291
2303
|
// ---------------------------------------------------------------------------
|
|
2292
2304
|
|
|
2293
2305
|
export function widgetSessionGroups(sessions: BsSession[], startedAt: (id: string) => number) {
|
|
2294
2306
|
const active = sessions.filter((session) => session.state === "running");
|
|
2295
|
-
|
|
2296
|
-
const
|
|
2297
|
-
|
|
2298
|
-
finished.
|
|
2299
|
-
return { active, finished };
|
|
2307
|
+
const started = new Map(sessions.map((session) => [session.id, startedAt(session.id)]));
|
|
2308
|
+
const newestFirst = (a: BsSession, b: BsSession) =>
|
|
2309
|
+
(started.get(b.id) ?? 0) - (started.get(a.id) ?? 0) || b.id.localeCompare(a.id);
|
|
2310
|
+
const finished = sessions.filter((session) => session.state !== "running").sort(newestFirst);
|
|
2311
|
+
return { active, finished, all: [...sessions].sort(newestFirst) };
|
|
2300
2312
|
}
|
|
2301
2313
|
|
|
2302
2314
|
// A subagent whose task is done stays alive as an idle RPC worker (for
|
|
@@ -2358,10 +2370,10 @@ function renderWidgetSessionHeader(
|
|
|
2358
2370
|
const WIDGET_COLLAPSED_TAIL_LINES = 1;
|
|
2359
2371
|
const WIDGET_EXPANDED_TAIL_LINES = 20;
|
|
2360
2372
|
const WIDGET_TAIL_WIDTH = 100;
|
|
2361
|
-
const
|
|
2373
|
+
const WIDGET_SESSION_PAGE_SIZE = 10;
|
|
2362
2374
|
|
|
2363
|
-
export interface
|
|
2364
|
-
|
|
2375
|
+
export interface WidgetSessionListState {
|
|
2376
|
+
mode: "running" | "all";
|
|
2365
2377
|
page: number;
|
|
2366
2378
|
}
|
|
2367
2379
|
|
|
@@ -2370,12 +2382,15 @@ export interface WidgetSessionDisplay {
|
|
|
2370
2382
|
header: string;
|
|
2371
2383
|
tail: string[];
|
|
2372
2384
|
expandable: boolean;
|
|
2385
|
+
active?: boolean;
|
|
2386
|
+
details?: string[];
|
|
2373
2387
|
}
|
|
2374
2388
|
|
|
2375
2389
|
interface WidgetMouseEvent {
|
|
2376
2390
|
type: string;
|
|
2377
2391
|
button: string;
|
|
2378
2392
|
y: number;
|
|
2393
|
+
x?: number;
|
|
2379
2394
|
}
|
|
2380
2395
|
|
|
2381
2396
|
/**
|
|
@@ -2387,17 +2402,17 @@ export function createWidgetComponent(
|
|
|
2387
2402
|
summaryLines: string[],
|
|
2388
2403
|
sessions: WidgetSessionDisplay[],
|
|
2389
2404
|
expandedSessionIds: Set<string>,
|
|
2390
|
-
|
|
2405
|
+
listState?: WidgetSessionListState,
|
|
2391
2406
|
) {
|
|
2392
|
-
type Target = { type: "session"; id: string } | { type: "
|
|
2407
|
+
type Target = { type: "session"; id: string } | { type: "sessions"; endX: number; prevX?: number; nextX?: number };
|
|
2393
2408
|
let targetByLine: Array<Target | undefined> = [];
|
|
2394
2409
|
|
|
2395
2410
|
return {
|
|
2396
2411
|
render(width: number): string[] {
|
|
2397
2412
|
const lines: string[] = [];
|
|
2398
2413
|
targetByLine = [];
|
|
2399
|
-
const push = (text: string, target?: Target) => {
|
|
2400
|
-
lines.push(truncateToWidth(text, Math.max(0, width),
|
|
2414
|
+
const push = (text: string, target?: Target, ellipsis = "") => {
|
|
2415
|
+
lines.push(truncateToWidth(text, Math.max(0, width), ellipsis));
|
|
2401
2416
|
targetByLine.push(target);
|
|
2402
2417
|
};
|
|
2403
2418
|
const pushSession = (session: WidgetSessionDisplay) => {
|
|
@@ -2406,50 +2421,86 @@ export function createWidgetComponent(
|
|
|
2406
2421
|
expanded ? -WIDGET_EXPANDED_TAIL_LINES : -WIDGET_COLLAPSED_TAIL_LINES,
|
|
2407
2422
|
);
|
|
2408
2423
|
const target = session.expandable ? { type: "session" as const, id: session.id } : undefined;
|
|
2409
|
-
|
|
2424
|
+
const details = session.details ?? [];
|
|
2425
|
+
const pushDetail = (detail: string) => {
|
|
2426
|
+
const text = detail.startsWith("task: ") ? detail.slice(6) : detail;
|
|
2427
|
+
if (width > 6) {
|
|
2428
|
+
for (const line of wrapTextWithAnsi(text, width - 6)) push(` ${line}`, target);
|
|
2429
|
+
} else {
|
|
2430
|
+
push(` ${text}`, target);
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
if (!expanded && details[0]?.startsWith("task: ")) {
|
|
2434
|
+
push(`${session.header} ${details[0].slice(6)}`, target, "…");
|
|
2435
|
+
} else if (!expanded && details.length) {
|
|
2436
|
+
push(`${session.header} ${details[0]}`, target);
|
|
2437
|
+
for (const detail of details.slice(1)) pushDetail(detail);
|
|
2438
|
+
} else if (!expanded && tail.length === 1) {
|
|
2410
2439
|
push(`${session.header} ${tail[0]}`, target);
|
|
2411
2440
|
} else {
|
|
2412
2441
|
push(session.header, target);
|
|
2442
|
+
for (const detail of details) pushDetail(detail);
|
|
2413
2443
|
for (const line of tail) push(` ${line}`, target);
|
|
2414
2444
|
if (expanded && tail.length === 0) push(" (no output available)", target);
|
|
2415
2445
|
}
|
|
2416
2446
|
};
|
|
2417
2447
|
|
|
2418
|
-
for (const line of summaryLines) push(line);
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2448
|
+
for (const line of summaryLines.slice(0, -1)) push(line);
|
|
2449
|
+
const summary = summaryLines.at(-1);
|
|
2450
|
+
if (listState && sessions.length) {
|
|
2451
|
+
const visible = listState.mode === "running" ? sessions.filter((session) => session.active) : sessions;
|
|
2452
|
+
const pageCount = Math.max(1, Math.ceil(visible.length / WIDGET_SESSION_PAGE_SIZE));
|
|
2453
|
+
listState.page = Math.min(Math.max(0, listState.page), pageCount - 1);
|
|
2454
|
+
const label = ` babysits(${visible.length} ${listState.mode})`;
|
|
2455
|
+
const showArrows = pageCount > 1;
|
|
2456
|
+
const suffix = `${label}${showArrows ? ` < ${listState.page + 1}/${pageCount} >` : ""}`;
|
|
2457
|
+
// Keep the count clickable on narrow terminals by moving it to its own row.
|
|
2458
|
+
const inline = summary !== undefined && visibleWidth(summary) + visibleWidth(suffix) <= width;
|
|
2459
|
+
if (summary !== undefined && !inline) {
|
|
2460
|
+
push(summary, { type: "sessions", endX: Math.min(visibleWidth(summary), Math.max(0, width)) });
|
|
2461
|
+
}
|
|
2462
|
+
const prefix = inline ? summary ?? "" : "";
|
|
2463
|
+
const prefixWidth = visibleWidth(prefix);
|
|
2464
|
+
push(`${prefix}${suffix}`, {
|
|
2465
|
+
type: "sessions",
|
|
2466
|
+
endX: Math.min(prefixWidth + visibleWidth(suffix), Math.max(0, width)),
|
|
2467
|
+
prevX: showArrows ? prefixWidth + suffix.indexOf("<") : undefined,
|
|
2468
|
+
nextX: showArrows ? prefixWidth + suffix.indexOf(">") : undefined,
|
|
2469
|
+
});
|
|
2470
|
+
const start = listState.page * WIDGET_SESSION_PAGE_SIZE;
|
|
2471
|
+
for (const session of visible.slice(start, start + WIDGET_SESSION_PAGE_SIZE)) {
|
|
2472
|
+
pushSession(session);
|
|
2435
2473
|
}
|
|
2474
|
+
} else {
|
|
2475
|
+
if (summary !== undefined) push(summary);
|
|
2476
|
+
for (const session of sessions) pushSession(session);
|
|
2436
2477
|
}
|
|
2437
|
-
for (const session of sessions) pushSession(session);
|
|
2438
2478
|
return lines;
|
|
2439
2479
|
},
|
|
2440
2480
|
handleMouse(event: WidgetMouseEvent) {
|
|
2441
2481
|
if (event.button !== "left") return undefined;
|
|
2442
2482
|
const target = targetByLine[event.y];
|
|
2443
2483
|
if (!target) return undefined;
|
|
2484
|
+
if (target.type === "sessions" && (event.x === undefined || event.x < 0 || event.x >= target.endX)) return undefined;
|
|
2444
2485
|
// Toggle on press so a terminal that omits release still works.
|
|
2445
2486
|
if (event.type === "press") {
|
|
2446
2487
|
if (target.type === "session") {
|
|
2447
|
-
if (expandedSessionIds.has(target.id)) expandedSessionIds.
|
|
2448
|
-
else
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2488
|
+
if (expandedSessionIds.has(target.id)) expandedSessionIds.clear();
|
|
2489
|
+
else {
|
|
2490
|
+
expandedSessionIds.clear();
|
|
2491
|
+
expandedSessionIds.add(target.id);
|
|
2492
|
+
}
|
|
2493
|
+
} else if (listState) {
|
|
2494
|
+
const count = listState.mode === "running" ? sessions.filter((session) => session.active).length : sessions.length;
|
|
2495
|
+
const pageCount = Math.max(1, Math.ceil(count / WIDGET_SESSION_PAGE_SIZE));
|
|
2496
|
+
if (event.x === target.prevX && target.prevX !== undefined) {
|
|
2497
|
+
listState.page = (listState.page - 1 + pageCount) % pageCount;
|
|
2498
|
+
} else if (event.x === target.nextX && target.nextX !== undefined) {
|
|
2499
|
+
listState.page = (listState.page + 1) % pageCount;
|
|
2500
|
+
} else {
|
|
2501
|
+
listState.mode = listState.mode === "running" ? "all" : "running";
|
|
2502
|
+
listState.page = 0;
|
|
2503
|
+
}
|
|
2453
2504
|
}
|
|
2454
2505
|
return { handled: true, render: true };
|
|
2455
2506
|
}
|
|
@@ -3417,12 +3468,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
3417
3468
|
}
|
|
3418
3469
|
|
|
3419
3470
|
const expandedWidgetSessions = new Set<string>();
|
|
3420
|
-
const
|
|
3471
|
+
const widgetListState: WidgetSessionListState = { mode: "running", page: 0 };
|
|
3421
3472
|
|
|
3422
3473
|
const refreshWidget = async (ctx: ExtensionContext, snapshot?: BsSession[]) => {
|
|
3423
3474
|
if (!ctx.hasUI) return;
|
|
3424
3475
|
const all = snapshot ?? (await listSessions()).sessions;
|
|
3425
|
-
const { active,
|
|
3476
|
+
const { active, all: ordered } = widgetSessionGroups(all, (id) => readMeta(id)?.startedAt ?? 0);
|
|
3426
3477
|
const subs = active.filter((session) => kindOf(session.id) === "subagent");
|
|
3427
3478
|
const procs = active.length - subs.length;
|
|
3428
3479
|
const progressById = new Map<string, Progress>();
|
|
@@ -3452,27 +3503,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
3452
3503
|
);
|
|
3453
3504
|
}),
|
|
3454
3505
|
);
|
|
3455
|
-
const
|
|
3456
|
-
|
|
3457
|
-
const state: WidgetSessionState = isSubagent && progressById.get(session.id)?.done
|
|
3458
|
-
? "idle"
|
|
3459
|
-
: "running";
|
|
3460
|
-
return {
|
|
3461
|
-
id: session.id,
|
|
3462
|
-
header: renderWidgetSessionHeader(
|
|
3463
|
-
session.id,
|
|
3464
|
-
isSubagent ? "agent" : "process",
|
|
3465
|
-
state,
|
|
3466
|
-
elapsedOf(session.id),
|
|
3467
|
-
theme,
|
|
3468
|
-
),
|
|
3469
|
-
tail: tails[index],
|
|
3470
|
-
expandable: true,
|
|
3471
|
-
};
|
|
3472
|
-
});
|
|
3473
|
-
const finishedDisplays = finished.map((session): WidgetSessionDisplay => {
|
|
3474
|
-
const isSubagent = kindOf(session.id) === "subagent";
|
|
3506
|
+
const activeTails = new Map(active.map((session, index) => [session.id, tails[index]]));
|
|
3507
|
+
const displays = ordered.map((session): WidgetSessionDisplay => {
|
|
3475
3508
|
const id = session.id;
|
|
3509
|
+
const meta = readMeta(id);
|
|
3510
|
+
const isSubagent = meta?.kind === "subagent";
|
|
3511
|
+
const details = isSubagent
|
|
3512
|
+
? [
|
|
3513
|
+
...(meta?.task ? [`task: ${escapeCommandForDisplay(meta.task)}`] : []),
|
|
3514
|
+
...(meta?.launchCommand ? [theme.bold(escapeCommandForDisplay(readableLaunchCommand(meta.launchCommand)))] : []),
|
|
3515
|
+
]
|
|
3516
|
+
: meta?.command ? [theme.bold(summarizeNotificationCommand(meta.command))] : [];
|
|
3517
|
+
if (session.state === "running") {
|
|
3518
|
+
const state: WidgetSessionState = isSubagent && progressById.get(id)?.done ? "idle" : "running";
|
|
3519
|
+
return {
|
|
3520
|
+
id,
|
|
3521
|
+
header: renderWidgetSessionHeader(id, isSubagent ? "agent" : "process", state, elapsedOf(id), theme),
|
|
3522
|
+
tail: activeTails.get(id) ?? [],
|
|
3523
|
+
expandable: true,
|
|
3524
|
+
active: true,
|
|
3525
|
+
details,
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3476
3528
|
return {
|
|
3477
3529
|
id,
|
|
3478
3530
|
header: renderWidgetSessionHeader(
|
|
@@ -3482,7 +3534,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
3482
3534
|
"",
|
|
3483
3535
|
theme,
|
|
3484
3536
|
),
|
|
3485
|
-
|
|
3537
|
+
details,
|
|
3538
|
+
// Only read/parse the five visible sessions on demand.
|
|
3486
3539
|
get tail() {
|
|
3487
3540
|
try {
|
|
3488
3541
|
return widgetTail(id, isSubagent, undefined, WIDGET_EXPANDED_TAIL_LINES);
|
|
@@ -3497,14 +3550,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
3497
3550
|
if (ctx.mode === "tui") {
|
|
3498
3551
|
ctx.ui.setWidget(
|
|
3499
3552
|
"pi-babysit",
|
|
3500
|
-
() => createWidgetComponent(summaryLines, displays, expandedWidgetSessions,
|
|
3501
|
-
sessions: finishedDisplays,
|
|
3502
|
-
state: finishedWidgetState,
|
|
3503
|
-
}),
|
|
3553
|
+
() => createWidgetComponent(summaryLines, displays, expandedWidgetSessions, widgetListState),
|
|
3504
3554
|
{ placement: "belowEditor" },
|
|
3505
3555
|
);
|
|
3506
3556
|
} else {
|
|
3507
|
-
const
|
|
3557
|
+
const activeIds = new Set(active.map((session) => session.id));
|
|
3558
|
+
const component = createWidgetComponent(summaryLines, displays.filter((session) => activeIds.has(session.id)), new Set());
|
|
3508
3559
|
ctx.ui.setWidget("pi-babysit", component.render(10_000), {
|
|
3509
3560
|
placement: "belowEditor",
|
|
3510
3561
|
});
|
|
@@ -4222,7 +4273,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4222
4273
|
const title = theme.bold(`babysit_run ${kind}`);
|
|
4223
4274
|
const detail =
|
|
4224
4275
|
typeof payload === "string" && payload.length > 0
|
|
4225
|
-
? ` ${
|
|
4276
|
+
? ` ${escapeCommandForDisplay(payload)}`
|
|
4226
4277
|
: "";
|
|
4227
4278
|
return new Text(theme.fg("warning", `${title}${agent}${detail}`), 0, 0);
|
|
4228
4279
|
},
|