pi-subagents-lite 1.4.0 → 1.4.2
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 +6 -5
- package/package.json +5 -4
- package/src/agents/agent-discovery.ts +2 -2
- package/src/agents/agent-manager.ts +2 -1
- package/src/agents/agent-runner.ts +16 -9
- package/src/agents/agent-status.ts +2 -2
- package/src/agents/output-file.ts +87 -5
- package/src/agents/tool-execution.ts +8 -3
- package/src/config/config-store.ts +7 -0
- package/src/config/types.ts +1 -0
- package/src/events.ts +3 -1
- package/src/models/model-precedence.ts +2 -0
- package/src/spawn/spawn-coordinator.ts +37 -22
- package/src/spawn/worktree-validator.ts +9 -3
- package/src/ui/agent-widget.ts +1 -1
- package/src/ui/menu/helpers.ts +37 -7
- package/src/ui/menu/menu-concurrency.ts +29 -17
- package/src/ui/menu/menu-model-settings.ts +26 -22
- package/src/ui/menu/menu-spawn-options.ts +5 -0
- package/src/ui/menu/menu-spawn-wizard.ts +30 -15
- package/src/ui/menu/menu-system-prompt.ts +5 -1
- package/src/ui/menu/menu-widget-settings.ts +28 -0
- package/src/ui/menu/submenus/model-select.ts +3 -3
- package/src/ui/menu/wrappers/settings-list.ts +73 -105
- package/src/ui/result-viewer.ts +42 -10
- package/src/{models/model-selector.ts → ui/searchable-select.ts} +29 -29
package/src/ui/menu/helpers.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* helpers.ts — Shared helpers for menu modules:
|
|
3
3
|
* theme builders for SettingsList/SelectList, numeric validation,
|
|
4
|
-
* model-option building,
|
|
4
|
+
* model-option building, a swappable delegating component, and a
|
|
5
|
+
* searchable pick-list submenu factory.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
7
|
import type { Component, SettingsListTheme } from "@earendil-works/pi-tui";
|
|
8
|
-
import type
|
|
8
|
+
import { SearchableSelectDialog, type SelectOption } from "../searchable-select.js";
|
|
9
9
|
import { parseModelKey } from "../../utils.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Build
|
|
12
|
+
* Build SelectOption[] from raw "provider/model-id" strings.
|
|
13
13
|
* Includes "(inherits parent)" as the first option.
|
|
14
14
|
*/
|
|
15
|
-
export function buildModelOptions(rawOptions: string[]):
|
|
16
|
-
const items:
|
|
15
|
+
export function buildModelOptions(rawOptions: string[]): SelectOption[] {
|
|
16
|
+
const items: SelectOption[] = [
|
|
17
17
|
{ value: "(inherits parent)", label: "(inherits parent)", provider: "" },
|
|
18
18
|
];
|
|
19
19
|
|
|
@@ -33,7 +33,7 @@ export function buildSettingsListTheme(theme: { fg(color: string, text: string):
|
|
|
33
33
|
return {
|
|
34
34
|
label: (text, selected) => selected ? theme.fg("accent", text) : text,
|
|
35
35
|
value: (text, selected) => selected ? theme.fg("accent", text) : theme.fg("muted", text),
|
|
36
|
-
description: (text) => theme.fg("
|
|
36
|
+
description: (text) => theme.fg("dim", text),
|
|
37
37
|
// Use "→ " (2 chars) to match non-selected prefix " " (2 spaces)
|
|
38
38
|
// This prevents menu items from shifting left/right when cursor moves
|
|
39
39
|
cursor: theme.fg("accent", "→ "),
|
|
@@ -91,3 +91,33 @@ export function buildSelectListTheme(theme: { fg(color: string, text: string): s
|
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Build a searchable pick-list submenu backed by SearchableSelectDialog.
|
|
96
|
+
*
|
|
97
|
+
* Hides the delegator-forward-declaration dance shared by every menu that
|
|
98
|
+
* needs "type to filter, Enter to pick" over a flat option list
|
|
99
|
+
* (provider/model/type/worktree selection). onSelect may return a Component
|
|
100
|
+
* to chain into next (e.g. a numeric-input submenu); returning void leaves
|
|
101
|
+
* the submenu as-is so the caller can close it via done().
|
|
102
|
+
*/
|
|
103
|
+
export function createSearchableSelect(
|
|
104
|
+
items: SelectOption[],
|
|
105
|
+
callbacks: { onSelect: (value: string) => Component | void; onCancel: () => void },
|
|
106
|
+
theme: any,
|
|
107
|
+
): Component {
|
|
108
|
+
let delegator: ReturnType<typeof createDelegatingComponent>;
|
|
109
|
+
const selector = new SearchableSelectDialog(
|
|
110
|
+
items,
|
|
111
|
+
null,
|
|
112
|
+
{
|
|
113
|
+
onSelect: (value) => {
|
|
114
|
+
const next = callbacks.onSelect(value);
|
|
115
|
+
if (next) delegator.setActive(next);
|
|
116
|
+
},
|
|
117
|
+
onCancel: callbacks.onCancel,
|
|
118
|
+
},
|
|
119
|
+
theme,
|
|
120
|
+
);
|
|
121
|
+
delegator = createDelegatingComponent(selector);
|
|
122
|
+
return delegator;
|
|
123
|
+
}
|
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
|
|
12
12
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { SettingsList, SelectList, type SettingItem } from "@earendil-works/pi-tui";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
buildSettingsListTheme,
|
|
16
|
+
buildSelectListTheme,
|
|
17
|
+
buildModelOptions,
|
|
18
|
+
createDelegatingComponent,
|
|
19
|
+
createSearchableSelect,
|
|
20
|
+
} from "./helpers.js";
|
|
15
21
|
import { createNumericSubmenu } from "./submenus/numeric-input.js";
|
|
16
22
|
import { createConfirmSubmenu } from "./submenus/confirm.js";
|
|
17
23
|
import { SettingsListWrapper } from "./wrappers/settings-list.js";
|
|
18
24
|
import { getStore } from "../../shell.js";
|
|
25
|
+
import type { SelectOption } from "../searchable-select.js";
|
|
19
26
|
|
|
20
27
|
export async function showConcurrencySettingsMenu(
|
|
21
28
|
ctx: ExtensionCommandContext,
|
|
@@ -52,28 +59,28 @@ export async function showConcurrencySettingsMenu(
|
|
|
52
59
|
return delegator;
|
|
53
60
|
};
|
|
54
61
|
|
|
55
|
-
// Submenu factory: pick
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
// Submenu factory: searchable-pick an option, then enter a numeric value.
|
|
63
|
+
// Used for both per-provider and per-model limits; items differ by caller.
|
|
64
|
+
const addPickThenValueSubmenu = (
|
|
65
|
+
items: SelectOption[],
|
|
58
66
|
onPick: (key: string, parsed: number) => void,
|
|
59
|
-
): SettingItem["submenu"] => (_currentValue, subDone) =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
): SettingItem["submenu"] => (_currentValue, subDone) =>
|
|
68
|
+
createSearchableSelect(
|
|
69
|
+
items,
|
|
70
|
+
{
|
|
71
|
+
onSelect: (key) =>
|
|
72
|
+
createNumericSubmenu(ctx, { min: 1 }, (parsed) => onPick(key, parsed))("1", subDone),
|
|
73
|
+
onCancel: () => subDone(),
|
|
74
|
+
},
|
|
75
|
+
theme,
|
|
63
76
|
);
|
|
64
|
-
const delegator = createDelegatingComponent(list);
|
|
65
|
-
list.onSelect = (item) => {
|
|
66
|
-
delegator.setActive(createNumericSubmenu(ctx, { min: 1 }, (parsed) => onPick(item.value, parsed))("1", subDone));
|
|
67
|
-
};
|
|
68
|
-
list.onCancel = () => subDone();
|
|
69
|
-
return delegator;
|
|
70
|
-
};
|
|
71
77
|
|
|
72
78
|
// Global default
|
|
73
79
|
items.push({
|
|
74
80
|
id: "defaultConcurrency",
|
|
75
81
|
label: "Default concurrency limit",
|
|
76
82
|
currentValue: String(store.concurrency.default),
|
|
83
|
+
description: "Concurrent agent slots when no per-provider or per-model limit applies.",
|
|
77
84
|
submenu: createNumericSubmenu(ctx, (parsed) => {
|
|
78
85
|
store.mutate.concurrency.setDefault(parsed);
|
|
79
86
|
ctx.ui.notify(`Default concurrency set to ${parsed}`, "info");
|
|
@@ -90,6 +97,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
90
97
|
id: `provider:${provider}`,
|
|
91
98
|
label: provider,
|
|
92
99
|
currentValue: `${limit} slots`,
|
|
100
|
+
description: `Concurrent slots reserved for agents using the ${provider} provider.`,
|
|
93
101
|
submenu: editOrRemoveSubmenu(
|
|
94
102
|
limit,
|
|
95
103
|
(parsed) => {
|
|
@@ -111,7 +119,8 @@ export async function showConcurrencySettingsMenu(
|
|
|
111
119
|
id: "addProviderLimit",
|
|
112
120
|
label: "Add per-provider limit...",
|
|
113
121
|
currentValue: "",
|
|
114
|
-
|
|
122
|
+
description: "Cap how many agents run at once for a single provider.",
|
|
123
|
+
submenu: addPickThenValueSubmenu(providers.map((o) => ({ value: o, label: o })), (provider, parsed) => {
|
|
115
124
|
store.mutate.concurrency.setProvider(provider, parsed);
|
|
116
125
|
ctx.ui.notify(`${provider} concurrency set to ${parsed}`, "info");
|
|
117
126
|
}),
|
|
@@ -128,6 +137,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
128
137
|
id: `model:${modelKey}`,
|
|
129
138
|
label: modelKey,
|
|
130
139
|
currentValue: `${limit} slots`,
|
|
140
|
+
description: `Concurrent slots reserved for agents using the ${modelKey} model.`,
|
|
131
141
|
submenu: editOrRemoveSubmenu(
|
|
132
142
|
limit,
|
|
133
143
|
(parsed) => {
|
|
@@ -149,7 +159,8 @@ export async function showConcurrencySettingsMenu(
|
|
|
149
159
|
id: "addModelLimit",
|
|
150
160
|
label: "Add per-model limit...",
|
|
151
161
|
currentValue: "",
|
|
152
|
-
|
|
162
|
+
description: "Cap how many agents run at once for a single model.",
|
|
163
|
+
submenu: addPickThenValueSubmenu(buildModelOptions(modelOptions), (modelKey, parsed) => {
|
|
153
164
|
store.mutate.concurrency.setModel(modelKey, parsed);
|
|
154
165
|
ctx.ui.notify(`${modelKey} concurrency set to ${parsed}`, "info");
|
|
155
166
|
}),
|
|
@@ -162,6 +173,7 @@ export async function showConcurrencySettingsMenu(
|
|
|
162
173
|
id: "resetAll",
|
|
163
174
|
label: "Reset all to defaults",
|
|
164
175
|
currentValue: "",
|
|
176
|
+
description: "Restore the default limit and remove all per-provider and per-model limits.",
|
|
165
177
|
submenu: createConfirmSubmenu({
|
|
166
178
|
message: "Reset all concurrency limits to defaults?",
|
|
167
179
|
theme,
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { SettingsList,
|
|
13
|
+
import { SettingsList, type SettingItem } from "@earendil-works/pi-tui";
|
|
14
14
|
import { getAgentConfig, getAllTypes } from "../../agents/agent-types.js";
|
|
15
15
|
import { CONFIG_AGENT_NON_MODEL_KEYS } from "../../config/types.js";
|
|
16
|
-
import { buildSettingsListTheme,
|
|
16
|
+
import { buildSettingsListTheme, createSearchableSelect } from "./helpers.js";
|
|
17
17
|
import { createModelSelectSubmenu } from "./submenus/model-select.js";
|
|
18
18
|
import { createConfirmSubmenu } from "./submenus/confirm.js";
|
|
19
19
|
import { SettingsListWrapper } from "./wrappers/settings-list.js";
|
|
@@ -71,6 +71,7 @@ export async function showModelSettingsMenu(
|
|
|
71
71
|
id: "defaultModel",
|
|
72
72
|
label: "Global default model",
|
|
73
73
|
currentValue: globalDisplayValue,
|
|
74
|
+
description: "Model used when no per-type override or frontmatter model applies.",
|
|
74
75
|
submenu: createModelSelectSubmenu({
|
|
75
76
|
modelOptions,
|
|
76
77
|
showClear: false,
|
|
@@ -105,6 +106,7 @@ export async function showModelSettingsMenu(
|
|
|
105
106
|
id: `type:${typeName}`,
|
|
106
107
|
label: typeName,
|
|
107
108
|
currentValue: `${frontmatterHint}${displayModel}`,
|
|
109
|
+
description: `Per-type model override for the ${typeName} agent type.`,
|
|
108
110
|
submenu: createModelSelectSubmenu({
|
|
109
111
|
modelOptions,
|
|
110
112
|
showClear: hasPerm,
|
|
@@ -121,26 +123,26 @@ export async function showModelSettingsMenu(
|
|
|
121
123
|
id: "overrideType",
|
|
122
124
|
label: "Override another type...",
|
|
123
125
|
currentValue: "",
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
description: "Add a model override for an agent type that currently inherits.",
|
|
127
|
+
submenu: (_currentValue, subDone) =>
|
|
128
|
+
createSearchableSelect(
|
|
129
|
+
nonOverridden.map(e => ({ value: e.typeName, label: e.typeName })),
|
|
130
|
+
{
|
|
131
|
+
onSelect: (typeName) => {
|
|
132
|
+
const entry = nonOverridden.find(e => e.typeName === typeName)!;
|
|
133
|
+
// Delegate to createModelSelectSubmenu for the 2-step model flow
|
|
134
|
+
const modelSubmenu = createModelSelectSubmenu({
|
|
135
|
+
modelOptions,
|
|
136
|
+
showClear: false,
|
|
137
|
+
theme,
|
|
138
|
+
onSelect: modelOverrideOnSelect(entry.typeName, entry.typeName),
|
|
139
|
+
});
|
|
140
|
+
return modelSubmenu(entry.effectiveModel, subDone);
|
|
141
|
+
},
|
|
142
|
+
onCancel: () => subDone(),
|
|
143
|
+
},
|
|
144
|
+
theme,
|
|
145
|
+
),
|
|
144
146
|
});
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -153,6 +155,7 @@ export async function showModelSettingsMenu(
|
|
|
153
155
|
id: "clearSession",
|
|
154
156
|
label: "Clear session overrides",
|
|
155
157
|
currentValue: "",
|
|
158
|
+
description: "Discard model overrides set only for this session.",
|
|
156
159
|
submenu: createConfirmSubmenu({
|
|
157
160
|
message: "Clear all session overrides?",
|
|
158
161
|
theme,
|
|
@@ -169,6 +172,7 @@ export async function showModelSettingsMenu(
|
|
|
169
172
|
id: "clearAll",
|
|
170
173
|
label: "Clear all overrides",
|
|
171
174
|
currentValue: "",
|
|
175
|
+
description: "Discard all model overrides (config and session).",
|
|
172
176
|
submenu: createConfirmSubmenu({
|
|
173
177
|
message: "Clear all model overrides?",
|
|
174
178
|
theme,
|
|
@@ -27,6 +27,7 @@ export async function showSpawnOptionsMenu(ctx: ExtensionCommandContext): Promis
|
|
|
27
27
|
label: "Force background",
|
|
28
28
|
currentValue: store.agent.forceBackground ? "ON" : "OFF",
|
|
29
29
|
values: ["ON", "OFF"],
|
|
30
|
+
description: "Spawn every agent in the background by default (no foreground wait).",
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
id: "graceTurns",
|
|
@@ -36,6 +37,7 @@ export async function showSpawnOptionsMenu(ctx: ExtensionCommandContext): Promis
|
|
|
36
37
|
store.mutate.agent.setGraceTurns(parsed);
|
|
37
38
|
ctx.ui.notify(`Grace turns set to ${parsed}`, "info");
|
|
38
39
|
}),
|
|
40
|
+
description: "Extra turns after the soft turn limit before a hard abort.",
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
id: "defaultMaxTurns",
|
|
@@ -48,18 +50,21 @@ export async function showSpawnOptionsMenu(ctx: ExtensionCommandContext): Promis
|
|
|
48
50
|
store.mutate.agent.setDefaultMaxTurns(undefined);
|
|
49
51
|
ctx.ui.notify("Default max turns cleared", "info");
|
|
50
52
|
}),
|
|
53
|
+
description: "Soft turn limit; agent is steered here, then hard-aborts after grace turns. Blank = unlimited.",
|
|
51
54
|
},
|
|
52
55
|
{
|
|
53
56
|
id: "defaultThinking",
|
|
54
57
|
label: "Default thinking level",
|
|
55
58
|
currentValue: store.agent.defaultThinking ?? "inherit",
|
|
56
59
|
values: ["off", "minimal", "low", "medium", "high", "xhigh", "inherit"],
|
|
60
|
+
description: "Thinking level applied when agent frontmatter omits one.",
|
|
57
61
|
},
|
|
58
62
|
{
|
|
59
63
|
id: "disableDefaultAgents",
|
|
60
64
|
label: "Disable default agents",
|
|
61
65
|
currentValue: store.agent.disableDefaultAgents ? "ON" : "OFF",
|
|
62
66
|
values: ["ON", "OFF"],
|
|
67
|
+
description: "Skip auto-loading built-in agent types next session; only .pi/agents types load.",
|
|
63
68
|
},
|
|
64
69
|
];
|
|
65
70
|
|
|
@@ -13,7 +13,7 @@ import { SettingsList, SelectList, type SettingItem } from "@earendil-works/pi-t
|
|
|
13
13
|
import type { ThinkingLevel } from "../../types.js";
|
|
14
14
|
import { getAgentConfig, getAvailableTypes, resolveType, discoverNewAgents } from "../../agents/agent-types.js";
|
|
15
15
|
import { findModelInRegistry } from "../../utils.js";
|
|
16
|
-
import { buildSettingsListTheme, buildSelectListTheme } from "./helpers.js";
|
|
16
|
+
import { buildSettingsListTheme, buildSelectListTheme, createSearchableSelect } from "./helpers.js";
|
|
17
17
|
import { DEFAULT_GRACE_TURNS } from "../../config/config-io.js";
|
|
18
18
|
import { createModelSelectSubmenu } from "./submenus/model-select.js";
|
|
19
19
|
import { createNumericSubmenu, createInputSubmenu } from "./submenus/numeric-input.js";
|
|
@@ -146,6 +146,7 @@ export async function showSpawnAgentMenu(
|
|
|
146
146
|
id: t,
|
|
147
147
|
label: t,
|
|
148
148
|
currentValue: t,
|
|
149
|
+
description: getAgentConfig(t)?.description ?? "Agent type",
|
|
149
150
|
submenu: (_v: string, _subDone: (value?: string) => void) => {
|
|
150
151
|
done(t);
|
|
151
152
|
return undefined as any;
|
|
@@ -214,6 +215,7 @@ export async function showSpawnAgentMenu(
|
|
|
214
215
|
id: "spawn",
|
|
215
216
|
label: "Spawn",
|
|
216
217
|
currentValue: "",
|
|
218
|
+
description: "Spawn the agent with current settings",
|
|
217
219
|
submenu: (_v, done) => {
|
|
218
220
|
const gtItem = items.find(i => i.id === "graceTurns");
|
|
219
221
|
const bgItem = items.find(i => i.id === "background");
|
|
@@ -304,6 +306,7 @@ export async function showSpawnAgentMenu(
|
|
|
304
306
|
id: "model",
|
|
305
307
|
label: "Model",
|
|
306
308
|
currentValue: displayModel,
|
|
309
|
+
description: "Override the default model for this agent",
|
|
307
310
|
submenu: createModelSelectSubmenu({
|
|
308
311
|
modelOptions,
|
|
309
312
|
showClear: false,
|
|
@@ -317,6 +320,7 @@ export async function showSpawnAgentMenu(
|
|
|
317
320
|
id: "background",
|
|
318
321
|
label: "Background",
|
|
319
322
|
currentValue: currentBackground ? "ON" : "OFF",
|
|
323
|
+
description: "Run the agent in the background",
|
|
320
324
|
values: ["ON", "OFF"],
|
|
321
325
|
},
|
|
322
326
|
...(inGitRepo
|
|
@@ -324,28 +328,33 @@ export async function showSpawnAgentMenu(
|
|
|
324
328
|
id: "worktree",
|
|
325
329
|
label: "Worktree",
|
|
326
330
|
currentValue: currentWorktreeLabel,
|
|
331
|
+
description: "Run in a linked git worktree instead of parent cwd",
|
|
327
332
|
submenu: (_v: string, done: (v?: string) => void) => {
|
|
328
333
|
const pickerItems = [
|
|
329
334
|
{ value: "Inherits parent cwd", label: "Inherits parent cwd" },
|
|
330
335
|
...worktrees.map(wt => {
|
|
331
336
|
const branchLabel = wt.isDetached ? "detached" : (wt.branch ?? "detached");
|
|
332
337
|
const truncPath = truncatePath(wt.path);
|
|
333
|
-
return { value: wt.path, label:
|
|
338
|
+
return { value: wt.path, label: truncPath, provider: branchLabel };
|
|
334
339
|
}),
|
|
335
340
|
];
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
341
|
+
return createSearchableSelect(
|
|
342
|
+
pickerItems,
|
|
343
|
+
{
|
|
344
|
+
onSelect: (value) => {
|
|
345
|
+
if (value === "Inherits parent cwd") {
|
|
346
|
+
currentWorktreePath = undefined;
|
|
347
|
+
done("Inherits parent cwd");
|
|
348
|
+
} else {
|
|
349
|
+
const wt = worktrees.find(w => w.path === value);
|
|
350
|
+
currentWorktreePath = wt?.path;
|
|
351
|
+
done(wt?.branch ?? "detached");
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
onCancel: () => done(),
|
|
355
|
+
},
|
|
356
|
+
theme,
|
|
357
|
+
);
|
|
349
358
|
},
|
|
350
359
|
} as SettingItem]
|
|
351
360
|
: []),
|
|
@@ -353,24 +362,28 @@ export async function showSpawnAgentMenu(
|
|
|
353
362
|
id: "thinkingLevel",
|
|
354
363
|
label: "Thinking level",
|
|
355
364
|
currentValue: currentThinking ?? "inherit",
|
|
365
|
+
description: "Set the reasoning effort level",
|
|
356
366
|
values: [...THINKING_LEVELS, "inherit"],
|
|
357
367
|
},
|
|
358
368
|
{
|
|
359
369
|
id: "maxTokens",
|
|
360
370
|
label: "Max tokens",
|
|
361
371
|
currentValue: fmtNum(currentMaxTokens),
|
|
372
|
+
description: "Maximum tokens the agent can consume",
|
|
362
373
|
submenu: createNumericSubmenu(ctx, (parsed) => { currentMaxTokens = parsed; }, () => { currentMaxTokens = undefined; }),
|
|
363
374
|
},
|
|
364
375
|
{
|
|
365
376
|
id: "maxTurns",
|
|
366
377
|
label: "Max turns",
|
|
367
378
|
currentValue: fmtNum(currentMaxTurns),
|
|
379
|
+
description: "Maximum conversation turns before hard stop",
|
|
368
380
|
submenu: createNumericSubmenu(ctx, (parsed) => { currentMaxTurns = parsed; }, () => { currentMaxTurns = undefined; }),
|
|
369
381
|
},
|
|
370
382
|
{
|
|
371
383
|
id: "graceTurns",
|
|
372
384
|
label: "Grace turns",
|
|
373
385
|
currentValue: String(currentGraceTurns),
|
|
386
|
+
description: "Extra turns after soft limit before abort",
|
|
374
387
|
submenu: createNumericSubmenu(ctx, { min: 0, default: DEFAULT_GRACE_TURNS }, (parsed) => { currentGraceTurns = parsed; }),
|
|
375
388
|
},
|
|
376
389
|
{ id: "__sep__", label: " ", currentValue: "" },
|
|
@@ -378,12 +391,14 @@ export async function showSpawnAgentMenu(
|
|
|
378
391
|
id: "description",
|
|
379
392
|
label: "Description",
|
|
380
393
|
currentValue: currentDescription,
|
|
394
|
+
description: "Short label shown in the agents list",
|
|
381
395
|
submenu: createInputSubmenu(ctx),
|
|
382
396
|
},
|
|
383
397
|
{
|
|
384
398
|
id: "prompt",
|
|
385
399
|
label: "Prompt",
|
|
386
400
|
currentValue: prompt,
|
|
401
|
+
description: "The user message sent to the agent",
|
|
387
402
|
submenu: createInputSubmenu(ctx, { required: true }),
|
|
388
403
|
}
|
|
389
404
|
];
|
|
@@ -29,6 +29,7 @@ export async function showSystemPromptMenu(ctx: ExtensionCommandContext): Promis
|
|
|
29
29
|
label: "System prompt mode",
|
|
30
30
|
currentValue: store.agent.systemPromptMode,
|
|
31
31
|
values: ["replace", "inherit", "custom"],
|
|
32
|
+
description: "How the subagent system prompt is built: replace, inherit, or custom.",
|
|
32
33
|
},
|
|
33
34
|
];
|
|
34
35
|
|
|
@@ -39,6 +40,7 @@ export async function showSystemPromptMenu(ctx: ExtensionCommandContext): Promis
|
|
|
39
40
|
label: "Create prompt file",
|
|
40
41
|
currentValue: CUSTOM_PROMPT_PATH,
|
|
41
42
|
values: ["Create"],
|
|
43
|
+
description: `Create ${CUSTOM_PROMPT_PATH} with a starter template for custom mode.`,
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -48,24 +50,26 @@ export async function showSystemPromptMenu(ctx: ExtensionCommandContext): Promis
|
|
|
48
50
|
label: "Include AGENTS.md",
|
|
49
51
|
currentValue: store.agent.includeContextFiles ? "ON" : "OFF",
|
|
50
52
|
values: ["ON", "OFF"],
|
|
53
|
+
description: "Load project and ~/.pi/agent AGENTS.md as shared <project_context>.",
|
|
51
54
|
},
|
|
52
55
|
{
|
|
53
56
|
id: "loadSkillsImplicitly",
|
|
54
57
|
label: "Load skills implicitly",
|
|
55
58
|
currentValue: store.agent.loadSkillsImplicitly ? "ON" : "OFF",
|
|
56
59
|
values: ["ON", "OFF"],
|
|
60
|
+
description: "Give new agents all skills when frontmatter omits the field.",
|
|
57
61
|
},
|
|
58
62
|
{
|
|
59
63
|
id: "loadExtensionsImplicitly",
|
|
60
64
|
label: "Load extensions implicitly",
|
|
61
65
|
currentValue: store.agent.loadExtensionsImplicitly ? "ON" : "OFF",
|
|
62
66
|
values: ["ON", "OFF"],
|
|
67
|
+
description: "Give new agents all extensions when frontmatter omits the field.",
|
|
63
68
|
},
|
|
64
69
|
);
|
|
65
70
|
|
|
66
71
|
return items;
|
|
67
72
|
};
|
|
68
|
-
|
|
69
73
|
let items = buildItems();
|
|
70
74
|
let rebuild: ((newItems: SettingItem[]) => void) | null = null;
|
|
71
75
|
|
|
@@ -54,15 +54,29 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
54
54
|
store.mutate.widget.setShortcut(newValue === "ON");
|
|
55
55
|
ctx.ui.notify(`Ctrl+o shortcut ${newValue}`, "info");
|
|
56
56
|
break;
|
|
57
|
+
case "thinkingBuffer":
|
|
58
|
+
store.mutate.agent.setOutputThinkingBufferSize(newValue === "OFF" ? 0 : Number(newValue));
|
|
59
|
+
ctx.ui.notify(`Thinking buffer ${newValue}`, "info");
|
|
60
|
+
break;
|
|
57
61
|
}
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
await ctx.ui.custom((_tui, theme, _kb, done) => {
|
|
65
|
+
const statDescriptions: Record<string, string> = {
|
|
66
|
+
showTools: "Show tool count (🛠 ) in the widget.",
|
|
67
|
+
showTurns: "Show turn count (⟳ ) in the widget.",
|
|
68
|
+
showInput: "Show input tokens (↑) in the widget.",
|
|
69
|
+
showOutput: "Show output tokens (↓) in the widget.",
|
|
70
|
+
showContext: "Show context-fill percent (%) in the widget.",
|
|
71
|
+
showCost: "Show dollar cost ($) in the widget.",
|
|
72
|
+
showTime: "Show elapsed time in the widget.",
|
|
73
|
+
};
|
|
61
74
|
const statItems: SettingItem[] = [...statConfig.entries()].map(([id, cfg]) => ({
|
|
62
75
|
id,
|
|
63
76
|
label: cfg.label,
|
|
64
77
|
currentValue: cfg.get() ? "ON" : "OFF",
|
|
65
78
|
values: ["ON", "OFF"],
|
|
79
|
+
description: statDescriptions[id],
|
|
66
80
|
}));
|
|
67
81
|
|
|
68
82
|
const items: SettingItem[] = [
|
|
@@ -71,6 +85,7 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
71
85
|
label: "Force compact mode",
|
|
72
86
|
currentValue: store.agent.widgetCompact ? "ON" : "OFF",
|
|
73
87
|
values: ["ON", "OFF"],
|
|
88
|
+
description: "Force compact widget mode regardless of ctrl+o state.",
|
|
74
89
|
},
|
|
75
90
|
{
|
|
76
91
|
id: "maxLines",
|
|
@@ -80,6 +95,7 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
80
95
|
store.mutate.widget.setMaxLines(parsed);
|
|
81
96
|
ctx.ui.notify(`Max lines (full) set to ${parsed}`, "info");
|
|
82
97
|
}),
|
|
98
|
+
description: "Max body lines in full widget mode (excluding heading).",
|
|
83
99
|
},
|
|
84
100
|
{
|
|
85
101
|
id: "descLengthFull",
|
|
@@ -89,6 +105,7 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
89
105
|
store.mutate.widget.setDescLengthFull(parsed);
|
|
90
106
|
ctx.ui.notify(`Description length (full) set to ${parsed}`, "info");
|
|
91
107
|
}),
|
|
108
|
+
description: "Max description length shown in full widget mode.",
|
|
92
109
|
},
|
|
93
110
|
{
|
|
94
111
|
id: "maxLinesCompact",
|
|
@@ -98,6 +115,7 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
98
115
|
store.mutate.widget.setMaxLinesCompact(parsed);
|
|
99
116
|
ctx.ui.notify(`Max lines (compact) set to ${parsed}`, "info");
|
|
100
117
|
}),
|
|
118
|
+
description: "Max body lines in compact widget mode.",
|
|
101
119
|
},
|
|
102
120
|
{
|
|
103
121
|
id: "descLengthCompact",
|
|
@@ -107,12 +125,21 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
107
125
|
store.mutate.widget.setDescLengthCompact(parsed);
|
|
108
126
|
ctx.ui.notify(`Description length (compact) set to ${parsed}`, "info");
|
|
109
127
|
}),
|
|
128
|
+
description: "Max description length shown in compact widget mode.",
|
|
110
129
|
},
|
|
111
130
|
{
|
|
112
131
|
id: "shortcut",
|
|
113
132
|
label: "Ctrl+o shortcut",
|
|
114
133
|
currentValue: store.agent.widgetShortcut ? "ON" : "OFF",
|
|
115
134
|
values: ["ON", "OFF"],
|
|
135
|
+
description: "When ON, ctrl+o toggles compact mode; when OFF, compact is set manually.",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "thinkingBuffer",
|
|
139
|
+
label: "Log file thinking buffer",
|
|
140
|
+
currentValue: store.agent.outputThinkingBufferSize === 0 ? "OFF" : String(store.agent.outputThinkingBufferSize),
|
|
141
|
+
values: ["OFF", "80", "200", "500", "1000"],
|
|
142
|
+
description: "Controls log file thinking buffering in chars. OFF = only at turn end, 80 = flush after 80 chars.",
|
|
116
143
|
},
|
|
117
144
|
{ id: "__sep__", label: " ", currentValue: "" },
|
|
118
145
|
{
|
|
@@ -121,6 +148,7 @@ export async function showWidgetSettingsMenu(ctx: ExtensionCommandContext): Prom
|
|
|
121
148
|
currentValue: "→",
|
|
122
149
|
submenu: (_currentValue, done2) =>
|
|
123
150
|
new SettingsList(statItems, 7, buildSettingsListTheme(theme), onChange, () => done2()),
|
|
151
|
+
description: "Toggle which usage stats appear in the widget.",
|
|
124
152
|
},
|
|
125
153
|
];
|
|
126
154
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* model-select-submenu.ts — 2-step model override submenu.
|
|
3
3
|
*
|
|
4
4
|
* Step 1: SelectList with override mode (session/permanent/clear)
|
|
5
|
-
* Step 2 (if session/permanent):
|
|
5
|
+
* Step 2 (if session/permanent): SearchableSelectDialog for model selection
|
|
6
6
|
*
|
|
7
7
|
* The submenu factory must be created inside ctx.ui.custom to capture the theme.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { SelectList, type Component } from "@earendil-works/pi-tui";
|
|
11
|
-
import {
|
|
11
|
+
import { SearchableSelectDialog, type SelectOption } from "../../../ui/searchable-select.js";
|
|
12
12
|
import { buildModelOptions, buildSelectListTheme, createDelegatingComponent } from "../helpers.js";
|
|
13
13
|
|
|
14
14
|
export interface ModelSelectSubmenuOptions {
|
|
@@ -52,7 +52,7 @@ export function createModelSelectSubmenu(
|
|
|
52
52
|
modeList.onCancel = () => done();
|
|
53
53
|
|
|
54
54
|
const modelOpts = buildModelOptions(options.modelOptions);
|
|
55
|
-
const modelSelector = new
|
|
55
|
+
const modelSelector = new SearchableSelectDialog(
|
|
56
56
|
modelOpts,
|
|
57
57
|
_currentValue === "(inherits parent)" ? null : _currentValue,
|
|
58
58
|
{
|