@ferris1225/pi-subagents 1.0.0 → 2.0.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 +2 -0
- package/README.md +188 -95
- package/agents/cleaner.md +51 -0
- package/agents/explore.md +6 -4
- package/agents/reviewer.md +2 -0
- package/package.json +9 -7
- package/src/agents.ts +2 -7
- package/src/announcements.ts +12 -1
- package/src/completion.ts +7 -36
- package/src/config.ts +310 -364
- package/src/dispatch.ts +145 -275
- package/src/fixloop.ts +0 -16
- package/src/format.ts +28 -30
- package/src/index.ts +6 -3
- package/src/models.ts +89 -106
- package/src/monitor.ts +57 -101
- package/src/prompt.ts +13 -8
- package/src/rpc-run.ts +90 -21
- package/src/runtime.ts +3 -17
- package/src/session-fork.ts +0 -4
- package/src/setup.ts +437 -639
- package/src/spawn.ts +587 -557
- package/src/tools.ts +27 -35
- package/src/ui.ts +3 -7
- package/src/widget.ts +144 -0
- package/src/worktree.ts +1 -1
- package/src/trajectory.ts +0 -312
package/src/setup.ts
CHANGED
|
@@ -1,639 +1,437 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive configuration wizard for /subagents-setup.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { stat } from "node:fs/promises";
|
|
11
|
-
import type {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
AGENT_SCOPE_VALUES,
|
|
15
|
-
BUILTIN_AGENT_NAMES,
|
|
16
|
-
DEFAULT_CONFIG,
|
|
17
|
-
DEFAULT_ENABLED_AGENTS,
|
|
18
|
-
DEFAULT_IDLE_TIMEOUT_SEC,
|
|
19
|
-
DEFAULT_MAX_CONCURRENCY,
|
|
20
|
-
DEFAULT_MAX_FIX_ROUNDS,
|
|
21
|
-
|
|
22
|
-
type AgentScope,
|
|
23
|
-
type SubagentsConfig,
|
|
24
|
-
type ThinkingLevel,
|
|
25
|
-
errorMessage,
|
|
26
|
-
getConfigPath,
|
|
27
|
-
loadConfig,
|
|
28
|
-
saveConfig,
|
|
29
|
-
} from "./config.ts";
|
|
30
|
-
import {
|
|
31
|
-
CURRENT_MAIN_MODEL,
|
|
32
|
-
|
|
33
|
-
availableModelsInScope,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
modelRef,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
type
|
|
41
|
-
} from "./models.ts";
|
|
42
|
-
import { promptSelectMany, promptSelectOne } from "./ui.ts";
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
const
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
title
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
),
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
): Promise<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
"
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
"
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (enabled === undefined) return notifyCancelled(ctx);
|
|
439
|
-
|
|
440
|
-
const thinkingLevel = await pickThinkingLevel(ctx, base.thinkingLevel);
|
|
441
|
-
if (thinkingLevel === undefined) return notifyCancelled(ctx);
|
|
442
|
-
|
|
443
|
-
const pools = await editModelPools(ctx, enabled, {
|
|
444
|
-
agentModels: base.agentModels,
|
|
445
|
-
agentBackupModels: base.agentBackupModels,
|
|
446
|
-
});
|
|
447
|
-
if (pools === undefined) return notifyCancelled(ctx);
|
|
448
|
-
|
|
449
|
-
const defaults = builtinThinkingDefaults();
|
|
450
|
-
const agentThinkingLevels = keepAgentEntries({ ...base.agentThinkingLevels }, enabled);
|
|
451
|
-
for (const agentName of enabled) {
|
|
452
|
-
const strength = await pickAgentStrength(
|
|
453
|
-
ctx,
|
|
454
|
-
agentName,
|
|
455
|
-
agentThinkingLevels[agentName],
|
|
456
|
-
thinkingLevel,
|
|
457
|
-
defaults,
|
|
458
|
-
);
|
|
459
|
-
if (strength === undefined) return notifyCancelled(ctx);
|
|
460
|
-
if (strength === INHERIT) delete agentThinkingLevels[agentName];
|
|
461
|
-
else agentThinkingLevels[agentName] = strength;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const visionModel = await pickVisionModel(ctx, base.visionModel);
|
|
465
|
-
if (visionModel === undefined) return notifyCancelled(ctx);
|
|
466
|
-
|
|
467
|
-
const injection = await pickInjection(ctx, base.proactiveInjection);
|
|
468
|
-
if (injection === undefined) return notifyCancelled(ctx);
|
|
469
|
-
|
|
470
|
-
const scope = await pickScope(ctx, base.agentScope);
|
|
471
|
-
if (scope === undefined) return notifyCancelled(ctx);
|
|
472
|
-
|
|
473
|
-
const maxConcurrency = await pickCount(
|
|
474
|
-
ctx,
|
|
475
|
-
"Max sub-agents running at once (and per parallel call)? (extra work queues)",
|
|
476
|
-
CONCURRENCY_STEPS,
|
|
477
|
-
base.maxConcurrency,
|
|
478
|
-
DEFAULT_MAX_CONCURRENCY,
|
|
479
|
-
);
|
|
480
|
-
if (maxConcurrency === undefined) return notifyCancelled(ctx);
|
|
481
|
-
|
|
482
|
-
const maxFixRounds = await pickCount(
|
|
483
|
-
ctx,
|
|
484
|
-
"Auto-fix rounds when a reviewer returns REQUEST_CHANGES? (0 = main agent handles fixes)",
|
|
485
|
-
FIX_ROUNDS_STEPS,
|
|
486
|
-
base.maxFixRounds,
|
|
487
|
-
DEFAULT_MAX_FIX_ROUNDS,
|
|
488
|
-
);
|
|
489
|
-
if (maxFixRounds === undefined) return notifyCancelled(ctx);
|
|
490
|
-
|
|
491
|
-
const idleTimeoutSec = await pickCount(
|
|
492
|
-
ctx,
|
|
493
|
-
"Idle timeout in seconds? (0 = disabled, kills a sub-agent whose output goes silent)",
|
|
494
|
-
IDLE_TIMEOUT_STEPS,
|
|
495
|
-
base.idleTimeoutSec,
|
|
496
|
-
DEFAULT_IDLE_TIMEOUT_SEC,
|
|
497
|
-
);
|
|
498
|
-
if (idleTimeoutSec === undefined) return notifyCancelled(ctx);
|
|
499
|
-
|
|
500
|
-
const next: SubagentsConfig = {
|
|
501
|
-
enabledAgents: enabled,
|
|
502
|
-
agentModels: keepAgentEntries(pools.agentModels, enabled),
|
|
503
|
-
agentBackupModels: keepAgentEntries(pools.agentBackupModels, enabled),
|
|
504
|
-
agentThinkingLevels,
|
|
505
|
-
thinkingLevel,
|
|
506
|
-
notifyOnReviewPass: base.notifyOnReviewPass,
|
|
507
|
-
maxResultLines: base.maxResultLines,
|
|
508
|
-
proactiveInjection: injection,
|
|
509
|
-
agentScope: scope,
|
|
510
|
-
maxConcurrency,
|
|
511
|
-
maxFixRounds,
|
|
512
|
-
idleTimeoutSec,
|
|
513
|
-
announcedFeatures: base.announcedFeatures,
|
|
514
|
-
};
|
|
515
|
-
if (visionModel !== CURRENT_MAIN_MODEL) next.visionModel = visionModel;
|
|
516
|
-
await saveConfig(next, configPath);
|
|
517
|
-
ctx.ui.notify(`pi-subagents configured. Saved to ${configPath}`, "info");
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
async function runMenu(ctx: ExtensionCommandContext, configPath: string, config: SubagentsConfig): Promise<void> {
|
|
521
|
-
const choice = await ctx.ui.select("pi-subagents is already configured. What would you like to change?", [
|
|
522
|
-
"Enable/disable agents",
|
|
523
|
-
"Configure an agent (model pool + thinking)",
|
|
524
|
-
"Change vision model (image tasks)",
|
|
525
|
-
"Toggle proactive injection",
|
|
526
|
-
"Change agent scope",
|
|
527
|
-
"Change max concurrent sub-agents",
|
|
528
|
-
"Change max fix rounds",
|
|
529
|
-
"Change idle timeout",
|
|
530
|
-
"Full re-setup",
|
|
531
|
-
]);
|
|
532
|
-
if (choice === undefined) return notifyCancelled(ctx);
|
|
533
|
-
|
|
534
|
-
if (choice.startsWith("Full")) return runFullSetup(ctx, configPath, config);
|
|
535
|
-
|
|
536
|
-
let next: SubagentsConfig = {
|
|
537
|
-
...config,
|
|
538
|
-
agentModels: { ...config.agentModels },
|
|
539
|
-
agentBackupModels: { ...config.agentBackupModels },
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
if (choice.startsWith("Enable")) {
|
|
543
|
-
const enabled = await pickEnabledAgents(ctx, config.enabledAgents);
|
|
544
|
-
if (enabled === undefined) return notifyCancelled(ctx);
|
|
545
|
-
next.enabledAgents = enabled;
|
|
546
|
-
} else if (choice.startsWith("Configure an agent")) {
|
|
547
|
-
// Per-agent loop: pick one agent, edit Primary + Backup together, then its
|
|
548
|
-
// thinking strength. Esc ends the loop; earlier saved agent changes remain.
|
|
549
|
-
const defaults = builtinThinkingDefaults();
|
|
550
|
-
let configuredAny = false;
|
|
551
|
-
next.agentThinkingLevels = { ...config.agentThinkingLevels };
|
|
552
|
-
while (true) {
|
|
553
|
-
const picked = await configureOneAgent(
|
|
554
|
-
ctx,
|
|
555
|
-
next.enabledAgents,
|
|
556
|
-
{
|
|
557
|
-
agentModels: next.agentModels,
|
|
558
|
-
agentBackupModels: next.agentBackupModels,
|
|
559
|
-
},
|
|
560
|
-
next.agentThinkingLevels,
|
|
561
|
-
next.thinkingLevel,
|
|
562
|
-
defaults,
|
|
563
|
-
);
|
|
564
|
-
if (picked === undefined) break;
|
|
565
|
-
configuredAny = true;
|
|
566
|
-
next.agentModels = picked.pools.agentModels;
|
|
567
|
-
next.agentBackupModels = picked.pools.agentBackupModels;
|
|
568
|
-
if (picked.strength === INHERIT) delete next.agentThinkingLevels[picked.name];
|
|
569
|
-
else next.agentThinkingLevels[picked.name] = picked.strength;
|
|
570
|
-
}
|
|
571
|
-
if (!configuredAny) return notifyCancelled(ctx);
|
|
572
|
-
} else if (choice.startsWith("Toggle")) {
|
|
573
|
-
const injection = await pickInjection(ctx, config.proactiveInjection);
|
|
574
|
-
if (injection === undefined) return notifyCancelled(ctx);
|
|
575
|
-
next.proactiveInjection = injection;
|
|
576
|
-
} else if (choice.startsWith("Change vision")) {
|
|
577
|
-
const visionModel = await pickVisionModel(ctx, config.visionModel);
|
|
578
|
-
if (visionModel === undefined) return notifyCancelled(ctx);
|
|
579
|
-
if (visionModel === CURRENT_MAIN_MODEL) delete next.visionModel;
|
|
580
|
-
else next.visionModel = visionModel;
|
|
581
|
-
} else if (choice.startsWith("Change agent scope")) {
|
|
582
|
-
const scope = await pickScope(ctx, config.agentScope);
|
|
583
|
-
if (scope === undefined) return notifyCancelled(ctx);
|
|
584
|
-
next.agentScope = scope;
|
|
585
|
-
} else if (choice.startsWith("Change max concurrent")) {
|
|
586
|
-
const maxConcurrency = await pickCount(
|
|
587
|
-
ctx,
|
|
588
|
-
"Max sub-agents running at once (and per parallel call)? (extra work queues)",
|
|
589
|
-
CONCURRENCY_STEPS,
|
|
590
|
-
config.maxConcurrency,
|
|
591
|
-
DEFAULT_MAX_CONCURRENCY,
|
|
592
|
-
);
|
|
593
|
-
if (maxConcurrency === undefined) return notifyCancelled(ctx);
|
|
594
|
-
next.maxConcurrency = maxConcurrency;
|
|
595
|
-
} else if (choice.startsWith("Change max fix")) {
|
|
596
|
-
const maxFixRounds = await pickCount(
|
|
597
|
-
ctx,
|
|
598
|
-
"Auto-fix rounds when a reviewer returns REQUEST_CHANGES? (0 = main agent handles fixes)",
|
|
599
|
-
FIX_ROUNDS_STEPS,
|
|
600
|
-
config.maxFixRounds,
|
|
601
|
-
DEFAULT_MAX_FIX_ROUNDS,
|
|
602
|
-
);
|
|
603
|
-
if (maxFixRounds === undefined) return notifyCancelled(ctx);
|
|
604
|
-
next.maxFixRounds = maxFixRounds;
|
|
605
|
-
} else if (choice.startsWith("Change idle")) {
|
|
606
|
-
const idleTimeoutSec = await pickCount(
|
|
607
|
-
ctx,
|
|
608
|
-
"Idle timeout in seconds? (0 = disabled, kills a sub-agent whose output goes silent)",
|
|
609
|
-
IDLE_TIMEOUT_STEPS,
|
|
610
|
-
config.idleTimeoutSec,
|
|
611
|
-
DEFAULT_IDLE_TIMEOUT_SEC,
|
|
612
|
-
);
|
|
613
|
-
if (idleTimeoutSec === undefined) return notifyCancelled(ctx);
|
|
614
|
-
next.idleTimeoutSec = idleTimeoutSec;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
await saveConfig(next, configPath);
|
|
618
|
-
ctx.ui.notify(`pi-subagents updated. Saved to ${configPath}`, "info");
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
function notifyCancelled(ctx: ExtensionCommandContext): void {
|
|
622
|
-
ctx.ui.notify("pi-subagents setup cancelled.", "info");
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/** Entry point for the /subagents-setup command. */
|
|
626
|
-
export async function runSetup(ctx: ExtensionCommandContext, configPath: string = getConfigPath()): Promise<void> {
|
|
627
|
-
if (ctx.mode !== "tui") {
|
|
628
|
-
ctx.ui.notify("/subagents-setup requires Pi's interactive TUI.", "error");
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
try {
|
|
632
|
-
const exists = await configExists(configPath);
|
|
633
|
-
const config = await loadConfig(configPath);
|
|
634
|
-
if (exists) await runMenu(ctx, configPath, config);
|
|
635
|
-
else await runFullSetup(ctx, configPath, { ...DEFAULT_CONFIG, enabledAgents: [...DEFAULT_ENABLED_AGENTS] });
|
|
636
|
-
} catch (error) {
|
|
637
|
-
ctx.ui.notify(`pi-subagents setup failed: ${errorMessage(error)}`, "error");
|
|
638
|
-
}
|
|
639
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Interactive configuration wizard for /subagents-setup.
|
|
3
|
+
*
|
|
4
|
+
* The UI intentionally has no backup pool or global thinking menu. Each agent
|
|
5
|
+
* gets one optional model override; failures hand directly to the current main
|
|
6
|
+
* model. Thinking defaults to Auto and manual choices are limited to levels Pi
|
|
7
|
+
* reports as supported by the selected model.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { stat } from "node:fs/promises";
|
|
11
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
12
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import {
|
|
14
|
+
AGENT_SCOPE_VALUES,
|
|
15
|
+
BUILTIN_AGENT_NAMES,
|
|
16
|
+
DEFAULT_CONFIG,
|
|
17
|
+
DEFAULT_ENABLED_AGENTS,
|
|
18
|
+
DEFAULT_IDLE_TIMEOUT_SEC,
|
|
19
|
+
DEFAULT_MAX_CONCURRENCY,
|
|
20
|
+
DEFAULT_MAX_FIX_ROUNDS,
|
|
21
|
+
DEFAULT_THINKING_LEVEL,
|
|
22
|
+
type AgentScope,
|
|
23
|
+
type SubagentsConfig,
|
|
24
|
+
type ThinkingLevel,
|
|
25
|
+
errorMessage,
|
|
26
|
+
getConfigPath,
|
|
27
|
+
loadConfig,
|
|
28
|
+
saveConfig,
|
|
29
|
+
} from "./config.ts";
|
|
30
|
+
import {
|
|
31
|
+
CURRENT_MAIN_MODEL,
|
|
32
|
+
applyAgentModelChoice,
|
|
33
|
+
availableModelsInScope,
|
|
34
|
+
buildModelPickerItems,
|
|
35
|
+
currentModelRef,
|
|
36
|
+
findModelByRef,
|
|
37
|
+
modelRef,
|
|
38
|
+
resolveThinkingLevel,
|
|
39
|
+
supportedThinkingLevels,
|
|
40
|
+
type ModelPickerSlot,
|
|
41
|
+
} from "./models.ts";
|
|
42
|
+
import { promptSelectMany, promptSelectOne } from "./ui.ts";
|
|
43
|
+
import { discoverAgents } from "./agents.ts";
|
|
44
|
+
|
|
45
|
+
const AUTO_THINKING = "__auto_thinking__";
|
|
46
|
+
|
|
47
|
+
function actualAgentThinkingDefault(
|
|
48
|
+
ctx: ExtensionCommandContext,
|
|
49
|
+
config: SubagentsConfig,
|
|
50
|
+
agentName: string,
|
|
51
|
+
): ThinkingLevel {
|
|
52
|
+
const { agents } = discoverAgents(ctx.cwd, {
|
|
53
|
+
scope: config.agentScope,
|
|
54
|
+
enabledNames: config.enabledAgents,
|
|
55
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
56
|
+
});
|
|
57
|
+
return agents.find((agent) => agent.name === agentName)?.thinking ?? DEFAULT_THINKING_LEVEL;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Short, selection-friendly descriptions for the built-in agents. */
|
|
61
|
+
const MODULE_HINTS: Record<string, string> = {
|
|
62
|
+
explore: "read-only codebase recon (fast model)",
|
|
63
|
+
worker: "implement / fix / refactor / test (full tools)",
|
|
64
|
+
cleaner: "evidence-first cleanup: audit or verified cuts (full tools)",
|
|
65
|
+
reviewer: "adversarial pre-commit review (read-only)",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function moduleLabel(name: string): string {
|
|
69
|
+
const hint = MODULE_HINTS[name];
|
|
70
|
+
return hint ? `${name} — ${hint}` : name;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function configExists(configPath: string): Promise<boolean> {
|
|
74
|
+
try {
|
|
75
|
+
await stat(configPath);
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function pickEnabledAgents(
|
|
83
|
+
ctx: ExtensionCommandContext,
|
|
84
|
+
current: readonly string[],
|
|
85
|
+
): Promise<string[] | undefined> {
|
|
86
|
+
const items = BUILTIN_AGENT_NAMES.map((name) => ({ value: name, label: moduleLabel(name) }));
|
|
87
|
+
return promptSelectMany(
|
|
88
|
+
ctx,
|
|
89
|
+
"Enable which sub-agents?",
|
|
90
|
+
"Space toggles • Enter confirms • Esc cancels",
|
|
91
|
+
items,
|
|
92
|
+
current,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function pickConfiguredModel(
|
|
97
|
+
ctx: ExtensionCommandContext,
|
|
98
|
+
title: string,
|
|
99
|
+
slot: ModelPickerSlot,
|
|
100
|
+
configuredRef: string | undefined,
|
|
101
|
+
escNote: string,
|
|
102
|
+
): Promise<string | undefined> {
|
|
103
|
+
const models = availableModelsInScope(ctx);
|
|
104
|
+
const items = buildModelPickerItems({
|
|
105
|
+
models,
|
|
106
|
+
slot,
|
|
107
|
+
configuredRef,
|
|
108
|
+
mainRef: currentModelRef(ctx),
|
|
109
|
+
});
|
|
110
|
+
return promptSelectOne(
|
|
111
|
+
ctx,
|
|
112
|
+
title,
|
|
113
|
+
`Type to filter by provider, model, capability, or thinking level • ↑/↓ • Enter selects • Esc ${escNote}`,
|
|
114
|
+
items,
|
|
115
|
+
configuredRef ?? CURRENT_MAIN_MODEL,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function pickAgentModel(
|
|
120
|
+
ctx: ExtensionCommandContext,
|
|
121
|
+
agentName: string,
|
|
122
|
+
currentRef: string | undefined,
|
|
123
|
+
escNote = "cancels setup",
|
|
124
|
+
): Promise<string | undefined> {
|
|
125
|
+
return pickConfiguredModel(ctx, `Model for "${agentName}"?`, "agent", currentRef, escNote);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function pickVisionModel(
|
|
129
|
+
ctx: ExtensionCommandContext,
|
|
130
|
+
currentRef: string | undefined,
|
|
131
|
+
): Promise<string | undefined> {
|
|
132
|
+
return pickConfiguredModel(
|
|
133
|
+
ctx,
|
|
134
|
+
"Vision model for image tasks? (failure → current main model)",
|
|
135
|
+
"vision",
|
|
136
|
+
currentRef,
|
|
137
|
+
"cancels setup",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const THINKING_LEVEL_HINTS: Record<ThinkingLevel, string> = {
|
|
142
|
+
off: "no reasoning tokens",
|
|
143
|
+
minimal: "minimal reasoning",
|
|
144
|
+
low: "light reasoning",
|
|
145
|
+
medium: "balanced reasoning",
|
|
146
|
+
high: "deep reasoning",
|
|
147
|
+
xhigh: "extra-deep reasoning",
|
|
148
|
+
max: "strongest reasoning",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function effectiveModelForChoice(
|
|
152
|
+
ctx: ExtensionCommandContext,
|
|
153
|
+
choice: string,
|
|
154
|
+
): Model<Api> | undefined {
|
|
155
|
+
if (choice === CURRENT_MAIN_MODEL) return ctx.model;
|
|
156
|
+
return findModelByRef(availableModelsInScope(ctx), choice);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Auto is the default. Manual rows are exactly the levels Pi exposes for the
|
|
160
|
+
* selected model; unsupported xhigh/max entries never appear. */
|
|
161
|
+
async function pickAgentStrength(
|
|
162
|
+
ctx: ExtensionCommandContext,
|
|
163
|
+
agentName: string,
|
|
164
|
+
model: Model<Api> | undefined,
|
|
165
|
+
current: ThinkingLevel | undefined,
|
|
166
|
+
agentDefault: ThinkingLevel,
|
|
167
|
+
escNote = "cancels setup",
|
|
168
|
+
): Promise<ThinkingLevel | typeof AUTO_THINKING | undefined> {
|
|
169
|
+
const supported = supportedThinkingLevels(model);
|
|
170
|
+
const automatic = resolveThinkingLevel(model, agentDefault);
|
|
171
|
+
// No model metadata, or a non-reasoning model whose only valid value is off:
|
|
172
|
+
// Auto is already the complete and least surprising choice.
|
|
173
|
+
if (supported.length <= 1) return AUTO_THINKING;
|
|
174
|
+
|
|
175
|
+
const currentEffective = current ? resolveThinkingLevel(model, current) : undefined;
|
|
176
|
+
const modelName = model ? modelRef(model) : "current main model";
|
|
177
|
+
const options = [
|
|
178
|
+
{
|
|
179
|
+
value: AUTO_THINKING,
|
|
180
|
+
label: `auto — ${automatic} for ${modelName}${current === undefined ? " (current, recommended)" : " (recommended)"}`,
|
|
181
|
+
},
|
|
182
|
+
...supported.map((level) => ({
|
|
183
|
+
value: level,
|
|
184
|
+
label: `${level} — ${THINKING_LEVEL_HINTS[level]}${current !== undefined && currentEffective === level ? " (current)" : ""}`,
|
|
185
|
+
})),
|
|
186
|
+
];
|
|
187
|
+
return promptSelectOne(
|
|
188
|
+
ctx,
|
|
189
|
+
`Thinking for "${agentName}"?`,
|
|
190
|
+
`Only levels supported by ${modelName} are shown • Enter selects • Esc ${escNote}`,
|
|
191
|
+
options,
|
|
192
|
+
current === undefined ? AUTO_THINKING : currentEffective,
|
|
193
|
+
) as Promise<ThinkingLevel | typeof AUTO_THINKING | undefined>;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function pickAgentToConfigure(
|
|
197
|
+
ctx: ExtensionCommandContext,
|
|
198
|
+
enabledAgents: readonly string[],
|
|
199
|
+
): Promise<string | undefined> {
|
|
200
|
+
if (enabledAgents.length === 0) {
|
|
201
|
+
ctx.ui.notify("No agents are enabled. Enable agents first.", "warning");
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
return promptSelectOne(
|
|
205
|
+
ctx,
|
|
206
|
+
"Configure which agent?",
|
|
207
|
+
"Type to filter • ↑/↓ • Enter selects • Esc cancels",
|
|
208
|
+
enabledAgents.map((name) => ({ value: name, label: moduleLabel(name) })),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function pickInjection(ctx: ExtensionCommandContext, current: boolean): Promise<boolean | undefined> {
|
|
213
|
+
const on = "On — inject the delegation directive (recommended)";
|
|
214
|
+
const off = "Off — rely on tool descriptions only";
|
|
215
|
+
const choice = await ctx.ui.select("Proactive dispatch injection?", [current ? `${on} (current)` : on, current ? off : `${off} (current)`]);
|
|
216
|
+
if (choice === undefined) return undefined;
|
|
217
|
+
return choice.startsWith("On");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const CONCURRENCY_STEPS = [1, 2, 3, 4, 6, 8, 12, 16];
|
|
221
|
+
const FIX_ROUNDS_STEPS = [0, 1, 2, 3, 5];
|
|
222
|
+
const IDLE_TIMEOUT_STEPS = [0, 30, 60, 90, 120, 180, 300, 600];
|
|
223
|
+
|
|
224
|
+
async function pickCount(
|
|
225
|
+
ctx: ExtensionCommandContext,
|
|
226
|
+
title: string,
|
|
227
|
+
steps: readonly number[],
|
|
228
|
+
current: number,
|
|
229
|
+
defaultValue: number,
|
|
230
|
+
): Promise<number | undefined> {
|
|
231
|
+
const values = [...new Set([...steps, current])].sort((a, b) => a - b);
|
|
232
|
+
const options = values.map((value) => {
|
|
233
|
+
const tags = [value === current ? "current" : "", value === defaultValue ? "default" : ""]
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
.join(", ");
|
|
236
|
+
return tags ? `${value} (${tags})` : String(value);
|
|
237
|
+
});
|
|
238
|
+
const choice = await ctx.ui.select(title, options);
|
|
239
|
+
return choice === undefined ? undefined : Number.parseInt(choice, 10);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function pickScope(ctx: ExtensionCommandContext, current: AgentScope): Promise<AgentScope | undefined> {
|
|
243
|
+
const labels: Record<AgentScope, string> = {
|
|
244
|
+
user: "user — built-in + ~/.pi/agent/agents (default)",
|
|
245
|
+
project: "project — built-in + nearest .pi/agents only",
|
|
246
|
+
both: "both — user agents, overridden by project agents",
|
|
247
|
+
};
|
|
248
|
+
const options = AGENT_SCOPE_VALUES.map((scope) =>
|
|
249
|
+
scope === current ? `${labels[scope]} (current)` : labels[scope],
|
|
250
|
+
);
|
|
251
|
+
const choice = await ctx.ui.select("Which agent directories to discover from?", options);
|
|
252
|
+
if (choice === undefined) return undefined;
|
|
253
|
+
return AGENT_SCOPE_VALUES.find((scope) => choice.startsWith(scope));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function keepAgentEntries<T>(record: Record<string, T>, enabled: readonly string[]): Record<string, T> {
|
|
257
|
+
const keep = new Set(enabled);
|
|
258
|
+
return Object.fromEntries(Object.entries(record).filter(([name]) => keep.has(name)));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function runFullSetup(ctx: ExtensionCommandContext, configPath: string, base: SubagentsConfig): Promise<void> {
|
|
262
|
+
const enabled = await pickEnabledAgents(ctx, base.enabledAgents);
|
|
263
|
+
if (enabled === undefined) return notifyCancelled(ctx);
|
|
264
|
+
|
|
265
|
+
let agentModels = keepAgentEntries(base.agentModels, enabled);
|
|
266
|
+
for (const agentName of enabled) {
|
|
267
|
+
const choice = await pickAgentModel(ctx, agentName, agentModels[agentName]);
|
|
268
|
+
if (choice === undefined) return notifyCancelled(ctx);
|
|
269
|
+
agentModels = applyAgentModelChoice(agentModels, agentName, choice);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const visionModel = await pickVisionModel(ctx, base.visionModel);
|
|
273
|
+
if (visionModel === undefined) return notifyCancelled(ctx);
|
|
274
|
+
const injection = await pickInjection(ctx, base.proactiveInjection);
|
|
275
|
+
if (injection === undefined) return notifyCancelled(ctx);
|
|
276
|
+
const scope = await pickScope(ctx, base.agentScope);
|
|
277
|
+
if (scope === undefined) return notifyCancelled(ctx);
|
|
278
|
+
const maxConcurrency = await pickCount(
|
|
279
|
+
ctx,
|
|
280
|
+
"Max sub-agents running at once?",
|
|
281
|
+
CONCURRENCY_STEPS,
|
|
282
|
+
base.maxConcurrency,
|
|
283
|
+
DEFAULT_MAX_CONCURRENCY,
|
|
284
|
+
);
|
|
285
|
+
if (maxConcurrency === undefined) return notifyCancelled(ctx);
|
|
286
|
+
const maxFixRounds = await pickCount(
|
|
287
|
+
ctx,
|
|
288
|
+
"Reviewer auto-fix rounds? (0 = main agent handles fixes)",
|
|
289
|
+
FIX_ROUNDS_STEPS,
|
|
290
|
+
base.maxFixRounds,
|
|
291
|
+
DEFAULT_MAX_FIX_ROUNDS,
|
|
292
|
+
);
|
|
293
|
+
if (maxFixRounds === undefined) return notifyCancelled(ctx);
|
|
294
|
+
const idleTimeoutSec = await pickCount(
|
|
295
|
+
ctx,
|
|
296
|
+
"Idle timeout in seconds? (0 = disabled)",
|
|
297
|
+
IDLE_TIMEOUT_STEPS,
|
|
298
|
+
base.idleTimeoutSec,
|
|
299
|
+
DEFAULT_IDLE_TIMEOUT_SEC,
|
|
300
|
+
);
|
|
301
|
+
if (idleTimeoutSec === undefined) return notifyCancelled(ctx);
|
|
302
|
+
|
|
303
|
+
const next: SubagentsConfig = {
|
|
304
|
+
enabledAgents: enabled,
|
|
305
|
+
agentModels,
|
|
306
|
+
// Full setup returns every agent to capability-aware Auto thinking.
|
|
307
|
+
agentThinkingLevels: {},
|
|
308
|
+
notifyOnReviewPass: base.notifyOnReviewPass,
|
|
309
|
+
maxResultLines: base.maxResultLines,
|
|
310
|
+
proactiveInjection: injection,
|
|
311
|
+
agentScope: scope,
|
|
312
|
+
maxConcurrency,
|
|
313
|
+
maxFixRounds,
|
|
314
|
+
idleTimeoutSec,
|
|
315
|
+
announcedFeatures: base.announcedFeatures,
|
|
316
|
+
};
|
|
317
|
+
if (visionModel !== CURRENT_MAIN_MODEL) next.visionModel = visionModel;
|
|
318
|
+
await saveConfig(next, configPath);
|
|
319
|
+
ctx.ui.notify(`pi-subagents configured with Auto thinking. Saved to ${configPath}`, "info");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function updateRuntimeSetting(
|
|
323
|
+
ctx: ExtensionCommandContext,
|
|
324
|
+
config: SubagentsConfig,
|
|
325
|
+
): Promise<SubagentsConfig | undefined> {
|
|
326
|
+
const choice = await ctx.ui.select("Runtime setting", [
|
|
327
|
+
"Proactive injection",
|
|
328
|
+
"Agent scope",
|
|
329
|
+
"Max concurrency",
|
|
330
|
+
"Reviewer auto-fix rounds",
|
|
331
|
+
"Idle timeout",
|
|
332
|
+
]);
|
|
333
|
+
if (choice === undefined) return undefined;
|
|
334
|
+
const next = { ...config };
|
|
335
|
+
if (choice.startsWith("Proactive")) {
|
|
336
|
+
const value = await pickInjection(ctx, config.proactiveInjection);
|
|
337
|
+
if (value === undefined) return undefined;
|
|
338
|
+
next.proactiveInjection = value;
|
|
339
|
+
} else if (choice.startsWith("Agent scope")) {
|
|
340
|
+
const value = await pickScope(ctx, config.agentScope);
|
|
341
|
+
if (value === undefined) return undefined;
|
|
342
|
+
next.agentScope = value;
|
|
343
|
+
} else if (choice.startsWith("Max concurrency")) {
|
|
344
|
+
const value = await pickCount(ctx, "Max sub-agents running at once?", CONCURRENCY_STEPS, config.maxConcurrency, DEFAULT_MAX_CONCURRENCY);
|
|
345
|
+
if (value === undefined) return undefined;
|
|
346
|
+
next.maxConcurrency = value;
|
|
347
|
+
} else if (choice.startsWith("Reviewer")) {
|
|
348
|
+
const value = await pickCount(ctx, "Reviewer auto-fix rounds?", FIX_ROUNDS_STEPS, config.maxFixRounds, DEFAULT_MAX_FIX_ROUNDS);
|
|
349
|
+
if (value === undefined) return undefined;
|
|
350
|
+
next.maxFixRounds = value;
|
|
351
|
+
} else {
|
|
352
|
+
const value = await pickCount(ctx, "Idle timeout in seconds?", IDLE_TIMEOUT_STEPS, config.idleTimeoutSec, DEFAULT_IDLE_TIMEOUT_SEC);
|
|
353
|
+
if (value === undefined) return undefined;
|
|
354
|
+
next.idleTimeoutSec = value;
|
|
355
|
+
}
|
|
356
|
+
return next;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function runMenu(ctx: ExtensionCommandContext, configPath: string, config: SubagentsConfig): Promise<void> {
|
|
360
|
+
const choice = await ctx.ui.select("pi-subagents settings", [
|
|
361
|
+
"Enable/disable agents",
|
|
362
|
+
"Configure an agent (model + thinking)",
|
|
363
|
+
"Change vision model",
|
|
364
|
+
"Runtime settings",
|
|
365
|
+
"Full re-setup",
|
|
366
|
+
]);
|
|
367
|
+
if (choice === undefined) return notifyCancelled(ctx);
|
|
368
|
+
if (choice.startsWith("Full")) return runFullSetup(ctx, configPath, config);
|
|
369
|
+
|
|
370
|
+
let next: SubagentsConfig = {
|
|
371
|
+
...config,
|
|
372
|
+
agentModels: { ...config.agentModels },
|
|
373
|
+
agentThinkingLevels: { ...config.agentThinkingLevels },
|
|
374
|
+
};
|
|
375
|
+
if (choice.startsWith("Enable")) {
|
|
376
|
+
const enabled = await pickEnabledAgents(ctx, config.enabledAgents);
|
|
377
|
+
if (enabled === undefined) return notifyCancelled(ctx);
|
|
378
|
+
next.enabledAgents = enabled;
|
|
379
|
+
next.agentModels = keepAgentEntries(next.agentModels, enabled);
|
|
380
|
+
next.agentThinkingLevels = keepAgentEntries(next.agentThinkingLevels, enabled);
|
|
381
|
+
} else if (choice.startsWith("Configure")) {
|
|
382
|
+
const agentName = await pickAgentToConfigure(ctx, next.enabledAgents);
|
|
383
|
+
if (agentName === undefined) return notifyCancelled(ctx);
|
|
384
|
+
const modelChoice = await pickAgentModel(
|
|
385
|
+
ctx,
|
|
386
|
+
agentName,
|
|
387
|
+
next.agentModels[agentName],
|
|
388
|
+
"cancels agent changes",
|
|
389
|
+
);
|
|
390
|
+
if (modelChoice === undefined) return notifyCancelled(ctx);
|
|
391
|
+
const model = effectiveModelForChoice(ctx, modelChoice);
|
|
392
|
+
const strength = await pickAgentStrength(
|
|
393
|
+
ctx,
|
|
394
|
+
agentName,
|
|
395
|
+
model,
|
|
396
|
+
next.agentThinkingLevels[agentName],
|
|
397
|
+
actualAgentThinkingDefault(ctx, next, agentName),
|
|
398
|
+
"cancels agent changes",
|
|
399
|
+
);
|
|
400
|
+
if (strength === undefined) return notifyCancelled(ctx);
|
|
401
|
+
next.agentModels = applyAgentModelChoice(next.agentModels, agentName, modelChoice);
|
|
402
|
+
if (strength === AUTO_THINKING) delete next.agentThinkingLevels[agentName];
|
|
403
|
+
else next.agentThinkingLevels[agentName] = strength;
|
|
404
|
+
} else if (choice.startsWith("Change vision")) {
|
|
405
|
+
const visionModel = await pickVisionModel(ctx, config.visionModel);
|
|
406
|
+
if (visionModel === undefined) return notifyCancelled(ctx);
|
|
407
|
+
if (visionModel === CURRENT_MAIN_MODEL) delete next.visionModel;
|
|
408
|
+
else next.visionModel = visionModel;
|
|
409
|
+
} else {
|
|
410
|
+
const updated = await updateRuntimeSetting(ctx, next);
|
|
411
|
+
if (updated === undefined) return notifyCancelled(ctx);
|
|
412
|
+
next = updated;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
await saveConfig(next, configPath);
|
|
416
|
+
ctx.ui.notify(`pi-subagents updated. Saved to ${configPath}`, "info");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function notifyCancelled(ctx: ExtensionCommandContext): void {
|
|
420
|
+
ctx.ui.notify("pi-subagents setup cancelled.", "info");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Entry point for the /subagents-setup command. */
|
|
424
|
+
export async function runSetup(ctx: ExtensionCommandContext, configPath: string = getConfigPath()): Promise<void> {
|
|
425
|
+
if (ctx.mode !== "tui") {
|
|
426
|
+
ctx.ui.notify("/subagents-setup requires Pi's interactive TUI.", "error");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const exists = await configExists(configPath);
|
|
431
|
+
const config = await loadConfig(configPath);
|
|
432
|
+
if (exists) await runMenu(ctx, configPath, config);
|
|
433
|
+
else await runFullSetup(ctx, configPath, { ...DEFAULT_CONFIG, enabledAgents: [...DEFAULT_ENABLED_AGENTS] });
|
|
434
|
+
} catch (error) {
|
|
435
|
+
ctx.ui.notify(`pi-subagents setup failed: ${errorMessage(error)}`, "error");
|
|
436
|
+
}
|
|
437
|
+
}
|