feat(coding-agent): hyperlink file paths in tool titles
Wrap the path shown in read/write/edit/ls tool titles in an OSC 8 file:// hyperlink when the terminal advertises hyperlink support. The link target is always the absolute path, while the visible text stays relative/home-shortened and line annotations (e.g. :120-140) are kept out of the target.
This commit is contained in:
@@ -4,6 +4,7 @@ import { constants } from "fs";
|
|||||||
import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
|
import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
|
||||||
import { type Static, Type } from "typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import { renderDiff } from "../../modes/interactive/components/diff.ts";
|
import { renderDiff } from "../../modes/interactive/components/diff.ts";
|
||||||
|
import type { Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import type { ToolDefinition } from "../extensions/types.ts";
|
import type { ToolDefinition } from "../extensions/types.ts";
|
||||||
import {
|
import {
|
||||||
applyEditsToNormalizedContent,
|
applyEditsToNormalizedContent,
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
} from "./edit-diff.ts";
|
} from "./edit-diff.ts";
|
||||||
import { withFileMutationQueue } from "./file-mutation-queue.ts";
|
import { withFileMutationQueue } from "./file-mutation-queue.ts";
|
||||||
import { resolveToCwd } from "./path-utils.ts";
|
import { resolveToCwd } from "./path-utils.ts";
|
||||||
import { invalidArgText, shortenPath, str } from "./render-utils.ts";
|
import { renderToolPath, str } from "./render-utils.ts";
|
||||||
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
||||||
|
|
||||||
type EditPreview = EditDiffResult | EditDiffError;
|
type EditPreview = EditDiffResult | EditDiffError;
|
||||||
@@ -191,14 +192,8 @@ function getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEditCall(
|
function formatEditCall(args: RenderableEditArgs | undefined, theme: Theme, cwd: string): string {
|
||||||
args: RenderableEditArgs | undefined,
|
const pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
|
||||||
): string {
|
|
||||||
const invalidArg = invalidArgText(theme);
|
|
||||||
const rawPath = str(args?.file_path ?? args?.path);
|
|
||||||
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
|
||||||
const pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
|
|
||||||
return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
|
return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +201,7 @@ function formatEditResult(
|
|||||||
args: RenderableEditArgs | undefined,
|
args: RenderableEditArgs | undefined,
|
||||||
preview: EditPreview | undefined,
|
preview: EditPreview | undefined,
|
||||||
result: EditToolResultLike,
|
result: EditToolResultLike,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
isError: boolean,
|
isError: boolean,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const rawPath = str(args?.file_path ?? args?.path);
|
const rawPath = str(args?.file_path ?? args?.path);
|
||||||
@@ -234,7 +229,7 @@ function formatEditResult(
|
|||||||
function getEditHeaderBg(
|
function getEditHeaderBg(
|
||||||
preview: EditPreview | undefined,
|
preview: EditPreview | undefined,
|
||||||
settledError: boolean | undefined,
|
settledError: boolean | undefined,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
): (text: string) => string {
|
): (text: string) => string {
|
||||||
if (preview) {
|
if (preview) {
|
||||||
if ("error" in preview) {
|
if ("error" in preview) {
|
||||||
@@ -251,11 +246,12 @@ function getEditHeaderBg(
|
|||||||
function buildEditCallComponent(
|
function buildEditCallComponent(
|
||||||
component: EditCallRenderComponent,
|
component: EditCallRenderComponent,
|
||||||
args: RenderableEditArgs | undefined,
|
args: RenderableEditArgs | undefined,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
|
cwd: string,
|
||||||
): EditCallRenderComponent {
|
): EditCallRenderComponent {
|
||||||
component.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));
|
component.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));
|
||||||
component.clear();
|
component.clear();
|
||||||
component.addChild(new Text(formatEditCall(args, theme), 0, 0));
|
component.addChild(new Text(formatEditCall(args, theme, cwd), 0, 0));
|
||||||
|
|
||||||
if (!component.preview) {
|
if (!component.preview) {
|
||||||
return component;
|
return component;
|
||||||
@@ -389,7 +385,7 @@ export function createEditToolDefinition(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildEditCallComponent(component, args, theme);
|
return buildEditCallComponent(component, args, theme, context.cwd);
|
||||||
},
|
},
|
||||||
renderResult(result, _options, theme, context) {
|
renderResult(result, _options, theme, context) {
|
||||||
const callComponent = context.state.callComponent;
|
const callComponent = context.state.callComponent;
|
||||||
@@ -414,7 +410,12 @@ export function createEditToolDefinition(
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
buildEditCallComponent(callComponent, context.args as RenderableEditArgs | undefined, theme);
|
buildEditCallComponent(
|
||||||
|
callComponent,
|
||||||
|
context.args as RenderableEditArgs | undefined,
|
||||||
|
theme,
|
||||||
|
context.cwd,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { spawn } from "child_process";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { type Static, Type } from "typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
||||||
|
import type { Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import { ensureTool } from "../../utils/tools-manager.ts";
|
import { ensureTool } from "../../utils/tools-manager.ts";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||||
import { pathExists, resolveToCwd } from "./path-utils.ts";
|
import { pathExists, resolveToCwd } from "./path-utils.ts";
|
||||||
@@ -55,10 +56,7 @@ export interface FindToolOptions {
|
|||||||
operations?: FindOperations;
|
operations?: FindOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatFindCall(
|
function formatFindCall(args: { pattern: string; path?: string; limit?: number } | undefined, theme: Theme): string {
|
||||||
args: { pattern: string; path?: string; limit?: number } | undefined,
|
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
|
||||||
): string {
|
|
||||||
const pattern = str(args?.pattern);
|
const pattern = str(args?.pattern);
|
||||||
const rawPath = str(args?.path);
|
const rawPath = str(args?.path);
|
||||||
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
||||||
@@ -81,7 +79,7 @@ function formatFindResult(
|
|||||||
details?: FindToolDetails;
|
details?: FindToolDetails;
|
||||||
},
|
},
|
||||||
options: ToolRenderResultOptions,
|
options: ToolRenderResultOptions,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
showImages: boolean,
|
showImages: boolean,
|
||||||
): string {
|
): string {
|
||||||
const output = getTextOutput(result, showImages).trim();
|
const output = getTextOutput(result, showImages).trim();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { spawn } from "child_process";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { type Static, Type } from "typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
||||||
|
import type { Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import { ensureTool } from "../../utils/tools-manager.ts";
|
import { ensureTool } from "../../utils/tools-manager.ts";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||||
import { resolveToCwd } from "./path-utils.ts";
|
import { resolveToCwd } from "./path-utils.ts";
|
||||||
@@ -66,7 +67,7 @@ export interface GrepToolOptions {
|
|||||||
|
|
||||||
function formatGrepCall(
|
function formatGrepCall(
|
||||||
args: { pattern: string; path?: string; glob?: string; limit?: number } | undefined,
|
args: { pattern: string; path?: string; glob?: string; limit?: number } | undefined,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
): string {
|
): string {
|
||||||
const pattern = str(args?.pattern);
|
const pattern = str(args?.pattern);
|
||||||
const rawPath = str(args?.path);
|
const rawPath = str(args?.path);
|
||||||
@@ -90,7 +91,7 @@ function formatGrepResult(
|
|||||||
details?: GrepToolDetails;
|
details?: GrepToolDetails;
|
||||||
},
|
},
|
||||||
options: ToolRenderResultOptions,
|
options: ToolRenderResultOptions,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
showImages: boolean,
|
showImages: boolean,
|
||||||
): string {
|
): string {
|
||||||
const output = getTextOutput(result, showImages).trim();
|
const output = getTextOutput(result, showImages).trim();
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import { Text } from "@earendil-works/pi-tui";
|
|||||||
import nodePath from "path";
|
import nodePath from "path";
|
||||||
import { type Static, Type } from "typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
||||||
|
import type { Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||||
import { pathExists, resolveToCwd } from "./path-utils.ts";
|
import { pathExists, resolveToCwd } from "./path-utils.ts";
|
||||||
import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.ts";
|
import { getTextOutput, renderToolPath, str } from "./render-utils.ts";
|
||||||
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
||||||
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.ts";
|
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.ts";
|
||||||
|
|
||||||
@@ -48,15 +49,10 @@ export interface LsToolOptions {
|
|||||||
operations?: LsOperations;
|
operations?: LsOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatLsCall(
|
function formatLsCall(args: { path?: string; limit?: number } | undefined, theme: Theme, cwd: string): string {
|
||||||
args: { path?: string; limit?: number } | undefined,
|
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
|
||||||
): string {
|
|
||||||
const rawPath = str(args?.path);
|
|
||||||
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
||||||
const limit = args?.limit;
|
const limit = args?.limit;
|
||||||
const invalidArg = invalidArgText(theme);
|
const pathDisplay = renderToolPath(str(args?.path), theme, cwd, { emptyFallback: "." });
|
||||||
let text = `${theme.fg("toolTitle", theme.bold("ls"))} ${path === null ? invalidArg : theme.fg("accent", path)}`;
|
let text = `${theme.fg("toolTitle", theme.bold("ls"))} ${pathDisplay}`;
|
||||||
if (limit !== undefined) {
|
if (limit !== undefined) {
|
||||||
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
||||||
}
|
}
|
||||||
@@ -69,7 +65,7 @@ function formatLsResult(
|
|||||||
details?: LsToolDetails;
|
details?: LsToolDetails;
|
||||||
},
|
},
|
||||||
options: ToolRenderResultOptions,
|
options: ToolRenderResultOptions,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
showImages: boolean,
|
showImages: boolean,
|
||||||
): string {
|
): string {
|
||||||
const output = getTextOutput(result, showImages).trim();
|
const output = getTextOutput(result, showImages).trim();
|
||||||
@@ -213,7 +209,7 @@ export function createLsToolDefinition(
|
|||||||
},
|
},
|
||||||
renderCall(args, theme, context) {
|
renderCall(args, theme, context) {
|
||||||
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
||||||
text.setText(formatLsCall(args, theme));
|
text.setText(formatLsCall(args, theme, context.cwd));
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
renderResult(result, options, theme, context) {
|
renderResult(result, options, theme, context) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime.ts";
|
|||||||
import { formatPathRelativeToCwdOrAbsolute } from "../../utils/paths.ts";
|
import { formatPathRelativeToCwdOrAbsolute } from "../../utils/paths.ts";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||||
import { resolveReadPathAsync, resolveToCwd } from "./path-utils.ts";
|
import { resolveReadPathAsync, resolveToCwd } from "./path-utils.ts";
|
||||||
import { getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from "./render-utils.ts";
|
import { getTextOutput, renderToolPath, replaceTabs, str } from "./render-utils.ts";
|
||||||
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
||||||
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.ts";
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.ts";
|
||||||
|
|
||||||
@@ -71,11 +71,8 @@ function formatReadLineRange(args: ReadRenderArgs | undefined, theme: Theme): st
|
|||||||
return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatReadCall(args: ReadRenderArgs | undefined, theme: Theme): string {
|
function formatReadCall(args: ReadRenderArgs | undefined, theme: Theme, cwd: string): string {
|
||||||
const rawPath = str(args?.file_path ?? args?.path);
|
const pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);
|
||||||
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
|
||||||
const invalidArg = invalidArgText(theme);
|
|
||||||
const pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
|
|
||||||
return `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;
|
return `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +341,9 @@ export function createReadToolDefinition(
|
|||||||
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
||||||
const classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;
|
const classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;
|
||||||
text.setText(
|
text.setText(
|
||||||
classification ? formatCompactReadCall(classification, args, theme) : formatReadCall(args, theme),
|
classification
|
||||||
|
? formatCompactReadCall(classification, args, theme)
|
||||||
|
: formatReadCall(args, theme, context.cwd),
|
||||||
);
|
);
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
|
import { pathToFileURL } from "node:url";
|
||||||
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
|
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
|
||||||
import { getCapabilities, getImageDimensions, imageFallback } from "@earendil-works/pi-tui";
|
import { getCapabilities, getImageDimensions, hyperlink, imageFallback } from "@earendil-works/pi-tui";
|
||||||
|
import type { Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import { stripAnsi } from "../../utils/ansi.ts";
|
import { stripAnsi } from "../../utils/ansi.ts";
|
||||||
|
import { resolvePath } from "../../utils/paths.ts";
|
||||||
import { sanitizeBinaryOutput } from "../../utils/shell.ts";
|
import { sanitizeBinaryOutput } from "../../utils/shell.ts";
|
||||||
|
|
||||||
export function shortenPath(path: unknown): string {
|
export function shortenPath(path: unknown): string {
|
||||||
@@ -13,6 +16,12 @@ export function shortenPath(path: unknown): string {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function linkPath(styledText: string, rawPath: string, cwd: string): string {
|
||||||
|
if (!getCapabilities().hyperlinks) return styledText;
|
||||||
|
const absolutePath = resolvePath(rawPath, cwd);
|
||||||
|
return hyperlink(styledText, pathToFileURL(absolutePath).href);
|
||||||
|
}
|
||||||
|
|
||||||
export function str(value: unknown): string | null {
|
export function str(value: unknown): string | null {
|
||||||
if (typeof value === "string") return value;
|
if (typeof value === "string") return value;
|
||||||
if (value == null) return "";
|
if (value == null) return "";
|
||||||
@@ -59,6 +68,18 @@ export type ToolRenderResultLike<TDetails> = {
|
|||||||
details: TDetails;
|
details: TDetails;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function invalidArgText(theme: { fg: (name: any, text: string) => string }): string {
|
export function invalidArgText(theme: Theme): string {
|
||||||
return theme.fg("error", "[invalid arg]");
|
return theme.fg("error", "[invalid arg]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderToolPath(
|
||||||
|
rawPath: string | null,
|
||||||
|
theme: Theme,
|
||||||
|
cwd: string,
|
||||||
|
options?: { emptyFallback?: string },
|
||||||
|
): string {
|
||||||
|
if (rawPath === null) return invalidArgText(theme);
|
||||||
|
const value = rawPath || options?.emptyFallback;
|
||||||
|
if (!value) return theme.fg("toolOutput", "...");
|
||||||
|
return linkPath(theme.fg("accent", shortenPath(value)), value, cwd);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { mkdir as fsMkdir, writeFile as fsWriteFile } from "fs/promises";
|
|||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { type Static, Type } from "typebox";
|
import { type Static, Type } from "typebox";
|
||||||
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.ts";
|
||||||
import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.ts";
|
import { getLanguageFromPath, highlightCode, type Theme } from "../../modes/interactive/theme/theme.ts";
|
||||||
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.ts";
|
||||||
import { withFileMutationQueue } from "./file-mutation-queue.ts";
|
import { withFileMutationQueue } from "./file-mutation-queue.ts";
|
||||||
import { resolveToCwd } from "./path-utils.ts";
|
import { resolveToCwd } from "./path-utils.ts";
|
||||||
import { invalidArgText, normalizeDisplayText, replaceTabs, shortenPath, str } from "./render-utils.ts";
|
import { normalizeDisplayText, renderToolPath, replaceTabs, str } from "./render-utils.ts";
|
||||||
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
import { wrapToolDefinition } from "./tool-definition-wrapper.ts";
|
||||||
|
|
||||||
const writeSchema = Type.Object({
|
const writeSchema = Type.Object({
|
||||||
@@ -131,14 +131,14 @@ function trimTrailingEmptyLines(lines: string[]): string[] {
|
|||||||
function formatWriteCall(
|
function formatWriteCall(
|
||||||
args: { path?: string; file_path?: string; content?: string } | undefined,
|
args: { path?: string; file_path?: string; content?: string } | undefined,
|
||||||
options: ToolRenderResultOptions,
|
options: ToolRenderResultOptions,
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
cache: WriteHighlightCache | undefined,
|
cache: WriteHighlightCache | undefined,
|
||||||
|
cwd: string,
|
||||||
): string {
|
): string {
|
||||||
const rawPath = str(args?.file_path ?? args?.path);
|
const rawPath = str(args?.file_path ?? args?.path);
|
||||||
const fileContent = str(args?.content);
|
const fileContent = str(args?.content);
|
||||||
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
const pathDisplay = renderToolPath(rawPath, theme, cwd);
|
||||||
const invalidArg = invalidArgText(theme);
|
let text = `${theme.fg("toolTitle", theme.bold("write"))} ${pathDisplay}`;
|
||||||
let text = `${theme.fg("toolTitle", theme.bold("write"))} ${path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...")}`;
|
|
||||||
|
|
||||||
if (fileContent === null) {
|
if (fileContent === null) {
|
||||||
text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
|
text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
|
||||||
@@ -163,7 +163,7 @@ function formatWriteCall(
|
|||||||
|
|
||||||
function formatWriteResult(
|
function formatWriteResult(
|
||||||
result: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },
|
result: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },
|
||||||
theme: typeof import("../../modes/interactive/theme/theme.ts").theme,
|
theme: Theme,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (!result.isError) {
|
if (!result.isError) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -243,6 +243,7 @@ export function createWriteToolDefinition(
|
|||||||
{ expanded: context.expanded, isPartial: context.isPartial },
|
{ expanded: context.expanded, isPartial: context.isPartial },
|
||||||
theme,
|
theme,
|
||||||
component.cache,
|
component.cache,
|
||||||
|
context.cwd,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return component;
|
return component;
|
||||||
|
|||||||
Reference in New Issue
Block a user