@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.
Files changed (133) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/package.json +6 -5
  3. package/src/cli/stats-cli.ts +191 -0
  4. package/src/core/agent-session.ts +103 -1
  5. package/src/core/extensions/index.ts +2 -0
  6. package/src/core/extensions/runner.ts +31 -0
  7. package/src/core/extensions/types.ts +24 -0
  8. package/src/core/messages.ts +48 -0
  9. package/src/core/session-manager.ts +10 -1
  10. package/src/core/tools/bash.ts +5 -7
  11. package/src/core/tools/index.ts +1 -1
  12. package/src/core/tools/patch/applicator.ts +115 -17
  13. package/src/core/tools/patch/index.ts +1 -1
  14. package/src/core/tools/patch/normalize.ts +185 -10
  15. package/src/core/tools/python.ts +444 -86
  16. package/src/core/tools/task/executor.ts +2 -6
  17. package/src/core/tools/task/index.ts +30 -12
  18. package/src/core/tools/task/render.ts +163 -30
  19. package/src/core/tools/task/template.ts +37 -0
  20. package/src/core/tools/task/types.ts +6 -2
  21. package/src/core/tools/task/worker.ts +1 -1
  22. package/src/index.ts +2 -0
  23. package/src/main.ts +12 -0
  24. package/src/modes/interactive/components/python-execution.ts +180 -0
  25. package/src/modes/interactive/components/welcome.ts +1 -0
  26. package/src/modes/interactive/controllers/command-controller.ts +46 -0
  27. package/src/modes/interactive/controllers/input-controller.ts +28 -1
  28. package/src/modes/interactive/interactive-mode.ts +10 -0
  29. package/src/modes/interactive/theme/dark.json +2 -9
  30. package/src/modes/interactive/theme/defaults/alabaster.json +2 -8
  31. package/src/modes/interactive/theme/defaults/amethyst.json +2 -9
  32. package/src/modes/interactive/theme/defaults/anthracite.json +2 -9
  33. package/src/modes/interactive/theme/defaults/basalt.json +89 -88
  34. package/src/modes/interactive/theme/defaults/birch.json +2 -8
  35. package/src/modes/interactive/theme/defaults/dark-abyss.json +2 -8
  36. package/src/modes/interactive/theme/defaults/dark-arctic.json +2 -9
  37. package/src/modes/interactive/theme/defaults/dark-aurora.json +3 -2
  38. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +2 -1
  39. package/src/modes/interactive/theme/defaults/dark-cavern.json +2 -8
  40. package/src/modes/interactive/theme/defaults/dark-copper.json +3 -2
  41. package/src/modes/interactive/theme/defaults/dark-cosmos.json +2 -8
  42. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +2 -9
  43. package/src/modes/interactive/theme/defaults/dark-dracula.json +2 -9
  44. package/src/modes/interactive/theme/defaults/dark-eclipse.json +2 -8
  45. package/src/modes/interactive/theme/defaults/dark-ember.json +3 -2
  46. package/src/modes/interactive/theme/defaults/dark-equinox.json +2 -8
  47. package/src/modes/interactive/theme/defaults/dark-forest.json +2 -9
  48. package/src/modes/interactive/theme/defaults/dark-github.json +2 -9
  49. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +2 -9
  50. package/src/modes/interactive/theme/defaults/dark-lavender.json +3 -2
  51. package/src/modes/interactive/theme/defaults/dark-lunar.json +2 -8
  52. package/src/modes/interactive/theme/defaults/dark-midnight.json +3 -2
  53. package/src/modes/interactive/theme/defaults/dark-monochrome.json +2 -9
  54. package/src/modes/interactive/theme/defaults/dark-monokai.json +2 -9
  55. package/src/modes/interactive/theme/defaults/dark-nebula.json +2 -8
  56. package/src/modes/interactive/theme/defaults/dark-nord.json +2 -9
  57. package/src/modes/interactive/theme/defaults/dark-ocean.json +2 -9
  58. package/src/modes/interactive/theme/defaults/dark-one.json +2 -9
  59. package/src/modes/interactive/theme/defaults/dark-rainforest.json +2 -8
  60. package/src/modes/interactive/theme/defaults/dark-reef.json +2 -8
  61. package/src/modes/interactive/theme/defaults/dark-retro.json +2 -9
  62. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +2 -1
  63. package/src/modes/interactive/theme/defaults/dark-sakura.json +3 -2
  64. package/src/modes/interactive/theme/defaults/dark-slate.json +3 -2
  65. package/src/modes/interactive/theme/defaults/dark-solarized.json +2 -1
  66. package/src/modes/interactive/theme/defaults/dark-solstice.json +2 -8
  67. package/src/modes/interactive/theme/defaults/dark-starfall.json +2 -8
  68. package/src/modes/interactive/theme/defaults/dark-sunset.json +2 -9
  69. package/src/modes/interactive/theme/defaults/dark-swamp.json +2 -8
  70. package/src/modes/interactive/theme/defaults/dark-synthwave.json +2 -1
  71. package/src/modes/interactive/theme/defaults/dark-taiga.json +2 -8
  72. package/src/modes/interactive/theme/defaults/dark-terminal.json +3 -2
  73. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +2 -9
  74. package/src/modes/interactive/theme/defaults/dark-tundra.json +2 -8
  75. package/src/modes/interactive/theme/defaults/dark-twilight.json +2 -8
  76. package/src/modes/interactive/theme/defaults/dark-volcanic.json +2 -8
  77. package/src/modes/interactive/theme/defaults/graphite.json +2 -9
  78. package/src/modes/interactive/theme/defaults/light-arctic.json +2 -1
  79. package/src/modes/interactive/theme/defaults/light-aurora-day.json +2 -8
  80. package/src/modes/interactive/theme/defaults/light-canyon.json +2 -8
  81. package/src/modes/interactive/theme/defaults/light-catppuccin.json +2 -1
  82. package/src/modes/interactive/theme/defaults/light-cirrus.json +2 -8
  83. package/src/modes/interactive/theme/defaults/light-coral.json +3 -2
  84. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +2 -9
  85. package/src/modes/interactive/theme/defaults/light-dawn.json +2 -8
  86. package/src/modes/interactive/theme/defaults/light-dunes.json +2 -8
  87. package/src/modes/interactive/theme/defaults/light-eucalyptus.json +3 -2
  88. package/src/modes/interactive/theme/defaults/light-forest.json +2 -9
  89. package/src/modes/interactive/theme/defaults/light-frost.json +3 -2
  90. package/src/modes/interactive/theme/defaults/light-github.json +2 -1
  91. package/src/modes/interactive/theme/defaults/light-glacier.json +2 -8
  92. package/src/modes/interactive/theme/defaults/light-gruvbox.json +2 -9
  93. package/src/modes/interactive/theme/defaults/light-haze.json +2 -8
  94. package/src/modes/interactive/theme/defaults/light-honeycomb.json +3 -2
  95. package/src/modes/interactive/theme/defaults/light-lagoon.json +2 -8
  96. package/src/modes/interactive/theme/defaults/light-lavender.json +3 -2
  97. package/src/modes/interactive/theme/defaults/light-meadow.json +2 -8
  98. package/src/modes/interactive/theme/defaults/light-mint.json +3 -2
  99. package/src/modes/interactive/theme/defaults/light-monochrome.json +2 -1
  100. package/src/modes/interactive/theme/defaults/light-ocean.json +2 -9
  101. package/src/modes/interactive/theme/defaults/light-one.json +2 -8
  102. package/src/modes/interactive/theme/defaults/light-opal.json +2 -8
  103. package/src/modes/interactive/theme/defaults/light-orchard.json +2 -8
  104. package/src/modes/interactive/theme/defaults/light-paper.json +3 -2
  105. package/src/modes/interactive/theme/defaults/light-prism.json +2 -8
  106. package/src/modes/interactive/theme/defaults/light-retro.json +2 -9
  107. package/src/modes/interactive/theme/defaults/light-sand.json +3 -2
  108. package/src/modes/interactive/theme/defaults/light-savanna.json +2 -8
  109. package/src/modes/interactive/theme/defaults/light-solarized.json +2 -1
  110. package/src/modes/interactive/theme/defaults/light-soleil.json +2 -8
  111. package/src/modes/interactive/theme/defaults/light-sunset.json +2 -9
  112. package/src/modes/interactive/theme/defaults/light-synthwave.json +2 -9
  113. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +2 -9
  114. package/src/modes/interactive/theme/defaults/light-wetland.json +2 -8
  115. package/src/modes/interactive/theme/defaults/light-zenith.json +2 -8
  116. package/src/modes/interactive/theme/defaults/limestone.json +2 -8
  117. package/src/modes/interactive/theme/defaults/mahogany.json +2 -9
  118. package/src/modes/interactive/theme/defaults/marble.json +2 -8
  119. package/src/modes/interactive/theme/defaults/obsidian.json +89 -88
  120. package/src/modes/interactive/theme/defaults/onyx.json +89 -88
  121. package/src/modes/interactive/theme/defaults/pearl.json +2 -8
  122. package/src/modes/interactive/theme/defaults/porcelain.json +89 -88
  123. package/src/modes/interactive/theme/defaults/quartz.json +2 -8
  124. package/src/modes/interactive/theme/defaults/sandstone.json +2 -8
  125. package/src/modes/interactive/theme/defaults/titanium.json +88 -87
  126. package/src/modes/interactive/theme/light.json +2 -8
  127. package/src/modes/interactive/theme/theme-schema.json +5 -0
  128. package/src/modes/interactive/theme/theme.ts +7 -0
  129. package/src/modes/interactive/types.ts +5 -0
  130. package/src/modes/interactive/utils/ui-helpers.ts +20 -0
  131. package/src/prompts/system/system-prompt.md +8 -0
  132. package/src/prompts/tools/python.md +40 -2
  133. 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
- if (wasBashMode !== this.ctx.isBashMode) {
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",