@oh-my-pi/pi-coding-agent 3.14.0 → 3.15.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 +79 -0
- package/docs/theme.md +38 -5
- package/examples/sdk/11-sessions.ts +2 -2
- package/package.json +7 -4
- package/src/cli/file-processor.ts +51 -2
- package/src/cli/plugin-cli.ts +25 -19
- package/src/cli/update-cli.ts +4 -3
- package/src/core/agent-session.ts +31 -4
- package/src/core/compaction/branch-summarization.ts +4 -32
- package/src/core/compaction/compaction.ts +6 -84
- package/src/core/compaction/utils.ts +2 -3
- package/src/core/custom-tools/types.ts +2 -0
- package/src/core/export-html/index.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +0 -1
- package/src/core/hooks/types.ts +2 -2
- package/src/core/plugins/doctor.ts +9 -1
- package/src/core/sdk.ts +2 -1
- package/src/core/session-manager.ts +518 -40
- package/src/core/settings-manager.ts +174 -0
- package/src/core/system-prompt.ts +9 -14
- package/src/core/title-generator.ts +2 -8
- package/src/core/tools/ask.ts +19 -37
- package/src/core/tools/bash.ts +2 -37
- package/src/core/tools/edit.ts +2 -9
- package/src/core/tools/exa/render.ts +52 -48
- package/src/core/tools/find.ts +10 -8
- package/src/core/tools/grep.ts +45 -17
- package/src/core/tools/ls.ts +22 -2
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +3 -0
- package/src/core/tools/lsp/index.ts +107 -55
- package/src/core/tools/lsp/render.ts +192 -79
- package/src/core/tools/lsp/types.ts +27 -0
- package/src/core/tools/lsp/utils.ts +62 -22
- package/src/core/tools/notebook.ts +9 -1
- package/src/core/tools/output.ts +37 -14
- package/src/core/tools/read.ts +349 -34
- package/src/core/tools/renderers.ts +290 -89
- package/src/core/tools/review.ts +12 -5
- package/src/core/tools/task/agents.ts +5 -5
- package/src/core/tools/task/commands.ts +3 -3
- package/src/core/tools/task/executor.ts +33 -1
- package/src/core/tools/task/index.ts +93 -6
- package/src/core/tools/task/render.ts +147 -66
- package/src/core/tools/task/types.ts +14 -9
- package/src/core/tools/web-fetch.ts +242 -103
- package/src/core/tools/web-search/index.ts +64 -20
- package/src/core/tools/web-search/providers/exa.ts +68 -172
- package/src/core/tools/web-search/render.ts +264 -74
- package/src/core/tools/write.ts +2 -8
- package/src/main.ts +10 -6
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +9 -4
- package/src/modes/interactive/components/bash-execution.ts +6 -3
- package/src/modes/interactive/components/branch-summary-message.ts +1 -1
- package/src/modes/interactive/components/compaction-summary-message.ts +1 -1
- package/src/modes/interactive/components/dynamic-border.ts +1 -1
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +4 -5
- package/src/modes/interactive/components/extensions/extension-list.ts +18 -16
- package/src/modes/interactive/components/extensions/inspector-panel.ts +8 -8
- package/src/modes/interactive/components/hook-message.ts +2 -2
- package/src/modes/interactive/components/hook-selector.ts +1 -1
- package/src/modes/interactive/components/model-selector.ts +22 -9
- package/src/modes/interactive/components/oauth-selector.ts +20 -4
- package/src/modes/interactive/components/plugin-settings.ts +4 -2
- package/src/modes/interactive/components/session-selector.ts +9 -6
- package/src/modes/interactive/components/settings-defs.ts +285 -1
- package/src/modes/interactive/components/settings-selector.ts +176 -3
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +170 -223
- package/src/modes/interactive/components/tool-execution.ts +446 -211
- package/src/modes/interactive/components/tree-selector.ts +17 -6
- package/src/modes/interactive/components/ttsr-notification.ts +4 -4
- package/src/modes/interactive/components/welcome.ts +27 -19
- package/src/modes/interactive/interactive-mode.ts +98 -13
- package/src/modes/interactive/theme/dark.json +3 -2
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +3 -2
- package/src/modes/interactive/theme/theme-schema.json +120 -4
- package/src/modes/interactive/theme/theme.ts +1228 -14
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/init.md +30 -0
- package/src/{core/tools/task/bundled-agents → prompts}/reviewer.md +6 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/{core/tools/task/bundled-agents → prompts}/task.md +2 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/commands/init.md +0 -20
- /package/src/{core/tools/task/bundled-commands → prompts}/architect-plan.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/browser.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/explore.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement-with-critic.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/plan.md +0 -0
|
@@ -438,7 +438,11 @@ class TreeList implements Component {
|
|
|
438
438
|
// Build prefix with gutters at their correct positions
|
|
439
439
|
// Each gutter has a position (displayIndent where its connector was shown)
|
|
440
440
|
const connector =
|
|
441
|
-
flatNode.showConnector && !flatNode.isVirtualRootChild
|
|
441
|
+
flatNode.showConnector && !flatNode.isVirtualRootChild
|
|
442
|
+
? flatNode.isLast
|
|
443
|
+
? `${theme.boxSharp.bottomLeft}${theme.boxSharp.horizontal} `
|
|
444
|
+
: `${theme.boxSharp.teeRight}${theme.boxSharp.horizontal} `
|
|
445
|
+
: "";
|
|
442
446
|
const connectorPosition = connector ? displayIndent - 1 : -1;
|
|
443
447
|
|
|
444
448
|
// Build prefix char by char, placing gutters and connector at their positions
|
|
@@ -452,16 +456,16 @@ class TreeList implements Component {
|
|
|
452
456
|
const gutter = flatNode.gutters.find((g) => g.position === level);
|
|
453
457
|
if (gutter) {
|
|
454
458
|
if (posInLevel === 0) {
|
|
455
|
-
prefixChars.push(gutter.show ?
|
|
459
|
+
prefixChars.push(gutter.show ? theme.boxSharp.vertical : " ");
|
|
456
460
|
} else {
|
|
457
461
|
prefixChars.push(" ");
|
|
458
462
|
}
|
|
459
463
|
} else if (connector && level === connectorPosition) {
|
|
460
464
|
// Connector at this level
|
|
461
465
|
if (posInLevel === 0) {
|
|
462
|
-
prefixChars.push(flatNode.isLast ?
|
|
466
|
+
prefixChars.push(flatNode.isLast ? theme.boxSharp.bottomLeft : theme.boxSharp.teeRight);
|
|
463
467
|
} else if (posInLevel === 1) {
|
|
464
|
-
prefixChars.push(
|
|
468
|
+
prefixChars.push(theme.boxSharp.horizontal);
|
|
465
469
|
} else {
|
|
466
470
|
prefixChars.push(" ");
|
|
467
471
|
}
|
|
@@ -473,7 +477,7 @@ class TreeList implements Component {
|
|
|
473
477
|
|
|
474
478
|
// Active path marker - shown right before the entry text
|
|
475
479
|
const isOnActivePath = this.activePathIds.has(entry.id);
|
|
476
|
-
const pathMarker = isOnActivePath ? theme.fg("accent",
|
|
480
|
+
const pathMarker = isOnActivePath ? theme.fg("accent", `${theme.md.bullet} `) : "";
|
|
477
481
|
|
|
478
482
|
const label = flatNode.node.label ? theme.fg("warning", `[${flatNode.node.label}] `) : "";
|
|
479
483
|
const content = this.getEntryDisplayText(flatNode.node, isSelected);
|
|
@@ -816,7 +820,14 @@ export class TreeSelectorComponent extends Container {
|
|
|
816
820
|
this.addChild(new DynamicBorder());
|
|
817
821
|
this.addChild(new Text(theme.bold(" Session Tree"), 1, 0));
|
|
818
822
|
this.addChild(
|
|
819
|
-
new TruncatedText(
|
|
823
|
+
new TruncatedText(
|
|
824
|
+
theme.fg(
|
|
825
|
+
"muted",
|
|
826
|
+
" Up/Down: move. Left/Right: page. l: label. Ctrl+O/Shift+Ctrl+O: filter. Type to search",
|
|
827
|
+
),
|
|
828
|
+
0,
|
|
829
|
+
0,
|
|
830
|
+
),
|
|
820
831
|
);
|
|
821
832
|
this.addChild(new SearchLine(this.treeList));
|
|
822
833
|
this.addChild(new DynamicBorder());
|
|
@@ -38,13 +38,13 @@ export class TtsrNotificationComponent extends Container {
|
|
|
38
38
|
private rebuild(): void {
|
|
39
39
|
this.box.clear();
|
|
40
40
|
|
|
41
|
-
// Build header:
|
|
41
|
+
// Build header: warning symbol + rule name + rewind icon
|
|
42
42
|
const ruleNames = this.rules.map((r) => theme.bold(r.name)).join(", ");
|
|
43
43
|
const label = this.rules.length === 1 ? "rule" : "rules";
|
|
44
|
-
const header =
|
|
44
|
+
const header = `${theme.icon.warning} Injecting ${label}: ${ruleNames}`;
|
|
45
45
|
|
|
46
46
|
// Create header with rewind icon on the right
|
|
47
|
-
const rewindIcon =
|
|
47
|
+
const rewindIcon = theme.icon.rewind;
|
|
48
48
|
|
|
49
49
|
this.box.addChild(new Text(`${header} ${rewindIcon}`, 0, 0));
|
|
50
50
|
|
|
@@ -59,7 +59,7 @@ export class TtsrNotificationComponent extends Container {
|
|
|
59
59
|
// Truncate to first 2 lines
|
|
60
60
|
const lines = displayText.split("\n");
|
|
61
61
|
if (lines.length > 2) {
|
|
62
|
-
displayText = `${lines.slice(0, 2).join("\n")}
|
|
62
|
+
displayText = `${lines.slice(0, 2).join("\n")}${theme.format.ellipsis}`;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Component, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { type Component, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { APP_NAME } from "../../../config";
|
|
3
3
|
import { theme } from "../theme/theme";
|
|
4
4
|
|
|
@@ -80,7 +80,7 @@ export class WelcomeComponent implements Component {
|
|
|
80
80
|
|
|
81
81
|
// Right column separator
|
|
82
82
|
const separatorWidth = rightCol - 2; // padding on each side
|
|
83
|
-
const separator = ` ${theme.fg("dim",
|
|
83
|
+
const separator = ` ${theme.fg("dim", theme.boxRound.horizontal.repeat(separatorWidth))}`;
|
|
84
84
|
|
|
85
85
|
// Recent sessions content
|
|
86
86
|
const sessionLines: string[] = [];
|
|
@@ -89,7 +89,7 @@ export class WelcomeComponent implements Component {
|
|
|
89
89
|
} else {
|
|
90
90
|
for (const session of this.recentSessions.slice(0, 3)) {
|
|
91
91
|
sessionLines.push(
|
|
92
|
-
` ${theme.fg("dim",
|
|
92
|
+
` ${theme.fg("dim", `${theme.md.bullet} `)}${theme.fg("muted", session.name)}${theme.fg("dim", ` (${session.timeAgo})`)}`,
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -102,10 +102,10 @@ export class WelcomeComponent implements Component {
|
|
|
102
102
|
for (const server of this.lspServers) {
|
|
103
103
|
const icon =
|
|
104
104
|
server.status === "ready"
|
|
105
|
-
? theme.
|
|
105
|
+
? theme.styledSymbol("status.success", "success")
|
|
106
106
|
: server.status === "connecting"
|
|
107
|
-
? theme.
|
|
108
|
-
: theme.
|
|
107
|
+
? theme.styledSymbol("status.disabled", "warning")
|
|
108
|
+
: theme.styledSymbol("status.error", "error");
|
|
109
109
|
const exts = server.fileTypes.slice(0, 3).join(" ");
|
|
110
110
|
lspLines.push(` ${icon} ${theme.fg("muted", server.name)} ${theme.fg("dim", exts)}`);
|
|
111
111
|
}
|
|
@@ -127,21 +127,24 @@ export class WelcomeComponent implements Component {
|
|
|
127
127
|
];
|
|
128
128
|
|
|
129
129
|
// Border characters (dim)
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
130
|
+
const hChar = theme.boxRound.horizontal;
|
|
131
|
+
const h = theme.fg("dim", hChar);
|
|
132
|
+
const v = theme.fg("dim", theme.boxRound.vertical);
|
|
133
|
+
const tl = theme.fg("dim", theme.boxRound.topLeft);
|
|
134
|
+
const tr = theme.fg("dim", theme.boxRound.topRight);
|
|
135
|
+
const bl = theme.fg("dim", theme.boxRound.bottomLeft);
|
|
136
|
+
const br = theme.fg("dim", theme.boxRound.bottomRight);
|
|
136
137
|
|
|
137
138
|
const lines: string[] = [];
|
|
138
139
|
|
|
139
140
|
// Top border with embedded title
|
|
140
141
|
const title = ` ${APP_NAME} v${this.version} `;
|
|
141
|
-
const
|
|
142
|
-
const
|
|
142
|
+
const titlePrefixRaw = hChar.repeat(3);
|
|
143
|
+
const titleStyled = theme.fg("dim", titlePrefixRaw) + theme.fg("muted", title);
|
|
144
|
+
const titleVisLen = visibleWidth(titlePrefixRaw) + visibleWidth(title);
|
|
143
145
|
const afterTitle = boxWidth - 2 - titleVisLen;
|
|
144
|
-
|
|
146
|
+
const afterTitleText = afterTitle > 0 ? theme.fg("dim", hChar.repeat(afterTitle)) : "";
|
|
147
|
+
lines.push(tl + titleStyled + afterTitleText + tr);
|
|
145
148
|
|
|
146
149
|
// Content rows
|
|
147
150
|
const maxRows = Math.max(leftLines.length, rightLines.length);
|
|
@@ -152,7 +155,7 @@ export class WelcomeComponent implements Component {
|
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
// Bottom border
|
|
155
|
-
lines.push(bl + h.repeat(leftCol) + theme.fg("dim",
|
|
158
|
+
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.boxSharp.teeUp) + h.repeat(rightCol) + br);
|
|
156
159
|
|
|
157
160
|
return lines;
|
|
158
161
|
}
|
|
@@ -160,7 +163,9 @@ export class WelcomeComponent implements Component {
|
|
|
160
163
|
/** Center text within a given width */
|
|
161
164
|
private centerText(text: string, width: number): string {
|
|
162
165
|
const visLen = visibleWidth(text);
|
|
163
|
-
if (visLen >= width)
|
|
166
|
+
if (visLen >= width) {
|
|
167
|
+
return truncateToWidth(text, width, theme.format.ellipsis);
|
|
168
|
+
}
|
|
164
169
|
const leftPad = Math.floor((width - visLen) / 2);
|
|
165
170
|
const rightPad = width - visLen - leftPad;
|
|
166
171
|
return " ".repeat(leftPad) + text + " ".repeat(rightPad);
|
|
@@ -200,6 +205,9 @@ export class WelcomeComponent implements Component {
|
|
|
200
205
|
private fitToWidth(str: string, width: number): string {
|
|
201
206
|
const visLen = visibleWidth(str);
|
|
202
207
|
if (visLen > width) {
|
|
208
|
+
const ellipsis = theme.format.ellipsis;
|
|
209
|
+
const ellipsisWidth = visibleWidth(ellipsis);
|
|
210
|
+
const maxWidth = Math.max(0, width - ellipsisWidth);
|
|
203
211
|
let truncated = "";
|
|
204
212
|
let currentWidth = 0;
|
|
205
213
|
let inEscape = false;
|
|
@@ -208,12 +216,12 @@ export class WelcomeComponent implements Component {
|
|
|
208
216
|
if (inEscape) {
|
|
209
217
|
truncated += char;
|
|
210
218
|
if (char === "m") inEscape = false;
|
|
211
|
-
} else if (currentWidth <
|
|
219
|
+
} else if (currentWidth < maxWidth) {
|
|
212
220
|
truncated += char;
|
|
213
221
|
currentWidth++;
|
|
214
222
|
}
|
|
215
223
|
}
|
|
216
|
-
return `${truncated}
|
|
224
|
+
return `${truncated}${ellipsis}`;
|
|
217
225
|
}
|
|
218
226
|
return str + " ".repeat(width - visLen);
|
|
219
227
|
}
|
|
@@ -34,6 +34,7 @@ import type { TruncationResult } from "../../core/tools/truncate";
|
|
|
34
34
|
import { disableProvider, enableProvider } from "../../discovery";
|
|
35
35
|
import { getChangelogPath, parseChangelog } from "../../utils/changelog";
|
|
36
36
|
import { copyToClipboard, readImageFromClipboard } from "../../utils/clipboard";
|
|
37
|
+
import { registerAsyncCleanup } from "../cleanup";
|
|
37
38
|
import { ArminComponent } from "./components/armin";
|
|
38
39
|
import { AssistantMessageComponent } from "./components/assistant-message";
|
|
39
40
|
import { BashExecutionComponent } from "./components/bash-execution";
|
|
@@ -62,7 +63,9 @@ import {
|
|
|
62
63
|
getAvailableThemes,
|
|
63
64
|
getEditorTheme,
|
|
64
65
|
getMarkdownTheme,
|
|
66
|
+
getSymbolTheme,
|
|
65
67
|
onThemeChange,
|
|
68
|
+
setSymbolPreset,
|
|
66
69
|
setTheme,
|
|
67
70
|
type Theme,
|
|
68
71
|
theme,
|
|
@@ -115,6 +118,9 @@ export class InteractiveMode {
|
|
|
115
118
|
// Agent subscription unsubscribe function
|
|
116
119
|
private unsubscribe?: () => void;
|
|
117
120
|
|
|
121
|
+
// Signal cleanup unsubscribe function (for SIGINT/SIGTERM flush)
|
|
122
|
+
private cleanupUnsubscribe?: () => void;
|
|
123
|
+
|
|
118
124
|
// Track if editor is in bash mode (text starts with !)
|
|
119
125
|
private isBashMode = false;
|
|
120
126
|
|
|
@@ -198,6 +204,7 @@ export class InteractiveMode {
|
|
|
198
204
|
{ name: "new", description: "Start a new session" },
|
|
199
205
|
{ name: "compact", description: "Manually compact the session context" },
|
|
200
206
|
{ name: "resume", description: "Resume a different session" },
|
|
207
|
+
{ name: "exit", description: "Exit the application" },
|
|
201
208
|
];
|
|
202
209
|
|
|
203
210
|
// Load hide thinking block setting
|
|
@@ -233,6 +240,9 @@ export class InteractiveMode {
|
|
|
233
240
|
async init(): Promise<void> {
|
|
234
241
|
if (this.isInitialized) return;
|
|
235
242
|
|
|
243
|
+
// Register session manager flush for signal handlers (SIGINT, SIGTERM, SIGHUP)
|
|
244
|
+
this.cleanupUnsubscribe = registerAsyncCleanup(() => this.sessionManager.flush());
|
|
245
|
+
|
|
236
246
|
// Get current model info for welcome screen
|
|
237
247
|
const modelName = this.session.model?.name ?? "Unknown";
|
|
238
248
|
const providerName = this.session.model?.provider ?? "Unknown";
|
|
@@ -403,7 +413,9 @@ export class InteractiveMode {
|
|
|
403
413
|
this.pendingTools.clear();
|
|
404
414
|
|
|
405
415
|
this.chatContainer.addChild(new Spacer(1));
|
|
406
|
-
this.chatContainer.addChild(
|
|
416
|
+
this.chatContainer.addChild(
|
|
417
|
+
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 1),
|
|
418
|
+
);
|
|
407
419
|
this.ui.requestRender();
|
|
408
420
|
|
|
409
421
|
return { cancelled: false };
|
|
@@ -838,6 +850,11 @@ export class InteractiveMode {
|
|
|
838
850
|
this.editor.setText("");
|
|
839
851
|
return;
|
|
840
852
|
}
|
|
853
|
+
if (text === "/exit") {
|
|
854
|
+
this.editor.setText("");
|
|
855
|
+
void this.shutdown();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
841
858
|
|
|
842
859
|
// Handle bash command
|
|
843
860
|
if (text.startsWith("!")) {
|
|
@@ -908,9 +925,9 @@ export class InteractiveMode {
|
|
|
908
925
|
const registry = this.session.modelRegistry;
|
|
909
926
|
const smolModel = this.settingsManager.getModelRole("smol");
|
|
910
927
|
generateSessionTitle(text, registry, smolModel)
|
|
911
|
-
.then((title) => {
|
|
928
|
+
.then(async (title) => {
|
|
912
929
|
if (title) {
|
|
913
|
-
this.sessionManager.setSessionTitle(title);
|
|
930
|
+
await this.sessionManager.setSessionTitle(title);
|
|
914
931
|
setTerminalTitle(`omp: ${title}`);
|
|
915
932
|
}
|
|
916
933
|
})
|
|
@@ -951,7 +968,8 @@ export class InteractiveMode {
|
|
|
951
968
|
this.ui,
|
|
952
969
|
(spinner) => theme.fg("accent", spinner),
|
|
953
970
|
(text) => theme.fg("muted", text),
|
|
954
|
-
|
|
971
|
+
`Working${theme.format.ellipsis} (esc to interrupt)`,
|
|
972
|
+
getSymbolTheme().spinnerFrames,
|
|
955
973
|
);
|
|
956
974
|
this.statusContainer.addChild(this.loadingAnimation);
|
|
957
975
|
this.ui.requestRender();
|
|
@@ -1118,7 +1136,8 @@ export class InteractiveMode {
|
|
|
1118
1136
|
this.ui,
|
|
1119
1137
|
(spinner) => theme.fg("accent", spinner),
|
|
1120
1138
|
(text) => theme.fg("muted", text),
|
|
1121
|
-
`${reasonText}Auto-compacting
|
|
1139
|
+
`${reasonText}Auto-compacting${theme.format.ellipsis} (esc to cancel)`,
|
|
1140
|
+
getSymbolTheme().spinnerFrames,
|
|
1122
1141
|
);
|
|
1123
1142
|
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
1124
1143
|
this.ui.requestRender();
|
|
@@ -1173,7 +1192,8 @@ export class InteractiveMode {
|
|
|
1173
1192
|
this.ui,
|
|
1174
1193
|
(spinner) => theme.fg("warning", spinner),
|
|
1175
1194
|
(text) => theme.fg("muted", text),
|
|
1176
|
-
`Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s
|
|
1195
|
+
`Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s${theme.format.ellipsis} (esc to cancel)`,
|
|
1196
|
+
getSymbolTheme().spinnerFrames,
|
|
1177
1197
|
);
|
|
1178
1198
|
this.statusContainer.addChild(this.retryLoader);
|
|
1179
1199
|
this.ui.requestRender();
|
|
@@ -1287,7 +1307,7 @@ export class InteractiveMode {
|
|
|
1287
1307
|
case "fileMention": {
|
|
1288
1308
|
// Render compact file mention display
|
|
1289
1309
|
for (const file of message.files) {
|
|
1290
|
-
const text = `${theme.fg("dim",
|
|
1310
|
+
const text = `${theme.fg("dim", `${theme.tree.hook} `)}${theme.fg("muted", "Read")} ${theme.fg("accent", file.path)} ${theme.fg("dim", `(${file.lineCount} lines)`)}`;
|
|
1291
1311
|
this.chatContainer.addChild(new Text(text, 0, 0));
|
|
1292
1312
|
}
|
|
1293
1313
|
break;
|
|
@@ -1434,6 +1454,9 @@ export class InteractiveMode {
|
|
|
1434
1454
|
* Emits shutdown event to hooks and tools, then exits.
|
|
1435
1455
|
*/
|
|
1436
1456
|
private async shutdown(): Promise<void> {
|
|
1457
|
+
// Flush pending session writes before shutdown
|
|
1458
|
+
await this.sessionManager.flush();
|
|
1459
|
+
|
|
1437
1460
|
// Emit shutdown event to hooks
|
|
1438
1461
|
const hookRunner = this.session.hookRunner;
|
|
1439
1462
|
if (hookRunner?.hasHandlers("session_shutdown")) {
|
|
@@ -1714,11 +1737,26 @@ export class InteractiveMode {
|
|
|
1714
1737
|
this.ui.requestRender();
|
|
1715
1738
|
}
|
|
1716
1739
|
},
|
|
1740
|
+
onStatusLinePreview: (settings) => {
|
|
1741
|
+
// Update status line with preview settings
|
|
1742
|
+
const currentSettings = this.settingsManager.getStatusLineSettings();
|
|
1743
|
+
this.statusLine.updateSettings({ ...currentSettings, ...settings });
|
|
1744
|
+
this.updateEditorTopBorder();
|
|
1745
|
+
this.ui.requestRender();
|
|
1746
|
+
},
|
|
1747
|
+
getStatusLinePreview: () => {
|
|
1748
|
+
// Return the rendered status line for inline preview
|
|
1749
|
+
const width = this.ui.getWidth();
|
|
1750
|
+
return this.statusLine.getTopBorder(width).content;
|
|
1751
|
+
},
|
|
1717
1752
|
onPluginsChanged: () => {
|
|
1718
1753
|
this.ui.requestRender();
|
|
1719
1754
|
},
|
|
1720
1755
|
onCancel: () => {
|
|
1721
1756
|
done();
|
|
1757
|
+
// Restore status line to saved settings
|
|
1758
|
+
this.statusLine.updateSettings(this.settingsManager.getStatusLineSettings());
|
|
1759
|
+
this.updateEditorTopBorder();
|
|
1722
1760
|
this.ui.requestRender();
|
|
1723
1761
|
},
|
|
1724
1762
|
},
|
|
@@ -1797,12 +1835,40 @@ export class InteractiveMode {
|
|
|
1797
1835
|
break;
|
|
1798
1836
|
case "theme": {
|
|
1799
1837
|
const result = setTheme(value as string, true);
|
|
1838
|
+
this.statusLine.invalidate();
|
|
1839
|
+
this.updateEditorTopBorder();
|
|
1800
1840
|
this.ui.invalidate();
|
|
1801
1841
|
if (!result.success) {
|
|
1802
1842
|
this.showError(`Failed to load theme "${value}": ${result.error}\nFell back to dark theme.`);
|
|
1803
1843
|
}
|
|
1804
1844
|
break;
|
|
1805
1845
|
}
|
|
1846
|
+
case "symbolPreset": {
|
|
1847
|
+
setSymbolPreset(value as "unicode" | "nerd" | "ascii");
|
|
1848
|
+
this.statusLine.invalidate();
|
|
1849
|
+
this.updateEditorTopBorder();
|
|
1850
|
+
this.ui.invalidate();
|
|
1851
|
+
break;
|
|
1852
|
+
}
|
|
1853
|
+
case "statusLinePreset":
|
|
1854
|
+
case "statusLineSeparator":
|
|
1855
|
+
case "statusLineShowHooks":
|
|
1856
|
+
case "statusLineSegments":
|
|
1857
|
+
case "statusLineModelThinking":
|
|
1858
|
+
case "statusLinePathAbbreviate":
|
|
1859
|
+
case "statusLinePathMaxLength":
|
|
1860
|
+
case "statusLinePathStripWorkPrefix":
|
|
1861
|
+
case "statusLineGitShowBranch":
|
|
1862
|
+
case "statusLineGitShowStaged":
|
|
1863
|
+
case "statusLineGitShowUnstaged":
|
|
1864
|
+
case "statusLineGitShowUntracked":
|
|
1865
|
+
case "statusLineTimeFormat":
|
|
1866
|
+
case "statusLineTimeShowSeconds": {
|
|
1867
|
+
this.statusLine.updateSettings(this.settingsManager.getStatusLineSettings());
|
|
1868
|
+
this.updateEditorTopBorder();
|
|
1869
|
+
this.ui.requestRender();
|
|
1870
|
+
break;
|
|
1871
|
+
}
|
|
1806
1872
|
|
|
1807
1873
|
// All other settings are handled by the definitions (get/set on SettingsManager)
|
|
1808
1874
|
// No additional side effects needed
|
|
@@ -1930,6 +1996,7 @@ export class InteractiveMode {
|
|
|
1930
1996
|
(spinner) => theme.fg("accent", spinner),
|
|
1931
1997
|
(text) => theme.fg("muted", text),
|
|
1932
1998
|
"Summarizing branch... (esc to cancel)",
|
|
1999
|
+
getSymbolTheme().spinnerFrames,
|
|
1933
2000
|
);
|
|
1934
2001
|
this.statusContainer.addChild(summaryLoader);
|
|
1935
2002
|
this.ui.requestRender();
|
|
@@ -2092,7 +2159,11 @@ export class InteractiveMode {
|
|
|
2092
2159
|
this.session.modelRegistry.refresh();
|
|
2093
2160
|
this.chatContainer.addChild(new Spacer(1));
|
|
2094
2161
|
this.chatContainer.addChild(
|
|
2095
|
-
new Text(
|
|
2162
|
+
new Text(
|
|
2163
|
+
theme.fg("success", `${theme.status.success} Successfully logged in to ${providerId}`),
|
|
2164
|
+
1,
|
|
2165
|
+
0,
|
|
2166
|
+
),
|
|
2096
2167
|
);
|
|
2097
2168
|
this.chatContainer.addChild(
|
|
2098
2169
|
new Text(theme.fg("dim", `Credentials saved to ${getAuthPath()}`), 1, 0),
|
|
@@ -2108,7 +2179,11 @@ export class InteractiveMode {
|
|
|
2108
2179
|
this.session.modelRegistry.refresh();
|
|
2109
2180
|
this.chatContainer.addChild(new Spacer(1));
|
|
2110
2181
|
this.chatContainer.addChild(
|
|
2111
|
-
new Text(
|
|
2182
|
+
new Text(
|
|
2183
|
+
theme.fg("success", `${theme.status.success} Successfully logged out of ${providerId}`),
|
|
2184
|
+
1,
|
|
2185
|
+
0,
|
|
2186
|
+
),
|
|
2112
2187
|
);
|
|
2113
2188
|
this.chatContainer.addChild(
|
|
2114
2189
|
new Text(theme.fg("dim", `Credentials removed from ${getAuthPath()}`), 1, 0),
|
|
@@ -2346,8 +2421,8 @@ export class InteractiveMode {
|
|
|
2346
2421
|
|
|
2347
2422
|
this.chatContainer.addChild(new Spacer(1));
|
|
2348
2423
|
this.chatContainer.addChild(new DynamicBorder());
|
|
2349
|
-
this.
|
|
2350
|
-
this.
|
|
2424
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
|
2425
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2351
2426
|
this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, getMarkdownTheme()));
|
|
2352
2427
|
this.chatContainer.addChild(new DynamicBorder());
|
|
2353
2428
|
this.ui.requestRender();
|
|
@@ -2420,7 +2495,9 @@ export class InteractiveMode {
|
|
|
2420
2495
|
this.pendingTools.clear();
|
|
2421
2496
|
|
|
2422
2497
|
this.chatContainer.addChild(new Spacer(1));
|
|
2423
|
-
this.chatContainer.addChild(
|
|
2498
|
+
this.chatContainer.addChild(
|
|
2499
|
+
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 1),
|
|
2500
|
+
);
|
|
2424
2501
|
this.ui.requestRender();
|
|
2425
2502
|
}
|
|
2426
2503
|
|
|
@@ -2451,7 +2528,11 @@ export class InteractiveMode {
|
|
|
2451
2528
|
|
|
2452
2529
|
this.chatContainer.addChild(new Spacer(1));
|
|
2453
2530
|
this.chatContainer.addChild(
|
|
2454
|
-
new Text(
|
|
2531
|
+
new Text(
|
|
2532
|
+
`${theme.fg("accent", `${theme.status.success} Debug log written`)}\n${theme.fg("muted", debugLogPath)}`,
|
|
2533
|
+
1,
|
|
2534
|
+
1,
|
|
2535
|
+
),
|
|
2455
2536
|
);
|
|
2456
2537
|
this.ui.requestRender();
|
|
2457
2538
|
}
|
|
@@ -2537,6 +2618,7 @@ export class InteractiveMode {
|
|
|
2537
2618
|
(spinner) => theme.fg("accent", spinner),
|
|
2538
2619
|
(text) => theme.fg("muted", text),
|
|
2539
2620
|
label,
|
|
2621
|
+
getSymbolTheme().spinnerFrames,
|
|
2540
2622
|
);
|
|
2541
2623
|
this.statusContainer.addChild(compactingLoader);
|
|
2542
2624
|
this.ui.requestRender();
|
|
@@ -2576,6 +2658,9 @@ export class InteractiveMode {
|
|
|
2576
2658
|
if (this.unsubscribe) {
|
|
2577
2659
|
this.unsubscribe();
|
|
2578
2660
|
}
|
|
2661
|
+
if (this.cleanupUnsubscribe) {
|
|
2662
|
+
this.cleanupUnsubscribe();
|
|
2663
|
+
}
|
|
2579
2664
|
if (this.isInitialized) {
|
|
2580
2665
|
this.ui.stop();
|
|
2581
2666
|
this.isInitialized = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json",
|
|
3
3
|
"name": "dark",
|
|
4
4
|
"vars": {
|
|
5
5
|
"cyan": "#0088fa",
|
|
@@ -91,7 +91,8 @@
|
|
|
91
91
|
"statusLineDirty": 178,
|
|
92
92
|
"statusLineUntracked": 39,
|
|
93
93
|
"statusLineOutput": 205,
|
|
94
|
-
"statusLineCost": 205
|
|
94
|
+
"statusLineCost": 205,
|
|
95
|
+
"statusLineSubagents": "accent"
|
|
95
96
|
},
|
|
96
97
|
"export": {
|
|
97
98
|
"pageBg": "#18181e",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/theme-schema.json",
|
|
3
|
+
"name": "dark-arctic",
|
|
4
|
+
"vars": {
|
|
5
|
+
"polarNight0": "#2e3440",
|
|
6
|
+
"polarNight1": "#3b4252",
|
|
7
|
+
"polarNight2": "#434c5e",
|
|
8
|
+
"polarNight3": "#4c566a",
|
|
9
|
+
"snowStorm0": "#d8dee9",
|
|
10
|
+
"snowStorm1": "#e5e9f0",
|
|
11
|
+
"snowStorm2": "#eceff4",
|
|
12
|
+
"frost0": "#8fbcbb",
|
|
13
|
+
"frost1": "#88c0d0",
|
|
14
|
+
"frost2": "#81a1c1",
|
|
15
|
+
"frost3": "#5e81ac",
|
|
16
|
+
"auroraRed": "#bf616a",
|
|
17
|
+
"auroraOrange": "#d08770",
|
|
18
|
+
"auroraYellow": "#ebcb8b",
|
|
19
|
+
"auroraGreen": "#a3be8c",
|
|
20
|
+
"auroraPurple": "#b48ead",
|
|
21
|
+
"iceBlue": "#88c0d0",
|
|
22
|
+
"deepPolar": "#242933",
|
|
23
|
+
"userMsgBg": "#1f2430",
|
|
24
|
+
"toolPendingBg": "#242933",
|
|
25
|
+
"toolSuccessBg": "#1f2835",
|
|
26
|
+
"toolErrorBg": "#2d2428",
|
|
27
|
+
"customMsgBg": "#2a2a3c"
|
|
28
|
+
},
|
|
29
|
+
"colors": {
|
|
30
|
+
"accent": "iceBlue",
|
|
31
|
+
"border": "frost3",
|
|
32
|
+
"borderAccent": "frost1",
|
|
33
|
+
"borderMuted": "polarNight3",
|
|
34
|
+
"success": "auroraGreen",
|
|
35
|
+
"error": "auroraRed",
|
|
36
|
+
"warning": "auroraYellow",
|
|
37
|
+
"muted": "polarNight3",
|
|
38
|
+
"dim": "polarNight2",
|
|
39
|
+
"text": "",
|
|
40
|
+
"thinkingText": "polarNight3",
|
|
41
|
+
|
|
42
|
+
"selectedBg": "polarNight1",
|
|
43
|
+
"userMessageBg": "userMsgBg",
|
|
44
|
+
"userMessageText": "",
|
|
45
|
+
"customMessageBg": "customMsgBg",
|
|
46
|
+
"customMessageText": "",
|
|
47
|
+
"customMessageLabel": "auroraPurple",
|
|
48
|
+
"toolPendingBg": "toolPendingBg",
|
|
49
|
+
"toolSuccessBg": "toolSuccessBg",
|
|
50
|
+
"toolErrorBg": "toolErrorBg",
|
|
51
|
+
"toolText": "",
|
|
52
|
+
"toolTitle": "",
|
|
53
|
+
"toolOutput": "polarNight3",
|
|
54
|
+
|
|
55
|
+
"mdHeading": "frost1",
|
|
56
|
+
"mdLink": "frost2",
|
|
57
|
+
"mdLinkUrl": "polarNight3",
|
|
58
|
+
"mdCode": "auroraPurple",
|
|
59
|
+
"mdCodeBlock": "snowStorm0",
|
|
60
|
+
"mdCodeBlockBorder": "polarNight2",
|
|
61
|
+
"mdQuote": "polarNight3",
|
|
62
|
+
"mdQuoteBorder": "polarNight2",
|
|
63
|
+
"mdHr": "polarNight2",
|
|
64
|
+
"mdListBullet": "iceBlue",
|
|
65
|
+
|
|
66
|
+
"toolDiffAdded": "auroraGreen",
|
|
67
|
+
"toolDiffRemoved": "auroraRed",
|
|
68
|
+
"toolDiffContext": "polarNight3",
|
|
69
|
+
|
|
70
|
+
"link": "frost2",
|
|
71
|
+
|
|
72
|
+
"syntaxComment": "#616e88",
|
|
73
|
+
"syntaxKeyword": "frost3",
|
|
74
|
+
"syntaxFunction": "frost1",
|
|
75
|
+
"syntaxVariable": "snowStorm0",
|
|
76
|
+
"syntaxString": "auroraGreen",
|
|
77
|
+
"syntaxNumber": "auroraPurple",
|
|
78
|
+
"syntaxType": "frost0",
|
|
79
|
+
"syntaxOperator": "frost2",
|
|
80
|
+
"syntaxPunctuation": "snowStorm1",
|
|
81
|
+
|
|
82
|
+
"thinkingOff": "polarNight2",
|
|
83
|
+
"thinkingMinimal": "polarNight3",
|
|
84
|
+
"thinkingLow": "frost3",
|
|
85
|
+
"thinkingMedium": "frost2",
|
|
86
|
+
"thinkingHigh": "auroraPurple",
|
|
87
|
+
"thinkingXhigh": "#c8a0d1",
|
|
88
|
+
|
|
89
|
+
"bashMode": "iceBlue",
|
|
90
|
+
|
|
91
|
+
"statusLineBg": "#1a1f2b",
|
|
92
|
+
"statusLineSep": 240,
|
|
93
|
+
"statusLineModel": "auroraPurple",
|
|
94
|
+
"statusLinePath": "frost0",
|
|
95
|
+
"statusLineGitClean": "auroraGreen",
|
|
96
|
+
"statusLineGitDirty": "auroraYellow",
|
|
97
|
+
"statusLineContext": "frost3",
|
|
98
|
+
"statusLineSpend": "frost1",
|
|
99
|
+
"statusLineStaged": 108,
|
|
100
|
+
"statusLineDirty": 180,
|
|
101
|
+
"statusLineUntracked": 109,
|
|
102
|
+
"statusLineOutput": 139,
|
|
103
|
+
"statusLineCost": 139,
|
|
104
|
+
"statusLineSubagents": "iceBlue"
|
|
105
|
+
},
|
|
106
|
+
"export": {
|
|
107
|
+
"pageBg": "#1a1f2b",
|
|
108
|
+
"cardBg": "#242933",
|
|
109
|
+
"infoBg": "#353a48"
|
|
110
|
+
}
|
|
111
|
+
}
|