@oh-my-pi/pi-coding-agent 15.0.0 → 15.0.2
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/examples/extensions/plan-mode.ts +0 -1
- package/package.json +10 -10
- package/scripts/build-binary.ts +5 -0
- package/src/autoresearch/helpers.ts +17 -0
- package/src/autoresearch/tools/log-experiment.ts +9 -17
- package/src/autoresearch/tools/run-experiment.ts +2 -17
- package/src/capability/skill.ts +7 -0
- package/src/cli/list-models.ts +1 -1
- package/src/cli/shell-cli.ts +3 -13
- package/src/cli/update-cli.ts +1 -1
- package/src/cli.ts +10 -29
- package/src/commands/commit.ts +10 -0
- package/src/commit/agentic/tools/propose-changelog.ts +8 -1
- package/src/commit/analysis/conventional.ts +8 -66
- package/src/commit/map-reduce/reduce-phase.ts +6 -65
- package/src/commit/pipeline.ts +2 -2
- package/src/commit/shared-llm.ts +89 -0
- package/src/config/config-file.ts +210 -0
- package/src/config/model-equivalence.ts +8 -11
- package/src/config/model-registry.ts +44 -3
- package/src/config/model-resolver.ts +1 -4
- package/src/config/settings-schema.ts +82 -1
- package/src/config/settings.ts +1 -1
- package/src/config.ts +3 -219
- package/src/discovery/claude-plugins.ts +19 -7
- package/src/edit/renderer.ts +7 -1
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/shared/rewrite-imports.ts +2 -2
- package/src/eval/py/executor.ts +5 -0
- package/src/eval/py/runner.py +42 -11
- package/src/eval/py/runtime.ts +1 -0
- package/src/exa/factory.ts +2 -2
- package/src/exa/mcp-client.ts +74 -1
- package/src/exec/bash-executor.ts +5 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -11
- package/src/extensibility/extensions/get-commands-handler.ts +77 -0
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +89 -223
- package/src/extensibility/hooks/types.ts +89 -314
- package/src/extensibility/plugins/legacy-pi-compat.ts +48 -31
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +9 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +500 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +237 -0
- package/src/hashline/anchors.ts +2 -2
- package/src/hashline/input.ts +2 -1
- package/src/hashline/parser.ts +27 -3
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/agent-protocol.ts +1 -20
- package/src/internal-urls/artifact-protocol.ts +1 -19
- package/src/internal-urls/docs-index.generated.ts +11 -12
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +8 -0
- package/src/internal-urls/types.ts +21 -0
- package/src/lsp/config.ts +15 -6
- package/src/lsp/defaults.json +6 -2
- package/src/main.ts +11 -2
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/modes/acp/acp-agent.ts +327 -95
- package/src/modes/components/assistant-message.ts +14 -8
- package/src/modes/components/bash-execution.ts +24 -63
- package/src/modes/components/custom-message.ts +14 -40
- package/src/modes/components/eval-execution.ts +27 -57
- package/src/modes/components/execution-shared.ts +102 -0
- package/src/modes/components/hook-message.ts +17 -49
- package/src/modes/components/mcp-add-wizard.ts +26 -5
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +6 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +93 -8
- package/src/modes/components/status-line/types.ts +4 -0
- package/src/modes/components/status-line.ts +28 -10
- package/src/modes/components/tool-execution.ts +7 -8
- package/src/modes/controllers/command-controller-shared.ts +108 -0
- package/src/modes/controllers/command-controller.ts +13 -4
- package/src/modes/controllers/event-controller.ts +36 -7
- package/src/modes/controllers/extension-ui-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +13 -0
- package/src/modes/controllers/mcp-command-controller.ts +56 -61
- package/src/modes/controllers/ssh-command-controller.ts +18 -57
- package/src/modes/interactive-mode.ts +624 -52
- package/src/modes/print-mode.ts +16 -86
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-mode.ts +41 -88
- package/src/modes/rpc/rpc-types.ts +57 -0
- package/src/modes/runtime-init.ts +116 -0
- package/src/modes/theme/defaults/dark-poimandres.json +3 -0
- package/src/modes/theme/defaults/light-poimandres.json +3 -0
- package/src/modes/theme/theme.ts +24 -6
- package/src/modes/types.ts +14 -3
- package/src/modes/utils/context-usage.ts +13 -13
- package/src/modes/utils/ui-helpers.ts +10 -3
- package/src/plan-mode/approved-plan.ts +35 -1
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/system/plan-mode-active.md +5 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/tools/bash.md +6 -0
- package/src/prompts/tools/github.md +4 -4
- package/src/prompts/tools/goal.md +13 -0
- package/src/prompts/tools/hashline.md +101 -117
- package/src/prompts/tools/read.md +55 -36
- package/src/prompts/tools/resolve.md +6 -5
- package/src/sdk.ts +12 -5
- package/src/session/agent-session.ts +428 -106
- package/src/session/blob-store.ts +36 -3
- package/src/session/messages.ts +67 -2
- package/src/session/session-manager.ts +131 -12
- package/src/session/session-storage.ts +33 -15
- package/src/session/streaming-output.ts +309 -13
- package/src/slash-commands/builtin-registry.ts +18 -0
- package/src/ssh/ssh-executor.ts +5 -0
- package/src/system-prompt.ts +4 -2
- package/src/task/discovery.ts +5 -2
- package/src/task/executor.ts +19 -8
- package/src/task/index.ts +3 -0
- package/src/task/render.ts +21 -15
- package/src/task/types.ts +4 -0
- package/src/tools/ast-edit.ts +21 -120
- package/src/tools/ast-grep.ts +21 -119
- package/src/tools/bash-command-fixup.ts +47 -0
- package/src/tools/bash-interactive.ts +9 -1
- package/src/tools/bash.ts +66 -19
- package/src/tools/browser/attach.ts +3 -3
- package/src/tools/browser/launch.ts +81 -18
- package/src/tools/browser/registry.ts +1 -5
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +51 -14
- package/src/tools/conflict-detect.ts +15 -4
- package/src/tools/eval.ts +12 -2
- package/src/tools/find.ts +20 -38
- package/src/tools/gh.ts +44 -10
- package/src/tools/index.ts +22 -11
- package/src/tools/inspect-image.ts +3 -10
- package/src/tools/job.ts +16 -7
- package/src/tools/output-meta.ts +202 -37
- package/src/tools/path-utils.ts +125 -2
- package/src/tools/read.ts +548 -237
- package/src/tools/render-utils.ts +92 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +72 -44
- package/src/tools/search.ts +120 -186
- package/src/tools/ssh.ts +3 -2
- package/src/tools/write.ts +64 -9
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/image-loading.ts +7 -3
- package/src/utils/image-resize.ts +32 -43
- package/src/vim/parser.ts +0 -17
- package/src/vim/render.ts +1 -1
- package/src/vim/types.ts +1 -1
- package/src/web/search/providers/anthropic.ts +5 -0
- package/src/web/search/providers/exa.ts +3 -0
- package/src/web/search/providers/gemini.ts +40 -95
- package/src/web/search/providers/jina.ts +5 -2
- package/src/web/search/providers/zai.ts +5 -2
- package/src/prompts/tools/exit-plan-mode.md +0 -6
- package/src/tools/exit-plan-mode.ts +0 -97
- package/src/utils/fuzzy.ts +0 -108
- package/src/utils/image-convert.ts +0 -27
|
@@ -7,19 +7,23 @@ import {
|
|
|
7
7
|
Container,
|
|
8
8
|
Ellipsis,
|
|
9
9
|
ImageProtocol,
|
|
10
|
-
Loader,
|
|
11
|
-
Spacer,
|
|
10
|
+
type Loader,
|
|
12
11
|
TERMINAL,
|
|
13
12
|
Text,
|
|
14
13
|
type TUI,
|
|
15
14
|
truncateToWidth,
|
|
16
15
|
visibleWidth,
|
|
17
16
|
} from "@oh-my-pi/pi-tui";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { theme } from "../../modes/theme/theme";
|
|
18
|
+
import type { TruncationMeta } from "../../tools/output-meta";
|
|
20
19
|
import { getSixelLineMask, isSixelPassthroughEnabled, sanitizeWithOptionalSixelPassthrough } from "../../utils/sixel";
|
|
21
|
-
import {
|
|
22
|
-
|
|
20
|
+
import {
|
|
21
|
+
buildExecutionFrame,
|
|
22
|
+
buildStatusFooter,
|
|
23
|
+
createCollapsedPreview,
|
|
24
|
+
type ExecutionStatus,
|
|
25
|
+
resolveExecutionStatus,
|
|
26
|
+
} from "./execution-shared";
|
|
23
27
|
|
|
24
28
|
// Preview line limit when not expanded (matches tool execution behavior)
|
|
25
29
|
const PREVIEW_LINES = 20;
|
|
@@ -31,7 +35,7 @@ const CHUNK_THROTTLE_MS = 50;
|
|
|
31
35
|
|
|
32
36
|
export class BashExecutionComponent extends Container {
|
|
33
37
|
#outputLines: string[] = [];
|
|
34
|
-
#status:
|
|
38
|
+
#status: ExecutionStatus = "running";
|
|
35
39
|
#exitCode: number | undefined = undefined;
|
|
36
40
|
#loader: Loader;
|
|
37
41
|
#truncation?: TruncationMeta;
|
|
@@ -50,34 +54,14 @@ export class BashExecutionComponent extends Container {
|
|
|
50
54
|
|
|
51
55
|
// Use dim border for excluded-from-context commands (!! prefix)
|
|
52
56
|
const colorKey = excludeFromContext ? "dim" : "bashMode";
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.addChild(new Spacer(1));
|
|
57
|
-
|
|
58
|
-
// Top border
|
|
59
|
-
this.addChild(new DynamicBorder(borderColor));
|
|
60
|
-
|
|
61
|
-
// Content container (holds dynamic content between borders)
|
|
62
|
-
this.#contentContainer = new Container();
|
|
63
|
-
this.addChild(this.#contentContainer);
|
|
57
|
+
const { contentContainer, loader } = buildExecutionFrame(this, ui, colorKey);
|
|
58
|
+
this.#contentContainer = contentContainer;
|
|
59
|
+
this.#loader = loader;
|
|
64
60
|
|
|
65
61
|
// Command header
|
|
66
62
|
this.#headerText = new Text(theme.fg(colorKey, theme.bold(`$ ${command}`)), 1, 0);
|
|
67
63
|
this.#contentContainer.addChild(this.#headerText);
|
|
68
|
-
|
|
69
|
-
// Loader
|
|
70
|
-
this.#loader = new Loader(
|
|
71
|
-
ui,
|
|
72
|
-
spinner => theme.fg(colorKey, spinner),
|
|
73
|
-
text => theme.fg("muted", text),
|
|
74
|
-
`Running… (esc to cancel)`,
|
|
75
|
-
getSymbolTheme().spinnerFrames,
|
|
76
|
-
);
|
|
77
64
|
this.#contentContainer.addChild(this.#loader);
|
|
78
|
-
|
|
79
|
-
// Bottom border
|
|
80
|
-
this.addChild(new DynamicBorder(borderColor));
|
|
81
65
|
}
|
|
82
66
|
|
|
83
67
|
/**
|
|
@@ -130,11 +114,7 @@ export class BashExecutionComponent extends Container {
|
|
|
130
114
|
options?: { output?: string; truncation?: TruncationMeta },
|
|
131
115
|
): void {
|
|
132
116
|
this.#exitCode = exitCode;
|
|
133
|
-
this.#status = cancelled
|
|
134
|
-
? "cancelled"
|
|
135
|
-
: exitCode !== 0 && exitCode !== undefined && exitCode !== null
|
|
136
|
-
? "error"
|
|
137
|
-
: "complete";
|
|
117
|
+
this.#status = resolveExecutionStatus(exitCode, cancelled);
|
|
138
118
|
this.#truncation = options?.truncation;
|
|
139
119
|
if (options?.output !== undefined) {
|
|
140
120
|
this.#setOutput(options.output);
|
|
@@ -182,14 +162,7 @@ export class BashExecutionComponent extends Container {
|
|
|
182
162
|
} else {
|
|
183
163
|
// Use shared visual truncation utility, recomputed per render width
|
|
184
164
|
const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n");
|
|
185
|
-
|
|
186
|
-
this.#contentContainer.addChild({
|
|
187
|
-
render: (width: number) => {
|
|
188
|
-
const { visualLines } = truncateToVisualLines(previewText, PREVIEW_LINES, width, 1);
|
|
189
|
-
return visualLines;
|
|
190
|
-
},
|
|
191
|
-
invalidate: () => {},
|
|
192
|
-
});
|
|
165
|
+
this.#contentContainer.addChild(createCollapsedPreview(`\n${styledOutput}`, PREVIEW_LINES));
|
|
193
166
|
}
|
|
194
167
|
}
|
|
195
168
|
|
|
@@ -197,26 +170,14 @@ export class BashExecutionComponent extends Container {
|
|
|
197
170
|
if (this.#status === "running") {
|
|
198
171
|
this.#contentContainer.addChild(this.#loader);
|
|
199
172
|
} else {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (this.#
|
|
208
|
-
statusParts.push(theme.fg("warning", "(cancelled)"));
|
|
209
|
-
} else if (this.#status === "error") {
|
|
210
|
-
statusParts.push(theme.fg("error", `(exit ${this.#exitCode})`));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (this.#truncation) {
|
|
214
|
-
statusParts.push(theme.fg("warning", formatTruncationMetaNotice(this.#truncation)));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (statusParts.length > 0) {
|
|
218
|
-
this.#contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
|
|
219
|
-
}
|
|
173
|
+
const footer = buildStatusFooter({
|
|
174
|
+
status: this.#status,
|
|
175
|
+
exitCode: this.#exitCode,
|
|
176
|
+
truncation: this.#truncation,
|
|
177
|
+
hiddenLineCount,
|
|
178
|
+
suppressHiddenCount: hasSixelOutput,
|
|
179
|
+
});
|
|
180
|
+
if (footer) this.#contentContainer.addChild(footer);
|
|
220
181
|
}
|
|
221
182
|
}
|
|
222
183
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { TextContent } from "@oh-my-pi/pi-ai";
|
|
2
1
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
|
-
import { Box, Container,
|
|
2
|
+
import { Box, Container, Spacer } from "@oh-my-pi/pi-tui";
|
|
4
3
|
import type { MessageRenderer } from "../../extensibility/extensions/types";
|
|
5
|
-
import {
|
|
4
|
+
import { theme } from "../../modes/theme/theme";
|
|
6
5
|
import type { CustomMessage } from "../../session/messages";
|
|
6
|
+
import { renderFramedMessage } from "./message-frame";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Component that renders a custom message entry from extensions.
|
|
@@ -41,51 +41,25 @@ export class CustomMessageComponent extends Container {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
#rebuild(): void {
|
|
44
|
-
// Remove previous content component
|
|
45
44
|
if (this.#customComponent) {
|
|
46
45
|
this.removeChild(this.#customComponent);
|
|
47
46
|
this.#customComponent = undefined;
|
|
48
47
|
}
|
|
49
48
|
this.removeChild(this.#box);
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
// Fall through to default rendering
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Default rendering uses our box
|
|
66
|
-
this.addChild(this.#box);
|
|
67
|
-
this.#box.clear();
|
|
50
|
+
const custom = renderFramedMessage({
|
|
51
|
+
message: this.message,
|
|
52
|
+
box: this.#box,
|
|
53
|
+
expanded: this.#expanded,
|
|
54
|
+
customRenderer: this.customRenderer,
|
|
55
|
+
// Extension messages render full content; no collapse-on-fold behaviour.
|
|
56
|
+
});
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.#box.addChild(new Spacer(1));
|
|
73
|
-
|
|
74
|
-
// Extract text content
|
|
75
|
-
let text: string;
|
|
76
|
-
if (typeof this.message.content === "string") {
|
|
77
|
-
text = this.message.content;
|
|
58
|
+
if (custom) {
|
|
59
|
+
this.#customComponent = custom;
|
|
60
|
+
this.addChild(custom);
|
|
78
61
|
} else {
|
|
79
|
-
|
|
80
|
-
.filter((c): c is TextContent => c.type === "text")
|
|
81
|
-
.map(c => c.text)
|
|
82
|
-
.join("\n");
|
|
62
|
+
this.addChild(this.#box);
|
|
83
63
|
}
|
|
84
|
-
|
|
85
|
-
this.#box.addChild(
|
|
86
|
-
new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
87
|
-
color: (value: string) => theme.fg("customMessageText", value),
|
|
88
|
-
}),
|
|
89
|
-
);
|
|
90
64
|
}
|
|
91
65
|
}
|
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { sanitizeText } from "@oh-my-pi/pi-natives";
|
|
7
|
-
import { Container, Loader,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
7
|
+
import { Container, type Loader, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
8
|
+
import { highlightCode, theme } from "../../modes/theme/theme";
|
|
9
|
+
import type { TruncationMeta } from "../../tools/output-meta";
|
|
10
|
+
import {
|
|
11
|
+
buildExecutionFrame,
|
|
12
|
+
buildStatusFooter,
|
|
13
|
+
createCollapsedPreview,
|
|
14
|
+
type ExecutionColorKey,
|
|
15
|
+
type ExecutionStatus,
|
|
16
|
+
resolveExecutionStatus,
|
|
17
|
+
} from "./execution-shared";
|
|
12
18
|
|
|
13
19
|
const PREVIEW_LINES = 20;
|
|
14
20
|
const MAX_DISPLAY_LINE_CHARS = 4000;
|
|
@@ -17,7 +23,7 @@ export type EvalExecutionLanguage = "python" | "js";
|
|
|
17
23
|
|
|
18
24
|
export class EvalExecutionComponent extends Container {
|
|
19
25
|
#outputLines: string[] = [];
|
|
20
|
-
#status:
|
|
26
|
+
#status: ExecutionStatus = "running";
|
|
21
27
|
#exitCode: number | undefined = undefined;
|
|
22
28
|
#loader: Loader;
|
|
23
29
|
#truncation?: TruncationMeta;
|
|
@@ -28,7 +34,7 @@ export class EvalExecutionComponent extends Container {
|
|
|
28
34
|
return this.language === "js" ? "javascript" : "python";
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
#formatHeader(colorKey:
|
|
37
|
+
#formatHeader(colorKey: ExecutionColorKey): Text {
|
|
32
38
|
const prompt = theme.fg(colorKey, theme.bold(">>>"));
|
|
33
39
|
const continuation = theme.fg(colorKey, " ");
|
|
34
40
|
const codeLines = highlightCode(this.code, this.#highlightLang());
|
|
@@ -46,26 +52,13 @@ export class EvalExecutionComponent extends Container {
|
|
|
46
52
|
) {
|
|
47
53
|
super();
|
|
48
54
|
|
|
49
|
-
const colorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
50
|
-
const
|
|
55
|
+
const colorKey: ExecutionColorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
56
|
+
const { contentContainer, loader } = buildExecutionFrame(this, ui, colorKey);
|
|
57
|
+
this.#contentContainer = contentContainer;
|
|
58
|
+
this.#loader = loader;
|
|
51
59
|
|
|
52
|
-
this.addChild(new Spacer(1));
|
|
53
|
-
this.addChild(new DynamicBorder(borderColor));
|
|
54
|
-
|
|
55
|
-
this.#contentContainer = new Container();
|
|
56
|
-
this.addChild(this.#contentContainer);
|
|
57
60
|
this.#contentContainer.addChild(this.#formatHeader(colorKey));
|
|
58
|
-
|
|
59
|
-
this.#loader = new Loader(
|
|
60
|
-
ui,
|
|
61
|
-
spinner => theme.fg(colorKey, spinner),
|
|
62
|
-
text => theme.fg("muted", text),
|
|
63
|
-
`Running… (esc to cancel)`,
|
|
64
|
-
getSymbolTheme().spinnerFrames,
|
|
65
|
-
);
|
|
66
61
|
this.#contentContainer.addChild(this.#loader);
|
|
67
|
-
|
|
68
|
-
this.addChild(new DynamicBorder(borderColor));
|
|
69
62
|
}
|
|
70
63
|
|
|
71
64
|
setExpanded(expanded: boolean): void {
|
|
@@ -99,11 +92,7 @@ export class EvalExecutionComponent extends Container {
|
|
|
99
92
|
options?: { output?: string; truncation?: TruncationMeta },
|
|
100
93
|
): void {
|
|
101
94
|
this.#exitCode = exitCode;
|
|
102
|
-
this.#status = cancelled
|
|
103
|
-
? "cancelled"
|
|
104
|
-
: exitCode !== 0 && exitCode !== undefined && exitCode !== null
|
|
105
|
-
? "error"
|
|
106
|
-
: "complete";
|
|
95
|
+
this.#status = resolveExecutionStatus(exitCode, cancelled);
|
|
107
96
|
this.#truncation = options?.truncation;
|
|
108
97
|
if (options?.output !== undefined) {
|
|
109
98
|
this.#setOutput(options.output);
|
|
@@ -120,7 +109,7 @@ export class EvalExecutionComponent extends Container {
|
|
|
120
109
|
|
|
121
110
|
this.#contentContainer.clear();
|
|
122
111
|
|
|
123
|
-
const colorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
112
|
+
const colorKey: ExecutionColorKey = this.excludeFromContext ? "dim" : "pythonMode";
|
|
124
113
|
this.#contentContainer.addChild(this.#formatHeader(colorKey));
|
|
125
114
|
|
|
126
115
|
if (availableLines.length > 0) {
|
|
@@ -129,39 +118,20 @@ export class EvalExecutionComponent extends Container {
|
|
|
129
118
|
this.#contentContainer.addChild(new Text(`\n${displayText}`, 1, 0));
|
|
130
119
|
} else {
|
|
131
120
|
const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n");
|
|
132
|
-
|
|
133
|
-
this.#contentContainer.addChild({
|
|
134
|
-
render: (width: number) => {
|
|
135
|
-
const { visualLines } = truncateToVisualLines(previewText, PREVIEW_LINES, width, 1);
|
|
136
|
-
return visualLines;
|
|
137
|
-
},
|
|
138
|
-
invalidate: () => {},
|
|
139
|
-
});
|
|
121
|
+
this.#contentContainer.addChild(createCollapsedPreview(`\n${styledOutput}`, PREVIEW_LINES));
|
|
140
122
|
}
|
|
141
123
|
}
|
|
142
124
|
|
|
143
125
|
if (this.#status === "running") {
|
|
144
126
|
this.#contentContainer.addChild(this.#loader);
|
|
145
127
|
} else {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (this.#
|
|
153
|
-
statusParts.push(theme.fg("warning", "(cancelled)"));
|
|
154
|
-
} else if (this.#status === "error") {
|
|
155
|
-
statusParts.push(theme.fg("error", `(exit ${this.#exitCode})`));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (this.#truncation) {
|
|
159
|
-
statusParts.push(theme.fg("warning", formatTruncationMetaNotice(this.#truncation)));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (statusParts.length > 0) {
|
|
163
|
-
this.#contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
|
|
164
|
-
}
|
|
128
|
+
const footer = buildStatusFooter({
|
|
129
|
+
status: this.#status,
|
|
130
|
+
exitCode: this.#exitCode,
|
|
131
|
+
truncation: this.#truncation,
|
|
132
|
+
hiddenLineCount,
|
|
133
|
+
});
|
|
134
|
+
if (footer) this.#contentContainer.addChild(footer);
|
|
165
135
|
}
|
|
166
136
|
}
|
|
167
137
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared rendering primitives for bash/eval execution components.
|
|
3
|
+
*
|
|
4
|
+
* Each helper isolates a piece of structure both components share verbatim
|
|
5
|
+
* (frame layout, collapsed preview, post-run status line). Differences in
|
|
6
|
+
* how each component prepares its header, output lines, or sixel masking
|
|
7
|
+
* stay in their respective files.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { type Component, Container, Loader, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
11
|
+
import { getSymbolTheme, theme } from "../../modes/theme/theme";
|
|
12
|
+
import { formatTruncationMetaNotice, type TruncationMeta } from "../../tools/output-meta";
|
|
13
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
14
|
+
import { truncateToVisualLines } from "./visual-truncate";
|
|
15
|
+
|
|
16
|
+
export type ExecutionStatus = "running" | "complete" | "cancelled" | "error";
|
|
17
|
+
|
|
18
|
+
/** Theme color keys valid for an execution frame. */
|
|
19
|
+
export type ExecutionColorKey = "dim" | "bashMode" | "pythonMode";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build the spacer + top border + content container + bottom border scaffold
|
|
23
|
+
* that bash and eval execution components share. The caller appends the
|
|
24
|
+
* header (command vs `>>>` prompt) and the returned loader to
|
|
25
|
+
* `contentContainer` so per-mode order is preserved.
|
|
26
|
+
*/
|
|
27
|
+
export function buildExecutionFrame(
|
|
28
|
+
parent: Container,
|
|
29
|
+
ui: TUI,
|
|
30
|
+
colorKey: ExecutionColorKey,
|
|
31
|
+
): { contentContainer: Container; loader: Loader } {
|
|
32
|
+
const borderColor = (str: string) => theme.fg(colorKey, str);
|
|
33
|
+
|
|
34
|
+
parent.addChild(new Spacer(1));
|
|
35
|
+
parent.addChild(new DynamicBorder(borderColor));
|
|
36
|
+
|
|
37
|
+
const contentContainer = new Container();
|
|
38
|
+
parent.addChild(contentContainer);
|
|
39
|
+
|
|
40
|
+
const loader = new Loader(
|
|
41
|
+
ui,
|
|
42
|
+
spinner => theme.fg(colorKey, spinner),
|
|
43
|
+
text => theme.fg("muted", text),
|
|
44
|
+
`Running… (esc to cancel)`,
|
|
45
|
+
getSymbolTheme().spinnerFrames,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
parent.addChild(new DynamicBorder(borderColor));
|
|
49
|
+
return { contentContainer, loader };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Wrap a styled preview block in a render-time visual-line truncator.
|
|
54
|
+
* Recomputed per render width so wrapping stays in sync with terminal size.
|
|
55
|
+
*/
|
|
56
|
+
export function createCollapsedPreview(previewText: string, previewLines: number): Component {
|
|
57
|
+
return {
|
|
58
|
+
render: (width: number) => truncateToVisualLines(previewText, previewLines, width, 1).visualLines,
|
|
59
|
+
invalidate: () => {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build the post-run status block (hidden-line hint, exit/cancel marker,
|
|
65
|
+
* truncation notice). Returns undefined when there is nothing to display so
|
|
66
|
+
* callers can skip appending a stray Text child.
|
|
67
|
+
*/
|
|
68
|
+
export function buildStatusFooter(opts: {
|
|
69
|
+
status: ExecutionStatus;
|
|
70
|
+
exitCode: number | undefined;
|
|
71
|
+
truncation: TruncationMeta | undefined;
|
|
72
|
+
hiddenLineCount: number;
|
|
73
|
+
/** Suppress the "… N more lines" hint (used when sixel passthrough renders the full output). */
|
|
74
|
+
suppressHiddenCount?: boolean;
|
|
75
|
+
}): Text | undefined {
|
|
76
|
+
const parts: string[] = [];
|
|
77
|
+
|
|
78
|
+
if (opts.hiddenLineCount > 0 && !opts.suppressHiddenCount) {
|
|
79
|
+
parts.push(theme.fg("dim", `… ${opts.hiddenLineCount} more lines (ctrl+o to expand)`));
|
|
80
|
+
}
|
|
81
|
+
if (opts.status === "cancelled") {
|
|
82
|
+
parts.push(theme.fg("warning", "(cancelled)"));
|
|
83
|
+
} else if (opts.status === "error") {
|
|
84
|
+
parts.push(theme.fg("error", `(exit ${opts.exitCode})`));
|
|
85
|
+
}
|
|
86
|
+
if (opts.truncation) {
|
|
87
|
+
parts.push(theme.fg("warning", formatTruncationMetaNotice(opts.truncation)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (parts.length === 0) return undefined;
|
|
91
|
+
return new Text(`\n${parts.join("\n")}`, 1, 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Derive the post-run status from an exit code + cancellation flag using the
|
|
96
|
+
* same precedence both execution components apply.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveExecutionStatus(exitCode: number | undefined, cancelled: boolean): ExecutionStatus {
|
|
99
|
+
if (cancelled) return "cancelled";
|
|
100
|
+
if (exitCode !== 0 && exitCode !== undefined && exitCode !== null) return "error";
|
|
101
|
+
return "complete";
|
|
102
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { TextContent } from "@oh-my-pi/pi-ai";
|
|
2
1
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
|
-
import { Box, Container,
|
|
2
|
+
import { Box, Container, Spacer } from "@oh-my-pi/pi-tui";
|
|
4
3
|
import type { HookMessageRenderer } from "../../extensibility/hooks/types";
|
|
5
|
-
import {
|
|
4
|
+
import { theme } from "../../modes/theme/theme";
|
|
6
5
|
import type { HookMessage } from "../../session/messages";
|
|
6
|
+
import { renderFramedMessage } from "./message-frame";
|
|
7
|
+
|
|
8
|
+
/** Lines of default markdown body shown before the "…" fold when collapsed. */
|
|
9
|
+
const HOOK_COLLAPSED_LINES = 5;
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Component that renders a custom message entry from hooks.
|
|
@@ -41,60 +44,25 @@ export class HookMessageComponent extends Container {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
#rebuild(): void {
|
|
44
|
-
// Remove previous content component
|
|
45
47
|
if (this.#customComponent) {
|
|
46
48
|
this.removeChild(this.#customComponent);
|
|
47
49
|
this.#customComponent = undefined;
|
|
48
50
|
}
|
|
49
51
|
this.removeChild(this.#box);
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.addChild(component);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
} catch {
|
|
62
|
-
// Fall through to default rendering
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Default rendering uses our box
|
|
67
|
-
this.addChild(this.#box);
|
|
68
|
-
this.#box.clear();
|
|
69
|
-
|
|
70
|
-
// Default rendering: label + content
|
|
71
|
-
const label = theme.fg("customMessageLabel", theme.bold(`[${this.message.customType}]`));
|
|
72
|
-
this.#box.addChild(new Text(label, 0, 0));
|
|
73
|
-
this.#box.addChild(new Spacer(1));
|
|
53
|
+
const custom = renderFramedMessage({
|
|
54
|
+
message: this.message,
|
|
55
|
+
box: this.#box,
|
|
56
|
+
expanded: this.#expanded,
|
|
57
|
+
customRenderer: this.customRenderer,
|
|
58
|
+
collapseAfterLines: HOOK_COLLAPSED_LINES,
|
|
59
|
+
});
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
text = this.message.content;
|
|
61
|
+
if (custom) {
|
|
62
|
+
this.#customComponent = custom;
|
|
63
|
+
this.addChild(custom);
|
|
79
64
|
} else {
|
|
80
|
-
|
|
81
|
-
.filter((c): c is TextContent => c.type === "text")
|
|
82
|
-
.map(c => c.text)
|
|
83
|
-
.join("\n");
|
|
65
|
+
this.addChild(this.#box);
|
|
84
66
|
}
|
|
85
|
-
|
|
86
|
-
// Limit lines when collapsed
|
|
87
|
-
if (!this.#expanded) {
|
|
88
|
-
const lines = text.split("\n");
|
|
89
|
-
if (lines.length > 5) {
|
|
90
|
-
text = `${lines.slice(0, 5).join("\n")}\n…`;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.#box.addChild(
|
|
95
|
-
new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
96
|
-
color: (text: string) => theme.fg("customMessageText", text),
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
99
67
|
}
|
|
100
68
|
}
|
|
@@ -47,6 +47,18 @@ type WizardStep =
|
|
|
47
47
|
| "scope"
|
|
48
48
|
| "confirm";
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Result of the wizard's OAuth callback. `credentialId` is mandatory;
|
|
52
|
+
* `clientId`/`clientSecret` are populated when the OAuth provider performed
|
|
53
|
+
* dynamic client registration (or when the caller pre-supplied them) so the
|
|
54
|
+
* wizard can fold them into the final `mcp.json` entry for refresh.
|
|
55
|
+
*/
|
|
56
|
+
export interface MCPAddWizardOAuthResult {
|
|
57
|
+
credentialId: string;
|
|
58
|
+
clientId?: string;
|
|
59
|
+
clientSecret?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
interface WizardState {
|
|
51
63
|
name: string;
|
|
52
64
|
transport: TransportType | null;
|
|
@@ -104,7 +116,13 @@ export class MCPAddWizard extends Container {
|
|
|
104
116
|
#onCompleteCallback: (name: string, config: MCPServerConfig, scope: Scope) => void;
|
|
105
117
|
#onCancelCallback: () => void;
|
|
106
118
|
#onOAuthCallback:
|
|
107
|
-
| ((
|
|
119
|
+
| ((
|
|
120
|
+
authUrl: string,
|
|
121
|
+
tokenUrl: string,
|
|
122
|
+
clientId: string,
|
|
123
|
+
clientSecret: string,
|
|
124
|
+
scopes: string,
|
|
125
|
+
) => Promise<MCPAddWizardOAuthResult>)
|
|
108
126
|
| null = null;
|
|
109
127
|
#onTestConnectionCallback: ((config: MCPServerConfig) => Promise<void>) | null = null;
|
|
110
128
|
#onRenderCallback: (() => void) | null = null;
|
|
@@ -118,7 +136,7 @@ export class MCPAddWizard extends Container {
|
|
|
118
136
|
clientId: string,
|
|
119
137
|
clientSecret: string,
|
|
120
138
|
scopes: string,
|
|
121
|
-
) => Promise<
|
|
139
|
+
) => Promise<MCPAddWizardOAuthResult>,
|
|
122
140
|
onTestConnection?: (config: MCPServerConfig) => Promise<void>,
|
|
123
141
|
onRender?: () => void,
|
|
124
142
|
initialName?: string,
|
|
@@ -1120,7 +1138,7 @@ export class MCPAddWizard extends Container {
|
|
|
1120
1138
|
|
|
1121
1139
|
try {
|
|
1122
1140
|
// Call OAuth handler
|
|
1123
|
-
const
|
|
1141
|
+
const oauthResult = await this.#onOAuthCallback(
|
|
1124
1142
|
this.#state.oauthAuthUrl,
|
|
1125
1143
|
this.#state.oauthTokenUrl,
|
|
1126
1144
|
this.#state.oauthClientId,
|
|
@@ -1128,8 +1146,11 @@ export class MCPAddWizard extends Container {
|
|
|
1128
1146
|
this.#state.oauthScopes,
|
|
1129
1147
|
);
|
|
1130
1148
|
|
|
1131
|
-
// Store credential ID
|
|
1132
|
-
|
|
1149
|
+
// Store credential ID + any dynamically-registered client credentials,
|
|
1150
|
+
// so the final mcp.json entry persists everything needed for refresh.
|
|
1151
|
+
this.#state.oauthCredentialId = oauthResult.credentialId;
|
|
1152
|
+
if (oauthResult.clientId) this.#state.oauthClientId = oauthResult.clientId;
|
|
1153
|
+
if (oauthResult.clientSecret) this.#state.oauthClientSecret = oauthResult.clientSecret;
|
|
1133
1154
|
|
|
1134
1155
|
// Show success message
|
|
1135
1156
|
this.#contentContainer.clear();
|