@oh-my-pi/pi-coding-agent 15.11.0 → 15.11.1
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 +30 -1
- package/dist/cli.js +73 -67
- package/dist/types/capability/mcp.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +13 -4
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/oauth-flow.d.ts +6 -1
- package/dist/types/mcp/transports/stdio.d.ts +1 -0
- package/dist/types/mcp/types.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/components/mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +3 -0
- package/dist/types/modes/components/transcript-container.d.ts +3 -2
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +43 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/task/index.d.ts +3 -3
- package/dist/types/tools/render-utils.d.ts +22 -0
- package/package.json +11 -11
- package/src/capability/mcp.ts +1 -0
- package/src/cli/gallery-cli.ts +5 -4
- package/src/config/mcp-schema.json +4 -0
- package/src/config/settings-schema.ts +15 -4
- package/src/edit/renderer.ts +96 -46
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +6 -1
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/oauth-discovery.ts +27 -2
- package/src/mcp/oauth-flow.ts +47 -1
- package/src/mcp/transports/stdio.ts +3 -0
- package/src/mcp/types.ts +2 -0
- package/src/modes/components/assistant-message.ts +15 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/mcp-add-wizard.ts +13 -0
- package/src/modes/components/settings-selector.ts +2 -0
- package/src/modes/components/status-line/component.ts +22 -12
- package/src/modes/components/status-line/types.ts +3 -0
- package/src/modes/components/transcript-container.ts +99 -18
- package/src/modes/components/tree-selector.ts +6 -1
- package/src/modes/controllers/event-controller.ts +28 -4
- package/src/modes/controllers/mcp-command-controller.ts +34 -2
- package/src/modes/controllers/selector-controller.ts +4 -0
- package/src/modes/controllers/tool-args-reveal.ts +174 -0
- package/src/modes/interactive-mode.ts +9 -2
- package/src/modes/theme/theme.ts +6 -0
- package/src/prompts/tools/task.md +7 -2
- package/src/session/agent-session.ts +25 -4
- package/src/task/index.ts +15 -10
- package/src/task/render.ts +10 -4
- package/src/tools/render-utils.ts +56 -0
- package/src/tools/write.ts +65 -47
- package/src/web/search/providers/anthropic.ts +29 -4
|
@@ -10,6 +10,7 @@ export interface OAuthEndpoints {
|
|
|
10
10
|
tokenUrl: string;
|
|
11
11
|
clientId?: string;
|
|
12
12
|
scopes?: string;
|
|
13
|
+
resource?: string;
|
|
13
14
|
}
|
|
14
15
|
export interface AuthDetectionResult {
|
|
15
16
|
requiresAuth: boolean;
|
|
@@ -41,4 +42,5 @@ export declare function analyzeAuthError(error: Error, serverUrl?: string): Auth
|
|
|
41
42
|
*/
|
|
42
43
|
export declare function discoverOAuthEndpoints(serverUrl: string, authServerUrl?: string, resourceMetadataUrl?: string, opts?: {
|
|
43
44
|
fetch?: FetchImpl;
|
|
45
|
+
protectedResource?: string;
|
|
44
46
|
}): Promise<OAuthEndpoints | null>;
|
|
@@ -24,6 +24,8 @@ export interface MCPOAuthConfig {
|
|
|
24
24
|
callbackPort?: number;
|
|
25
25
|
/** Custom callback path (default: /callback or redirectUri pathname) */
|
|
26
26
|
callbackPath?: string;
|
|
27
|
+
/** MCP resource URI for RFC 8707 resource indicators */
|
|
28
|
+
resource?: string;
|
|
27
29
|
/** Fetch implementation for token exchange and discovery requests. */
|
|
28
30
|
fetch?: FetchImpl;
|
|
29
31
|
}
|
|
@@ -49,6 +51,7 @@ export declare class MCPOAuthFlow extends OAuthCallbackFlow {
|
|
|
49
51
|
* client id via config.
|
|
50
52
|
*/
|
|
51
53
|
get registeredClientSecret(): string | undefined;
|
|
54
|
+
get resource(): string | undefined;
|
|
52
55
|
generateAuthUrl(state: string, redirectUri: string): Promise<{
|
|
53
56
|
url: string;
|
|
54
57
|
instructions?: string;
|
|
@@ -59,6 +62,8 @@ export declare class MCPOAuthFlow extends OAuthCallbackFlow {
|
|
|
59
62
|
* Refresh an MCP OAuth token using the standard refresh_token grant.
|
|
60
63
|
* Returns updated credentials; preserves the old refresh token if the server doesn't rotate it.
|
|
61
64
|
*/
|
|
62
|
-
export declare function refreshMCPOAuthToken(tokenUrl: string, refreshToken: string, clientId?: string, clientSecret?: string,
|
|
65
|
+
export declare function refreshMCPOAuthToken(tokenUrl: string, refreshToken: string, clientId?: string, clientSecret?: string, resourceOrOpts?: string | {
|
|
66
|
+
fetch?: FetchImpl;
|
|
67
|
+
}, opts?: {
|
|
63
68
|
fetch?: FetchImpl;
|
|
64
69
|
}): Promise<OAuthCredentials>;
|
|
@@ -8,6 +8,7 @@ import type { MCPRequestOptions, MCPStdioServerConfig, MCPTransport } from "../.
|
|
|
8
8
|
/** Subprocess argv for launching an MCP stdio server. */
|
|
9
9
|
export interface StdioSpawnCommand {
|
|
10
10
|
cmd: string[];
|
|
11
|
+
windowsHide?: boolean;
|
|
11
12
|
}
|
|
12
13
|
/** Inputs used to resolve platform-specific stdio spawn behavior. */
|
|
13
14
|
export interface ResolveStdioSpawnOptions {
|
|
@@ -40,6 +40,8 @@ export interface MCPAuthConfig {
|
|
|
40
40
|
clientId?: string;
|
|
41
41
|
/** Client secret — persisted for token refresh */
|
|
42
42
|
clientSecret?: string;
|
|
43
|
+
/** MCP resource URI — persisted for OAuth resource indicators during refresh */
|
|
44
|
+
resource?: string;
|
|
43
45
|
}
|
|
44
46
|
/** Base server config with shared options */
|
|
45
47
|
interface MCPServerConfigBase {
|
|
@@ -19,6 +19,7 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
19
19
|
*/
|
|
20
20
|
setErrorPinned(pinned: boolean): void;
|
|
21
21
|
isTranscriptBlockFinalized(): boolean;
|
|
22
|
+
getTranscriptBlockVersion(): number;
|
|
22
23
|
markTranscriptBlockFinalized(): void;
|
|
23
24
|
setToolResultImages(toolCallId: string, images: ImageContent[]): void;
|
|
24
25
|
setUsageInfo(usage: Usage): void;
|
|
@@ -16,10 +16,11 @@ export interface MCPAddWizardOAuthResult {
|
|
|
16
16
|
credentialId: string;
|
|
17
17
|
clientId?: string;
|
|
18
18
|
clientSecret?: string;
|
|
19
|
+
resource?: string;
|
|
19
20
|
}
|
|
20
21
|
export declare class MCPAddWizard extends Container {
|
|
21
22
|
#private;
|
|
22
|
-
constructor(onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void, onCancel: () => void, onOAuth?: (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string) => Promise<MCPAddWizardOAuthResult>, onTestConnection?: (config: MCPServerConfig) => Promise<void>, onRender?: () => void, initialName?: string);
|
|
23
|
+
constructor(onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void, onCancel: () => void, onOAuth?: (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string, resource?: string) => Promise<MCPAddWizardOAuthResult>, onTestConnection?: (config: MCPServerConfig) => Promise<void>, onRender?: () => void, initialName?: string);
|
|
23
24
|
handleInput(keyData: string): void;
|
|
24
25
|
}
|
|
25
26
|
export {};
|
|
@@ -25,6 +25,7 @@ export interface StatusLinePreviewSettings {
|
|
|
25
25
|
rightSegments?: StatusLineSegmentId[];
|
|
26
26
|
separator?: StatusLineSeparatorStyle;
|
|
27
27
|
sessionAccent?: boolean;
|
|
28
|
+
transparent?: boolean;
|
|
28
29
|
}
|
|
29
30
|
export interface SettingsCallbacks {
|
|
30
31
|
/** Called when any setting value changes */
|
|
@@ -29,6 +29,9 @@ export interface StatusLineSettings {
|
|
|
29
29
|
segmentOptions?: StatusLineSegmentOptions;
|
|
30
30
|
showHookStatus?: boolean;
|
|
31
31
|
sessionAccent?: boolean;
|
|
32
|
+
/** Drop the theme's `statusLineBg` fill and powerline caps so the bar
|
|
33
|
+
* inherits the terminal's default background. */
|
|
34
|
+
transparent?: boolean;
|
|
32
35
|
}
|
|
33
36
|
export type EffectiveStatusLineSettings = Required<Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">> & StatusLineSettings;
|
|
34
37
|
export type RGB = readonly [number, number, number];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Component, Container, type NativeScrollbackLiveRegion, type RenderStablePrefix } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { type Component, Container, type NativeScrollbackCommittedRows, type NativeScrollbackLiveRegion, type RenderStablePrefix } from "@oh-my-pi/pi-tui";
|
|
2
2
|
/**
|
|
3
3
|
* Transcript container that renders every block's current content each frame
|
|
4
4
|
* and reports the live-region seam (`NativeScrollbackLiveRegion`) that gates
|
|
@@ -22,10 +22,11 @@ import { type Component, Container, type NativeScrollbackLiveRegion, type Render
|
|
|
22
22
|
* through {@link RenderStablePrefix} so the engine can skip marker scanning,
|
|
23
23
|
* line preparation, and the committed-prefix audit for those rows.
|
|
24
24
|
*/
|
|
25
|
-
export declare class TranscriptContainer extends Container implements NativeScrollbackLiveRegion, RenderStablePrefix {
|
|
25
|
+
export declare class TranscriptContainer extends Container implements NativeScrollbackLiveRegion, NativeScrollbackCommittedRows, RenderStablePrefix {
|
|
26
26
|
#private;
|
|
27
27
|
invalidate(): void;
|
|
28
28
|
clear(): void;
|
|
29
|
+
setNativeScrollbackCommittedRows(rows: number): void;
|
|
29
30
|
getRenderStablePrefixRows(): number;
|
|
30
31
|
getNativeScrollbackLiveRegionStart(): number | undefined;
|
|
31
32
|
getNativeScrollbackCommitSafeEnd(): number | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Minimal component surface the reveal pushes frames into. */
|
|
2
|
+
type ToolArgsRevealComponent = {
|
|
3
|
+
updateArgs(args: unknown, toolCallId?: string): void;
|
|
4
|
+
};
|
|
5
|
+
type ToolArgsRevealControllerOptions = {
|
|
6
|
+
getSmoothStreaming(): boolean;
|
|
7
|
+
requestRender(): void;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Paces streamed tool-call arguments the same way StreamingRevealController
|
|
11
|
+
* paces assistant text: providers that deliver `partialJson` in large batches
|
|
12
|
+
* (or throttle their partial parses) would otherwise make write/edit/bash
|
|
13
|
+
* streaming previews jump in chunks. Each pending tool call reveals its raw
|
|
14
|
+
* argument stream at the shared 30fps cadence with the same adaptive
|
|
15
|
+
* catch-up step, re-parsing the revealed prefix per frame.
|
|
16
|
+
*
|
|
17
|
+
* Reveal units are UTF-16 code units of the raw stream, not graphemes —
|
|
18
|
+
* the prefix goes through a JSON parser rather than straight to the screen,
|
|
19
|
+
* so only surrogate-pair integrity matters (see {@link clampSliceEnd}).
|
|
20
|
+
*/
|
|
21
|
+
export declare class ToolArgsRevealController {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(options: ToolArgsRevealControllerOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Record the latest streamed argument text for a tool call and return the
|
|
26
|
+
* args to render right now. With smoothing disabled the full target passes
|
|
27
|
+
* through in the caller's legacy shape (`{ ...args, __partialJson }`).
|
|
28
|
+
*/
|
|
29
|
+
setTarget(id: string, partialJson: string, rawInput: boolean, fullArgs: Record<string, unknown>): Record<string, unknown>;
|
|
30
|
+
/** Attach the component future ticks push frames into. */
|
|
31
|
+
bind(id: string, component: ToolArgsRevealComponent): void;
|
|
32
|
+
/** Final arguments arrived (the JSON closed): drop the reveal so the
|
|
33
|
+
* caller's final-args render wins immediately, mirroring how assistant
|
|
34
|
+
* text snaps to the full message at message_end. */
|
|
35
|
+
finish(id: string): void;
|
|
36
|
+
/** Snap every live entry to its full received stream and clear. Used at
|
|
37
|
+
* message_end (abort/error mid-stream) so sealed components freeze showing
|
|
38
|
+
* everything that arrived rather than a mid-reveal prefix. */
|
|
39
|
+
flushAll(): void;
|
|
40
|
+
/** Clear without pushing (teardown). */
|
|
41
|
+
stop(): void;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
|
|
|
6
6
|
/**
|
|
7
7
|
* All available symbol keys organized by category.
|
|
8
8
|
*/
|
|
9
|
-
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc";
|
|
9
|
+
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.job" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc";
|
|
10
10
|
export type SpinnerType = "status" | "activity";
|
|
11
11
|
export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
|
|
12
12
|
/** Check if a string is a valid ThemeColor value */
|
|
@@ -150,6 +150,7 @@ export declare class Theme {
|
|
|
150
150
|
time: string;
|
|
151
151
|
pi: string;
|
|
152
152
|
agents: string;
|
|
153
|
+
job: string;
|
|
153
154
|
cache: string;
|
|
154
155
|
input: string;
|
|
155
156
|
output: string;
|
|
@@ -22,8 +22,8 @@ export declare function formatResultOutputFallback(result: Pick<SingleResult, "o
|
|
|
22
22
|
* Task tool - Delegate tasks to specialized agents.
|
|
23
23
|
*
|
|
24
24
|
* Each call spawns one subagent — or, with `task.batch`, one per `tasks[]`
|
|
25
|
-
* item.
|
|
26
|
-
*
|
|
25
|
+
* item. When `async.enabled` is on, spawns run as AsyncJobManager jobs; when
|
|
26
|
+
* disabled, the tool blocks until every spawn finishes.
|
|
27
27
|
*/
|
|
28
28
|
export declare class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetails, Theme> {
|
|
29
29
|
#private;
|
|
@@ -32,7 +32,7 @@ export declare class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskT
|
|
|
32
32
|
readonly approval: "exec";
|
|
33
33
|
readonly formatApprovalDetails: (args: unknown) => string[];
|
|
34
34
|
readonly label = "Task";
|
|
35
|
-
readonly summary = "Spawn
|
|
35
|
+
readonly summary = "Spawn subagents to complete delegated tasks";
|
|
36
36
|
readonly strict = true;
|
|
37
37
|
readonly loadMode = "discoverable";
|
|
38
38
|
readonly renderResult: typeof renderResult;
|
|
@@ -173,6 +173,28 @@ export declare function capParseErrors(errors: string[] | undefined, limit?: num
|
|
|
173
173
|
export declare function createCachedComponent(getExpanded: () => boolean, compute: (width: number, expanded: boolean) => string[], options?: {
|
|
174
174
|
paddingX?: number;
|
|
175
175
|
}): Component;
|
|
176
|
+
/**
|
|
177
|
+
* Single-slot memo for an expensive rendered string (syntax highlighting, diff
|
|
178
|
+
* coloring) keyed by the exact inputs that shape the bytes: theme instance,
|
|
179
|
+
* expanded state, a caller-chosen salt (path/language), and the source content.
|
|
180
|
+
* Field-wise comparison instead of a concatenated key string: a cache hit costs
|
|
181
|
+
* one string value-compare (engines short-circuit on length) and a miss never
|
|
182
|
+
* allocates a key. Comparing the {@link Theme} by reference is sound because
|
|
183
|
+
* theme switches replace the instance wholesale (`setTheme`/`previewTheme`/
|
|
184
|
+
* `setSymbolPreset` in modes/theme/theme.ts) — themes are never mutated in
|
|
185
|
+
* place.
|
|
186
|
+
*/
|
|
187
|
+
export interface RenderedStringCache {
|
|
188
|
+
theme: Theme | null;
|
|
189
|
+
expanded: boolean;
|
|
190
|
+
salt: string;
|
|
191
|
+
content: string;
|
|
192
|
+
value: string;
|
|
193
|
+
}
|
|
194
|
+
export declare function createRenderedStringCache(): RenderedStringCache;
|
|
195
|
+
/** Drop the memo so the next lookup re-renders (e.g. the render function identity changed). */
|
|
196
|
+
export declare function invalidateRenderedStringCache(cache: RenderedStringCache): void;
|
|
197
|
+
export declare function cachedRenderedString(cache: RenderedStringCache | undefined, theme: Theme, expanded: boolean, salt: string, content: string, render: () => string): string;
|
|
176
198
|
/**
|
|
177
199
|
* Append the indented bullet list of parse errors (capped at
|
|
178
200
|
* {@link PARSE_ERRORS_LIMIT}) to `lines`, with an overflow summary line if the
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.11.
|
|
4
|
+
"version": "15.11.1",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.22.1",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.11.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.11.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.11.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.11.
|
|
54
|
-
"@oh-my-pi/pi-catalog": "15.11.
|
|
55
|
-
"@oh-my-pi/pi-mnemopi": "15.11.
|
|
56
|
-
"@oh-my-pi/pi-natives": "15.11.
|
|
57
|
-
"@oh-my-pi/pi-tui": "15.11.
|
|
58
|
-
"@oh-my-pi/pi-utils": "15.11.
|
|
59
|
-
"@oh-my-pi/snapcompact": "15.11.
|
|
50
|
+
"@oh-my-pi/hashline": "15.11.1",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.11.1",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.11.1",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.11.1",
|
|
54
|
+
"@oh-my-pi/pi-catalog": "15.11.1",
|
|
55
|
+
"@oh-my-pi/pi-mnemopi": "15.11.1",
|
|
56
|
+
"@oh-my-pi/pi-natives": "15.11.1",
|
|
57
|
+
"@oh-my-pi/pi-tui": "15.11.1",
|
|
58
|
+
"@oh-my-pi/pi-utils": "15.11.1",
|
|
59
|
+
"@oh-my-pi/snapcompact": "15.11.1",
|
|
60
60
|
"@opentelemetry/api": "^1.9.1",
|
|
61
61
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
62
62
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
package/src/capability/mcp.ts
CHANGED
|
@@ -36,6 +36,7 @@ export interface MCPServer {
|
|
|
36
36
|
tokenUrl?: string;
|
|
37
37
|
clientId?: string;
|
|
38
38
|
clientSecret?: string;
|
|
39
|
+
resource?: string;
|
|
39
40
|
};
|
|
40
41
|
/** OAuth configuration (clientId, clientSecret, redirectUri, callbackPort, callbackPath) for servers requiring explicit client credentials */
|
|
41
42
|
oauth?: {
|
package/src/cli/gallery-cli.ts
CHANGED
|
@@ -111,10 +111,11 @@ export async function renderGalleryState(
|
|
|
111
111
|
|
|
112
112
|
const tool = fakeToolFor(name, fixture);
|
|
113
113
|
const streamingArgs = state === "streaming" ? (fixture.streamingArgs ?? fixture.args) : fixture.args;
|
|
114
|
-
// The component only calls `requestRender`
|
|
115
|
-
// `imageBudget` is consulted solely
|
|
116
|
-
// disables. A cast avoids
|
|
117
|
-
|
|
114
|
+
// The component only calls `requestRender`/`requestComponentRender` (via
|
|
115
|
+
// its loader) during a static render; `imageBudget` is consulted solely
|
|
116
|
+
// when images render, which the gallery disables. A cast avoids
|
|
117
|
+
// constructing a real terminal.
|
|
118
|
+
const ui = { requestRender() {}, requestComponentRender() {} } as unknown as TUI;
|
|
118
119
|
const component = new ToolExecutionComponent(name, streamingArgs, { showImages: false }, tool, ui, getProjectDir());
|
|
119
120
|
component.setExpanded(expanded);
|
|
120
121
|
|
|
@@ -452,6 +452,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
452
452
|
description: "Use the session name color for the editor border and status line gap",
|
|
453
453
|
},
|
|
454
454
|
},
|
|
455
|
+
|
|
456
|
+
"statusLine.transparent": {
|
|
457
|
+
type: "boolean",
|
|
458
|
+
default: false,
|
|
459
|
+
ui: {
|
|
460
|
+
tab: "appearance",
|
|
461
|
+
label: "Transparent Status Line",
|
|
462
|
+
description:
|
|
463
|
+
"Use the terminal's default background for the status line instead of the theme's `statusLineBg`. Powerline end caps are dropped because they need a contrasting fill to bridge into the surrounding terminal.",
|
|
464
|
+
},
|
|
465
|
+
},
|
|
455
466
|
"tools.artifactSpillThreshold": {
|
|
456
467
|
type: "number",
|
|
457
468
|
default: 50,
|
|
@@ -668,7 +679,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
668
679
|
ui: {
|
|
669
680
|
tab: "appearance",
|
|
670
681
|
label: "Smooth Streaming",
|
|
671
|
-
description: "Reveal assistant text smoothly while
|
|
682
|
+
description: "Reveal assistant text and streamed tool input smoothly while chunks arrive",
|
|
672
683
|
},
|
|
673
684
|
},
|
|
674
685
|
|
|
@@ -2501,11 +2512,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
2501
2512
|
// Async jobs
|
|
2502
2513
|
"async.enabled": {
|
|
2503
2514
|
type: "boolean",
|
|
2504
|
-
default:
|
|
2515
|
+
default: true,
|
|
2505
2516
|
ui: {
|
|
2506
2517
|
tab: "tools",
|
|
2507
2518
|
label: "Async Execution",
|
|
2508
|
-
description: "Enable async bash commands",
|
|
2519
|
+
description: "Enable async bash commands and background task execution",
|
|
2509
2520
|
},
|
|
2510
2521
|
},
|
|
2511
2522
|
|
|
@@ -2758,7 +2769,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2758
2769
|
tab: "tasks",
|
|
2759
2770
|
label: "Batch Task Calls",
|
|
2760
2771
|
description:
|
|
2761
|
-
"Switch the task tool to its batch shape: one call carries { agent, context, tasks[] } — one subagent per item (with per-item isolation) and a required shared context prepended to every assignment.
|
|
2772
|
+
"Switch the task tool to its batch shape: one call carries { agent, context, tasks[] } — one subagent per item (with per-item isolation) and a required shared context prepended to every assignment. With async.enabled=true, each spawn runs as an independent background agent with the normal idle/parked lifecycle; otherwise the call blocks for merged results. Disable to restore the flat single-spawn schema.",
|
|
2762
2773
|
},
|
|
2763
2774
|
},
|
|
2764
2775
|
|
package/src/edit/renderer.ts
CHANGED
|
@@ -12,13 +12,17 @@ import { renderDiff as renderDiffColored } from "../modes/components/diff";
|
|
|
12
12
|
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
13
13
|
import type { OutputMeta } from "../tools/output-meta";
|
|
14
14
|
import {
|
|
15
|
+
cachedRenderedString,
|
|
16
|
+
createRenderedStringCache,
|
|
15
17
|
formatDiagnostics,
|
|
16
18
|
formatExpandHint,
|
|
17
19
|
formatStatusIcon,
|
|
18
20
|
getDiffStats,
|
|
19
21
|
getLspBatchRequest,
|
|
22
|
+
invalidateRenderedStringCache,
|
|
20
23
|
type LspBatchRequest,
|
|
21
24
|
PREVIEW_LIMITS,
|
|
25
|
+
type RenderedStringCache,
|
|
22
26
|
replaceTabs,
|
|
23
27
|
shortenPath,
|
|
24
28
|
truncateDiffByHunk,
|
|
@@ -138,6 +142,26 @@ export interface EditRenderContext {
|
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
const EDIT_STREAMING_PREVIEW_LINES = 12;
|
|
145
|
+
|
|
146
|
+
function plainDiffRender(diffText: string): string {
|
|
147
|
+
return diffText;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Lazily grown per-file preview cache slots: the file count of a streaming
|
|
152
|
+
* multi-file patch is discovered mid-stream, so a fixed-size array would
|
|
153
|
+
* silently bypass caching for late files.
|
|
154
|
+
*/
|
|
155
|
+
function previewCacheAt(caches: RenderedStringCache[] | undefined, index: number): RenderedStringCache | undefined {
|
|
156
|
+
if (!caches) return undefined;
|
|
157
|
+
let cache = caches[index];
|
|
158
|
+
if (cache === undefined) {
|
|
159
|
+
cache = createRenderedStringCache();
|
|
160
|
+
caches[index] = cache;
|
|
161
|
+
}
|
|
162
|
+
return cache;
|
|
163
|
+
}
|
|
164
|
+
|
|
141
165
|
const CALL_TEXT_PREVIEW_LINES = 6;
|
|
142
166
|
const CALL_TEXT_PREVIEW_WIDTH = 80;
|
|
143
167
|
|
|
@@ -313,7 +337,6 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, filePath?: string)
|
|
|
313
337
|
}
|
|
314
338
|
return preview.trimEnd();
|
|
315
339
|
}
|
|
316
|
-
|
|
317
340
|
function formatStreamingDiff(
|
|
318
341
|
diff: string,
|
|
319
342
|
rawPath: string,
|
|
@@ -321,26 +344,30 @@ function formatStreamingDiff(
|
|
|
321
344
|
expanded: boolean,
|
|
322
345
|
label = "streaming",
|
|
323
346
|
spinnerFrame?: number,
|
|
347
|
+
cache?: RenderedStringCache,
|
|
324
348
|
): string {
|
|
325
349
|
if (!diff) return "";
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
let text = cachedRenderedString(cache, uiTheme, expanded, rawPath, diff, () => {
|
|
351
|
+
// Collapsed uses a "Cursor" tail window: pin the last
|
|
352
|
+
// EDIT_STREAMING_PREVIEW_LINES rows to the bottom so freshly streamed changes
|
|
353
|
+
// stay on screen. The whole-file diff is recomputed on every streamed chunk
|
|
354
|
+
// and its Myers alignment is not monotonic in payload length, so a hunk-aware
|
|
355
|
+
// window stutters as rows move between hunks. Expanded deliberately lifts that
|
|
356
|
+
// cap for the approval-time full view.
|
|
357
|
+
const allLines = diff.replace(/\n+$/u, "").split("\n");
|
|
358
|
+
const hiddenLines = expanded ? 0 : Math.max(0, allLines.length - EDIT_STREAMING_PREVIEW_LINES);
|
|
359
|
+
const visible = hiddenLines > 0 ? allLines.slice(hiddenLines) : allLines;
|
|
360
|
+
let rendered = "\n\n";
|
|
361
|
+
if (hiddenLines > 0) {
|
|
362
|
+
const hiddenHunks = getDiffStats(allLines.slice(0, hiddenLines).join("\n")).hunks;
|
|
363
|
+
const remainder: string[] = [];
|
|
364
|
+
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
365
|
+
remainder.push(`${hiddenLines} more lines`);
|
|
366
|
+
rendered += `${uiTheme.fg("dim", `… (${remainder.join(", ")} above)`)}\n`;
|
|
367
|
+
}
|
|
368
|
+
rendered += renderDiffColored(visible.join("\n"), { filePath: rawPath });
|
|
369
|
+
return rendered;
|
|
370
|
+
});
|
|
344
371
|
// The animated glyph rides this trailing line — inside the transcript's
|
|
345
372
|
// volatile-tail holdback — never the block header: an animating head row
|
|
346
373
|
// pins the native-scrollback commit boundary at the top of the block, so a
|
|
@@ -360,9 +387,11 @@ function formatMultiFileStreamingDiff(
|
|
|
360
387
|
uiTheme: Theme,
|
|
361
388
|
expanded: boolean,
|
|
362
389
|
spinnerFrame?: number,
|
|
390
|
+
caches?: RenderedStringCache[],
|
|
363
391
|
): string {
|
|
364
392
|
const parts: string[] = [];
|
|
365
|
-
for (
|
|
393
|
+
for (let index = 0; index < previews.length; index++) {
|
|
394
|
+
const preview = previews[index]!;
|
|
366
395
|
if (!preview.diff && !preview.error) continue;
|
|
367
396
|
const header = uiTheme.fg("dim", `\n\n── ${shortenPath(preview.path)} ──`);
|
|
368
397
|
if (preview.error) {
|
|
@@ -373,9 +402,10 @@ function formatMultiFileStreamingDiff(
|
|
|
373
402
|
// Only the last file's preview carries the animated streaming glyph;
|
|
374
403
|
// earlier files have settled and must stay byte-stable so their rows
|
|
375
404
|
// can commit to native scrollback mid-stream.
|
|
376
|
-
const isLast =
|
|
405
|
+
const isLast = index === previews.length - 1;
|
|
406
|
+
const cache = previewCacheAt(caches, index);
|
|
377
407
|
parts.push(
|
|
378
|
-
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined)}`,
|
|
408
|
+
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
379
409
|
);
|
|
380
410
|
}
|
|
381
411
|
}
|
|
@@ -389,16 +419,18 @@ function getCallPreview(
|
|
|
389
419
|
renderContext: EditRenderContext | undefined,
|
|
390
420
|
expanded: boolean,
|
|
391
421
|
spinnerFrame?: number,
|
|
422
|
+
caches?: RenderedStringCache[],
|
|
392
423
|
): string {
|
|
393
424
|
const multi = renderContext?.perFileDiffPreview;
|
|
394
425
|
if (multi && multi.length > 1 && multi.some(p => p.diff || p.error)) {
|
|
395
|
-
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame);
|
|
426
|
+
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame, caches);
|
|
396
427
|
}
|
|
428
|
+
const cache = previewCacheAt(caches, 0);
|
|
397
429
|
if (args.previewDiff) {
|
|
398
|
-
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame);
|
|
430
|
+
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
399
431
|
}
|
|
400
432
|
if (args.diff && args.op) {
|
|
401
|
-
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame);
|
|
433
|
+
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
402
434
|
}
|
|
403
435
|
if (args.diff) {
|
|
404
436
|
return renderPlainTextPreview(args.diff, uiTheme, rawPath);
|
|
@@ -492,30 +524,32 @@ function formatDiffStatsSuffix(diff: string, uiTheme: Theme): string {
|
|
|
492
524
|
].filter(value => value !== undefined);
|
|
493
525
|
return ` ${uiTheme.fg("dim", uiTheme.format.bracketLeft)}${stats.join(uiTheme.fg("dim", "/"))}${uiTheme.fg("dim", uiTheme.format.bracketRight)}`;
|
|
494
526
|
}
|
|
495
|
-
|
|
496
527
|
function renderDiffSection(
|
|
497
528
|
diff: string,
|
|
498
529
|
rawPath: string,
|
|
499
530
|
expanded: boolean,
|
|
500
531
|
uiTheme: Theme,
|
|
501
532
|
renderDiffFn: (t: string, o?: { filePath?: string }) => string,
|
|
533
|
+
cache?: RenderedStringCache,
|
|
502
534
|
): string {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
535
|
+
return cachedRenderedString(cache, uiTheme, expanded, rawPath, diff, () => {
|
|
536
|
+
const {
|
|
537
|
+
text: truncatedDiff,
|
|
538
|
+
hiddenHunks,
|
|
539
|
+
hiddenLines,
|
|
540
|
+
} = expanded
|
|
541
|
+
? { text: diff, hiddenHunks: 0, hiddenLines: 0 }
|
|
542
|
+
: truncateDiffByHunk(diff, PREVIEW_LIMITS.DIFF_COLLAPSED_HUNKS, PREVIEW_LIMITS.DIFF_COLLAPSED_LINES);
|
|
543
|
+
|
|
544
|
+
let text = `\n${renderDiffFn(truncatedDiff, { filePath: rawPath })}`;
|
|
545
|
+
if (!expanded && (hiddenHunks > 0 || hiddenLines > 0)) {
|
|
546
|
+
const remainder: string[] = [];
|
|
547
|
+
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
548
|
+
if (hiddenLines > 0) remainder.push(`${hiddenLines} more lines`);
|
|
549
|
+
text += uiTheme.fg("toolOutput", `\n… (${remainder.join(", ")}) ${formatExpandHint(uiTheme)}`);
|
|
550
|
+
}
|
|
551
|
+
return text;
|
|
552
|
+
});
|
|
519
553
|
}
|
|
520
554
|
|
|
521
555
|
function wrapEditRendererLine(line: string, width: number): string[] {
|
|
@@ -574,6 +608,7 @@ export const editToolRenderer = {
|
|
|
574
608
|
if (Array.isArray(editArgs.edits)) {
|
|
575
609
|
fileCount = countEditFiles(editArgs.edits);
|
|
576
610
|
}
|
|
611
|
+
const callPreviewCaches: RenderedStringCache[] = [];
|
|
577
612
|
return framedBlock(uiTheme, width => {
|
|
578
613
|
// Static pending icon, never the animated glyph: the header is the
|
|
579
614
|
// head row of the framed block, and native-scrollback commits are
|
|
@@ -588,7 +623,15 @@ export const editToolRenderer = {
|
|
|
588
623
|
rename,
|
|
589
624
|
extraSuffix: fileCount > 1 ? uiTheme.fg("dim", ` (+${fileCount - 1} more)`) : undefined,
|
|
590
625
|
});
|
|
591
|
-
let body = getCallPreview(
|
|
626
|
+
let body = getCallPreview(
|
|
627
|
+
editArgs,
|
|
628
|
+
rawPath,
|
|
629
|
+
uiTheme,
|
|
630
|
+
renderContext,
|
|
631
|
+
options.expanded,
|
|
632
|
+
options?.spinnerFrame,
|
|
633
|
+
callPreviewCaches,
|
|
634
|
+
);
|
|
592
635
|
if (applyPatchSummary?.error) {
|
|
593
636
|
body += `\n${uiTheme.fg("error", truncateToWidth(replaceTabs(applyPatchSummary.error, rawPath), Math.max(1, width - 2)))}`;
|
|
594
637
|
}
|
|
@@ -652,11 +695,18 @@ function renderSingleFileResult(
|
|
|
652
695
|
(result.content?.find(c => c.type === "text")?.text ?? "")
|
|
653
696
|
: "";
|
|
654
697
|
|
|
698
|
+
let diffSectionRenderDiffFn: ((t: string, o?: { filePath?: string }) => string) | undefined;
|
|
699
|
+
const diffSectionCache = createRenderedStringCache();
|
|
700
|
+
|
|
655
701
|
return framedBlock(uiTheme, width => {
|
|
656
702
|
const { expanded, renderContext } = options;
|
|
657
703
|
const editDiffPreview = renderContext?.editDiffPreview;
|
|
658
|
-
const renderDiffFn = renderContext?.renderDiff ??
|
|
704
|
+
const renderDiffFn = renderContext?.renderDiff ?? plainDiffRender;
|
|
659
705
|
|
|
706
|
+
if (diffSectionRenderDiffFn !== renderDiffFn) {
|
|
707
|
+
diffSectionRenderDiffFn = renderDiffFn;
|
|
708
|
+
invalidateRenderedStringCache(diffSectionCache);
|
|
709
|
+
}
|
|
660
710
|
const firstChangedLine =
|
|
661
711
|
(editDiffPreview && "firstChangedLine" in editDiffPreview ? editDiffPreview.firstChangedLine : undefined) ||
|
|
662
712
|
(details && !isError ? details.firstChangedLine : undefined);
|
|
@@ -681,11 +731,11 @@ function renderSingleFileResult(
|
|
|
681
731
|
if (isError) {
|
|
682
732
|
if (errorText) body = uiTheme.fg("error", replaceTabs(errorText, rawPath));
|
|
683
733
|
} else if (details?.diff) {
|
|
684
|
-
body = renderDiffSection(details.diff, rawPath, expanded, uiTheme, renderDiffFn);
|
|
734
|
+
body = renderDiffSection(details.diff, rawPath, expanded, uiTheme, renderDiffFn, diffSectionCache);
|
|
685
735
|
} else if (editDiffPreview) {
|
|
686
736
|
if ("error" in editDiffPreview) body = uiTheme.fg("error", replaceTabs(editDiffPreview.error, rawPath));
|
|
687
737
|
else if (editDiffPreview.diff)
|
|
688
|
-
body = renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, renderDiffFn);
|
|
738
|
+
body = renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, renderDiffFn, diffSectionCache);
|
|
689
739
|
}
|
|
690
740
|
if (details?.diagnostics) {
|
|
691
741
|
body += formatDiagnostics(details.diagnostics, expanded, uiTheme, (fp: string) =>
|