@esso0428/pi-subagents 0.17.24 → 0.17.26
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/src/index.ts +13 -13
- package/src/ui/agent-widget.ts +19 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.17.26] - 2026-10-02
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Drew the last agents roster row as `└─` when the roster overflows**: the branch fixup only ran on the path where every row fits, so a clipped or scrolled roster kept `├─` on its final visible entry. The bounded viewport also pads the body with blank lines, so the row is not at the end of the rendered output; both render paths now share a `markLastBranch()` helper that resolves the final visible row explicitly and also blanks the `│` continuation under it.
|
|
14
|
+
|
|
15
|
+
## [0.17.25] - 2026-10-02
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **Suspended the roster key listener for the remaining widget-owned dialogs**: v0.17.24 routed `ctx.ui.select`, `ctx.ui.editor`, and `ctx.ui.custom` through `withModal()` but left `ctx.ui.input` and `ctx.ui.confirm` outside it. Those dialogs are reached only after a wrapped modal has already cleared roster navigation, so the listener could still swallow a stray `↓`, and any future path that opens one directly would lose `escape`/`↑`/`↓`/`enter` entirely. Every remaining modal call now runs inside `withModal()` as well.
|
|
19
|
+
|
|
10
20
|
## [0.17.24] - 2026-10-02
|
|
11
21
|
|
|
12
22
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esso0428/pi-subagents",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.26",
|
|
4
4
|
"description": "A pi extension that brings smart Claude Code-style autonomous sub-agents to pi, with npm:pi-subagents-style JSON agent overrides.",
|
|
5
5
|
"author": "ESSO0428",
|
|
6
6
|
"repository": {
|
package/src/index.ts
CHANGED
|
@@ -2158,7 +2158,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2158
2158
|
}
|
|
2159
2159
|
} else if (choice === "Delete") {
|
|
2160
2160
|
if (file) {
|
|
2161
|
-
const confirmed = await ctx.ui.confirm("Delete agent", `Delete ${name} from ${file.location} (${file.path})?`);
|
|
2161
|
+
const confirmed = await withModal(() => ctx.ui.confirm("Delete agent", `Delete ${name} from ${file.location} (${file.path})?`));
|
|
2162
2162
|
if (confirmed) {
|
|
2163
2163
|
unlinkSync(file.path);
|
|
2164
2164
|
reloadCustomAgents();
|
|
@@ -2166,7 +2166,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2166
2166
|
}
|
|
2167
2167
|
}
|
|
2168
2168
|
} else if (choice === "Reset to default" && file) {
|
|
2169
|
-
const confirmed = await ctx.ui.confirm("Reset to default", `Delete override ${file.path} and restore embedded default?`);
|
|
2169
|
+
const confirmed = await withModal(() => ctx.ui.confirm("Reset to default", `Delete override ${file.path} and restore embedded default?`));
|
|
2170
2170
|
if (confirmed) {
|
|
2171
2171
|
unlinkSync(file.path);
|
|
2172
2172
|
reloadCustomAgents();
|
|
@@ -2194,7 +2194,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2194
2194
|
|
|
2195
2195
|
const targetPath = join(targetDir, `${name}.md`);
|
|
2196
2196
|
if (existsSync(targetPath)) {
|
|
2197
|
-
const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
|
|
2197
|
+
const overwrite = await withModal(() => ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`));
|
|
2198
2198
|
if (!overwrite) return;
|
|
2199
2199
|
}
|
|
2200
2200
|
|
|
@@ -2307,17 +2307,17 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
2307
2307
|
}
|
|
2308
2308
|
|
|
2309
2309
|
async function showGenerateWizard(ctx: ExtensionCommandContext, targetDir: string) {
|
|
2310
|
-
const description = await ctx.ui.input("Describe what this agent should do");
|
|
2310
|
+
const description = await withModal(() => ctx.ui.input("Describe what this agent should do"));
|
|
2311
2311
|
if (!description) return;
|
|
2312
2312
|
|
|
2313
|
-
const name = await ctx.ui.input("Agent name (filename, no spaces)");
|
|
2313
|
+
const name = await withModal(() => ctx.ui.input("Agent name (filename, no spaces)"));
|
|
2314
2314
|
if (!name) return;
|
|
2315
2315
|
|
|
2316
2316
|
mkdirSync(targetDir, { recursive: true });
|
|
2317
2317
|
|
|
2318
2318
|
const targetPath = join(targetDir, `${name}.md`);
|
|
2319
2319
|
if (existsSync(targetPath)) {
|
|
2320
|
-
const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
|
|
2320
|
+
const overwrite = await withModal(() => ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`));
|
|
2321
2321
|
if (!overwrite) return;
|
|
2322
2322
|
}
|
|
2323
2323
|
|
|
@@ -2384,11 +2384,11 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
2384
2384
|
|
|
2385
2385
|
async function showManualWizard(ctx: ExtensionCommandContext, targetDir: string) {
|
|
2386
2386
|
// 1. Name
|
|
2387
|
-
const name = await ctx.ui.input("Agent name (filename, no spaces)");
|
|
2387
|
+
const name = await withModal(() => ctx.ui.input("Agent name (filename, no spaces)"));
|
|
2388
2388
|
if (!name) return;
|
|
2389
2389
|
|
|
2390
2390
|
// 2. Description
|
|
2391
|
-
const description = await ctx.ui.input("Description (one line)");
|
|
2391
|
+
const description = await withModal(() => ctx.ui.input("Description (one line)"));
|
|
2392
2392
|
if (!description) return;
|
|
2393
2393
|
|
|
2394
2394
|
// 3. Tools
|
|
@@ -2403,7 +2403,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
2403
2403
|
} else if (toolChoice.startsWith("read-only")) {
|
|
2404
2404
|
tools = "read, bash, grep, find, ls";
|
|
2405
2405
|
} else {
|
|
2406
|
-
const customTools = await ctx.ui.input("Tools (comma-separated)", BUILTIN_TOOL_NAMES.join(", "));
|
|
2406
|
+
const customTools = await withModal(() => ctx.ui.input("Tools (comma-separated)", BUILTIN_TOOL_NAMES.join(", ")));
|
|
2407
2407
|
if (!customTools) return;
|
|
2408
2408
|
tools = customTools;
|
|
2409
2409
|
}
|
|
@@ -2423,7 +2423,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
2423
2423
|
else if (modelChoice === "sonnet") modelLine = "\nmodel: anthropic/claude-sonnet-4-6";
|
|
2424
2424
|
else if (modelChoice === "opus") modelLine = "\nmodel: anthropic/claude-opus-4-6";
|
|
2425
2425
|
else if (modelChoice === "custom...") {
|
|
2426
|
-
const customModel = await ctx.ui.input("Model (provider/modelId)");
|
|
2426
|
+
const customModel = await withModal(() => ctx.ui.input("Model (provider/modelId)"));
|
|
2427
2427
|
if (customModel) modelLine = `\nmodel: ${customModel}`;
|
|
2428
2428
|
}
|
|
2429
2429
|
|
|
@@ -2453,7 +2453,7 @@ ${systemPrompt}
|
|
|
2453
2453
|
const targetPath = join(targetDir, `${name}.md`);
|
|
2454
2454
|
|
|
2455
2455
|
if (existsSync(targetPath)) {
|
|
2456
|
-
const overwrite = await ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
|
|
2456
|
+
const overwrite = await withModal(() => ctx.ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`));
|
|
2457
2457
|
if (!overwrite) return;
|
|
2458
2458
|
}
|
|
2459
2459
|
|
|
@@ -2680,7 +2680,7 @@ ${systemPrompt}
|
|
|
2680
2680
|
|
|
2681
2681
|
// Loop until user enters a valid integer or cancels (Esc / null).
|
|
2682
2682
|
// Silently trims whitespace; rejects non-numeric input by re-prompting.
|
|
2683
|
-
let input: string | undefined = await ctx.ui.input(label, current);
|
|
2683
|
+
let input: string | undefined = await withModal(() => ctx.ui.input(label, current));
|
|
2684
2684
|
while (input != null) {
|
|
2685
2685
|
const trimmed = input.trim();
|
|
2686
2686
|
const n = Number(trimmed);
|
|
@@ -2690,7 +2690,7 @@ ${systemPrompt}
|
|
|
2690
2690
|
return;
|
|
2691
2691
|
}
|
|
2692
2692
|
// Invalid — re-prompt with the user's last entry so they can edit it
|
|
2693
|
-
input = await ctx.ui.input(label, trimmed);
|
|
2693
|
+
input = await withModal(() => ctx.ui.input(label, trimmed));
|
|
2694
2694
|
}
|
|
2695
2695
|
}
|
|
2696
2696
|
}
|
package/src/ui/agent-widget.ts
CHANGED
|
@@ -238,6 +238,21 @@ export function describeActivity(activeTools: Map<string, string>, responseText?
|
|
|
238
238
|
return "thinking…";
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Mark the final visible roster row as the tree's last branch.
|
|
243
|
+
*
|
|
244
|
+
* `bodyIndex` is the offset of that row's header line inside the assembled body,
|
|
245
|
+
* which is not simply `body.length - 1`: the bounded viewport pads the body
|
|
246
|
+
* with blank lines, and two-line rows carry a `│` continuation underneath.
|
|
247
|
+
*/
|
|
248
|
+
function markLastBranch(body: string[], bodyIndex: number): void {
|
|
249
|
+
const header = body[bodyIndex];
|
|
250
|
+
if (header === undefined) return;
|
|
251
|
+
body[bodyIndex] = header.replace("├─", "└─");
|
|
252
|
+
const continuation = body[bodyIndex + 1];
|
|
253
|
+
if (continuation?.includes("│ ")) body[bodyIndex + 1] = continuation.replace("│ ", " ");
|
|
254
|
+
}
|
|
255
|
+
|
|
241
256
|
// ---- Widget manager ----
|
|
242
257
|
|
|
243
258
|
export class AgentWidget {
|
|
@@ -631,11 +646,7 @@ export class AgentWidget {
|
|
|
631
646
|
for (const row of rows) lines.push(...row.lines);
|
|
632
647
|
if (rows.length > 0) {
|
|
633
648
|
const lastRow = rows[rows.length - 1];
|
|
634
|
-
|
|
635
|
-
lines[lastStart] = lines[lastStart].replace("├─", "└─");
|
|
636
|
-
if (lastRow.lines.length === 2) {
|
|
637
|
-
lines[lastStart + 1] = lines[lastStart + 1].replace("│ ", " ");
|
|
638
|
-
}
|
|
649
|
+
markLastBranch(lines, lines.length - lastRow.lines.length);
|
|
639
650
|
}
|
|
640
651
|
} else {
|
|
641
652
|
// Reserve one line for a directional overflow summary. The viewport is
|
|
@@ -675,8 +686,10 @@ export class AgentWidget {
|
|
|
675
686
|
|
|
676
687
|
let used = 0;
|
|
677
688
|
let end = start;
|
|
689
|
+
let lastRowLine = -1;
|
|
678
690
|
const visibleBody: string[] = [];
|
|
679
691
|
while (end < rows.length && used + heightAt(end) <= contentBudget) {
|
|
692
|
+
lastRowLine = visibleBody.length;
|
|
680
693
|
visibleBody.push(...rows[end].lines);
|
|
681
694
|
used += heightAt(end);
|
|
682
695
|
end++;
|
|
@@ -687,6 +700,7 @@ export class AgentWidget {
|
|
|
687
700
|
visibleBody.push(rows[start].lines[0]);
|
|
688
701
|
end = start + 1;
|
|
689
702
|
}
|
|
703
|
+
markLastBranch(visibleBody, lastRowLine);
|
|
690
704
|
|
|
691
705
|
const hiddenBefore = start;
|
|
692
706
|
const hiddenAfter = Math.max(0, rows.length - end);
|