@ferris1225/pi-subagents 0.21.0 → 0.23.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/README.md +5 -3
- package/package.json +53 -53
- package/src/index.ts +68 -47
- package/src/setup.ts +41 -28
package/README.md
CHANGED
|
@@ -350,9 +350,11 @@ agent's default — its frontmatter `thinking`, else the global default). The gl
|
|
|
350
350
|
When the config already exists, re-running `/subagents-setup` opens a menu whose
|
|
351
351
|
**Configure an agent (model + thinking)** entry lets you pick one agent and set just its
|
|
352
352
|
model and thinking strength — so changing a single agent no longer walks every enabled
|
|
353
|
-
agent.
|
|
354
|
-
|
|
355
|
-
|
|
353
|
+
agent. After one agent's model + strength picks, the wizard returns to the agent picker
|
|
354
|
+
so several agents can be configured in one pass; Esc at any step ends the pass and keeps
|
|
355
|
+
every agent already configured. **Change default thinking strength** sets only the global
|
|
356
|
+
fallback. The rest of the menu toggles injection, scope, concurrency, fix rounds, and
|
|
357
|
+
idle timeout; **Full re-setup** re-runs the whole first-time wizard.
|
|
356
358
|
|
|
357
359
|
```json
|
|
358
360
|
{
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ferris1225/pi-subagents",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Focused sub-agent delegation for pi: explore / worker / reviewer agents in isolated context, with proactive dispatch injection and per-agent model selection.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
-
},
|
|
10
|
-
"keywords": [
|
|
11
|
-
"pi-package",
|
|
12
|
-
"pi-extension",
|
|
13
|
-
"subagent",
|
|
14
|
-
"sub-agent",
|
|
15
|
-
"delegation"
|
|
16
|
-
],
|
|
17
|
-
"files": [
|
|
18
|
-
"src",
|
|
19
|
-
"agents",
|
|
20
|
-
"README.md",
|
|
21
|
-
"LICENSE"
|
|
22
|
-
],
|
|
23
|
-
"pi": {
|
|
24
|
-
"extensions": [
|
|
25
|
-
"./src/index.ts"
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"check": "tsc --noEmit",
|
|
30
|
-
"test": "vitest run tests",
|
|
31
|
-
"prepack": "npm run check && npm test"
|
|
32
|
-
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@earendil-works/pi-agent-core": ">=0.80.6",
|
|
35
|
-
"@earendil-works/pi-ai": ">=0.80.6",
|
|
36
|
-
"@earendil-works/pi-coding-agent": ">=0.80.6",
|
|
37
|
-
"@earendil-works/pi-tui": ">=0.80.6",
|
|
38
|
-
"typebox": "*"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@earendil-works/pi-agent-core": "^0.83.0",
|
|
42
|
-
"@earendil-works/pi-ai": "^0.83.0",
|
|
43
|
-
"@earendil-works/pi-coding-agent": "^0.83.0",
|
|
44
|
-
"@earendil-works/pi-tui": "^0.83.0",
|
|
45
|
-
"@types/node": "^22.10.0",
|
|
46
|
-
"typebox": "^1.3.9",
|
|
47
|
-
"typescript": "^5.9.0",
|
|
48
|
-
"vitest": "^4.1.0"
|
|
49
|
-
},
|
|
50
|
-
"engines": {
|
|
51
|
-
"node": ">=22.19.0"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ferris1225/pi-subagents",
|
|
3
|
+
"version": "0.23.0",
|
|
4
|
+
"description": "Focused sub-agent delegation for pi: explore / worker / reviewer agents in isolated context, with proactive dispatch injection and per-agent model selection.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"pi-package",
|
|
12
|
+
"pi-extension",
|
|
13
|
+
"subagent",
|
|
14
|
+
"sub-agent",
|
|
15
|
+
"delegation"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"agents",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"pi": {
|
|
24
|
+
"extensions": [
|
|
25
|
+
"./src/index.ts"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"check": "tsc --noEmit",
|
|
30
|
+
"test": "vitest run tests",
|
|
31
|
+
"prepack": "npm run check && npm test"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@earendil-works/pi-agent-core": ">=0.80.6",
|
|
35
|
+
"@earendil-works/pi-ai": ">=0.80.6",
|
|
36
|
+
"@earendil-works/pi-coding-agent": ">=0.80.6",
|
|
37
|
+
"@earendil-works/pi-tui": ">=0.80.6",
|
|
38
|
+
"typebox": "*"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@earendil-works/pi-agent-core": "^0.83.0",
|
|
42
|
+
"@earendil-works/pi-ai": "^0.83.0",
|
|
43
|
+
"@earendil-works/pi-coding-agent": "^0.83.0",
|
|
44
|
+
"@earendil-works/pi-tui": "^0.83.0",
|
|
45
|
+
"@types/node": "^22.10.0",
|
|
46
|
+
"typebox": "^1.3.9",
|
|
47
|
+
"typescript": "^5.9.0",
|
|
48
|
+
"vitest": "^4.1.0"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22.19.0"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -776,54 +776,75 @@ export default function (pi: ExtensionAPI): void {
|
|
|
776
776
|
}
|
|
777
777
|
}, 1000);
|
|
778
778
|
return {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
779
|
+
render(width: number): string[] {
|
|
780
|
+
const runs = monitor.getRuns();
|
|
781
|
+
if (runs.length === 0) return [];
|
|
782
|
+
const now = Date.now();
|
|
783
|
+
const lines: string[] = [];
|
|
784
|
+
// Tree layout: each top-level agent is a root whose title/activity hang
|
|
785
|
+
// off it as branches; auto-fix chain runs (groupId) become child nodes
|
|
786
|
+
// under their parent root, with a "│" continuation while more siblings
|
|
787
|
+
// follow. Blank lines separate agent blocks so parallel runs don't blur
|
|
788
|
+
// into one wall of text.
|
|
789
|
+
const dim = (t: string): string => theme.fg("dim", t);
|
|
790
|
+
for (let idx = 0; idx < runs.length; idx++) {
|
|
791
|
+
const r = runs[idx];
|
|
792
|
+
const isChain = Boolean(r.groupId);
|
|
793
|
+
const chainContinues = isChain && runs[idx + 1]?.groupId === r.groupId;
|
|
794
|
+
const activity =
|
|
795
|
+
r.activity && (r.status === "running" || r.status === "queued") ? r.activity : undefined;
|
|
796
|
+
const hasActivity = activity !== undefined;
|
|
797
|
+
const icon = statusIcon(r.status, theme);
|
|
798
|
+
// Chain-internal runs (auto-fix worker/reviewer) are child nodes under
|
|
799
|
+
// their parent reviewer. Their relationLabel ("fix round 1") is more
|
|
800
|
+
// distinguishing than the repeated worker/reviewer name.
|
|
801
|
+
const name = isChain ? (r.relationLabel ?? r.agent) : r.agent;
|
|
802
|
+
// Header row stays short: icon, run id, agent name — the task summary
|
|
803
|
+
// (keys-only fragments: paths, symbols, quoted phrases) gets its own
|
|
804
|
+
// line below with a full width budget, so parallel runs of the SAME
|
|
805
|
+
// agent are still distinguishable at a glance.
|
|
806
|
+
const title = formatTaskSummary(r.task, Math.max(16, Math.floor(width * 0.65)), true);
|
|
807
|
+
// Hierarchy: top-level agent names are the visual anchor (accent +
|
|
808
|
+
// bold); chain nodes and all metadata stay quiet, so the widget reads
|
|
809
|
+
// top-down: roots → their branches → the fine print.
|
|
810
|
+
if (!isChain && lines.length > 0) lines.push("");
|
|
811
|
+
const nodeBranch = isChain ? (chainContinues ? "├─ " : "└─ ") : "";
|
|
812
|
+
const left = `${dim(nodeBranch)}${icon} ${dim(`#${r.id}`)} ${isChain ? name : theme.fg("accent", theme.bold(name))}`;
|
|
813
|
+
|
|
814
|
+
// Right side: full model ref (provider/model), token usage (in/out +
|
|
815
|
+
// cache read/write), tool count, elapsed, and the soft activity-state
|
|
816
|
+
// annotation (idle / long-running). Trailing the header with a single
|
|
817
|
+
// " · " chain keeps the row compact (no center gap); compactLine
|
|
818
|
+
// clips on overflow, never the right side on its own.
|
|
819
|
+
const model = r.model ?? "?";
|
|
820
|
+
const usage = formatUsageCompact(r.usage);
|
|
821
|
+
const tools = r.toolCount ? `${r.toolCount} tool${r.toolCount === 1 ? "" : "s"}` : "";
|
|
822
|
+
const elapsed = formatElapsed(r, now);
|
|
823
|
+
const metaParts = [model, usage, tools, elapsed].filter(Boolean);
|
|
824
|
+
// Running is conveyed by the icon + elapsed; spell out the label only for
|
|
825
|
+
// the other states (ready / done / stopped) so they are unambiguous.
|
|
826
|
+
if (r.status !== "running") metaParts.push(statusLabel(r.status));
|
|
827
|
+
const state = deriveActivityState(r, now);
|
|
828
|
+
if (state) metaParts.push(activityStateLabel(state));
|
|
829
|
+
if (r.annotation) metaParts.push(r.annotation);
|
|
830
|
+
const right = metaParts.length ? dim(` · ${metaParts.join(" · ")}`) : "";
|
|
831
|
+
lines.push(compactLine(left, right, width));
|
|
832
|
+
|
|
833
|
+
// Branches hang off the node's content column; chain nodes that still
|
|
834
|
+
// have siblings carry a "│" continuation down to the last one.
|
|
835
|
+
const continuation = isChain ? (chainContinues ? "│ " : " ") : "";
|
|
836
|
+
// Task summary is the first branch; the summary itself is plain text —
|
|
837
|
+
// no accent, so it never competes with the agent name or pi's own UI.
|
|
838
|
+
lines.push(
|
|
839
|
+
rightAlign(`${continuation}${dim(hasActivity ? "├─ " : "└─ ")}${dim("title: ")}${theme.fg("text", title)}`, "", width),
|
|
840
|
+
);
|
|
841
|
+
// Current activity is the last branch, only while the run is active.
|
|
842
|
+
if (hasActivity) {
|
|
843
|
+
lines.push(rightAlign(`${continuation}${dim("└─ ")}${dim(activity)}`, "", width));
|
|
844
|
+
}
|
|
823
845
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
},
|
|
846
|
+
return lines;
|
|
847
|
+
},
|
|
827
848
|
invalidate() {},
|
|
828
849
|
dispose() {
|
|
829
850
|
unsub();
|
package/src/setup.ts
CHANGED
|
@@ -76,12 +76,15 @@ async function pickEnabledAgents(
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/** Single-agent model pick; the inherit option drops any existing override.
|
|
79
|
+
/** Single-agent model pick; the inherit option drops any existing override.
|
|
80
|
+
* `escNote` describes what Esc does at this pick (whole-wizard cancel in the
|
|
81
|
+
* full setup vs. ending the per-agent loop in the menu). */
|
|
80
82
|
async function pickAgentModel(
|
|
81
83
|
ctx: ExtensionCommandContext,
|
|
82
84
|
name: string,
|
|
83
85
|
currentRef: string | undefined,
|
|
84
86
|
refs: readonly string[],
|
|
87
|
+
escNote = "cancels setup",
|
|
85
88
|
): Promise<string | typeof INHERIT | undefined> {
|
|
86
89
|
const items = [
|
|
87
90
|
{
|
|
@@ -95,7 +98,7 @@ async function pickAgentModel(
|
|
|
95
98
|
return promptSelectOne(
|
|
96
99
|
ctx,
|
|
97
100
|
`Model for "${name}"`,
|
|
98
|
-
|
|
101
|
+
`Type to filter • ↑/↓ • PgUp/PgDn • Enter selects • Esc ${escNote}`,
|
|
99
102
|
items,
|
|
100
103
|
);
|
|
101
104
|
}
|
|
@@ -140,13 +143,16 @@ const THINKING_LEVEL_HINTS: Record<ThinkingLevel, string> = {
|
|
|
140
143
|
max: "strongest reasoning",
|
|
141
144
|
};
|
|
142
145
|
|
|
143
|
-
/** Single strength pick for one agent; the inherit option keeps the effective default.
|
|
146
|
+
/** Single strength pick for one agent; the inherit option keeps the effective default.
|
|
147
|
+
* `escNote` describes what Esc does at this pick (whole-wizard cancel in the
|
|
148
|
+
* full setup vs. ending the per-agent loop in the menu). */
|
|
144
149
|
async function pickAgentStrength(
|
|
145
150
|
ctx: ExtensionCommandContext,
|
|
146
151
|
agentName: string,
|
|
147
152
|
current: ThinkingLevel | undefined,
|
|
148
153
|
defaultLevel: ThinkingLevel,
|
|
149
154
|
defaults: ReadonlyMap<string, ThinkingLevel>,
|
|
155
|
+
escNote = "cancels setup",
|
|
150
156
|
): Promise<ThinkingLevel | typeof INHERIT | undefined> {
|
|
151
157
|
const options = THINKING_LEVEL_VALUES.map((level) => ({
|
|
152
158
|
value: level,
|
|
@@ -159,7 +165,7 @@ async function pickAgentStrength(
|
|
|
159
165
|
const choice = await promptSelectOne(
|
|
160
166
|
ctx,
|
|
161
167
|
`Thinking strength for "${agentName}"?`,
|
|
162
|
-
|
|
168
|
+
`Type to filter • ↑/↓ • Enter selects • Esc ${escNote}`,
|
|
163
169
|
[{ value: INHERIT, label: inheritLabel }, ...options],
|
|
164
170
|
);
|
|
165
171
|
if (choice === undefined) return undefined;
|
|
@@ -179,7 +185,7 @@ async function pickAgentToConfigure(
|
|
|
179
185
|
return promptSelectOne(
|
|
180
186
|
ctx,
|
|
181
187
|
"Configure which agent?",
|
|
182
|
-
"Type to filter • ↑/↓ • PgUp/PgDn • Enter selects • Esc
|
|
188
|
+
"Type to filter • ↑/↓ • PgUp/PgDn • Enter selects • Esc stops",
|
|
183
189
|
items,
|
|
184
190
|
);
|
|
185
191
|
}
|
|
@@ -189,7 +195,8 @@ async function pickAgentToConfigure(
|
|
|
189
195
|
* its thinking strength. Only that one agent is touched, so re-running the menu
|
|
190
196
|
* to tweak one agent no longer walks every enabled agent. Both picks offer an
|
|
191
197
|
* "inherit" option that drops any existing per-agent override for that field.
|
|
192
|
-
* Resolves undefined when the user
|
|
198
|
+
* Resolves undefined when the user presses Esc at any step; the caller keeps
|
|
199
|
+
* changes from agents already configured earlier in the same pass.
|
|
193
200
|
*/
|
|
194
201
|
async function configureOneAgent(
|
|
195
202
|
ctx: ExtensionCommandContext,
|
|
@@ -216,13 +223,13 @@ async function configureOneAgent(
|
|
|
216
223
|
if (!modelsAvailable) {
|
|
217
224
|
ctx.ui.notify("No Pi models are currently available; model override left unchanged.", "warning");
|
|
218
225
|
} else {
|
|
219
|
-
const modelChoice = await pickAgentModel(ctx, name, currentModels[name], refs);
|
|
220
|
-
if (modelChoice === undefined) return undefined; // Esc
|
|
226
|
+
const modelChoice = await pickAgentModel(ctx, name, currentModels[name], refs, "stops — earlier agent picks are kept");
|
|
227
|
+
if (modelChoice === undefined) return undefined; // Esc ends the loop
|
|
221
228
|
model = modelChoice;
|
|
222
229
|
}
|
|
223
230
|
|
|
224
|
-
const strength = await pickAgentStrength(ctx, name, currentStrengths[name], defaultLevel, defaults);
|
|
225
|
-
if (strength === undefined) return undefined; // Esc
|
|
231
|
+
const strength = await pickAgentStrength(ctx, name, currentStrengths[name], defaultLevel, defaults, "stops — earlier agent picks are kept");
|
|
232
|
+
if (strength === undefined) return undefined; // Esc ends the loop
|
|
226
233
|
|
|
227
234
|
return { name, model, modelsAvailable, strength };
|
|
228
235
|
}
|
|
@@ -403,27 +410,33 @@ async function runMenu(ctx: ExtensionCommandContext, configPath: string, config:
|
|
|
403
410
|
if (enabled === undefined) return notifyCancelled(ctx);
|
|
404
411
|
next.enabledAgents = enabled;
|
|
405
412
|
} else if (choice.startsWith("Configure an agent")) {
|
|
406
|
-
// Per-agent: pick one agent, then its model and thinking strength
|
|
407
|
-
// agent
|
|
413
|
+
// Per-agent loop: pick one agent, then its model and thinking strength, then
|
|
414
|
+
// return to the agent picker so several agents can be configured in one
|
|
415
|
+
// pass. Esc at any step ends the loop; agents already configured in this
|
|
416
|
+
// pass are kept.
|
|
408
417
|
const defaults = builtinThinkingDefaults();
|
|
409
|
-
|
|
410
|
-
ctx,
|
|
411
|
-
config.enabledAgents,
|
|
412
|
-
config.agentModels,
|
|
413
|
-
config.agentThinkingLevels,
|
|
414
|
-
config.thinkingLevel,
|
|
415
|
-
defaults,
|
|
416
|
-
);
|
|
417
|
-
if (picked === undefined) return notifyCancelled(ctx);
|
|
418
|
-
next.agentModels = { ...config.agentModels };
|
|
418
|
+
let configuredAny = false;
|
|
419
419
|
next.agentThinkingLevels = { ...config.agentThinkingLevels };
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
while (true) {
|
|
421
|
+
const picked = await configureOneAgent(
|
|
422
|
+
ctx,
|
|
423
|
+
next.enabledAgents,
|
|
424
|
+
next.agentModels,
|
|
425
|
+
next.agentThinkingLevels,
|
|
426
|
+
next.thinkingLevel,
|
|
427
|
+
defaults,
|
|
428
|
+
);
|
|
429
|
+
if (picked === undefined) break; // Esc ends the loop
|
|
430
|
+
configuredAny = true;
|
|
431
|
+
if (picked.modelsAvailable) {
|
|
432
|
+
if (picked.model === INHERIT) delete next.agentModels[picked.name];
|
|
433
|
+
else next.agentModels[picked.name] = picked.model;
|
|
434
|
+
}
|
|
435
|
+
if (picked.strength === INHERIT) delete next.agentThinkingLevels[picked.name];
|
|
436
|
+
else next.agentThinkingLevels[picked.name] = picked.strength;
|
|
424
437
|
}
|
|
425
|
-
if (
|
|
426
|
-
|
|
438
|
+
if (!configuredAny) return notifyCancelled(ctx);
|
|
439
|
+
next.agentModels = repairStaleModels(ctx, next.agentModels);
|
|
427
440
|
} else if (choice.startsWith("Change default thinking")) {
|
|
428
441
|
const thinkingLevel = await pickThinkingLevel(ctx, config.thinkingLevel);
|
|
429
442
|
if (thinkingLevel === undefined) return notifyCancelled(ctx);
|