@ferris1225/pi-subagents 0.4.0 → 0.5.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/LICENSE +21 -21
- package/README-zh.md +153 -147
- package/README.md +167 -159
- package/agents/explore.md +42 -42
- package/agents/plan.md +41 -41
- package/agents/reviewer.md +45 -45
- package/agents/worker.md +44 -44
- package/package.json +54 -54
- package/src/agents.ts +157 -157
- package/src/config.ts +168 -155
- package/src/index.ts +437 -417
- package/src/models.ts +69 -0
- package/src/monitor.ts +275 -275
- package/src/prompt.ts +58 -57
- package/src/setup.ts +264 -222
- package/src/spawn.ts +473 -386
- package/src/ui.ts +231 -231
package/src/prompt.ts
CHANGED
|
@@ -1,57 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the delegation directive injected into the parent model's system prompt
|
|
3
|
-
* via the `before_agent_start` hook. This is the lever that makes the main model
|
|
4
|
-
* actually USE the subagent tool proactively (pi never shows it the per-agent
|
|
5
|
-
* descriptions otherwise).
|
|
6
|
-
*
|
|
7
|
-
* The directive is a self-contained replacement for the "Sub-agent Dispatch" and
|
|
8
|
-
* "Review, Verification & Commit" sections users otherwise keep in a global
|
|
9
|
-
* AGENTS.md — so installing this extension lets them delete those sections without
|
|
10
|
-
* losing the behavior. (Other AGENTS.md sections — Behavior, Git & Security,
|
|
11
|
-
* platform/language rules — are unrelated and stay put.)
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { AgentConfig } from "./agents.ts";
|
|
15
|
-
import { formatCatalogEntry } from "./agents.ts";
|
|
16
|
-
|
|
17
|
-
/** Compact role routing hints, emitted only for roles that are enabled. */
|
|
18
|
-
const ROLE_ROUTING: Record<string, string> = {
|
|
19
|
-
explore: "explore — broad/open-ended code search, \"where is X\", multi-file lookups (read-only, cheap).",
|
|
20
|
-
plan: "plan — a separate, human-reviewable implementation plan before any code (read-only).",
|
|
21
|
-
worker: "worker — implement/fix/refactor/test a well-scoped task (full tools; plans internally).",
|
|
22
|
-
reviewer: "reviewer — adversarial pre-commit review of a diff (read-only; independent context).",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export function buildDelegationDirective(agents: AgentConfig[]): string {
|
|
26
|
-
if (agents.length === 0) return "";
|
|
27
|
-
|
|
28
|
-
const catalog = agents.map(formatCatalogEntry).join("\n");
|
|
29
|
-
const routing = agents
|
|
30
|
-
.map((a) => ROLE_ROUTING[a.name])
|
|
31
|
-
.filter((line): line is string => Boolean(line))
|
|
32
|
-
.map((line) => `- ${line}`)
|
|
33
|
-
.join("\n");
|
|
34
|
-
const hasReviewer = agents.some((a) => a.name === "reviewer");
|
|
35
|
-
const hasMultiple = agents.length > 1;
|
|
36
|
-
|
|
37
|
-
return `
|
|
38
|
-
## Sub-agent delegation (pi-subagents)
|
|
39
|
-
|
|
40
|
-
You have a \`subagent\` tool that runs specialized agents in ISOLATED context windows.
|
|
41
|
-
Delegate discrete, self-contained tasks to it instead of doing everything inline, so the
|
|
42
|
-
main window stays focused on orchestration, synthesis, and verification.
|
|
43
|
-
|
|
44
|
-
Available agents:
|
|
45
|
-
${catalog}
|
|
46
|
-
|
|
47
|
-
${routing ? `Routing:\n${routing}\n` : ""}Dispatch discipline:
|
|
48
|
-
- Default to delegating every discrete task to a sub-agent; do the orchestration and verification yourself in the main window.
|
|
49
|
-
- Only handle inline: pure Q&A, a single trivial edit/lookup, or when the user explicitly says to do it directly. When in doubt, delegate.
|
|
50
|
-
- For an already-known or trivial target, use a direct search/read tool (e.g. grep/find/read) — do not over-delegate a one-line lookup.
|
|
51
|
-
${hasMultiple ? "- Run INDEPENDENT tasks in parallel: one subagent call with a `tasks` array, and track them with your todo list. Keep dependent work sequential (e.g. explore, then worker, then reviewer).\n" : ""}- Brief each sub-agent as self-contained: goal, exact paths, constraints, expected output. It has NO memory of this conversation.
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Builds the delegation directive injected into the parent model's system prompt
|
|
3
|
+
* via the `before_agent_start` hook. This is the lever that makes the main model
|
|
4
|
+
* actually USE the subagent tool proactively (pi never shows it the per-agent
|
|
5
|
+
* descriptions otherwise).
|
|
6
|
+
*
|
|
7
|
+
* The directive is a self-contained replacement for the "Sub-agent Dispatch" and
|
|
8
|
+
* "Review, Verification & Commit" sections users otherwise keep in a global
|
|
9
|
+
* AGENTS.md — so installing this extension lets them delete those sections without
|
|
10
|
+
* losing the behavior. (Other AGENTS.md sections — Behavior, Git & Security,
|
|
11
|
+
* platform/language rules — are unrelated and stay put.)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { AgentConfig } from "./agents.ts";
|
|
15
|
+
import { formatCatalogEntry } from "./agents.ts";
|
|
16
|
+
|
|
17
|
+
/** Compact role routing hints, emitted only for roles that are enabled. */
|
|
18
|
+
const ROLE_ROUTING: Record<string, string> = {
|
|
19
|
+
explore: "explore — broad/open-ended code search, \"where is X\", multi-file lookups (read-only, cheap).",
|
|
20
|
+
plan: "plan — a separate, human-reviewable implementation plan before any code (read-only).",
|
|
21
|
+
worker: "worker — implement/fix/refactor/test a well-scoped task (full tools; plans internally).",
|
|
22
|
+
reviewer: "reviewer — adversarial pre-commit review of a diff (read-only; independent context).",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function buildDelegationDirective(agents: AgentConfig[]): string {
|
|
26
|
+
if (agents.length === 0) return "";
|
|
27
|
+
|
|
28
|
+
const catalog = agents.map(formatCatalogEntry).join("\n");
|
|
29
|
+
const routing = agents
|
|
30
|
+
.map((a) => ROLE_ROUTING[a.name])
|
|
31
|
+
.filter((line): line is string => Boolean(line))
|
|
32
|
+
.map((line) => `- ${line}`)
|
|
33
|
+
.join("\n");
|
|
34
|
+
const hasReviewer = agents.some((a) => a.name === "reviewer");
|
|
35
|
+
const hasMultiple = agents.length > 1;
|
|
36
|
+
|
|
37
|
+
return `
|
|
38
|
+
## Sub-agent delegation (pi-subagents)
|
|
39
|
+
|
|
40
|
+
You have a \`subagent\` tool that runs specialized agents in ISOLATED context windows.
|
|
41
|
+
Delegate discrete, self-contained tasks to it instead of doing everything inline, so the
|
|
42
|
+
main window stays focused on orchestration, synthesis, and verification.
|
|
43
|
+
|
|
44
|
+
Available agents:
|
|
45
|
+
${catalog}
|
|
46
|
+
|
|
47
|
+
${routing ? `Routing:\n${routing}\n` : ""}Dispatch discipline:
|
|
48
|
+
- Default to delegating every discrete task to a sub-agent; do the orchestration and verification yourself in the main window.
|
|
49
|
+
- Only handle inline: pure Q&A, a single trivial edit/lookup, or when the user explicitly says to do it directly. When in doubt, delegate.
|
|
50
|
+
- For an already-known or trivial target, use a direct search/read tool (e.g. grep/find/read) — do not over-delegate a one-line lookup.
|
|
51
|
+
${hasMultiple ? "- Run INDEPENDENT tasks in parallel: one subagent call with a `tasks` array, and track them with your todo list. Keep dependent work sequential (e.g. explore, then worker, then reviewer).\n" : ""}- Brief each sub-agent as self-contained: goal, exact paths, constraints, expected output. It has NO memory of this conversation.
|
|
52
|
+
- Treat delegated agents as leaf workers: do not ask a sub-agent to dispatch another sub-agent; child processes do not have this tool.
|
|
53
|
+
- Trust but verify: a sub-agent's summary describes intent, not outcome. Check the actual changes/results before reporting work done.
|
|
54
|
+
|
|
55
|
+
Review & verification:
|
|
56
|
+
- Never report an unrun check as passed; report it as unavailable or as a pre-existing failure.
|
|
57
|
+
${hasReviewer ? "- For non-trivial diffs, run one fresh read-only `reviewer` sub-agent before reporting done. Fix only concrete blockers and re-review at most once.\n- Use multi-model cross-review only when explicitly requested or for genuinely high-risk changes (security, unsafe/FFI, persistence-migration, concurrency). Reviewers are read-only; only the main agent edits.\n" : ""}- Commit or push only when explicitly requested, applicable checks pass, and no accepted blockers remain.`;
|
|
58
|
+
}
|
package/src/setup.ts
CHANGED
|
@@ -1,222 +1,264 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive configuration wizard for /subagents-setup.
|
|
3
|
-
*
|
|
4
|
-
* Everything is selection-driven (no free-text answers): a multi-select for which
|
|
5
|
-
* agents to enable, a per-agent single-select for model overrides (fuzzy filter +
|
|
6
|
-
* paging), and simple menus for the injection toggle and agent scope. Config is
|
|
7
|
-
* written to <agentDir>/pi-subagents.json.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { stat } from "node:fs/promises";
|
|
11
|
-
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import {
|
|
13
|
-
AGENT_SCOPE_VALUES,
|
|
14
|
-
BUILTIN_AGENT_NAMES,
|
|
15
|
-
DEFAULT_CONFIG,
|
|
16
|
-
DEFAULT_ENABLED_AGENTS,
|
|
17
|
-
|
|
18
|
-
type
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
);
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
await saveConfig(next, configPath);
|
|
201
|
-
ctx.ui.notify(`pi-subagents
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function
|
|
205
|
-
ctx.ui.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Interactive configuration wizard for /subagents-setup.
|
|
3
|
+
*
|
|
4
|
+
* Everything is selection-driven (no free-text answers): a multi-select for which
|
|
5
|
+
* agents to enable, a per-agent single-select for model overrides (fuzzy filter +
|
|
6
|
+
* paging), and simple menus for the injection toggle and agent scope. Config is
|
|
7
|
+
* written to <agentDir>/pi-subagents.json.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { stat } from "node:fs/promises";
|
|
11
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
import {
|
|
13
|
+
AGENT_SCOPE_VALUES,
|
|
14
|
+
BUILTIN_AGENT_NAMES,
|
|
15
|
+
DEFAULT_CONFIG,
|
|
16
|
+
DEFAULT_ENABLED_AGENTS,
|
|
17
|
+
THINKING_LEVEL_VALUES,
|
|
18
|
+
type AgentScope,
|
|
19
|
+
type SubagentsConfig,
|
|
20
|
+
type ThinkingLevel,
|
|
21
|
+
errorMessage,
|
|
22
|
+
getConfigPath,
|
|
23
|
+
loadConfig,
|
|
24
|
+
saveConfig,
|
|
25
|
+
} from "./config.ts";
|
|
26
|
+
import { availableModelRefs, repairUnavailableModelOverrides } from "./models.ts";
|
|
27
|
+
import { promptSelectMany, promptSelectOne } from "./ui.ts";
|
|
28
|
+
|
|
29
|
+
const INHERIT = "__inherit__";
|
|
30
|
+
|
|
31
|
+
/** Short, selection-friendly descriptions for the built-in agents. */
|
|
32
|
+
const MODULE_HINTS: Record<string, string> = {
|
|
33
|
+
explore: "read-only codebase recon (fast model)",
|
|
34
|
+
plan: "implementation plan before code (opt-in)",
|
|
35
|
+
worker: "implement / fix / refactor / test (full tools)",
|
|
36
|
+
reviewer: "adversarial pre-commit review (read-only)",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function moduleLabel(name: string): string {
|
|
40
|
+
const hint = MODULE_HINTS[name];
|
|
41
|
+
return hint ? `${name} — ${hint}` : name;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function configExists(configPath: string): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
await stat(configPath);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function pickEnabledAgents(
|
|
54
|
+
ctx: ExtensionCommandContext,
|
|
55
|
+
current: readonly string[],
|
|
56
|
+
): Promise<string[] | undefined> {
|
|
57
|
+
const items = BUILTIN_AGENT_NAMES.map((name) => ({ value: name, label: moduleLabel(name) }));
|
|
58
|
+
return promptSelectMany(
|
|
59
|
+
ctx,
|
|
60
|
+
"Enable which sub-agents?",
|
|
61
|
+
"Space toggles • Enter confirms • Esc cancels",
|
|
62
|
+
items,
|
|
63
|
+
current,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function pickAgentModels(
|
|
68
|
+
ctx: ExtensionCommandContext,
|
|
69
|
+
enabledAgents: readonly string[],
|
|
70
|
+
current: Record<string, string>,
|
|
71
|
+
): Promise<Record<string, string> | undefined> {
|
|
72
|
+
const refs = availableModelRefs(ctx);
|
|
73
|
+
if (refs.length === 0) {
|
|
74
|
+
ctx.ui.notify("No Pi models are currently available; model overrides left unchanged.", "warning");
|
|
75
|
+
return { ...current };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const result: Record<string, string> = {};
|
|
79
|
+
for (const name of enabledAgents) {
|
|
80
|
+
const currentRef = current[name];
|
|
81
|
+
const items = [
|
|
82
|
+
{
|
|
83
|
+
value: INHERIT,
|
|
84
|
+
label: currentRef
|
|
85
|
+
? `(use main session's model — drop override "${currentRef}")`
|
|
86
|
+
: "(use main session's current model — no override)",
|
|
87
|
+
},
|
|
88
|
+
...refs.map((ref) => ({ value: ref, label: ref === currentRef ? `${ref} (current)` : ref })),
|
|
89
|
+
];
|
|
90
|
+
const choice = await promptSelectOne(
|
|
91
|
+
ctx,
|
|
92
|
+
`Model for "${name}"`,
|
|
93
|
+
"Type to filter • ↑/↓ • PgUp/PgDn • Enter selects • Esc cancels setup",
|
|
94
|
+
items,
|
|
95
|
+
);
|
|
96
|
+
if (choice === undefined) return undefined; // Esc aborts the whole wizard
|
|
97
|
+
if (choice !== INHERIT) result[name] = choice;
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const THINKING_LEVEL_HINTS: Record<ThinkingLevel, string> = {
|
|
103
|
+
off: "no reasoning tokens (fastest)",
|
|
104
|
+
minimal: "minimal reasoning",
|
|
105
|
+
low: "light reasoning",
|
|
106
|
+
medium: "balanced reasoning",
|
|
107
|
+
high: "deep reasoning",
|
|
108
|
+
xhigh: "extra-deep reasoning",
|
|
109
|
+
max: "strongest reasoning (default)",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
async function pickThinkingLevel(
|
|
113
|
+
ctx: ExtensionCommandContext,
|
|
114
|
+
current: ThinkingLevel,
|
|
115
|
+
): Promise<ThinkingLevel | undefined> {
|
|
116
|
+
const options = THINKING_LEVEL_VALUES.map((level) =>
|
|
117
|
+
level === current ? `${level} — ${THINKING_LEVEL_HINTS[level]} (current)` : `${level} — ${THINKING_LEVEL_HINTS[level]}`,
|
|
118
|
+
);
|
|
119
|
+
const choice = await ctx.ui.select("Sub-agent thinking strength?", options);
|
|
120
|
+
if (choice === undefined) return undefined;
|
|
121
|
+
return THINKING_LEVEL_VALUES.find((level) => choice.startsWith(`${level} —`));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function pickInjection(ctx: ExtensionCommandContext, current: boolean): Promise<boolean | undefined> {
|
|
125
|
+
const on = "On — inject the delegation directive into the system prompt (recommended)";
|
|
126
|
+
const off = "Off — do not inject (rely on the tool description alone)";
|
|
127
|
+
const choice = await ctx.ui.select("Proactive dispatch injection?", [current ? `${on} (current)` : on, current ? off : `${off} (current)`]);
|
|
128
|
+
if (choice === undefined) return undefined;
|
|
129
|
+
return choice.startsWith("On");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function pickScope(ctx: ExtensionCommandContext, current: AgentScope): Promise<AgentScope | undefined> {
|
|
133
|
+
const labels: Record<AgentScope, string> = {
|
|
134
|
+
user: "user — built-in + ~/.pi/agent/agents (default)",
|
|
135
|
+
project: "project — built-in + nearest .pi/agents only",
|
|
136
|
+
both: "both — user agents, overridden by project agents",
|
|
137
|
+
};
|
|
138
|
+
const options = AGENT_SCOPE_VALUES.map((scope) =>
|
|
139
|
+
scope === current ? `${labels[scope]} (current)` : labels[scope],
|
|
140
|
+
);
|
|
141
|
+
const choice = await ctx.ui.select("Which agent directories to discover from?", options);
|
|
142
|
+
if (choice === undefined) return undefined;
|
|
143
|
+
const scope = AGENT_SCOPE_VALUES.find((s) => choice.startsWith(s));
|
|
144
|
+
return scope;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Replace unavailable overrides with a model usable by the current main window. */
|
|
148
|
+
function repairStaleModels(ctx: ExtensionCommandContext, agentModels: Record<string, string>): Record<string, string> {
|
|
149
|
+
const repair = repairUnavailableModelOverrides(ctx, agentModels);
|
|
150
|
+
if (repair.changed) {
|
|
151
|
+
const detail = repair.fallbackRef
|
|
152
|
+
? `Switched ${repair.replaced} unavailable model override(s) to ${repair.fallbackRef}.`
|
|
153
|
+
: `Removed ${repair.removed} unavailable model override(s); no model is currently available.`;
|
|
154
|
+
ctx.ui.notify(detail, "warning");
|
|
155
|
+
}
|
|
156
|
+
return repair.agentModels;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function repairConfigModels(
|
|
160
|
+
ctx: ExtensionCommandContext,
|
|
161
|
+
configPath: string,
|
|
162
|
+
config: SubagentsConfig,
|
|
163
|
+
): Promise<SubagentsConfig> {
|
|
164
|
+
const repairedModels = repairUnavailableModelOverrides(ctx, config.agentModels);
|
|
165
|
+
if (!repairedModels.changed) return config;
|
|
166
|
+
|
|
167
|
+
const repaired = { ...config, agentModels: repairedModels.agentModels };
|
|
168
|
+
try {
|
|
169
|
+
await saveConfig(repaired, configPath);
|
|
170
|
+
ctx.ui.notify(`Repaired unavailable model overrides in ${configPath}.`, "warning");
|
|
171
|
+
} catch (error) {
|
|
172
|
+
ctx.ui.notify(`Could not persist repaired model overrides: ${errorMessage(error)}`, "warning");
|
|
173
|
+
}
|
|
174
|
+
return repaired;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function runFullSetup(ctx: ExtensionCommandContext, configPath: string, base: SubagentsConfig): Promise<void> {
|
|
178
|
+
const enabled = await pickEnabledAgents(ctx, base.enabledAgents);
|
|
179
|
+
if (enabled === undefined) return notifyCancelled(ctx);
|
|
180
|
+
|
|
181
|
+
const models = await pickAgentModels(ctx, enabled, base.agentModels);
|
|
182
|
+
if (models === undefined) return notifyCancelled(ctx);
|
|
183
|
+
|
|
184
|
+
const thinkingLevel = await pickThinkingLevel(ctx, base.thinkingLevel);
|
|
185
|
+
if (thinkingLevel === undefined) return notifyCancelled(ctx);
|
|
186
|
+
|
|
187
|
+
const injection = await pickInjection(ctx, base.proactiveInjection);
|
|
188
|
+
if (injection === undefined) return notifyCancelled(ctx);
|
|
189
|
+
|
|
190
|
+
const scope = await pickScope(ctx, base.agentScope);
|
|
191
|
+
if (scope === undefined) return notifyCancelled(ctx);
|
|
192
|
+
|
|
193
|
+
const next: SubagentsConfig = {
|
|
194
|
+
enabledAgents: enabled,
|
|
195
|
+
agentModels: repairStaleModels(ctx, models),
|
|
196
|
+
thinkingLevel,
|
|
197
|
+
proactiveInjection: injection,
|
|
198
|
+
agentScope: scope,
|
|
199
|
+
};
|
|
200
|
+
await saveConfig(next, configPath);
|
|
201
|
+
ctx.ui.notify(`pi-subagents configured. Saved to ${configPath}`, "info");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function runMenu(ctx: ExtensionCommandContext, configPath: string, config: SubagentsConfig): Promise<void> {
|
|
205
|
+
const choice = await ctx.ui.select("pi-subagents is already configured. What would you like to change?", [
|
|
206
|
+
"Enable/disable agents",
|
|
207
|
+
"Change agent models",
|
|
208
|
+
"Change thinking strength",
|
|
209
|
+
"Toggle proactive injection",
|
|
210
|
+
"Change agent scope",
|
|
211
|
+
"Full re-setup",
|
|
212
|
+
]);
|
|
213
|
+
if (choice === undefined) return notifyCancelled(ctx);
|
|
214
|
+
|
|
215
|
+
if (choice.startsWith("Full")) return runFullSetup(ctx, configPath, config);
|
|
216
|
+
|
|
217
|
+
let next: SubagentsConfig = { ...config, agentModels: { ...config.agentModels } };
|
|
218
|
+
|
|
219
|
+
if (choice.startsWith("Enable")) {
|
|
220
|
+
const enabled = await pickEnabledAgents(ctx, config.enabledAgents);
|
|
221
|
+
if (enabled === undefined) return notifyCancelled(ctx);
|
|
222
|
+
next.enabledAgents = enabled;
|
|
223
|
+
} else if (choice.startsWith("Change agent models")) {
|
|
224
|
+
const models = await pickAgentModels(ctx, config.enabledAgents, config.agentModels);
|
|
225
|
+
if (models === undefined) return notifyCancelled(ctx);
|
|
226
|
+
next.agentModels = repairStaleModels(ctx, models);
|
|
227
|
+
} else if (choice.startsWith("Change thinking")) {
|
|
228
|
+
const thinkingLevel = await pickThinkingLevel(ctx, config.thinkingLevel);
|
|
229
|
+
if (thinkingLevel === undefined) return notifyCancelled(ctx);
|
|
230
|
+
next.thinkingLevel = thinkingLevel;
|
|
231
|
+
} else if (choice.startsWith("Toggle")) {
|
|
232
|
+
const injection = await pickInjection(ctx, config.proactiveInjection);
|
|
233
|
+
if (injection === undefined) return notifyCancelled(ctx);
|
|
234
|
+
next.proactiveInjection = injection;
|
|
235
|
+
} else if (choice.startsWith("Change agent scope")) {
|
|
236
|
+
const scope = await pickScope(ctx, config.agentScope);
|
|
237
|
+
if (scope === undefined) return notifyCancelled(ctx);
|
|
238
|
+
next.agentScope = scope;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await saveConfig(next, configPath);
|
|
242
|
+
ctx.ui.notify(`pi-subagents updated. Saved to ${configPath}`, "info");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function notifyCancelled(ctx: ExtensionCommandContext): void {
|
|
246
|
+
ctx.ui.notify("pi-subagents setup cancelled.", "info");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Entry point for the /subagents-setup command. */
|
|
250
|
+
export async function runSetup(ctx: ExtensionCommandContext, configPath: string = getConfigPath()): Promise<void> {
|
|
251
|
+
if (ctx.mode !== "tui") {
|
|
252
|
+
ctx.ui.notify("/subagents-setup requires Pi's interactive TUI.", "error");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const exists = await configExists(configPath);
|
|
257
|
+
const loaded = await loadConfig(configPath);
|
|
258
|
+
const config = await repairConfigModels(ctx, configPath, loaded);
|
|
259
|
+
if (exists) await runMenu(ctx, configPath, config);
|
|
260
|
+
else await runFullSetup(ctx, configPath, { ...DEFAULT_CONFIG, enabledAgents: [...DEFAULT_ENABLED_AGENTS] });
|
|
261
|
+
} catch (error) {
|
|
262
|
+
ctx.ui.notify(`pi-subagents setup failed: ${errorMessage(error)}`, "error");
|
|
263
|
+
}
|
|
264
|
+
}
|