@oh-my-pi/pi-coding-agent 1.337.1 → 1.340.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/CHANGELOG.md +32 -0
- package/package.json +3 -3
- package/src/cli/args.ts +14 -8
- package/src/core/export-html/index.ts +48 -15
- package/src/core/export-html/template.html +3 -11
- package/src/core/mcp/client.ts +43 -16
- package/src/core/mcp/config.ts +152 -6
- package/src/core/mcp/index.ts +6 -2
- package/src/core/mcp/loader.ts +30 -3
- package/src/core/mcp/manager.ts +69 -10
- package/src/core/mcp/types.ts +9 -3
- package/src/core/sdk.ts +19 -3
- package/src/core/settings-manager.ts +34 -0
- package/src/modes/interactive/components/settings-defs.ts +229 -0
- package/src/modes/interactive/components/settings-selector.ts +156 -234
- package/src/modes/interactive/interactive-mode.ts +58 -75
- package/src/utils/shell.ts +12 -4
- package/src/utils/tools-manager.ts +5 -14
- package/src/core/export-html/vendor/highlight.min.js +0 -1213
- package/src/core/export-html/vendor/marked.min.js +0 -6
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import {
|
|
3
3
|
Container,
|
|
4
|
-
getCapabilities,
|
|
5
4
|
isArrowLeft,
|
|
6
5
|
isArrowRight,
|
|
7
6
|
isEscape,
|
|
@@ -17,55 +16,11 @@ import {
|
|
|
17
16
|
type TabBarTheme,
|
|
18
17
|
Text,
|
|
19
18
|
} from "@oh-my-pi/pi-tui";
|
|
19
|
+
import type { SettingsManager } from "../../../core/settings-manager.js";
|
|
20
20
|
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme.js";
|
|
21
21
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
22
22
|
import { PluginSettingsComponent } from "./plugin-settings.js";
|
|
23
|
-
|
|
24
|
-
const THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {
|
|
25
|
-
off: "No reasoning",
|
|
26
|
-
minimal: "Very brief reasoning (~1k tokens)",
|
|
27
|
-
low: "Light reasoning (~2k tokens)",
|
|
28
|
-
medium: "Moderate reasoning (~8k tokens)",
|
|
29
|
-
high: "Deep reasoning (~16k tokens)",
|
|
30
|
-
xhigh: "Maximum reasoning (~32k tokens)",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export interface ExaToolsConfig {
|
|
34
|
-
enabled: boolean;
|
|
35
|
-
enableSearch: boolean;
|
|
36
|
-
enableLinkedin: boolean;
|
|
37
|
-
enableCompany: boolean;
|
|
38
|
-
enableResearcher: boolean;
|
|
39
|
-
enableWebsets: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface SettingsConfig {
|
|
43
|
-
autoCompact: boolean;
|
|
44
|
-
showImages: boolean;
|
|
45
|
-
queueMode: "all" | "one-at-a-time";
|
|
46
|
-
thinkingLevel: ThinkingLevel;
|
|
47
|
-
availableThinkingLevels: ThinkingLevel[];
|
|
48
|
-
currentTheme: string;
|
|
49
|
-
availableThemes: string[];
|
|
50
|
-
hideThinkingBlock: boolean;
|
|
51
|
-
collapseChangelog: boolean;
|
|
52
|
-
cwd: string;
|
|
53
|
-
exa: ExaToolsConfig;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface SettingsCallbacks {
|
|
57
|
-
onAutoCompactChange: (enabled: boolean) => void;
|
|
58
|
-
onShowImagesChange: (enabled: boolean) => void;
|
|
59
|
-
onQueueModeChange: (mode: "all" | "one-at-a-time") => void;
|
|
60
|
-
onThinkingLevelChange: (level: ThinkingLevel) => void;
|
|
61
|
-
onThemeChange: (theme: string) => void;
|
|
62
|
-
onThemePreview?: (theme: string) => void;
|
|
63
|
-
onHideThinkingBlockChange: (hidden: boolean) => void;
|
|
64
|
-
onCollapseChangelogChange: (collapsed: boolean) => void;
|
|
65
|
-
onPluginsChanged?: () => void;
|
|
66
|
-
onExaSettingChange: (setting: keyof ExaToolsConfig, enabled: boolean) => void;
|
|
67
|
-
onCancel: () => void;
|
|
68
|
-
}
|
|
23
|
+
import { getSettingsForTab, type SettingDef } from "./settings-defs.js";
|
|
69
24
|
|
|
70
25
|
function getTabBarTheme(): TabBarTheme {
|
|
71
26
|
return {
|
|
@@ -146,8 +101,41 @@ const SETTINGS_TABS: Tab[] = [
|
|
|
146
101
|
{ id: "plugins", label: "Plugins" },
|
|
147
102
|
];
|
|
148
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Dynamic context for settings that need runtime data.
|
|
106
|
+
* Some settings (like thinking level) are managed by the session, not SettingsManager.
|
|
107
|
+
*/
|
|
108
|
+
export interface SettingsRuntimeContext {
|
|
109
|
+
/** Available thinking levels (from session) */
|
|
110
|
+
availableThinkingLevels: ThinkingLevel[];
|
|
111
|
+
/** Current thinking level (from session) */
|
|
112
|
+
thinkingLevel: ThinkingLevel;
|
|
113
|
+
/** Available themes */
|
|
114
|
+
availableThemes: string[];
|
|
115
|
+
/** Working directory for plugins tab */
|
|
116
|
+
cwd: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Callback when any setting changes.
|
|
121
|
+
* The handler should dispatch based on settingId.
|
|
122
|
+
*/
|
|
123
|
+
export type SettingChangeHandler = (settingId: string, newValue: string | boolean) => void;
|
|
124
|
+
|
|
125
|
+
export interface SettingsCallbacks {
|
|
126
|
+
/** Called when any setting value changes */
|
|
127
|
+
onChange: SettingChangeHandler;
|
|
128
|
+
/** Called for theme preview while browsing */
|
|
129
|
+
onThemePreview?: (theme: string) => void;
|
|
130
|
+
/** Called when plugins change */
|
|
131
|
+
onPluginsChanged?: () => void;
|
|
132
|
+
/** Called when settings panel is closed */
|
|
133
|
+
onCancel: () => void;
|
|
134
|
+
}
|
|
135
|
+
|
|
149
136
|
/**
|
|
150
137
|
* Main tabbed settings selector component.
|
|
138
|
+
* Uses declarative settings definitions from settings-defs.ts.
|
|
151
139
|
*/
|
|
152
140
|
export class SettingsSelectorComponent extends Container {
|
|
153
141
|
private tabBar: TabBar;
|
|
@@ -155,13 +143,15 @@ export class SettingsSelectorComponent extends Container {
|
|
|
155
143
|
private currentSubmenu: Container | null = null;
|
|
156
144
|
private pluginComponent: PluginSettingsComponent | null = null;
|
|
157
145
|
|
|
158
|
-
private
|
|
146
|
+
private settingsManager: SettingsManager;
|
|
147
|
+
private context: SettingsRuntimeContext;
|
|
159
148
|
private callbacks: SettingsCallbacks;
|
|
160
149
|
|
|
161
|
-
constructor(
|
|
150
|
+
constructor(settingsManager: SettingsManager, context: SettingsRuntimeContext, callbacks: SettingsCallbacks) {
|
|
162
151
|
super();
|
|
163
152
|
|
|
164
|
-
this.
|
|
153
|
+
this.settingsManager = settingsManager;
|
|
154
|
+
this.context = context;
|
|
165
155
|
this.callbacks = callbacks;
|
|
166
156
|
|
|
167
157
|
// Add top border
|
|
@@ -201,10 +191,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
201
191
|
|
|
202
192
|
switch (tabId) {
|
|
203
193
|
case "config":
|
|
204
|
-
this.showConfigTab();
|
|
205
|
-
break;
|
|
206
194
|
case "exa":
|
|
207
|
-
this.
|
|
195
|
+
this.showSettingsTab(tabId);
|
|
208
196
|
break;
|
|
209
197
|
case "plugins":
|
|
210
198
|
this.showPluginsTab();
|
|
@@ -215,201 +203,135 @@ export class SettingsSelectorComponent extends Container {
|
|
|
215
203
|
this.addChild(bottomBorder);
|
|
216
204
|
}
|
|
217
205
|
|
|
218
|
-
|
|
219
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Convert a setting definition to a SettingItem for the UI.
|
|
208
|
+
*/
|
|
209
|
+
private defToItem(def: SettingDef): SettingItem | null {
|
|
210
|
+
// Check condition
|
|
211
|
+
if (def.type === "boolean" && def.condition && !def.condition()) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
220
214
|
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
this.config.availableThemes.map((t) => ({
|
|
282
|
-
value: t,
|
|
283
|
-
label: t,
|
|
284
|
-
})),
|
|
285
|
-
currentValue,
|
|
286
|
-
(value) => {
|
|
287
|
-
this.callbacks.onThemeChange(value);
|
|
288
|
-
done(value);
|
|
289
|
-
},
|
|
290
|
-
() => {
|
|
291
|
-
this.callbacks.onThemePreview?.(currentValue);
|
|
292
|
-
done();
|
|
293
|
-
},
|
|
294
|
-
(value) => {
|
|
295
|
-
this.callbacks.onThemePreview?.(value);
|
|
296
|
-
},
|
|
297
|
-
),
|
|
298
|
-
},
|
|
299
|
-
];
|
|
300
|
-
|
|
301
|
-
// Add image toggle if supported
|
|
302
|
-
if (supportsImages) {
|
|
303
|
-
items.splice(1, 0, {
|
|
304
|
-
id: "show-images",
|
|
305
|
-
label: "Show images",
|
|
306
|
-
description: "Render images inline in terminal",
|
|
307
|
-
currentValue: this.config.showImages ? "true" : "false",
|
|
308
|
-
values: ["true", "false"],
|
|
215
|
+
const currentValue = this.getCurrentValue(def);
|
|
216
|
+
|
|
217
|
+
switch (def.type) {
|
|
218
|
+
case "boolean":
|
|
219
|
+
return {
|
|
220
|
+
id: def.id,
|
|
221
|
+
label: def.label,
|
|
222
|
+
description: def.description,
|
|
223
|
+
currentValue: currentValue ? "true" : "false",
|
|
224
|
+
values: ["true", "false"],
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
case "enum":
|
|
228
|
+
return {
|
|
229
|
+
id: def.id,
|
|
230
|
+
label: def.label,
|
|
231
|
+
description: def.description,
|
|
232
|
+
currentValue: currentValue as string,
|
|
233
|
+
values: [...def.values],
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
case "submenu":
|
|
237
|
+
return {
|
|
238
|
+
id: def.id,
|
|
239
|
+
label: def.label,
|
|
240
|
+
description: def.description,
|
|
241
|
+
currentValue: currentValue as string,
|
|
242
|
+
submenu: (cv, done) => this.createSubmenu(def, cv, done),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get the current value for a setting, using runtime context for special cases.
|
|
249
|
+
*/
|
|
250
|
+
private getCurrentValue(def: SettingDef): string | boolean {
|
|
251
|
+
// Special cases that come from runtime context instead of SettingsManager
|
|
252
|
+
switch (def.id) {
|
|
253
|
+
case "thinkingLevel":
|
|
254
|
+
return this.context.thinkingLevel;
|
|
255
|
+
default:
|
|
256
|
+
return def.get(this.settingsManager);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create a submenu for a submenu-type setting.
|
|
262
|
+
*/
|
|
263
|
+
private createSubmenu(
|
|
264
|
+
def: SettingDef & { type: "submenu" },
|
|
265
|
+
currentValue: string,
|
|
266
|
+
done: (value?: string) => void,
|
|
267
|
+
): Container {
|
|
268
|
+
let options = def.getOptions(this.settingsManager);
|
|
269
|
+
|
|
270
|
+
// Special case: inject runtime options
|
|
271
|
+
if (def.id === "thinkingLevel") {
|
|
272
|
+
options = this.context.availableThinkingLevels.map((level) => {
|
|
273
|
+
const baseOpt = def.getOptions(this.settingsManager).find((o) => o.value === level);
|
|
274
|
+
return baseOpt || { value: level, label: level };
|
|
309
275
|
});
|
|
276
|
+
} else if (def.id === "theme") {
|
|
277
|
+
options = this.context.availableThemes.map((t) => ({ value: t, label: t }));
|
|
310
278
|
}
|
|
311
279
|
|
|
312
|
-
this.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
break;
|
|
327
|
-
case "hide-thinking":
|
|
328
|
-
this.callbacks.onHideThinkingBlockChange(newValue === "true");
|
|
329
|
-
break;
|
|
330
|
-
case "collapse-changelog":
|
|
331
|
-
this.callbacks.onCollapseChangelogChange(newValue === "true");
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
280
|
+
const onPreview = def.id === "theme" ? this.callbacks.onThemePreview : undefined;
|
|
281
|
+
const onPreviewCancel = def.id === "theme" ? () => this.callbacks.onThemePreview?.(currentValue) : undefined;
|
|
282
|
+
|
|
283
|
+
return new SelectSubmenu(
|
|
284
|
+
def.label,
|
|
285
|
+
def.description,
|
|
286
|
+
options,
|
|
287
|
+
currentValue,
|
|
288
|
+
(value) => {
|
|
289
|
+
// Persist to SettingsManager
|
|
290
|
+
def.set(this.settingsManager, value);
|
|
291
|
+
// Notify for side effects
|
|
292
|
+
this.callbacks.onChange(def.id, value);
|
|
293
|
+
done(value);
|
|
334
294
|
},
|
|
335
|
-
() =>
|
|
295
|
+
() => {
|
|
296
|
+
onPreviewCancel?.();
|
|
297
|
+
done();
|
|
298
|
+
},
|
|
299
|
+
onPreview,
|
|
336
300
|
);
|
|
337
|
-
|
|
338
|
-
this.addChild(this.currentList);
|
|
339
301
|
}
|
|
340
302
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
{
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
currentValue: this.config.exa.enableSearch ? "true" : "false",
|
|
355
|
-
values: ["true", "false"],
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
id: "exa-linkedin",
|
|
359
|
-
label: "Exa LinkedIn",
|
|
360
|
-
description: "Search LinkedIn for people and companies",
|
|
361
|
-
currentValue: this.config.exa.enableLinkedin ? "true" : "false",
|
|
362
|
-
values: ["true", "false"],
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
id: "exa-company",
|
|
366
|
-
label: "Exa company",
|
|
367
|
-
description: "Comprehensive company research tool",
|
|
368
|
-
currentValue: this.config.exa.enableCompany ? "true" : "false",
|
|
369
|
-
values: ["true", "false"],
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
id: "exa-researcher",
|
|
373
|
-
label: "Exa researcher",
|
|
374
|
-
description: "AI-powered deep research tasks",
|
|
375
|
-
currentValue: this.config.exa.enableResearcher ? "true" : "false",
|
|
376
|
-
values: ["true", "false"],
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
id: "exa-websets",
|
|
380
|
-
label: "Exa websets",
|
|
381
|
-
description: "Webset management and enrichment tools",
|
|
382
|
-
currentValue: this.config.exa.enableWebsets ? "true" : "false",
|
|
383
|
-
values: ["true", "false"],
|
|
384
|
-
},
|
|
385
|
-
];
|
|
303
|
+
/**
|
|
304
|
+
* Show a settings tab (config or exa) using definitions.
|
|
305
|
+
*/
|
|
306
|
+
private showSettingsTab(tabId: "config" | "exa"): void {
|
|
307
|
+
const defs = getSettingsForTab(tabId);
|
|
308
|
+
const items: SettingItem[] = [];
|
|
309
|
+
|
|
310
|
+
for (const def of defs) {
|
|
311
|
+
const item = this.defToItem(def);
|
|
312
|
+
if (item) {
|
|
313
|
+
items.push(item);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
386
316
|
|
|
387
317
|
this.currentList = new SettingsList(
|
|
388
318
|
items,
|
|
389
319
|
10,
|
|
390
320
|
getSettingsListTheme(),
|
|
391
321
|
(id, newValue) => {
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
case "exa-company":
|
|
404
|
-
this.callbacks.onExaSettingChange("enableCompany", enabled);
|
|
405
|
-
break;
|
|
406
|
-
case "exa-researcher":
|
|
407
|
-
this.callbacks.onExaSettingChange("enableResearcher", enabled);
|
|
408
|
-
break;
|
|
409
|
-
case "exa-websets":
|
|
410
|
-
this.callbacks.onExaSettingChange("enableWebsets", enabled);
|
|
411
|
-
break;
|
|
322
|
+
const def = defs.find((d) => d.id === id);
|
|
323
|
+
if (!def) return;
|
|
324
|
+
|
|
325
|
+
// Persist to SettingsManager based on type
|
|
326
|
+
if (def.type === "boolean") {
|
|
327
|
+
const boolValue = newValue === "true";
|
|
328
|
+
def.set(this.settingsManager, boolValue);
|
|
329
|
+
this.callbacks.onChange(id, boolValue);
|
|
330
|
+
} else if (def.type === "enum") {
|
|
331
|
+
def.set(this.settingsManager, newValue);
|
|
332
|
+
this.callbacks.onChange(id, newValue);
|
|
412
333
|
}
|
|
334
|
+
// Submenu types are handled in createSubmenu
|
|
413
335
|
},
|
|
414
336
|
() => this.callbacks.onCancel(),
|
|
415
337
|
);
|
|
@@ -418,7 +340,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
418
340
|
}
|
|
419
341
|
|
|
420
342
|
private showPluginsTab(): void {
|
|
421
|
-
this.pluginComponent = new PluginSettingsComponent(this.
|
|
343
|
+
this.pluginComponent = new PluginSettingsComponent(this.context.cwd, {
|
|
422
344
|
onClose: () => this.callbacks.onCancel(),
|
|
423
345
|
onPluginChanged: () => this.callbacks.onPluginsChanged?.(),
|
|
424
346
|
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
9
|
+
import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
10
10
|
import type { AssistantMessage, ImageContent, Message, OAuthProvider } from "@oh-my-pi/pi-ai";
|
|
11
11
|
import type { SlashCommand } from "@oh-my-pi/pi-tui";
|
|
12
12
|
import {
|
|
@@ -1597,48 +1597,15 @@ export class InteractiveMode {
|
|
|
1597
1597
|
private showSettingsSelector(): void {
|
|
1598
1598
|
this.showSelector((done) => {
|
|
1599
1599
|
const selector = new SettingsSelectorComponent(
|
|
1600
|
+
this.settingsManager,
|
|
1600
1601
|
{
|
|
1601
|
-
autoCompact: this.session.autoCompactionEnabled,
|
|
1602
|
-
showImages: this.settingsManager.getShowImages(),
|
|
1603
|
-
queueMode: this.session.queueMode,
|
|
1604
|
-
thinkingLevel: this.session.thinkingLevel,
|
|
1605
1602
|
availableThinkingLevels: this.session.getAvailableThinkingLevels(),
|
|
1606
|
-
|
|
1603
|
+
thinkingLevel: this.session.thinkingLevel,
|
|
1607
1604
|
availableThemes: getAvailableThemes(),
|
|
1608
|
-
hideThinkingBlock: this.hideThinkingBlock,
|
|
1609
|
-
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
1610
1605
|
cwd: process.cwd(),
|
|
1611
|
-
exa: this.settingsManager.getExaSettings(),
|
|
1612
1606
|
},
|
|
1613
1607
|
{
|
|
1614
|
-
|
|
1615
|
-
this.session.setAutoCompactionEnabled(enabled);
|
|
1616
|
-
this.footer.setAutoCompactEnabled(enabled);
|
|
1617
|
-
},
|
|
1618
|
-
onShowImagesChange: (enabled) => {
|
|
1619
|
-
this.settingsManager.setShowImages(enabled);
|
|
1620
|
-
for (const child of this.chatContainer.children) {
|
|
1621
|
-
if (child instanceof ToolExecutionComponent) {
|
|
1622
|
-
child.setShowImages(enabled);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
},
|
|
1626
|
-
onQueueModeChange: (mode) => {
|
|
1627
|
-
this.session.setQueueMode(mode);
|
|
1628
|
-
},
|
|
1629
|
-
onThinkingLevelChange: (level) => {
|
|
1630
|
-
this.session.setThinkingLevel(level);
|
|
1631
|
-
this.footer.invalidate();
|
|
1632
|
-
this.updateEditorBorderColor();
|
|
1633
|
-
},
|
|
1634
|
-
onThemeChange: (themeName) => {
|
|
1635
|
-
const result = setTheme(themeName, true);
|
|
1636
|
-
this.settingsManager.setTheme(themeName);
|
|
1637
|
-
this.ui.invalidate();
|
|
1638
|
-
if (!result.success) {
|
|
1639
|
-
this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to dark theme.`);
|
|
1640
|
-
}
|
|
1641
|
-
},
|
|
1608
|
+
onChange: (id, value) => this.handleSettingChange(id, value),
|
|
1642
1609
|
onThemePreview: (themeName) => {
|
|
1643
1610
|
const result = setTheme(themeName, true);
|
|
1644
1611
|
if (result.success) {
|
|
@@ -1646,46 +1613,9 @@ export class InteractiveMode {
|
|
|
1646
1613
|
this.ui.requestRender();
|
|
1647
1614
|
}
|
|
1648
1615
|
},
|
|
1649
|
-
onHideThinkingBlockChange: (hidden) => {
|
|
1650
|
-
this.hideThinkingBlock = hidden;
|
|
1651
|
-
this.settingsManager.setHideThinkingBlock(hidden);
|
|
1652
|
-
for (const child of this.chatContainer.children) {
|
|
1653
|
-
if (child instanceof AssistantMessageComponent) {
|
|
1654
|
-
child.setHideThinkingBlock(hidden);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
this.chatContainer.clear();
|
|
1658
|
-
this.rebuildChatFromMessages();
|
|
1659
|
-
},
|
|
1660
|
-
onCollapseChangelogChange: (collapsed) => {
|
|
1661
|
-
this.settingsManager.setCollapseChangelog(collapsed);
|
|
1662
|
-
},
|
|
1663
1616
|
onPluginsChanged: () => {
|
|
1664
|
-
// Plugin config changed - could trigger reload if needed
|
|
1665
1617
|
this.ui.requestRender();
|
|
1666
1618
|
},
|
|
1667
|
-
onExaSettingChange: (setting, enabled) => {
|
|
1668
|
-
switch (setting) {
|
|
1669
|
-
case "enabled":
|
|
1670
|
-
this.settingsManager.setExaEnabled(enabled);
|
|
1671
|
-
break;
|
|
1672
|
-
case "enableSearch":
|
|
1673
|
-
this.settingsManager.setExaSearchEnabled(enabled);
|
|
1674
|
-
break;
|
|
1675
|
-
case "enableLinkedin":
|
|
1676
|
-
this.settingsManager.setExaLinkedinEnabled(enabled);
|
|
1677
|
-
break;
|
|
1678
|
-
case "enableCompany":
|
|
1679
|
-
this.settingsManager.setExaCompanyEnabled(enabled);
|
|
1680
|
-
break;
|
|
1681
|
-
case "enableResearcher":
|
|
1682
|
-
this.settingsManager.setExaResearcherEnabled(enabled);
|
|
1683
|
-
break;
|
|
1684
|
-
case "enableWebsets":
|
|
1685
|
-
this.settingsManager.setExaWebsetsEnabled(enabled);
|
|
1686
|
-
break;
|
|
1687
|
-
}
|
|
1688
|
-
},
|
|
1689
1619
|
onCancel: () => {
|
|
1690
1620
|
done();
|
|
1691
1621
|
this.ui.requestRender();
|
|
@@ -1696,6 +1626,59 @@ export class InteractiveMode {
|
|
|
1696
1626
|
});
|
|
1697
1627
|
}
|
|
1698
1628
|
|
|
1629
|
+
/**
|
|
1630
|
+
* Handle setting changes from the settings selector.
|
|
1631
|
+
* Most settings are saved directly via SettingsManager in the definitions.
|
|
1632
|
+
* This handles side effects and session-specific settings.
|
|
1633
|
+
*/
|
|
1634
|
+
private handleSettingChange(id: string, value: string | boolean): void {
|
|
1635
|
+
switch (id) {
|
|
1636
|
+
// Session-managed settings (not in SettingsManager)
|
|
1637
|
+
case "autoCompact":
|
|
1638
|
+
this.session.setAutoCompactionEnabled(value as boolean);
|
|
1639
|
+
this.footer.setAutoCompactEnabled(value as boolean);
|
|
1640
|
+
break;
|
|
1641
|
+
case "queueMode":
|
|
1642
|
+
this.session.setQueueMode(value as "all" | "one-at-a-time");
|
|
1643
|
+
break;
|
|
1644
|
+
case "thinkingLevel":
|
|
1645
|
+
this.session.setThinkingLevel(value as ThinkingLevel);
|
|
1646
|
+
this.footer.invalidate();
|
|
1647
|
+
this.updateEditorBorderColor();
|
|
1648
|
+
break;
|
|
1649
|
+
|
|
1650
|
+
// Settings with UI side effects
|
|
1651
|
+
case "showImages":
|
|
1652
|
+
for (const child of this.chatContainer.children) {
|
|
1653
|
+
if (child instanceof ToolExecutionComponent) {
|
|
1654
|
+
child.setShowImages(value as boolean);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
break;
|
|
1658
|
+
case "hideThinking":
|
|
1659
|
+
this.hideThinkingBlock = value as boolean;
|
|
1660
|
+
for (const child of this.chatContainer.children) {
|
|
1661
|
+
if (child instanceof AssistantMessageComponent) {
|
|
1662
|
+
child.setHideThinkingBlock(value as boolean);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
this.chatContainer.clear();
|
|
1666
|
+
this.rebuildChatFromMessages();
|
|
1667
|
+
break;
|
|
1668
|
+
case "theme": {
|
|
1669
|
+
const result = setTheme(value as string, true);
|
|
1670
|
+
this.ui.invalidate();
|
|
1671
|
+
if (!result.success) {
|
|
1672
|
+
this.showError(`Failed to load theme "${value}": ${result.error}\nFell back to dark theme.`);
|
|
1673
|
+
}
|
|
1674
|
+
break;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// All other settings are handled by the definitions (get/set on SettingsManager)
|
|
1678
|
+
// No additional side effects needed
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1699
1682
|
private showModelSelector(): void {
|
|
1700
1683
|
this.showSelector((done) => {
|
|
1701
1684
|
const selector = new ModelSelectorComponent(
|
|
@@ -2139,7 +2122,7 @@ export class InteractiveMode {
|
|
|
2139
2122
|
}
|
|
2140
2123
|
|
|
2141
2124
|
// Create the preview URL
|
|
2142
|
-
const previewUrl = `https://
|
|
2125
|
+
const previewUrl = `https://gistpreview.github.io/?${gistId}`;
|
|
2143
2126
|
this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
|
|
2144
2127
|
} catch (error: unknown) {
|
|
2145
2128
|
if (!loader.signal.aborted) {
|
package/src/utils/shell.ts
CHANGED
|
@@ -83,13 +83,21 @@ export function getShellConfig(): { shell: string; args: string[] } {
|
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// Unix: prefer bash
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
// Unix: prefer user's shell from $SHELL, fallback to bash, then sh
|
|
87
|
+
const userShell = process.env.SHELL;
|
|
88
|
+
if (userShell && existsSync(userShell)) {
|
|
89
|
+
cachedShellConfig = { shell: userShell, args: ["-c"] };
|
|
89
90
|
return cachedShellConfig;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
const bashPath = Bun.which("bash");
|
|
94
|
+
if (bashPath) {
|
|
95
|
+
cachedShellConfig = { shell: bashPath, args: ["-c"] };
|
|
96
|
+
return cachedShellConfig;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const shPath = Bun.which("sh");
|
|
100
|
+
cachedShellConfig = { shell: shPath || "sh", args: ["-c"] };
|
|
93
101
|
return cachedShellConfig;
|
|
94
102
|
}
|
|
95
103
|
|