@oh-my-pi/pi-coding-agent 11.8.2 → 11.8.3
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/docs/tui.md +9 -9
- package/package.json +7 -7
- package/src/cli/file-processor.ts +8 -13
- package/src/cli/oclif-help.ts +1 -1
- package/src/cli.ts +14 -0
- package/src/commit/git/index.ts +16 -16
- package/src/config/keybindings.ts +11 -11
- package/src/config/model-registry.ts +31 -66
- package/src/config/settings.ts +88 -95
- package/src/config.ts +2 -2
- package/src/cursor.ts +4 -4
- package/src/debug/index.ts +28 -28
- package/src/discovery/codex.ts +5 -13
- package/src/discovery/cursor.ts +2 -7
- package/src/exa/mcp-client.ts +2 -2
- package/src/exa/websets.ts +2 -2
- package/src/export/html/index.ts +3 -3
- package/src/export/ttsr.ts +27 -27
- package/src/extensibility/custom-tools/loader.ts +9 -9
- package/src/extensibility/extensions/runner.ts +64 -64
- package/src/extensibility/hooks/runner.ts +46 -46
- package/src/extensibility/plugins/manager.ts +49 -49
- package/src/index.ts +0 -1
- package/src/internal-urls/router.ts +5 -5
- package/src/ipy/kernel.ts +61 -57
- package/src/lsp/client.ts +1 -1
- package/src/lsp/clients/biome-client.ts +2 -2
- package/src/lsp/clients/lsp-linter-client.ts +7 -7
- package/src/lsp/index.ts +9 -9
- package/src/mcp/manager.ts +47 -47
- package/src/mcp/tool-bridge.ts +12 -12
- package/src/mcp/transports/http.ts +34 -34
- package/src/mcp/transports/stdio.ts +47 -47
- package/src/modes/components/assistant-message.ts +25 -25
- package/src/modes/components/bash-execution.ts +51 -51
- package/src/modes/components/bordered-loader.ts +7 -7
- package/src/modes/components/branch-summary-message.ts +7 -7
- package/src/modes/components/compaction-summary-message.ts +7 -7
- package/src/modes/components/countdown-timer.ts +15 -15
- package/src/modes/components/custom-editor.ts +22 -22
- package/src/modes/components/custom-message.ts +21 -21
- package/src/modes/components/dynamic-border.ts +3 -3
- package/src/modes/components/extensions/extension-dashboard.ts +72 -72
- package/src/modes/components/extensions/extension-list.ts +99 -97
- package/src/modes/components/extensions/inspector-panel.ts +26 -26
- package/src/modes/components/footer.ts +36 -36
- package/src/modes/components/history-search.ts +52 -52
- package/src/modes/components/hook-editor.ts +20 -20
- package/src/modes/components/hook-input.ts +20 -20
- package/src/modes/components/hook-message.ts +22 -22
- package/src/modes/components/hook-selector.ts +52 -52
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/login-dialog.ts +57 -57
- package/src/modes/components/model-selector.ts +173 -173
- package/src/modes/components/oauth-selector.ts +45 -45
- package/src/modes/components/plugin-settings.ts +52 -52
- package/src/modes/components/python-execution.ts +53 -53
- package/src/modes/components/queue-mode-selector.ts +7 -7
- package/src/modes/components/read-tool-group.ts +23 -23
- package/src/modes/components/session-selector.ts +40 -37
- package/src/modes/components/settings-selector.ts +80 -80
- package/src/modes/components/show-images-selector.ts +7 -7
- package/src/modes/components/skill-message.ts +27 -27
- package/src/modes/components/status-line-segment-editor.ts +81 -81
- package/src/modes/components/status-line.ts +73 -73
- package/src/modes/components/theme-selector.ts +11 -11
- package/src/modes/components/thinking-selector.ts +7 -7
- package/src/modes/components/todo-display.ts +19 -19
- package/src/modes/components/todo-reminder.ts +9 -9
- package/src/modes/components/tool-execution.ts +204 -196
- package/src/modes/components/tree-selector.ts +144 -144
- package/src/modes/components/ttsr-notification.ts +17 -17
- package/src/modes/components/user-message-selector.ts +18 -18
- package/src/modes/components/welcome.ts +10 -10
- package/src/modes/controllers/command-controller.ts +0 -7
- package/src/modes/controllers/event-controller.ts +23 -23
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +4 -9
- package/src/modes/interactive-mode.ts +234 -241
- package/src/modes/rpc/rpc-client.ts +77 -77
- package/src/modes/rpc/rpc-mode.ts +5 -5
- package/src/modes/theme/theme.ts +113 -113
- package/src/modes/types.ts +0 -1
- package/src/patch/index.ts +45 -45
- package/src/prompts/tools/task.md +22 -2
- package/src/session/agent-session.ts +463 -476
- package/src/session/agent-storage.ts +72 -75
- package/src/session/auth-storage.ts +186 -252
- package/src/session/history-storage.ts +36 -38
- package/src/session/session-manager.ts +300 -299
- package/src/session/session-storage.ts +65 -90
- package/src/ssh/connection-manager.ts +9 -9
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +2 -2
- package/src/task/index.ts +13 -12
- package/src/task/subprocess-tool-registry.ts +5 -5
- package/src/tools/ask.ts +7 -7
- package/src/tools/bash.ts +8 -7
- package/src/tools/browser.ts +123 -123
- package/src/tools/calculator.ts +46 -46
- package/src/tools/context.ts +9 -9
- package/src/tools/exit-plan-mode.ts +5 -5
- package/src/tools/fetch.ts +5 -5
- package/src/tools/find.ts +16 -16
- package/src/tools/grep.ts +10 -10
- package/src/tools/notebook.ts +6 -6
- package/src/tools/output-meta.ts +10 -2
- package/src/tools/python.ts +12 -11
- package/src/tools/read.ts +17 -17
- package/src/tools/ssh.ts +9 -9
- package/src/tools/submit-result.ts +13 -13
- package/src/tools/todo-write.ts +6 -6
- package/src/tools/write.ts +10 -10
- package/src/tui/output-block.ts +6 -6
- package/src/tui/utils.ts +9 -9
- package/src/utils/event-bus.ts +10 -10
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/ignore-files.ts +1 -1
- package/src/web/search/index.ts +5 -5
- package/src/web/search/providers/anthropic.ts +7 -2
- package/examples/hooks/snake.ts +0 -342
- package/src/modes/components/armin.ts +0 -379
|
@@ -8,14 +8,14 @@ import { DynamicBorder } from "./dynamic-border";
|
|
|
8
8
|
* Component that renders an OAuth provider selector
|
|
9
9
|
*/
|
|
10
10
|
export class OAuthSelectorComponent extends Container {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
#listContainer: Container;
|
|
12
|
+
#allProviders: OAuthProviderInfo[] = [];
|
|
13
|
+
#selectedIndex: number = 0;
|
|
14
|
+
#mode: "login" | "logout";
|
|
15
|
+
#authStorage: AuthStorage;
|
|
16
|
+
#onSelectCallback: (providerId: string) => void;
|
|
17
|
+
#onCancelCallback: () => void;
|
|
18
|
+
#statusMessage: string | undefined;
|
|
19
19
|
|
|
20
20
|
constructor(
|
|
21
21
|
mode: "login" | "logout",
|
|
@@ -25,13 +25,13 @@ export class OAuthSelectorComponent extends Container {
|
|
|
25
25
|
) {
|
|
26
26
|
super();
|
|
27
27
|
|
|
28
|
-
this
|
|
29
|
-
this
|
|
30
|
-
this
|
|
31
|
-
this
|
|
28
|
+
this.#mode = mode;
|
|
29
|
+
this.#authStorage = authStorage;
|
|
30
|
+
this.#onSelectCallback = onSelect;
|
|
31
|
+
this.#onCancelCallback = onCancel;
|
|
32
32
|
|
|
33
33
|
// Load all OAuth providers
|
|
34
|
-
this
|
|
34
|
+
this.#loadProviders();
|
|
35
35
|
|
|
36
36
|
// Add top border
|
|
37
37
|
this.addChild(new DynamicBorder());
|
|
@@ -43,8 +43,8 @@ export class OAuthSelectorComponent extends Container {
|
|
|
43
43
|
this.addChild(new Spacer(1));
|
|
44
44
|
|
|
45
45
|
// Create list container
|
|
46
|
-
this
|
|
47
|
-
this.addChild(this
|
|
46
|
+
this.#listContainer = new Container();
|
|
47
|
+
this.addChild(this.#listContainer);
|
|
48
48
|
|
|
49
49
|
this.addChild(new Spacer(1));
|
|
50
50
|
|
|
@@ -52,25 +52,25 @@ export class OAuthSelectorComponent extends Container {
|
|
|
52
52
|
this.addChild(new DynamicBorder());
|
|
53
53
|
|
|
54
54
|
// Initial render
|
|
55
|
-
this
|
|
55
|
+
this.#updateList();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
this
|
|
58
|
+
#loadProviders(): void {
|
|
59
|
+
this.#allProviders = getOAuthProviders();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
this
|
|
62
|
+
#updateList(): void {
|
|
63
|
+
this.#listContainer.clear();
|
|
64
64
|
|
|
65
|
-
for (let i = 0; i < this
|
|
66
|
-
const provider = this
|
|
65
|
+
for (let i = 0; i < this.#allProviders.length; i++) {
|
|
66
|
+
const provider = this.#allProviders[i];
|
|
67
67
|
if (!provider) continue;
|
|
68
68
|
|
|
69
|
-
const isSelected = i === this
|
|
69
|
+
const isSelected = i === this.#selectedIndex;
|
|
70
70
|
const isAvailable = provider.available;
|
|
71
71
|
|
|
72
72
|
// Check if user is logged in for this provider
|
|
73
|
-
const isLoggedIn = this
|
|
73
|
+
const isLoggedIn = this.#authStorage.hasOAuth(provider.id);
|
|
74
74
|
const statusIndicator = isLoggedIn ? theme.fg("success", ` ${theme.status.success} logged in`) : "";
|
|
75
75
|
|
|
76
76
|
let line = "";
|
|
@@ -83,53 +83,53 @@ export class OAuthSelectorComponent extends Container {
|
|
|
83
83
|
line = text + statusIndicator;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
this
|
|
86
|
+
this.#listContainer.addChild(new TruncatedText(line, 0, 0));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Show "no providers" if empty
|
|
90
|
-
if (this
|
|
90
|
+
if (this.#allProviders.length === 0) {
|
|
91
91
|
const message =
|
|
92
|
-
this
|
|
93
|
-
this
|
|
92
|
+
this.#mode === "login" ? "No OAuth providers available" : "No OAuth providers logged in. Use /login first.";
|
|
93
|
+
this.#listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
if (this
|
|
97
|
-
this
|
|
98
|
-
this
|
|
96
|
+
if (this.#statusMessage) {
|
|
97
|
+
this.#listContainer.addChild(new Spacer(1));
|
|
98
|
+
this.#listContainer.addChild(new TruncatedText(theme.fg("warning", ` ${this.#statusMessage}`), 0, 0));
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
handleInput(keyData: string): void {
|
|
103
103
|
// Up arrow
|
|
104
104
|
if (matchesKey(keyData, "up")) {
|
|
105
|
-
if (this
|
|
106
|
-
this
|
|
105
|
+
if (this.#allProviders.length > 0) {
|
|
106
|
+
this.#selectedIndex = this.#selectedIndex === 0 ? this.#allProviders.length - 1 : this.#selectedIndex - 1;
|
|
107
107
|
}
|
|
108
|
-
this
|
|
109
|
-
this
|
|
108
|
+
this.#statusMessage = undefined;
|
|
109
|
+
this.#updateList();
|
|
110
110
|
}
|
|
111
111
|
// Down arrow
|
|
112
112
|
else if (matchesKey(keyData, "down")) {
|
|
113
|
-
if (this
|
|
114
|
-
this
|
|
113
|
+
if (this.#allProviders.length > 0) {
|
|
114
|
+
this.#selectedIndex = this.#selectedIndex === this.#allProviders.length - 1 ? 0 : this.#selectedIndex + 1;
|
|
115
115
|
}
|
|
116
|
-
this
|
|
117
|
-
this
|
|
116
|
+
this.#statusMessage = undefined;
|
|
117
|
+
this.#updateList();
|
|
118
118
|
}
|
|
119
119
|
// Enter
|
|
120
120
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
121
|
-
const selectedProvider = this
|
|
121
|
+
const selectedProvider = this.#allProviders[this.#selectedIndex];
|
|
122
122
|
if (selectedProvider?.available) {
|
|
123
|
-
this
|
|
124
|
-
this
|
|
123
|
+
this.#statusMessage = undefined;
|
|
124
|
+
this.#onSelectCallback(selectedProvider.id);
|
|
125
125
|
} else if (selectedProvider) {
|
|
126
|
-
this
|
|
127
|
-
this
|
|
126
|
+
this.#statusMessage = "Provider unavailable in this environment.";
|
|
127
|
+
this.#updateList();
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
// Escape or Ctrl+C
|
|
131
131
|
else if (matchesKey(keyData, "escape") || matchesKey(keyData, "esc") || matchesKey(keyData, "ctrl+c")) {
|
|
132
|
-
this
|
|
132
|
+
this.#onCancelCallback();
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -36,7 +36,7 @@ export interface PluginListCallbacks {
|
|
|
36
36
|
* Selecting a plugin opens its detail view.
|
|
37
37
|
*/
|
|
38
38
|
export class PluginListComponent extends Container {
|
|
39
|
-
|
|
39
|
+
readonly #selectList: SelectList;
|
|
40
40
|
|
|
41
41
|
constructor(
|
|
42
42
|
private readonly plugins: InstalledPlugin[],
|
|
@@ -57,8 +57,8 @@ export class PluginListComponent extends Container {
|
|
|
57
57
|
this.addChild(new DynamicBorder());
|
|
58
58
|
|
|
59
59
|
// Create empty list that just handles escape
|
|
60
|
-
this
|
|
61
|
-
this
|
|
60
|
+
this.#selectList = new SelectList([], 1, getSelectListTheme());
|
|
61
|
+
this.#selectList.onCancel = callbacks.onCancel;
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -81,25 +81,25 @@ export class PluginListComponent extends Container {
|
|
|
81
81
|
};
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
this
|
|
84
|
+
this.#selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
|
|
85
85
|
|
|
86
|
-
this
|
|
86
|
+
this.#selectList.onSelect = item => {
|
|
87
87
|
const plugin = this.plugins.find(p => p.name === item.value);
|
|
88
88
|
if (plugin) {
|
|
89
89
|
callbacks.onPluginSelect(plugin);
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
this
|
|
93
|
+
this.#selectList.onCancel = callbacks.onCancel;
|
|
94
94
|
|
|
95
|
-
this.addChild(this
|
|
95
|
+
this.addChild(this.#selectList);
|
|
96
96
|
this.addChild(new Spacer(1));
|
|
97
97
|
this.addChild(new Text(theme.fg("dim", " Enter to configure · Esc to go back"), 0, 0));
|
|
98
98
|
this.addChild(new DynamicBorder());
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
handleInput(data: string): void {
|
|
102
|
-
this
|
|
102
|
+
this.#selectList.handleInput(data);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -121,7 +121,7 @@ export interface PluginDetailCallbacks {
|
|
|
121
121
|
* - Config settings
|
|
122
122
|
*/
|
|
123
123
|
export class PluginDetailComponent extends Container {
|
|
124
|
-
|
|
124
|
+
#settingsList!: SettingsList;
|
|
125
125
|
|
|
126
126
|
constructor(
|
|
127
127
|
private plugin: InstalledPlugin,
|
|
@@ -130,10 +130,10 @@ export class PluginDetailComponent extends Container {
|
|
|
130
130
|
) {
|
|
131
131
|
super();
|
|
132
132
|
|
|
133
|
-
void this
|
|
133
|
+
void this.#rebuild();
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
async #rebuild(): Promise<void> {
|
|
137
137
|
this.clear();
|
|
138
138
|
|
|
139
139
|
const plugin = this.plugin;
|
|
@@ -239,7 +239,7 @@ export class PluginDetailComponent extends Container {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
this
|
|
242
|
+
this.#settingsList = new SettingsList(
|
|
243
243
|
items,
|
|
244
244
|
Math.min(items.length, 10),
|
|
245
245
|
getSettingsListTheme(),
|
|
@@ -269,15 +269,15 @@ export class PluginDetailComponent extends Container {
|
|
|
269
269
|
this.callbacks.onBack,
|
|
270
270
|
);
|
|
271
271
|
|
|
272
|
-
this.addChild(this
|
|
272
|
+
this.addChild(this.#settingsList);
|
|
273
273
|
this.addChild(new Spacer(1));
|
|
274
274
|
this.addChild(new Text(theme.fg("dim", " Enter to edit · Esc to go back"), 0, 0));
|
|
275
275
|
this.addChild(new DynamicBorder());
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
handleInput(data: string): void {
|
|
279
|
-
if (!this
|
|
280
|
-
this
|
|
279
|
+
if (!this.#settingsList) return;
|
|
280
|
+
this.#settingsList.handleInput(data);
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
|
|
@@ -289,7 +289,7 @@ export class PluginDetailComponent extends Container {
|
|
|
289
289
|
* Submenu for enum config values.
|
|
290
290
|
*/
|
|
291
291
|
class ConfigEnumSubmenu extends Container {
|
|
292
|
-
|
|
292
|
+
#selectList: SelectList;
|
|
293
293
|
|
|
294
294
|
constructor(
|
|
295
295
|
key: string,
|
|
@@ -309,23 +309,23 @@ class ConfigEnumSubmenu extends Container {
|
|
|
309
309
|
this.addChild(new Spacer(1));
|
|
310
310
|
|
|
311
311
|
const items: SelectItem[] = values.map(v => ({ value: v, label: v }));
|
|
312
|
-
this
|
|
312
|
+
this.#selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme());
|
|
313
313
|
|
|
314
314
|
const currentIndex = values.indexOf(currentValue);
|
|
315
315
|
if (currentIndex !== -1) {
|
|
316
|
-
this
|
|
316
|
+
this.#selectList.setSelectedIndex(currentIndex);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
this
|
|
320
|
-
this
|
|
319
|
+
this.#selectList.onSelect = item => onSelect(item.value);
|
|
320
|
+
this.#selectList.onCancel = onCancel;
|
|
321
321
|
|
|
322
|
-
this.addChild(this
|
|
322
|
+
this.addChild(this.#selectList);
|
|
323
323
|
this.addChild(new Spacer(1));
|
|
324
324
|
this.addChild(new Text(theme.fg("dim", " Enter to select · Esc to cancel"), 0, 0));
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
handleInput(data: string): void {
|
|
328
|
-
this
|
|
328
|
+
this.#selectList.handleInput(data);
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
|
|
@@ -333,7 +333,7 @@ class ConfigEnumSubmenu extends Container {
|
|
|
333
333
|
* Submenu for string/number config values with text input.
|
|
334
334
|
*/
|
|
335
335
|
class ConfigInputSubmenu extends Container {
|
|
336
|
-
|
|
336
|
+
#input: Input;
|
|
337
337
|
|
|
338
338
|
constructor(
|
|
339
339
|
key: string,
|
|
@@ -364,12 +364,12 @@ class ConfigInputSubmenu extends Container {
|
|
|
364
364
|
this.addChild(new Spacer(1));
|
|
365
365
|
|
|
366
366
|
// Input field
|
|
367
|
-
this
|
|
367
|
+
this.#input = new Input();
|
|
368
368
|
if (!schema.secret && currentValue) {
|
|
369
|
-
this
|
|
369
|
+
this.#input.setValue(currentValue);
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
this
|
|
372
|
+
this.#input.onSubmit = value => {
|
|
373
373
|
if (value.trim()) {
|
|
374
374
|
this.onSubmit(value);
|
|
375
375
|
} else {
|
|
@@ -377,7 +377,7 @@ class ConfigInputSubmenu extends Container {
|
|
|
377
377
|
}
|
|
378
378
|
};
|
|
379
379
|
|
|
380
|
-
this.addChild(this
|
|
380
|
+
this.addChild(this.#input);
|
|
381
381
|
this.addChild(new Spacer(1));
|
|
382
382
|
this.addChild(new Text(theme.fg("dim", " Enter to save · Esc to cancel"), 0, 0));
|
|
383
383
|
}
|
|
@@ -387,7 +387,7 @@ class ConfigInputSubmenu extends Container {
|
|
|
387
387
|
this.onCancel();
|
|
388
388
|
return;
|
|
389
389
|
}
|
|
390
|
-
this
|
|
390
|
+
this.#input.handleInput(data);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
@@ -410,68 +410,68 @@ interface InputHandler {
|
|
|
410
410
|
* Manages navigation between plugin list and plugin detail views.
|
|
411
411
|
*/
|
|
412
412
|
export class PluginSettingsComponent extends Container {
|
|
413
|
-
|
|
414
|
-
|
|
413
|
+
#manager: PluginManager;
|
|
414
|
+
#viewComponent: (Container & InputHandler) | null = null;
|
|
415
415
|
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: state tracking for view management
|
|
416
|
-
|
|
416
|
+
#currentView: "list" | "detail" = "list";
|
|
417
417
|
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: state tracking for view management
|
|
418
|
-
|
|
418
|
+
#currentPlugin: InstalledPlugin | null = null;
|
|
419
419
|
|
|
420
420
|
constructor(
|
|
421
421
|
cwd: string,
|
|
422
422
|
private readonly callbacks: PluginSettingsCallbacks,
|
|
423
423
|
) {
|
|
424
424
|
super();
|
|
425
|
-
this
|
|
426
|
-
this
|
|
425
|
+
this.#manager = new PluginManager(cwd);
|
|
426
|
+
this.#showPluginList();
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
this
|
|
431
|
-
this
|
|
429
|
+
async #showPluginList(): Promise<void> {
|
|
430
|
+
this.#currentView = "list";
|
|
431
|
+
this.#currentPlugin = null;
|
|
432
432
|
this.clear();
|
|
433
433
|
|
|
434
|
-
const plugins = await this
|
|
434
|
+
const plugins = await this.#manager.list();
|
|
435
435
|
|
|
436
|
-
this
|
|
437
|
-
onPluginSelect: plugin => this
|
|
436
|
+
this.#viewComponent = new PluginListComponent(plugins, {
|
|
437
|
+
onPluginSelect: plugin => this.#showPluginDetail(plugin),
|
|
438
438
|
onCancel: () => this.callbacks.onClose(),
|
|
439
439
|
});
|
|
440
440
|
|
|
441
|
-
this.addChild(this
|
|
441
|
+
this.addChild(this.#viewComponent);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
|
|
445
|
-
this
|
|
446
|
-
this
|
|
444
|
+
#showPluginDetail(plugin: InstalledPlugin): void {
|
|
445
|
+
this.#currentView = "detail";
|
|
446
|
+
this.#currentPlugin = plugin;
|
|
447
447
|
this.clear();
|
|
448
448
|
|
|
449
|
-
this
|
|
449
|
+
this.#viewComponent = new PluginDetailComponent(plugin, this.#manager, {
|
|
450
450
|
onEnabledChange: async enabled => {
|
|
451
|
-
await this
|
|
451
|
+
await this.#manager.setEnabled(plugin.name, enabled);
|
|
452
452
|
this.callbacks.onPluginChanged();
|
|
453
453
|
},
|
|
454
454
|
onFeatureChange: async (feature, enabled) => {
|
|
455
|
-
const current = new Set((await this
|
|
455
|
+
const current = new Set((await this.#manager.getEnabledFeatures(plugin.name)) ?? []);
|
|
456
456
|
if (enabled) {
|
|
457
457
|
current.add(feature);
|
|
458
458
|
} else {
|
|
459
459
|
current.delete(feature);
|
|
460
460
|
}
|
|
461
|
-
await this
|
|
461
|
+
await this.#manager.setEnabledFeatures(plugin.name, [...current]);
|
|
462
462
|
this.callbacks.onPluginChanged();
|
|
463
463
|
},
|
|
464
464
|
onConfigChange: async (key, value) => {
|
|
465
|
-
await this
|
|
465
|
+
await this.#manager.setPluginSetting(plugin.name, key, value);
|
|
466
466
|
this.callbacks.onPluginChanged();
|
|
467
467
|
},
|
|
468
|
-
onBack: () => this
|
|
468
|
+
onBack: () => this.#showPluginList(),
|
|
469
469
|
});
|
|
470
470
|
|
|
471
|
-
this.addChild(this
|
|
471
|
+
this.addChild(this.#viewComponent);
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
handleInput(data: string): void {
|
|
475
|
-
this
|
|
475
|
+
this.#viewComponent?.handleInput(data);
|
|
476
476
|
}
|
|
477
477
|
}
|
|
@@ -12,15 +12,15 @@ import { truncateToVisualLines } from "./visual-truncate";
|
|
|
12
12
|
const PREVIEW_LINES = 20;
|
|
13
13
|
|
|
14
14
|
export class PythonExecutionComponent extends Container {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
#outputLines: string[] = [];
|
|
16
|
+
#status: "running" | "complete" | "cancelled" | "error" = "running";
|
|
17
|
+
#exitCode: number | undefined = undefined;
|
|
18
|
+
#loader: Loader;
|
|
19
|
+
#truncation?: TruncationMeta;
|
|
20
|
+
#expanded = false;
|
|
21
|
+
#contentContainer: Container;
|
|
22
|
+
|
|
23
|
+
#formatHeader(colorKey: "dim" | "pythonMode"): Text {
|
|
24
24
|
const prompt = theme.fg(colorKey, theme.bold(">>>"));
|
|
25
25
|
const continuation = theme.fg(colorKey, " ");
|
|
26
26
|
const codeLines = highlightCode(this.code, "python");
|
|
@@ -43,44 +43,44 @@ export class PythonExecutionComponent extends Container {
|
|
|
43
43
|
this.addChild(new Spacer(1));
|
|
44
44
|
this.addChild(new DynamicBorder(borderColor));
|
|
45
45
|
|
|
46
|
-
this
|
|
47
|
-
this.addChild(this
|
|
48
|
-
this
|
|
46
|
+
this.#contentContainer = new Container();
|
|
47
|
+
this.addChild(this.#contentContainer);
|
|
48
|
+
this.#contentContainer.addChild(this.#formatHeader(colorKey));
|
|
49
49
|
|
|
50
|
-
this
|
|
50
|
+
this.#loader = new Loader(
|
|
51
51
|
ui,
|
|
52
52
|
spinner => theme.fg(colorKey, spinner),
|
|
53
53
|
text => theme.fg("muted", text),
|
|
54
54
|
`Running… (esc to cancel)`,
|
|
55
55
|
getSymbolTheme().spinnerFrames,
|
|
56
56
|
);
|
|
57
|
-
this
|
|
57
|
+
this.#contentContainer.addChild(this.#loader);
|
|
58
58
|
|
|
59
59
|
this.addChild(new DynamicBorder(borderColor));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
setExpanded(expanded: boolean): void {
|
|
63
|
-
this
|
|
64
|
-
this
|
|
63
|
+
this.#expanded = expanded;
|
|
64
|
+
this.#updateDisplay();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
override invalidate(): void {
|
|
68
68
|
super.invalidate();
|
|
69
|
-
this
|
|
69
|
+
this.#updateDisplay();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
appendOutput(chunk: string): void {
|
|
73
|
-
const clean = this
|
|
73
|
+
const clean = this.#normalizeOutput(chunk);
|
|
74
74
|
|
|
75
75
|
const newLines = clean.split("\n");
|
|
76
|
-
if (this
|
|
77
|
-
this
|
|
78
|
-
this
|
|
76
|
+
if (this.#outputLines.length > 0 && newLines.length > 0) {
|
|
77
|
+
this.#outputLines[this.#outputLines.length - 1] += newLines[0];
|
|
78
|
+
this.#outputLines.push(...newLines.slice(1));
|
|
79
79
|
} else {
|
|
80
|
-
this
|
|
80
|
+
this.#outputLines.push(...newLines);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
this
|
|
83
|
+
this.#updateDisplay();
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
setComplete(
|
|
@@ -88,39 +88,39 @@ export class PythonExecutionComponent extends Container {
|
|
|
88
88
|
cancelled: boolean,
|
|
89
89
|
options?: { output?: string; truncation?: TruncationMeta },
|
|
90
90
|
): void {
|
|
91
|
-
this
|
|
92
|
-
this
|
|
91
|
+
this.#exitCode = exitCode;
|
|
92
|
+
this.#status = cancelled
|
|
93
93
|
? "cancelled"
|
|
94
94
|
: exitCode !== 0 && exitCode !== undefined && exitCode !== null
|
|
95
95
|
? "error"
|
|
96
96
|
: "complete";
|
|
97
|
-
this
|
|
97
|
+
this.#truncation = options?.truncation;
|
|
98
98
|
if (options?.output !== undefined) {
|
|
99
|
-
this
|
|
99
|
+
this.#setOutput(options.output);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
this
|
|
103
|
-
this
|
|
102
|
+
this.#loader.stop();
|
|
103
|
+
this.#updateDisplay();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
const availableLines = this
|
|
106
|
+
#updateDisplay(): void {
|
|
107
|
+
const availableLines = this.#outputLines;
|
|
108
108
|
const previewLogicalLines = availableLines.slice(-PREVIEW_LINES);
|
|
109
109
|
const hiddenLineCount = availableLines.length - previewLogicalLines.length;
|
|
110
110
|
|
|
111
|
-
this
|
|
111
|
+
this.#contentContainer.clear();
|
|
112
112
|
|
|
113
113
|
const colorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
114
|
-
this
|
|
114
|
+
this.#contentContainer.addChild(this.#formatHeader(colorKey));
|
|
115
115
|
|
|
116
116
|
if (availableLines.length > 0) {
|
|
117
|
-
if (this
|
|
117
|
+
if (this.#expanded) {
|
|
118
118
|
const displayText = availableLines.map(line => theme.fg("muted", line)).join("\n");
|
|
119
|
-
this
|
|
119
|
+
this.#contentContainer.addChild(new Text(`\n${displayText}`, 1, 0));
|
|
120
120
|
} else {
|
|
121
121
|
const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n");
|
|
122
122
|
const previewText = `\n${styledOutput}`;
|
|
123
|
-
this
|
|
123
|
+
this.#contentContainer.addChild({
|
|
124
124
|
render: (width: number) => {
|
|
125
125
|
const { visualLines } = truncateToVisualLines(previewText, PREVIEW_LINES, width, 1);
|
|
126
126
|
return visualLines;
|
|
@@ -130,8 +130,8 @@ export class PythonExecutionComponent extends Container {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
if (this
|
|
134
|
-
this
|
|
133
|
+
if (this.#status === "running") {
|
|
134
|
+
this.#contentContainer.addChild(this.#loader);
|
|
135
135
|
} else {
|
|
136
136
|
const statusParts: string[] = [];
|
|
137
137
|
|
|
@@ -139,46 +139,46 @@ export class PythonExecutionComponent extends Container {
|
|
|
139
139
|
statusParts.push(theme.fg("dim", `… ${hiddenLineCount} more lines (ctrl+o to expand)`));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
if (this
|
|
142
|
+
if (this.#status === "cancelled") {
|
|
143
143
|
statusParts.push(theme.fg("warning", "(cancelled)"));
|
|
144
|
-
} else if (this
|
|
145
|
-
statusParts.push(theme.fg("error", `(exit ${this
|
|
144
|
+
} else if (this.#status === "error") {
|
|
145
|
+
statusParts.push(theme.fg("error", `(exit ${this.#exitCode})`));
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
if (this
|
|
148
|
+
if (this.#truncation) {
|
|
149
149
|
const warnings: string[] = [];
|
|
150
|
-
if (this
|
|
151
|
-
warnings.push(`Full output: artifact://${this
|
|
150
|
+
if (this.#truncation.artifactId) {
|
|
151
|
+
warnings.push(`Full output: artifact://${this.#truncation.artifactId}`);
|
|
152
152
|
}
|
|
153
|
-
if (this
|
|
153
|
+
if (this.#truncation.truncatedBy === "lines") {
|
|
154
154
|
warnings.push(
|
|
155
|
-
`Truncated: showing ${this
|
|
155
|
+
`Truncated: showing ${this.#truncation.outputLines} of ${this.#truncation.totalLines} lines`,
|
|
156
156
|
);
|
|
157
157
|
} else {
|
|
158
158
|
warnings.push(
|
|
159
|
-
`Truncated: ${this
|
|
159
|
+
`Truncated: ${this.#truncation.outputLines} lines shown (${formatSize(this.#truncation.outputBytes)} limit)`,
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
statusParts.push(theme.fg("warning", warnings.join(". ")));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
if (statusParts.length > 0) {
|
|
166
|
-
this
|
|
166
|
+
this.#contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
#normalizeOutput(text: string): string {
|
|
172
172
|
return Bun.stripANSI(text).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
const clean = this
|
|
177
|
-
this
|
|
175
|
+
#setOutput(output: string): void {
|
|
176
|
+
const clean = this.#normalizeOutput(output);
|
|
177
|
+
this.#outputLines = clean ? clean.split("\n") : [];
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
getOutput(): string {
|
|
181
|
-
return this
|
|
181
|
+
return this.#outputLines.join("\n");
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
getCode(): string {
|