@oh-my-pi/pi-coding-agent 6.9.0 → 6.9.69
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 +41 -0
- package/package.json +6 -5
- package/src/cli/stats-cli.ts +191 -0
- package/src/core/agent-session.ts +103 -1
- package/src/core/extensions/index.ts +2 -0
- package/src/core/extensions/runner.ts +31 -0
- package/src/core/extensions/types.ts +24 -0
- package/src/core/messages.ts +48 -0
- package/src/core/session-manager.ts +10 -1
- package/src/core/tools/bash.ts +5 -7
- package/src/core/tools/index.ts +1 -1
- package/src/core/tools/patch/applicator.ts +115 -17
- package/src/core/tools/patch/index.ts +1 -1
- package/src/core/tools/patch/normalize.ts +185 -10
- package/src/core/tools/python.ts +444 -86
- package/src/core/tools/task/executor.ts +2 -6
- package/src/core/tools/task/index.ts +30 -12
- package/src/core/tools/task/render.ts +163 -30
- package/src/core/tools/task/template.ts +37 -0
- package/src/core/tools/task/types.ts +6 -2
- package/src/core/tools/task/worker.ts +1 -1
- package/src/index.ts +2 -0
- package/src/main.ts +12 -0
- package/src/modes/interactive/components/python-execution.ts +180 -0
- package/src/modes/interactive/components/welcome.ts +1 -0
- package/src/modes/interactive/controllers/command-controller.ts +46 -0
- package/src/modes/interactive/controllers/input-controller.ts +28 -1
- package/src/modes/interactive/interactive-mode.ts +10 -0
- package/src/modes/interactive/theme/dark.json +2 -9
- package/src/modes/interactive/theme/defaults/alabaster.json +2 -8
- package/src/modes/interactive/theme/defaults/amethyst.json +2 -9
- package/src/modes/interactive/theme/defaults/anthracite.json +2 -9
- package/src/modes/interactive/theme/defaults/basalt.json +89 -88
- package/src/modes/interactive/theme/defaults/birch.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-abyss.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-arctic.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-aurora.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +2 -1
- package/src/modes/interactive/theme/defaults/dark-cavern.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-copper.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-cosmos.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-dracula.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-eclipse.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-ember.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-equinox.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-forest.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-github.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-lavender.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-lunar.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-midnight.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-monokai.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-nebula.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-nord.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-ocean.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-one.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-rainforest.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-reef.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-retro.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +2 -1
- package/src/modes/interactive/theme/defaults/dark-sakura.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-slate.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-solarized.json +2 -1
- package/src/modes/interactive/theme/defaults/dark-solstice.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-starfall.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-sunset.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-swamp.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +2 -1
- package/src/modes/interactive/theme/defaults/dark-taiga.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-terminal.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +2 -9
- package/src/modes/interactive/theme/defaults/dark-tundra.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-twilight.json +2 -8
- package/src/modes/interactive/theme/defaults/dark-volcanic.json +2 -8
- package/src/modes/interactive/theme/defaults/graphite.json +2 -9
- package/src/modes/interactive/theme/defaults/light-arctic.json +2 -1
- package/src/modes/interactive/theme/defaults/light-aurora-day.json +2 -8
- package/src/modes/interactive/theme/defaults/light-canyon.json +2 -8
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +2 -1
- package/src/modes/interactive/theme/defaults/light-cirrus.json +2 -8
- package/src/modes/interactive/theme/defaults/light-coral.json +3 -2
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +2 -9
- package/src/modes/interactive/theme/defaults/light-dawn.json +2 -8
- package/src/modes/interactive/theme/defaults/light-dunes.json +2 -8
- package/src/modes/interactive/theme/defaults/light-eucalyptus.json +3 -2
- package/src/modes/interactive/theme/defaults/light-forest.json +2 -9
- package/src/modes/interactive/theme/defaults/light-frost.json +3 -2
- package/src/modes/interactive/theme/defaults/light-github.json +2 -1
- package/src/modes/interactive/theme/defaults/light-glacier.json +2 -8
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +2 -9
- package/src/modes/interactive/theme/defaults/light-haze.json +2 -8
- package/src/modes/interactive/theme/defaults/light-honeycomb.json +3 -2
- package/src/modes/interactive/theme/defaults/light-lagoon.json +2 -8
- package/src/modes/interactive/theme/defaults/light-lavender.json +3 -2
- package/src/modes/interactive/theme/defaults/light-meadow.json +2 -8
- package/src/modes/interactive/theme/defaults/light-mint.json +3 -2
- package/src/modes/interactive/theme/defaults/light-monochrome.json +2 -1
- package/src/modes/interactive/theme/defaults/light-ocean.json +2 -9
- package/src/modes/interactive/theme/defaults/light-one.json +2 -8
- package/src/modes/interactive/theme/defaults/light-opal.json +2 -8
- package/src/modes/interactive/theme/defaults/light-orchard.json +2 -8
- package/src/modes/interactive/theme/defaults/light-paper.json +3 -2
- package/src/modes/interactive/theme/defaults/light-prism.json +2 -8
- package/src/modes/interactive/theme/defaults/light-retro.json +2 -9
- package/src/modes/interactive/theme/defaults/light-sand.json +3 -2
- package/src/modes/interactive/theme/defaults/light-savanna.json +2 -8
- package/src/modes/interactive/theme/defaults/light-solarized.json +2 -1
- package/src/modes/interactive/theme/defaults/light-soleil.json +2 -8
- package/src/modes/interactive/theme/defaults/light-sunset.json +2 -9
- package/src/modes/interactive/theme/defaults/light-synthwave.json +2 -9
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +2 -9
- package/src/modes/interactive/theme/defaults/light-wetland.json +2 -8
- package/src/modes/interactive/theme/defaults/light-zenith.json +2 -8
- package/src/modes/interactive/theme/defaults/limestone.json +2 -8
- package/src/modes/interactive/theme/defaults/mahogany.json +2 -9
- package/src/modes/interactive/theme/defaults/marble.json +2 -8
- package/src/modes/interactive/theme/defaults/obsidian.json +89 -88
- package/src/modes/interactive/theme/defaults/onyx.json +89 -88
- package/src/modes/interactive/theme/defaults/pearl.json +2 -8
- package/src/modes/interactive/theme/defaults/porcelain.json +89 -88
- package/src/modes/interactive/theme/defaults/quartz.json +2 -8
- package/src/modes/interactive/theme/defaults/sandstone.json +2 -8
- package/src/modes/interactive/theme/defaults/titanium.json +88 -87
- package/src/modes/interactive/theme/light.json +2 -8
- package/src/modes/interactive/theme/theme-schema.json +5 -0
- package/src/modes/interactive/theme/theme.ts +7 -0
- package/src/modes/interactive/types.ts +5 -0
- package/src/modes/interactive/utils/ui-helpers.ts +20 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/python.md +40 -2
- package/src/prompts/tools/task.md +8 -13
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component for displaying user-initiated Python execution with streaming output.
|
|
3
|
+
* Shares the same kernel session as the agent's Python tool.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Container, Loader, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
7
|
+
import stripAnsi from "strip-ansi";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_MAX_BYTES,
|
|
10
|
+
DEFAULT_MAX_LINES,
|
|
11
|
+
type TruncationResult,
|
|
12
|
+
truncateTail,
|
|
13
|
+
} from "../../../core/tools/truncate";
|
|
14
|
+
import { getSymbolTheme, highlightCode, theme } from "../theme/theme";
|
|
15
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
16
|
+
import { truncateToVisualLines } from "./visual-truncate";
|
|
17
|
+
|
|
18
|
+
const PREVIEW_LINES = 20;
|
|
19
|
+
|
|
20
|
+
export class PythonExecutionComponent extends Container {
|
|
21
|
+
private code: string;
|
|
22
|
+
private outputLines: string[] = [];
|
|
23
|
+
private status: "running" | "complete" | "cancelled" | "error" = "running";
|
|
24
|
+
private exitCode: number | undefined = undefined;
|
|
25
|
+
private loader: Loader;
|
|
26
|
+
private truncationResult?: TruncationResult;
|
|
27
|
+
private fullOutputPath?: string;
|
|
28
|
+
private expanded = false;
|
|
29
|
+
private contentContainer: Container;
|
|
30
|
+
private excludeFromContext: boolean;
|
|
31
|
+
|
|
32
|
+
private formatHeader(colorKey: "dim" | "pythonMode"): Text {
|
|
33
|
+
const prompt = theme.fg(colorKey, theme.bold(">>>"));
|
|
34
|
+
const continuation = theme.fg(colorKey, " ");
|
|
35
|
+
const codeLines = highlightCode(this.code, "python");
|
|
36
|
+
const headerLines = codeLines.map((line, index) =>
|
|
37
|
+
index === 0 ? `${prompt} ${line}` : `${continuation}${line}`,
|
|
38
|
+
);
|
|
39
|
+
return new Text(headerLines.join("\n"), 1, 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
constructor(code: string, ui: TUI, excludeFromContext = false) {
|
|
43
|
+
super();
|
|
44
|
+
this.code = code;
|
|
45
|
+
this.excludeFromContext = excludeFromContext;
|
|
46
|
+
|
|
47
|
+
const colorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
48
|
+
const borderColor = (str: string) => theme.fg(colorKey, str);
|
|
49
|
+
|
|
50
|
+
this.addChild(new Spacer(1));
|
|
51
|
+
this.addChild(new DynamicBorder(borderColor));
|
|
52
|
+
|
|
53
|
+
this.contentContainer = new Container();
|
|
54
|
+
this.addChild(this.contentContainer);
|
|
55
|
+
this.contentContainer.addChild(this.formatHeader(colorKey));
|
|
56
|
+
|
|
57
|
+
this.loader = new Loader(
|
|
58
|
+
ui,
|
|
59
|
+
(spinner) => theme.fg(colorKey, spinner),
|
|
60
|
+
(text) => theme.fg("muted", text),
|
|
61
|
+
`Running${theme.format.ellipsis} (esc to cancel)`,
|
|
62
|
+
getSymbolTheme().spinnerFrames,
|
|
63
|
+
);
|
|
64
|
+
this.contentContainer.addChild(this.loader);
|
|
65
|
+
|
|
66
|
+
this.addChild(new DynamicBorder(borderColor));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setExpanded(expanded: boolean): void {
|
|
70
|
+
this.expanded = expanded;
|
|
71
|
+
this.updateDisplay();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override invalidate(): void {
|
|
75
|
+
super.invalidate();
|
|
76
|
+
this.updateDisplay();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
appendOutput(chunk: string): void {
|
|
80
|
+
const clean = stripAnsi(chunk).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
81
|
+
|
|
82
|
+
const newLines = clean.split("\n");
|
|
83
|
+
if (this.outputLines.length > 0 && newLines.length > 0) {
|
|
84
|
+
this.outputLines[this.outputLines.length - 1] += newLines[0];
|
|
85
|
+
this.outputLines.push(...newLines.slice(1));
|
|
86
|
+
} else {
|
|
87
|
+
this.outputLines.push(...newLines);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.updateDisplay();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setComplete(
|
|
94
|
+
exitCode: number | undefined,
|
|
95
|
+
cancelled: boolean,
|
|
96
|
+
truncationResult?: TruncationResult,
|
|
97
|
+
fullOutputPath?: string,
|
|
98
|
+
): void {
|
|
99
|
+
this.exitCode = exitCode;
|
|
100
|
+
this.status = cancelled
|
|
101
|
+
? "cancelled"
|
|
102
|
+
: exitCode !== 0 && exitCode !== undefined && exitCode !== null
|
|
103
|
+
? "error"
|
|
104
|
+
: "complete";
|
|
105
|
+
this.truncationResult = truncationResult;
|
|
106
|
+
this.fullOutputPath = fullOutputPath;
|
|
107
|
+
|
|
108
|
+
this.loader.stop();
|
|
109
|
+
this.updateDisplay();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private updateDisplay(): void {
|
|
113
|
+
const fullOutput = this.outputLines.join("\n");
|
|
114
|
+
const contextTruncation = truncateTail(fullOutput, {
|
|
115
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
116
|
+
maxBytes: DEFAULT_MAX_BYTES,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const availableLines = contextTruncation.content ? contextTruncation.content.split("\n") : [];
|
|
120
|
+
const previewLogicalLines = availableLines.slice(-PREVIEW_LINES);
|
|
121
|
+
const hiddenLineCount = availableLines.length - previewLogicalLines.length;
|
|
122
|
+
|
|
123
|
+
this.contentContainer.clear();
|
|
124
|
+
|
|
125
|
+
const colorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
126
|
+
this.contentContainer.addChild(this.formatHeader(colorKey));
|
|
127
|
+
|
|
128
|
+
if (availableLines.length > 0) {
|
|
129
|
+
if (this.expanded) {
|
|
130
|
+
const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n");
|
|
131
|
+
this.contentContainer.addChild(new Text(`\n${displayText}`, 1, 0));
|
|
132
|
+
} else {
|
|
133
|
+
const styledOutput = previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n");
|
|
134
|
+
const previewText = `\n${styledOutput}`;
|
|
135
|
+
this.contentContainer.addChild({
|
|
136
|
+
render: (width: number) => {
|
|
137
|
+
const { visualLines } = truncateToVisualLines(previewText, PREVIEW_LINES, width, 1);
|
|
138
|
+
return visualLines;
|
|
139
|
+
},
|
|
140
|
+
invalidate: () => {},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this.status === "running") {
|
|
146
|
+
this.contentContainer.addChild(this.loader);
|
|
147
|
+
} else {
|
|
148
|
+
const statusParts: string[] = [];
|
|
149
|
+
|
|
150
|
+
if (hiddenLineCount > 0) {
|
|
151
|
+
statusParts.push(
|
|
152
|
+
theme.fg("dim", `${theme.format.ellipsis} ${hiddenLineCount} more lines (ctrl+o to expand)`),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (this.status === "cancelled") {
|
|
157
|
+
statusParts.push(theme.fg("warning", "(cancelled)"));
|
|
158
|
+
} else if (this.status === "error") {
|
|
159
|
+
statusParts.push(theme.fg("error", `(exit ${this.exitCode})`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;
|
|
163
|
+
if (wasTruncated && this.fullOutputPath) {
|
|
164
|
+
statusParts.push(theme.fg("warning", `Output truncated. Full output: ${this.fullOutputPath}`));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (statusParts.length > 0) {
|
|
168
|
+
this.contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getOutput(): string {
|
|
174
|
+
return this.outputLines.join("\n");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getCode(): string {
|
|
178
|
+
return this.code;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -117,6 +117,7 @@ export class WelcomeComponent implements Component {
|
|
|
117
117
|
` ${theme.fg("dim", "?")}${theme.fg("muted", " for keyboard shortcuts")}`,
|
|
118
118
|
` ${theme.fg("dim", "/")}${theme.fg("muted", " for commands")}`,
|
|
119
119
|
` ${theme.fg("dim", "!")}${theme.fg("muted", " to run bash")}`,
|
|
120
|
+
` ${theme.fg("dim", "$")}${theme.fg("muted", " to run python")}`,
|
|
120
121
|
separator,
|
|
121
122
|
` ${theme.bold(theme.fg("accent", "LSP Servers"))}`,
|
|
122
123
|
...lspLines,
|
|
@@ -16,6 +16,7 @@ import { ArminComponent } from "../components/armin";
|
|
|
16
16
|
import { BashExecutionComponent } from "../components/bash-execution";
|
|
17
17
|
import { BorderedLoader } from "../components/bordered-loader";
|
|
18
18
|
import { DynamicBorder } from "../components/dynamic-border";
|
|
19
|
+
import { PythonExecutionComponent } from "../components/python-execution";
|
|
19
20
|
import { getMarkdownTheme, getSymbolTheme, theme } from "../theme/theme";
|
|
20
21
|
import type { InteractiveModeContext } from "../types";
|
|
21
22
|
|
|
@@ -344,6 +345,8 @@ export class CommandController {
|
|
|
344
345
|
| \`/\` | Slash commands |
|
|
345
346
|
| \`!\` | Run bash command |
|
|
346
347
|
| \`!!\` | Run bash command (excluded from context) |
|
|
348
|
+
| \`$\` | Run Python in shared kernel |
|
|
349
|
+
| \`$$\` | Run Python (excluded from context) |
|
|
347
350
|
`;
|
|
348
351
|
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
349
352
|
this.ctx.chatContainer.addChild(new DynamicBorder());
|
|
@@ -471,6 +474,49 @@ export class CommandController {
|
|
|
471
474
|
this.ctx.ui.requestRender();
|
|
472
475
|
}
|
|
473
476
|
|
|
477
|
+
async handlePythonCommand(code: string, excludeFromContext = false): Promise<void> {
|
|
478
|
+
const isDeferred = this.ctx.session.isStreaming;
|
|
479
|
+
this.ctx.pythonComponent = new PythonExecutionComponent(code, this.ctx.ui, excludeFromContext);
|
|
480
|
+
|
|
481
|
+
if (isDeferred) {
|
|
482
|
+
this.ctx.pendingMessagesContainer.addChild(this.ctx.pythonComponent);
|
|
483
|
+
this.ctx.pendingPythonComponents.push(this.ctx.pythonComponent);
|
|
484
|
+
} else {
|
|
485
|
+
this.ctx.chatContainer.addChild(this.ctx.pythonComponent);
|
|
486
|
+
}
|
|
487
|
+
this.ctx.ui.requestRender();
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
const result = await this.ctx.session.executePython(
|
|
491
|
+
code,
|
|
492
|
+
(chunk) => {
|
|
493
|
+
if (this.ctx.pythonComponent) {
|
|
494
|
+
this.ctx.pythonComponent.appendOutput(chunk);
|
|
495
|
+
this.ctx.ui.requestRender();
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
{ excludeFromContext },
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
if (this.ctx.pythonComponent) {
|
|
502
|
+
this.ctx.pythonComponent.setComplete(
|
|
503
|
+
result.exitCode,
|
|
504
|
+
result.cancelled,
|
|
505
|
+
result.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,
|
|
506
|
+
result.fullOutputPath,
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (this.ctx.pythonComponent) {
|
|
511
|
+
this.ctx.pythonComponent.setComplete(undefined, false);
|
|
512
|
+
}
|
|
513
|
+
this.ctx.showError(`Python execution failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.ctx.pythonComponent = undefined;
|
|
517
|
+
this.ctx.ui.requestRender();
|
|
518
|
+
}
|
|
519
|
+
|
|
474
520
|
async handleCompactCommand(customInstructions?: string): Promise<void> {
|
|
475
521
|
const entries = this.ctx.sessionManager.getEntries();
|
|
476
522
|
const messageCount = entries.filter((e) => e.type === "message").length;
|
|
@@ -31,6 +31,12 @@ export class InputController {
|
|
|
31
31
|
this.ctx.editor.setText("");
|
|
32
32
|
this.ctx.isBashMode = false;
|
|
33
33
|
this.ctx.updateEditorBorderColor();
|
|
34
|
+
} else if (this.ctx.isPythonMode) {
|
|
35
|
+
this.ctx.editor.setText("");
|
|
36
|
+
this.ctx.isPythonMode = false;
|
|
37
|
+
this.ctx.updateEditorBorderColor();
|
|
38
|
+
} else if (this.ctx.session.isPythonRunning) {
|
|
39
|
+
this.ctx.session.abortPython();
|
|
34
40
|
} else if (!this.ctx.editor.getText().trim()) {
|
|
35
41
|
// Double-escape with empty editor triggers /tree or /branch based on setting
|
|
36
42
|
const now = Date.now();
|
|
@@ -83,8 +89,11 @@ export class InputController {
|
|
|
83
89
|
|
|
84
90
|
this.ctx.editor.onChange = (text: string) => {
|
|
85
91
|
const wasBashMode = this.ctx.isBashMode;
|
|
92
|
+
const wasPythonMode = this.ctx.isPythonMode;
|
|
93
|
+
const trimmed = text.trimStart();
|
|
86
94
|
this.ctx.isBashMode = text.trimStart().startsWith("!");
|
|
87
|
-
|
|
95
|
+
this.ctx.isPythonMode = trimmed.startsWith("$") && !trimmed.startsWith("${");
|
|
96
|
+
if (wasBashMode !== this.ctx.isBashMode || wasPythonMode !== this.ctx.isPythonMode) {
|
|
88
97
|
this.ctx.updateEditorBorderColor();
|
|
89
98
|
}
|
|
90
99
|
};
|
|
@@ -309,6 +318,24 @@ export class InputController {
|
|
|
309
318
|
}
|
|
310
319
|
}
|
|
311
320
|
|
|
321
|
+
// Handle python command ($ for normal, $$ for excluded from context)
|
|
322
|
+
if (text.startsWith("$")) {
|
|
323
|
+
const isExcluded = text.startsWith("$$");
|
|
324
|
+
const code = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
|
|
325
|
+
if (code) {
|
|
326
|
+
if (this.ctx.session.isPythonRunning) {
|
|
327
|
+
this.ctx.showWarning("A Python execution is already running. Press Esc to cancel it first.");
|
|
328
|
+
this.ctx.editor.setText(text);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.ctx.editor.addToHistory(text);
|
|
332
|
+
await this.ctx.handlePythonCommand(code, isExcluded);
|
|
333
|
+
this.ctx.isPythonMode = false;
|
|
334
|
+
this.ctx.updateEditorBorderColor();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
312
339
|
// Queue input during compaction
|
|
313
340
|
if (this.ctx.session.isCompacting) {
|
|
314
341
|
if (this.ctx.pendingImages.length > 0) {
|
|
@@ -35,6 +35,7 @@ import { DynamicBorder } from "./components/dynamic-border";
|
|
|
35
35
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
36
36
|
import type { HookInputComponent } from "./components/hook-input";
|
|
37
37
|
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
38
|
+
import type { PythonExecutionComponent } from "./components/python-execution";
|
|
38
39
|
import { StatusLineComponent } from "./components/status-line";
|
|
39
40
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
40
41
|
import { WelcomeComponent } from "./components/welcome";
|
|
@@ -93,6 +94,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
93
94
|
public pendingTools = new Map<string, ToolExecutionHandle>();
|
|
94
95
|
public pendingBashComponents: BashExecutionComponent[] = [];
|
|
95
96
|
public bashComponent: BashExecutionComponent | undefined = undefined;
|
|
97
|
+
public pendingPythonComponents: PythonExecutionComponent[] = [];
|
|
98
|
+
public pythonComponent: PythonExecutionComponent | undefined = undefined;
|
|
99
|
+
public isPythonMode = false;
|
|
96
100
|
public streamingComponent: AssistantMessageComponent | undefined = undefined;
|
|
97
101
|
public streamingMessage: AssistantMessage | undefined = undefined;
|
|
98
102
|
public loadingAnimation: Loader | undefined = undefined;
|
|
@@ -367,6 +371,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
367
371
|
updateEditorBorderColor(): void {
|
|
368
372
|
if (this.isBashMode) {
|
|
369
373
|
this.editor.borderColor = theme.getBashModeBorderColor();
|
|
374
|
+
} else if (this.isPythonMode) {
|
|
375
|
+
this.editor.borderColor = theme.getPythonModeBorderColor();
|
|
370
376
|
} else {
|
|
371
377
|
const level = this.session.thinkingLevel || "off";
|
|
372
378
|
this.editor.borderColor = theme.getThinkingBorderColor(level);
|
|
@@ -631,6 +637,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
631
637
|
return this.commandController.handleBashCommand(command, excludeFromContext);
|
|
632
638
|
}
|
|
633
639
|
|
|
640
|
+
handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void> {
|
|
641
|
+
return this.commandController.handlePythonCommand(code, excludeFromContext);
|
|
642
|
+
}
|
|
643
|
+
|
|
634
644
|
handleCompactCommand(customInstructions?: string): Promise<void> {
|
|
635
645
|
return this.commandController.handleCompactCommand(customInstructions);
|
|
636
646
|
}
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"dim": "dimGray",
|
|
31
31
|
"text": "",
|
|
32
32
|
"thinkingText": "gray",
|
|
33
|
-
|
|
34
33
|
"selectedBg": "selectedBg",
|
|
35
34
|
"userMessageBg": "userMsgBg",
|
|
36
35
|
"userMessageText": "",
|
|
@@ -42,7 +41,6 @@
|
|
|
42
41
|
"toolErrorBg": "toolErrorBg",
|
|
43
42
|
"toolTitle": "",
|
|
44
43
|
"toolOutput": "gray",
|
|
45
|
-
|
|
46
44
|
"mdHeading": "#febc38",
|
|
47
45
|
"mdLink": "#0088fa",
|
|
48
46
|
"mdLinkUrl": "dimGray",
|
|
@@ -53,13 +51,10 @@
|
|
|
53
51
|
"mdQuoteBorder": "darkGray",
|
|
54
52
|
"mdHr": "darkGray",
|
|
55
53
|
"mdListBullet": "accent",
|
|
56
|
-
|
|
57
54
|
"toolDiffAdded": "green",
|
|
58
55
|
"toolDiffRemoved": "red",
|
|
59
56
|
"toolDiffContext": "gray",
|
|
60
|
-
|
|
61
57
|
"link": "#0088fa",
|
|
62
|
-
|
|
63
58
|
"syntaxComment": "#6A9955",
|
|
64
59
|
"syntaxKeyword": "#569CD6",
|
|
65
60
|
"syntaxFunction": "#DCDCAA",
|
|
@@ -69,16 +64,13 @@
|
|
|
69
64
|
"syntaxType": "#4EC9B0",
|
|
70
65
|
"syntaxOperator": "#D4D4D4",
|
|
71
66
|
"syntaxPunctuation": "#D4D4D4",
|
|
72
|
-
|
|
73
67
|
"thinkingOff": "darkGray",
|
|
74
68
|
"thinkingMinimal": "dimGray",
|
|
75
69
|
"thinkingLow": "#178fb9",
|
|
76
70
|
"thinkingMedium": "#0088fa",
|
|
77
71
|
"thinkingHigh": "#b281d6",
|
|
78
72
|
"thinkingXhigh": "#e5c1ff",
|
|
79
|
-
|
|
80
73
|
"bashMode": "cyan",
|
|
81
|
-
|
|
82
74
|
"statusLineBg": "#121212",
|
|
83
75
|
"statusLineSep": 244,
|
|
84
76
|
"statusLineModel": "#d787af",
|
|
@@ -92,7 +84,8 @@
|
|
|
92
84
|
"statusLineUntracked": 39,
|
|
93
85
|
"statusLineOutput": 205,
|
|
94
86
|
"statusLineCost": 205,
|
|
95
|
-
"statusLineSubagents": "accent"
|
|
87
|
+
"statusLineSubagents": "accent",
|
|
88
|
+
"pythonMode": "yellow"
|
|
96
89
|
},
|
|
97
90
|
"export": {
|
|
98
91
|
"pageBg": "#18181e",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"dim": "#909098",
|
|
30
30
|
"text": "",
|
|
31
31
|
"thinkingText": "carvedDepth",
|
|
32
|
-
|
|
33
32
|
"selectedBg": "selectedBg",
|
|
34
33
|
"userMessageBg": "userMsgBg",
|
|
35
34
|
"userMessageText": "",
|
|
@@ -41,7 +40,6 @@
|
|
|
41
40
|
"toolErrorBg": "toolErrorBg",
|
|
42
41
|
"toolTitle": "",
|
|
43
42
|
"toolOutput": "carvedDepth",
|
|
44
|
-
|
|
45
43
|
"mdHeading": "chiseledBlack",
|
|
46
44
|
"mdLink": "shadowGray",
|
|
47
45
|
"mdLinkUrl": "carvedDepth",
|
|
@@ -52,11 +50,9 @@
|
|
|
52
50
|
"mdQuoteBorder": "subtleShadow",
|
|
53
51
|
"mdHr": "subtleShadow",
|
|
54
52
|
"mdListBullet": "shadowGray",
|
|
55
|
-
|
|
56
53
|
"toolDiffAdded": "#405840",
|
|
57
54
|
"toolDiffRemoved": "#704040",
|
|
58
55
|
"toolDiffContext": "carvedDepth",
|
|
59
|
-
|
|
60
56
|
"syntaxComment": "#707880",
|
|
61
57
|
"syntaxKeyword": "#303840",
|
|
62
58
|
"syntaxFunction": "chiseledBlack",
|
|
@@ -66,16 +62,13 @@
|
|
|
66
62
|
"syntaxType": "#303840",
|
|
67
63
|
"syntaxOperator": "#404850",
|
|
68
64
|
"syntaxPunctuation": "#505860",
|
|
69
|
-
|
|
70
65
|
"thinkingOff": "#c0c0c8",
|
|
71
66
|
"thinkingMinimal": "#a0a0a8",
|
|
72
67
|
"thinkingLow": "#808090",
|
|
73
68
|
"thinkingMedium": "#606870",
|
|
74
69
|
"thinkingHigh": "#505860",
|
|
75
70
|
"thinkingXhigh": "#303840",
|
|
76
|
-
|
|
77
71
|
"bashMode": "#405840",
|
|
78
|
-
|
|
79
72
|
"statusLineBg": "#ececf0",
|
|
80
73
|
"statusLineSep": "#a0a0a8",
|
|
81
74
|
"statusLineModel": "#505860",
|
|
@@ -89,7 +82,8 @@
|
|
|
89
82
|
"statusLineUntracked": 31,
|
|
90
83
|
"statusLineOutput": 133,
|
|
91
84
|
"statusLineCost": 133,
|
|
92
|
-
"statusLineSubagents": "shadowGray"
|
|
85
|
+
"statusLineSubagents": "shadowGray",
|
|
86
|
+
"pythonMode": "#f0c040"
|
|
93
87
|
},
|
|
94
88
|
"export": {
|
|
95
89
|
"pageBg": "#fdfcfb",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"dim": "#6a5a78",
|
|
32
32
|
"text": "",
|
|
33
33
|
"thinkingText": "#8b7a99",
|
|
34
|
-
|
|
35
34
|
"selectedBg": "selectedBg",
|
|
36
35
|
"userMessageBg": "userMsgBg",
|
|
37
36
|
"userMessageText": "",
|
|
@@ -43,7 +42,6 @@
|
|
|
43
42
|
"toolErrorBg": "toolErrorBg",
|
|
44
43
|
"toolTitle": "",
|
|
45
44
|
"toolOutput": "#8b7a99",
|
|
46
|
-
|
|
47
45
|
"mdHeading": "amethyst",
|
|
48
46
|
"mdLink": "violet",
|
|
49
47
|
"mdLinkUrl": "#6a5a78",
|
|
@@ -54,13 +52,10 @@
|
|
|
54
52
|
"mdQuoteBorder": "crystalFacet",
|
|
55
53
|
"mdHr": "crystalFacet",
|
|
56
54
|
"mdListBullet": "amethyst",
|
|
57
|
-
|
|
58
55
|
"toolDiffAdded": "#8eb897",
|
|
59
56
|
"toolDiffRemoved": "#d96c75",
|
|
60
57
|
"toolDiffContext": "#8b7a99",
|
|
61
|
-
|
|
62
58
|
"link": "violet",
|
|
63
|
-
|
|
64
59
|
"syntaxComment": "#7a6b88",
|
|
65
60
|
"syntaxKeyword": "amethyst",
|
|
66
61
|
"syntaxFunction": "gold",
|
|
@@ -70,16 +65,13 @@
|
|
|
70
65
|
"syntaxType": "violet",
|
|
71
66
|
"syntaxOperator": "quartz",
|
|
72
67
|
"syntaxPunctuation": "#c0b0cf",
|
|
73
|
-
|
|
74
68
|
"thinkingOff": "crystalFacet",
|
|
75
69
|
"thinkingMinimal": "#6a5a78",
|
|
76
70
|
"thinkingLow": "deepPurple",
|
|
77
71
|
"thinkingMedium": "violet",
|
|
78
72
|
"thinkingHigh": "amethyst",
|
|
79
73
|
"thinkingXhigh": "lavender",
|
|
80
|
-
|
|
81
74
|
"bashMode": "amethyst",
|
|
82
|
-
|
|
83
75
|
"statusLineBg": "caveDeep",
|
|
84
76
|
"statusLineSep": 240,
|
|
85
77
|
"statusLineModel": "roseQuartz",
|
|
@@ -93,7 +85,8 @@
|
|
|
93
85
|
"statusLineUntracked": 183,
|
|
94
86
|
"statusLineOutput": 213,
|
|
95
87
|
"statusLineCost": 213,
|
|
96
|
-
"statusLineSubagents": "amethyst"
|
|
88
|
+
"statusLineSubagents": "amethyst",
|
|
89
|
+
"pythonMode": "gold"
|
|
97
90
|
},
|
|
98
91
|
"export": {
|
|
99
92
|
"pageBg": "caveDark",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"dim": "soot",
|
|
29
29
|
"text": "",
|
|
30
30
|
"thinkingText": "soot",
|
|
31
|
-
|
|
32
31
|
"selectedBg": "#252932",
|
|
33
32
|
"userMessageBg": "#1a1511",
|
|
34
33
|
"userMessageText": "",
|
|
@@ -40,7 +39,6 @@
|
|
|
40
39
|
"toolErrorBg": "#221617",
|
|
41
40
|
"toolTitle": "",
|
|
42
41
|
"toolOutput": "ashGray",
|
|
43
|
-
|
|
44
42
|
"mdHeading": "ember",
|
|
45
43
|
"mdLink": "#6ba3d4",
|
|
46
44
|
"mdLinkUrl": "soot",
|
|
@@ -51,13 +49,10 @@
|
|
|
51
49
|
"mdQuoteBorder": "sootDark",
|
|
52
50
|
"mdHr": "steel",
|
|
53
51
|
"mdListBullet": "ember",
|
|
54
|
-
|
|
55
52
|
"toolDiffAdded": "success",
|
|
56
53
|
"toolDiffRemoved": "error",
|
|
57
54
|
"toolDiffContext": "soot",
|
|
58
|
-
|
|
59
55
|
"link": "#6ba3d4",
|
|
60
|
-
|
|
61
56
|
"syntaxComment": "#6b8099",
|
|
62
57
|
"syntaxKeyword": "#7ba8d4",
|
|
63
58
|
"syntaxFunction": "#e8a069",
|
|
@@ -67,16 +62,13 @@
|
|
|
67
62
|
"syntaxType": "#67b8a8",
|
|
68
63
|
"syntaxOperator": "#b8bfcc",
|
|
69
64
|
"syntaxPunctuation": "#9ca3b5",
|
|
70
|
-
|
|
71
65
|
"thinkingOff": "sootDark",
|
|
72
66
|
"thinkingMinimal": "soot",
|
|
73
67
|
"thinkingLow": "#5f7fa3",
|
|
74
68
|
"thinkingMedium": "#6ba3d4",
|
|
75
69
|
"thinkingHigh": "#c99a6e",
|
|
76
70
|
"thinkingXhigh": "#e8b089",
|
|
77
|
-
|
|
78
71
|
"bashMode": "#6ba3d4",
|
|
79
|
-
|
|
80
72
|
"statusLineBg": "#0f1114",
|
|
81
73
|
"statusLineSep": 240,
|
|
82
74
|
"statusLineModel": "#c99a6e",
|
|
@@ -90,7 +82,8 @@
|
|
|
90
82
|
"statusLineUntracked": 74,
|
|
91
83
|
"statusLineOutput": 173,
|
|
92
84
|
"statusLineCost": 173,
|
|
93
|
-
"statusLineSubagents": "ember"
|
|
85
|
+
"statusLineSubagents": "ember",
|
|
86
|
+
"pythonMode": "#f0c040"
|
|
94
87
|
},
|
|
95
88
|
"export": {
|
|
96
89
|
"pageBg": "#121419",
|