pi-subagent-in-memory 0.1.6 โ 0.2.1
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 +21 -5
- package/extensions/index.ts +214 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,12 +40,20 @@ Each running subagent is displayed as a colored card widget above the editor:
|
|
|
40
40
|
|
|
41
41
|
### ๐ Subagent Detail Overlay (`Ctrl+N`)
|
|
42
42
|
|
|
43
|
-
Press **Ctrl+1** through **Ctrl+9** to open a detail popup for the
|
|
43
|
+
Press **Ctrl+1** through **Ctrl+9** to open a detail popup for the **N-th visible** subagent card (1 = leftmost/topmost in the current window):
|
|
44
44
|
|
|
45
45
|
- **Prompt** โ Full prompt text with word wrapping (up to 5 lines)
|
|
46
46
|
- **Messages** โ Live-updating stream of the subagent's activity (text output, tool calls, status changes), always showing the latest 5 lines
|
|
47
47
|
- Press the same **Ctrl+N** shortcut or **Escape** to close the overlay
|
|
48
48
|
|
|
49
|
+
### ๐ Paging Through Cards (`Ctrl+Alt+โ/โ`)
|
|
50
|
+
|
|
51
|
+
When more subagents have been spawned than fit in the visible window (see `/saim-set-max-tui-overlays` below):
|
|
52
|
+
|
|
53
|
+
- **Ctrl+Alt+โ** scrolls back to older subagent cards
|
|
54
|
+
- **Ctrl+Alt+โ** scrolls forward to newer subagent cards
|
|
55
|
+
- A `subagents XโY of N (Ctrl+Alt+โ/โ to page)` hint is displayed above the cards whenever paging is active
|
|
56
|
+
|
|
49
57
|
### ๐ JSONL Session Logging
|
|
50
58
|
|
|
51
59
|
Every subagent session is logged to disk for debugging and auditing:
|
|
@@ -71,9 +79,17 @@ The JSONL log includes:
|
|
|
71
79
|
|
|
72
80
|
Subagents can spawn their own subagents. All nested cards render in the main agent's widget โ they share the same module-level state regardless of nesting depth. This is achieved by passing the `subagent_create` tool directly as an `AgentTool` to child sessions.
|
|
73
81
|
|
|
74
|
-
###
|
|
82
|
+
### ๐๏ธ TUI Overlay Slash Commands
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---------|-------------|
|
|
86
|
+
| `/saim-toggle-overlay [on\|off\|toggle]` | Enable, disable, or toggle the subagent TUI overlay. When disabled, **no card widget is mounted even while subagents are actively running** โ they continue executing silently in the background. |
|
|
87
|
+
| `/saim-set-max-tui-overlays <N>` | Set the maximum number of cards displayed at once (1โ9, default 3). Older cards remain accessible via **Ctrl+Alt+โ/โ**. |
|
|
88
|
+
| `/saim-clear-tui-overlay` | Clear all subagent cards from the TUI and close any open detail overlay. |
|
|
89
|
+
|
|
90
|
+
### ๐ฉ `--saim-no-tui` CLI Flag
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
Start `pi` with `--saim-no-tui` to launch with the subagent overlay disabled (equivalent to running `/saim-toggle-overlay off` immediately on startup). Subagents still run normally โ only the TUI cards are hidden.
|
|
77
93
|
|
|
78
94
|
## Install
|
|
79
95
|
|
|
@@ -92,7 +108,7 @@ pi remove npm:pi-subagent-in-memory
|
|
|
92
108
|
After installing, start pi and check:
|
|
93
109
|
|
|
94
110
|
1. The `subagent_create` tool should appear in the tool list
|
|
95
|
-
2. The `/
|
|
111
|
+
2. The `/saim-toggle-overlay`, `/saim-set-max-tui-overlays`, and `/saim-clear-tui-overlay` commands should be available (type `/` to see commands)
|
|
96
112
|
3. Ask the agent to "run a subagent to list files" โ you should see a card widget appear
|
|
97
113
|
|
|
98
114
|
## Usage Examples
|
|
@@ -118,7 +134,7 @@ Once installed, the LLM will discover the `subagent_create` tool from its schema
|
|
|
118
134
|
|
|
119
135
|
## How It Works
|
|
120
136
|
|
|
121
|
-
1. **Tool registration** โ On load, registers `subagent_create` as a tool and
|
|
137
|
+
1. **Tool registration** โ On load, registers `subagent_create` as a tool plus the `/saim-*` commands and `--saim-no-tui` flag. No system prompt modifications.
|
|
122
138
|
2. **Session creation** โ When the LLM calls `subagent_create`, a new `createAgentSession` is created in-process with its own model, auth, and coding tools (read, write, edit, bash, grep, find, ls).
|
|
123
139
|
3. **Event streaming** โ All subagent events (text deltas, tool calls, completions) are forwarded as `tool_execution_update` events to the parent agent and logged to JSONL.
|
|
124
140
|
4. **Widget rendering** โ A TUI widget renders card(s) above the editor, updated on every event.
|
package/extensions/index.ts
CHANGED
|
@@ -13,9 +13,15 @@
|
|
|
13
13
|
* - Live TUI card widgets showing subagent status and output
|
|
14
14
|
* - JSONL event logging to ~/.pi/subagent-in-memory/<sessionId>/
|
|
15
15
|
* - Nested subagent support (subagents can spawn subagents)
|
|
16
|
-
* -
|
|
16
|
+
* - Slash commands to control TUI overlay:
|
|
17
|
+
* /saim-toggle-overlay [on|off] โ enable/disable rendering
|
|
18
|
+
* /saim-set-max-tui-overlays <N> โ limit visible cards (1-9)
|
|
19
|
+
* /saim-clear-tui-overlay โ clear all cards & close any overlay
|
|
17
20
|
* - Multi-provider support (Anthropic, OpenAI, Google, etc.)
|
|
18
|
-
* - Ctrl
|
|
21
|
+
* - Ctrl+<N> to inspect subagent prompt & live messages
|
|
22
|
+
* - Ctrl+Alt+Left / Ctrl+Alt+Right to page through cards when there are
|
|
23
|
+
* more subagents than the visible window allows
|
|
24
|
+
* - --saim-no-tui CLI flag to start with the overlay disabled
|
|
19
25
|
*
|
|
20
26
|
* Results are written to ./.pi/subagent-in-memory/<mainSessionId>/subagent_<N>/result.md
|
|
21
27
|
* (or error.md on failure) so the calling agent gets a short pointer instead of
|
|
@@ -24,19 +30,12 @@
|
|
|
24
30
|
|
|
25
31
|
import { Type, type Static } from "@sinclair/typebox";
|
|
26
32
|
import {
|
|
27
|
-
|
|
33
|
+
createAgentSessionFromServices,
|
|
34
|
+
createAgentSessionServices,
|
|
28
35
|
SessionManager,
|
|
29
|
-
AuthStorage,
|
|
30
|
-
DefaultResourceLoader,
|
|
31
|
-
getAgentDir,
|
|
32
|
-
createCodingTools,
|
|
33
|
-
createGrepTool,
|
|
34
|
-
createFindTool,
|
|
35
|
-
createLsTool,
|
|
36
36
|
} from "@mariozechner/pi-coding-agent";
|
|
37
37
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
38
|
-
import type { AgentToolResult, AgentToolUpdateCallback } from "@mariozechner/pi-coding-agent";
|
|
39
|
-
import { resolveModel } from "./model.ts";
|
|
38
|
+
import type { AgentToolResult, AgentToolUpdateCallback, ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
40
39
|
import { renderCard, type CardTheme } from "./tui-draw.ts";
|
|
41
40
|
|
|
42
41
|
import { visibleWidth, truncateToWidth, wrapTextWithAnsi, matchesKey, Key } from "@mariozechner/pi-tui";
|
|
@@ -142,21 +141,72 @@ let widgetRenderVersion = 0;
|
|
|
142
141
|
|
|
143
142
|
// Track open detail overlay so we can trigger re-renders
|
|
144
143
|
let activeDetailTui: any = null;
|
|
144
|
+
let activeDetailDone: ((result: void) => void) | null = null;
|
|
145
|
+
|
|
146
|
+
// โโ TUI overlay controls (commands & flag) โโโโโโโโโโโโโโโโโโโโโโ
|
|
147
|
+
const DEFAULT_MAX_VISIBLE_OVERLAYS = 3;
|
|
148
|
+
const MAX_OVERLAYS_HARD_LIMIT = 9;
|
|
149
|
+
let overlayEnabled = true;
|
|
150
|
+
let maxVisibleOverlays = DEFAULT_MAX_VISIBLE_OVERLAYS;
|
|
151
|
+
// Number of cards to skip from the END of the list. 0 means "show the latest
|
|
152
|
+
// `maxVisibleOverlays`". Increasing windowOffset pages back into older cards.
|
|
153
|
+
let windowOffset = 0;
|
|
154
|
+
|
|
155
|
+
function getVisibleSubagents(): SubagentCard[] {
|
|
156
|
+
if (subagents.length === 0) return [];
|
|
157
|
+
const total = subagents.length;
|
|
158
|
+
const window = Math.min(maxVisibleOverlays, total);
|
|
159
|
+
// Clamp offset to valid range; user paging may have left it stale.
|
|
160
|
+
const maxOffset = Math.max(0, total - window);
|
|
161
|
+
if (windowOffset > maxOffset) windowOffset = maxOffset;
|
|
162
|
+
if (windowOffset < 0) windowOffset = 0;
|
|
163
|
+
const end = total - windowOffset;
|
|
164
|
+
const start = Math.max(0, end - window);
|
|
165
|
+
return subagents.slice(start, end);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function unmountWidget() {
|
|
169
|
+
if (!currentCtx) return;
|
|
170
|
+
if (widgetMounted) {
|
|
171
|
+
currentCtx.ui.setWidget("in-memory-subagent-cards", undefined);
|
|
172
|
+
}
|
|
173
|
+
widgetMounted = false;
|
|
174
|
+
widgetTui = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function closeActiveDetail() {
|
|
178
|
+
if (activeDetailDone) {
|
|
179
|
+
try { activeDetailDone(); } catch {}
|
|
180
|
+
}
|
|
181
|
+
activeDetailTui = null;
|
|
182
|
+
activeDetailDone = null;
|
|
183
|
+
}
|
|
145
184
|
|
|
146
185
|
function renderSubagentCards(theme: any, width: number): string[] {
|
|
147
|
-
if (
|
|
186
|
+
if (!overlayEnabled) return [];
|
|
187
|
+
const visible = getVisibleSubagents();
|
|
188
|
+
if (visible.length === 0) return [];
|
|
148
189
|
|
|
149
190
|
// Derive cols from columnWidthPercent (all cards share the same value).
|
|
150
|
-
const pct =
|
|
191
|
+
const pct = visible[visible.length - 1].columnWidthPercent;
|
|
151
192
|
const cols = Math.min(3, Math.max(1, Math.round(100 / pct)));
|
|
152
193
|
const gap = 1;
|
|
153
194
|
const colWidth = Math.floor((width - gap * (cols - 1)) / cols);
|
|
154
195
|
const maxContentLines = 4;
|
|
155
196
|
const lines: string[] = [""];
|
|
156
197
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
198
|
+
// Page indicator if there are more cards than fit on screen
|
|
199
|
+
const total = subagents.length;
|
|
200
|
+
if (total > visible.length) {
|
|
201
|
+
const firstVisible = total - windowOffset - visible.length + 1;
|
|
202
|
+
const lastVisible = total - windowOffset;
|
|
203
|
+
const hint = `subagents ${firstVisible}โ${lastVisible} of ${total} (Ctrl+Alt+โ/โ to page)`;
|
|
204
|
+
lines.push(theme.fg("dim", truncateToWidth(hint, width)));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < visible.length; i += cols) {
|
|
208
|
+
const rowCards = visible.slice(i, i + cols).map((sa, idx) => {
|
|
209
|
+
const cardTheme = CARD_THEMES[(sa.num - 1) % CARD_THEMES.length];
|
|
160
210
|
|
|
161
211
|
const titleText = `${sa.title} [${sa.modelLabel}]`;
|
|
162
212
|
const innerW = colWidth - 4;
|
|
@@ -166,8 +216,8 @@ function renderSubagentCards(theme: any, width: number): string[] {
|
|
|
166
216
|
const trimmedLines = contentLines.map((l) =>
|
|
167
217
|
visibleWidth(l) > innerW ? truncateToWidth(l, innerW - 1) + "โฆ" : l
|
|
168
218
|
);
|
|
169
|
-
const
|
|
170
|
-
const content =
|
|
219
|
+
const visibleContentLines = trimmedLines.slice(0, maxContentLines);
|
|
220
|
+
const content = visibleContentLines.join("\n") + (contentLines.length > maxContentLines ? "\nโฆ" : "");
|
|
171
221
|
|
|
172
222
|
let statusRaw: string;
|
|
173
223
|
if (sa.status === "created") {
|
|
@@ -286,12 +336,8 @@ function requestSubagentRender() {
|
|
|
286
336
|
syncAnimationTimer();
|
|
287
337
|
|
|
288
338
|
if (!currentCtx) return;
|
|
289
|
-
if (subagents.length === 0) {
|
|
290
|
-
|
|
291
|
-
currentCtx.ui.setWidget("in-memory-subagent-cards", undefined);
|
|
292
|
-
widgetMounted = false;
|
|
293
|
-
widgetTui = null;
|
|
294
|
-
}
|
|
339
|
+
if (!overlayEnabled || subagents.length === 0) {
|
|
340
|
+
unmountWidget();
|
|
295
341
|
try { activeDetailTui?.requestRender(); } catch {}
|
|
296
342
|
return;
|
|
297
343
|
}
|
|
@@ -477,36 +523,44 @@ async function executeSubagent(
|
|
|
477
523
|
throw new Error("Could not determine model. Provide provider and model parameters.");
|
|
478
524
|
}
|
|
479
525
|
|
|
480
|
-
const { model: resolvedModel, apiKey } = await resolveModel(providerName, modelId);
|
|
481
|
-
|
|
482
526
|
const cwd = params.cwd ?? fallbackCwd ?? process.cwd();
|
|
483
527
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
528
|
+
// Create pi's normal cwd-bound services first. This loads packages from
|
|
529
|
+
// ~/.pi/agent/settings.json and <cwd>/.pi/settings.json, then applies any
|
|
530
|
+
// extension-provided registerProvider() calls to the model registry. Resolving
|
|
531
|
+
// the requested model after this step is what makes package-provided models
|
|
532
|
+
// such as openai-codex/gpt-5.5 visible to subagents.
|
|
533
|
+
const services = await createAgentSessionServices({ cwd });
|
|
534
|
+
const resolvedModel = services.modelRegistry.find(providerName, modelId);
|
|
535
|
+
if (!resolvedModel) {
|
|
536
|
+
const providerModels = services.modelRegistry
|
|
537
|
+
.getAll()
|
|
538
|
+
.filter((model) => model.provider === providerName)
|
|
539
|
+
.map((model) => model.id)
|
|
540
|
+
.sort();
|
|
541
|
+
const diagnostics = services.diagnostics
|
|
542
|
+
.filter((diagnostic) => diagnostic.type === "error")
|
|
543
|
+
.map((diagnostic) => diagnostic.message);
|
|
544
|
+
const details = [
|
|
545
|
+
providerModels.length > 0
|
|
546
|
+
? `Known models for ${providerName}: ${providerModels.join(", ")}`
|
|
547
|
+
: `Provider ${providerName} has no registered models.`,
|
|
548
|
+
diagnostics.length > 0 ? `Service diagnostics: ${diagnostics.join("; ")}` : undefined,
|
|
549
|
+
].filter(Boolean).join(" ");
|
|
550
|
+
throw new Error(`Could not find model ${providerName}/${modelId}. ${details}`.trim());
|
|
551
|
+
}
|
|
501
552
|
|
|
502
|
-
const { session } = await
|
|
553
|
+
const { session } = await createAgentSessionFromServices({
|
|
554
|
+
services,
|
|
555
|
+
sessionManager: SessionManager.inMemory(),
|
|
503
556
|
model: resolvedModel,
|
|
504
|
-
authStorage,
|
|
505
|
-
tools,
|
|
506
557
|
thinkingLevel: "off",
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
558
|
+
// Keep subagent context/tool surface intentionally small: built-in coding
|
|
559
|
+
// tools plus nested subagent support. Package extensions are still loaded
|
|
560
|
+
// above so provider registrations are available, but their tools are not
|
|
561
|
+
// activated unless explicitly listed here.
|
|
562
|
+
tools: ["read", "bash", "edit", "write", "grep", "find", "ls", "subagent_create"],
|
|
563
|
+
customTools: [createSubagentAgentTool(providerName, modelId, cwd)],
|
|
510
564
|
});
|
|
511
565
|
|
|
512
566
|
// Set up JSONL event log
|
|
@@ -766,7 +820,7 @@ function createSubagentAgentTool(
|
|
|
766
820
|
parentProvider: string,
|
|
767
821
|
parentModel: string,
|
|
768
822
|
parentCwd: string,
|
|
769
|
-
) {
|
|
823
|
+
): ToolDefinition<typeof SubagentParams> {
|
|
770
824
|
return {
|
|
771
825
|
name: "subagent_create",
|
|
772
826
|
label: "Subagent",
|
|
@@ -779,6 +833,7 @@ function createSubagentAgentTool(
|
|
|
779
833
|
params: SubagentParamsType,
|
|
780
834
|
signal?: AbortSignal,
|
|
781
835
|
onUpdate?: AgentToolUpdateCallback,
|
|
836
|
+
_ctx?: any,
|
|
782
837
|
) {
|
|
783
838
|
return executeSubagent(
|
|
784
839
|
toolCallId,
|
|
@@ -795,46 +850,142 @@ function createSubagentAgentTool(
|
|
|
795
850
|
|
|
796
851
|
// โโ Extension entry point โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
797
852
|
export default function (pi: ExtensionAPI) {
|
|
853
|
+
pi.registerFlag("saim-no-tui", {
|
|
854
|
+
description: "Start with the subagent TUI overlay disabled (equivalent to /saim-toggle-overlay off).",
|
|
855
|
+
type: "boolean",
|
|
856
|
+
default: false,
|
|
857
|
+
});
|
|
858
|
+
|
|
798
859
|
pi.on("session_start", async (_event, ctx) => {
|
|
799
860
|
currentCtx = ctx;
|
|
800
861
|
mainSessionId = ctx.sessionManager.getSessionId?.() ?? `session-${Date.now()}`;
|
|
801
862
|
subagentCount = 0;
|
|
802
863
|
subagents.length = 0;
|
|
803
864
|
activeDetailTui = null;
|
|
865
|
+
activeDetailDone = null;
|
|
804
866
|
widgetMounted = false;
|
|
805
867
|
widgetTui = null;
|
|
868
|
+
windowOffset = 0;
|
|
806
869
|
if (flashTimer) { clearInterval(flashTimer); flashTimer = null; }
|
|
807
870
|
ctx.ui.setWidget("in-memory-subagent-cards", undefined);
|
|
871
|
+
|
|
872
|
+
// Apply --saim-no-tui flag if present
|
|
873
|
+
if (pi.getFlag("saim-no-tui") === true) {
|
|
874
|
+
overlayEnabled = false;
|
|
875
|
+
}
|
|
876
|
+
|
|
808
877
|
requestSubagentRender();
|
|
809
878
|
});
|
|
810
879
|
|
|
811
|
-
pi.registerCommand("
|
|
812
|
-
description: "Clear
|
|
880
|
+
pi.registerCommand("saim-clear-tui-overlay", {
|
|
881
|
+
description: "Clear subagent TUI cards and close any open detail overlay",
|
|
813
882
|
handler: async (_args, ctx) => {
|
|
814
883
|
subagents.length = 0;
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
884
|
+
windowOffset = 0;
|
|
885
|
+
closeActiveDetail();
|
|
886
|
+
unmountWidget();
|
|
818
887
|
if (flashTimer) { clearInterval(flashTimer); flashTimer = null; }
|
|
819
|
-
ctx.ui.
|
|
820
|
-
|
|
888
|
+
ctx.ui.notify("Subagent TUI cards cleared", "info");
|
|
889
|
+
},
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
pi.registerCommand("saim-toggle-overlay", {
|
|
893
|
+
description: "Enable or disable subagent detail overlays. Usage: /saim-toggle-overlay [on|off|toggle]",
|
|
894
|
+
handler: async (args, ctx) => {
|
|
895
|
+
const arg = (args ?? "").trim().toLowerCase();
|
|
896
|
+
let next: boolean;
|
|
897
|
+
if (arg === "on" || arg === "true" || arg === "1") {
|
|
898
|
+
next = true;
|
|
899
|
+
} else if (arg === "off" || arg === "false" || arg === "0") {
|
|
900
|
+
next = false;
|
|
901
|
+
} else if (arg === "" || arg === "toggle") {
|
|
902
|
+
next = !overlayEnabled;
|
|
903
|
+
} else {
|
|
904
|
+
ctx.ui.notify(`Unknown argument "${arg}". Use on, off, or toggle.`, "warning");
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
overlayEnabled = next;
|
|
908
|
+
if (!overlayEnabled) {
|
|
909
|
+
// Force-tear down even while subagents are running.
|
|
910
|
+
closeActiveDetail();
|
|
911
|
+
unmountWidget();
|
|
912
|
+
}
|
|
913
|
+
requestSubagentRender();
|
|
914
|
+
ctx.ui.notify(`Subagent TUI overlay ${overlayEnabled ? "enabled" : "disabled"}`, "info");
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
pi.registerCommand("saim-set-max-tui-overlays", {
|
|
919
|
+
description: `Set max visible subagent cards (1-${MAX_OVERLAYS_HARD_LIMIT}). Older cards remain accessible via Ctrl+Alt+โ/โ.`,
|
|
920
|
+
handler: async (args, ctx) => {
|
|
921
|
+
const n = parseInt((args ?? "").trim(), 10);
|
|
922
|
+
if (!Number.isFinite(n) || n < 1 || n > MAX_OVERLAYS_HARD_LIMIT) {
|
|
923
|
+
ctx.ui.notify(
|
|
924
|
+
`Provide an integer between 1 and ${MAX_OVERLAYS_HARD_LIMIT}.`,
|
|
925
|
+
"warning",
|
|
926
|
+
);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
maxVisibleOverlays = n;
|
|
930
|
+
windowOffset = 0;
|
|
931
|
+
requestSubagentRender();
|
|
932
|
+
ctx.ui.notify(`Max visible subagent cards set to ${n}`, "info");
|
|
821
933
|
},
|
|
822
934
|
});
|
|
823
935
|
|
|
824
|
-
//
|
|
936
|
+
// Page through cards when there are more subagents than fit on screen.
|
|
937
|
+
pi.registerShortcut(Key.ctrlAlt("left"), {
|
|
938
|
+
description: "Page to older subagent cards",
|
|
939
|
+
handler: async (ctx) => {
|
|
940
|
+
if (!overlayEnabled) return;
|
|
941
|
+
const total = subagents.length;
|
|
942
|
+
const window = Math.min(maxVisibleOverlays, total);
|
|
943
|
+
const maxOffset = Math.max(0, total - window);
|
|
944
|
+
if (windowOffset >= maxOffset) {
|
|
945
|
+
ctx.ui.notify("Already showing the oldest subagents", "info");
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
windowOffset = Math.min(maxOffset, windowOffset + window);
|
|
949
|
+
requestSubagentRender();
|
|
950
|
+
},
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
pi.registerShortcut(Key.ctrlAlt("right"), {
|
|
954
|
+
description: "Page to newer subagent cards",
|
|
955
|
+
handler: async (ctx) => {
|
|
956
|
+
if (!overlayEnabled) return;
|
|
957
|
+
if (windowOffset === 0) {
|
|
958
|
+
ctx.ui.notify("Already showing the latest subagents", "info");
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
const window = Math.min(maxVisibleOverlays, subagents.length);
|
|
962
|
+
windowOffset = Math.max(0, windowOffset - window);
|
|
963
|
+
requestSubagentRender();
|
|
964
|
+
},
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
// Register Ctrl+1 through Ctrl+9 to open subagent detail overlay.
|
|
968
|
+
// The number maps to the position WITHIN the currently visible window
|
|
969
|
+
// (1 = first visible card), not the absolute subagent number.
|
|
825
970
|
for (let n = 1; n <= 9; n++) {
|
|
826
971
|
pi.registerShortcut(Key.ctrl(`${n}` as any), {
|
|
827
|
-
description: `Inspect subagent #${n}`,
|
|
972
|
+
description: `Inspect visible subagent #${n}`,
|
|
828
973
|
handler: async (ctx) => {
|
|
829
|
-
|
|
974
|
+
if (!overlayEnabled) {
|
|
975
|
+
ctx.ui.notify("Subagent TUI overlay is disabled", "warning");
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const visible = getVisibleSubagents();
|
|
979
|
+
const card = visible[n - 1];
|
|
830
980
|
if (!card) {
|
|
831
|
-
ctx.ui.notify(`No subagent #${n}`, "warning");
|
|
981
|
+
ctx.ui.notify(`No visible subagent #${n}`, "warning");
|
|
832
982
|
return;
|
|
833
983
|
}
|
|
834
984
|
|
|
835
985
|
await ctx.ui.custom<void>(
|
|
836
986
|
(tui: any, theme: any, _keybindings: any, done: (result: void) => void) => {
|
|
837
987
|
activeDetailTui = tui;
|
|
988
|
+
activeDetailDone = done;
|
|
838
989
|
return new SubagentDetailOverlay(card, n, theme, done);
|
|
839
990
|
},
|
|
840
991
|
{
|
|
@@ -849,6 +1000,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
849
1000
|
);
|
|
850
1001
|
|
|
851
1002
|
activeDetailTui = null;
|
|
1003
|
+
activeDetailDone = null;
|
|
852
1004
|
syncAnimationTimer();
|
|
853
1005
|
requestSubagentRender();
|
|
854
1006
|
},
|
package/package.json
CHANGED