@oh-my-pi/pi-coding-agent 8.0.16 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/package.json +14 -11
- package/scripts/generate-wasm-b64.ts +24 -0
- package/src/capability/context-file.ts +1 -1
- package/src/capability/extension-module.ts +1 -1
- package/src/capability/extension.ts +1 -1
- package/src/capability/hook.ts +1 -1
- package/src/capability/instruction.ts +1 -1
- package/src/capability/mcp.ts +1 -1
- package/src/capability/prompt.ts +1 -1
- package/src/capability/rule.ts +1 -1
- package/src/capability/settings.ts +1 -1
- package/src/capability/skill.ts +1 -1
- package/src/capability/slash-command.ts +1 -1
- package/src/capability/ssh.ts +1 -1
- package/src/capability/system-prompt.ts +1 -1
- package/src/capability/tool.ts +1 -1
- package/src/cli/args.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -5
- package/src/commit/agentic/agent.ts +309 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +359 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +26 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +40 -0
- package/src/commit/agentic/state.ts +74 -0
- package/src/commit/agentic/tools/analyze-file.ts +131 -0
- package/src/commit/agentic/tools/git-file-diff.ts +194 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +84 -0
- package/src/commit/agentic/tools/index.ts +56 -0
- package/src/commit/agentic/tools/propose-changelog.ts +128 -0
- package/src/commit/agentic/tools/propose-commit.ts +154 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/split-commit.ts +284 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +200 -0
- package/src/commit/analysis/conventional.ts +169 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +114 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +36 -0
- package/src/commit/changelog/generate.ts +112 -0
- package/src/commit/changelog/index.ts +233 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +93 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/git/errors.ts +11 -0
- package/src/commit/git/index.ts +217 -0
- package/src/commit/git/operations.ts +53 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
- package/src/commit/map-reduce/index.ts +63 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +147 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +84 -0
- package/src/commit/pipeline.ts +242 -0
- package/src/commit/prompts/analysis-system.md +155 -0
- package/src/commit/prompts/analysis-user.md +41 -0
- package/src/commit/prompts/changelog-system.md +56 -0
- package/src/commit/prompts/changelog-user.md +19 -0
- package/src/commit/prompts/file-observer-system.md +26 -0
- package/src/commit/prompts/file-observer-user.md +9 -0
- package/src/commit/prompts/reduce-system.md +60 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +4 -0
- package/src/commit/prompts/summary-system.md +52 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +109 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/config/file-lock.ts +111 -0
- package/src/config/model-registry.ts +16 -7
- package/src/config/settings-manager.ts +115 -40
- package/src/config.ts +5 -5
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +1 -1
- package/src/discovery/claude.ts +1 -1
- package/src/discovery/cline.ts +1 -1
- package/src/discovery/codex.ts +1 -1
- package/src/discovery/cursor.ts +1 -1
- package/src/discovery/gemini.ts +1 -1
- package/src/discovery/github.ts +1 -1
- package/src/discovery/index.ts +11 -11
- package/src/discovery/mcp-json.ts +1 -1
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/vscode.ts +1 -1
- package/src/discovery/windsurf.ts +1 -1
- package/src/extensibility/custom-commands/loader.ts +1 -1
- package/src/extensibility/custom-commands/types.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/hooks/types.ts +3 -3
- package/src/index.ts +10 -10
- package/src/ipy/executor.ts +97 -1
- package/src/lsp/index.ts +1 -1
- package/src/lsp/render.ts +90 -46
- package/src/main.ts +16 -3
- package/src/mcp/loader.ts +3 -3
- package/src/migrations.ts +3 -3
- package/src/modes/components/assistant-message.ts +29 -1
- package/src/modes/components/tool-execution.ts +5 -3
- package/src/modes/components/tree-selector.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -3
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-mode.ts +1 -4
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/theme/mermaid-cache.ts +89 -0
- package/src/modes/theme/theme.ts +2 -0
- package/src/modes/types.ts +2 -2
- package/src/patch/index.ts +3 -9
- package/src/patch/shared.ts +33 -5
- package/src/prompts/tools/task.md +2 -0
- package/src/sdk.ts +60 -22
- package/src/session/agent-session.ts +3 -3
- package/src/session/agent-storage.ts +32 -28
- package/src/session/artifacts.ts +24 -1
- package/src/session/auth-storage.ts +25 -10
- package/src/session/storage-migration.ts +12 -53
- package/src/system-prompt.ts +2 -2
- package/src/task/.executor.ts.kate-swp +0 -0
- package/src/task/executor.ts +1 -1
- package/src/task/index.ts +10 -1
- package/src/task/output-manager.ts +94 -0
- package/src/task/render.ts +7 -12
- package/src/task/worker.ts +1 -1
- package/src/tools/ask.ts +35 -13
- package/src/tools/bash.ts +80 -87
- package/src/tools/calculator.ts +42 -40
- package/src/tools/complete.ts +1 -1
- package/src/tools/fetch.ts +67 -104
- package/src/tools/find.ts +83 -86
- package/src/tools/grep.ts +80 -96
- package/src/tools/index.ts +10 -7
- package/src/tools/ls.ts +39 -65
- package/src/tools/notebook.ts +48 -64
- package/src/tools/output-utils.ts +1 -1
- package/src/tools/python.ts +71 -183
- package/src/tools/read.ts +74 -15
- package/src/tools/render-utils.ts +1 -15
- package/src/tools/ssh.ts +43 -24
- package/src/tools/todo-write.ts +27 -15
- package/src/tools/write.ts +93 -64
- package/src/tui/code-cell.ts +115 -0
- package/src/tui/file-list.ts +48 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +73 -0
- package/src/tui/status-line.ts +40 -0
- package/src/tui/tree-list.ts +56 -0
- package/src/tui/types.ts +17 -0
- package/src/tui/utils.ts +49 -0
- package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
- package/src/web/search/auth.ts +1 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/render.ts +119 -163
- package/tsconfig.json +0 -42
package/src/tools/read.ts
CHANGED
|
@@ -5,11 +5,12 @@ import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
|
5
5
|
import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-coding-agent/config";
|
|
6
6
|
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
7
7
|
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
8
|
-
import type
|
|
8
|
+
import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
9
9
|
import readDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/read.md" with { type: "text" };
|
|
10
10
|
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/sdk";
|
|
11
11
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
12
12
|
import { ToolAbortError, ToolError, throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
13
|
+
import { renderCodeCell, renderOutputBlock, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
|
|
13
14
|
import { formatDimensionNote, resizeImage } from "@oh-my-pi/pi-coding-agent/utils/image-resize";
|
|
14
15
|
import { detectSupportedImageMimeTypeFromFile } from "@oh-my-pi/pi-coding-agent/utils/mime";
|
|
15
16
|
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
@@ -378,6 +379,7 @@ const readSchema = Type.Object({
|
|
|
378
379
|
export interface ReadToolDetails {
|
|
379
380
|
truncation?: TruncationResult;
|
|
380
381
|
redirectedTo?: "ls";
|
|
382
|
+
resolvedPath?: string;
|
|
381
383
|
meta?: OutputMeta;
|
|
382
384
|
}
|
|
383
385
|
|
|
@@ -708,11 +710,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
708
710
|
|
|
709
711
|
// If extraction was used, return directly (no pagination)
|
|
710
712
|
if (hasExtraction) {
|
|
711
|
-
|
|
713
|
+
const details: ReadToolDetails = {};
|
|
712
714
|
if (resource.sourcePath) {
|
|
713
|
-
|
|
715
|
+
details.resolvedPath = resource.sourcePath;
|
|
714
716
|
}
|
|
715
|
-
return toolResult
|
|
717
|
+
return toolResult(details).text(resource.content).sourceInternal(url).done();
|
|
716
718
|
}
|
|
717
719
|
|
|
718
720
|
// Apply pagination similar to file reading
|
|
@@ -806,9 +808,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
806
808
|
details = {};
|
|
807
809
|
}
|
|
808
810
|
|
|
809
|
-
// Append resolved path notice
|
|
810
811
|
if (resource.sourcePath) {
|
|
811
|
-
|
|
812
|
+
details.resolvedPath = resource.sourcePath;
|
|
812
813
|
}
|
|
813
814
|
|
|
814
815
|
const resultBuilder = toolResult(details).text(outputText).sourceInternal(url);
|
|
@@ -837,14 +838,14 @@ export const readToolRenderer = {
|
|
|
837
838
|
const offset = args.offset;
|
|
838
839
|
const limit = args.limit;
|
|
839
840
|
|
|
840
|
-
let pathDisplay = filePath
|
|
841
|
+
let pathDisplay = filePath || uiTheme.format.ellipsis;
|
|
841
842
|
if (offset !== undefined || limit !== undefined) {
|
|
842
843
|
const startLine = offset ?? 1;
|
|
843
844
|
const endLine = limit !== undefined ? startLine + limit - 1 : "";
|
|
844
|
-
pathDisplay +=
|
|
845
|
+
pathDisplay += `:${startLine}${endLine ? `-${endLine}` : ""}`;
|
|
845
846
|
}
|
|
846
847
|
|
|
847
|
-
const text =
|
|
848
|
+
const text = renderStatusLine({ icon: "pending", title: "Read", description: pathDisplay }, uiTheme);
|
|
848
849
|
return new Text(text, 0, 0);
|
|
849
850
|
},
|
|
850
851
|
|
|
@@ -852,15 +853,24 @@ export const readToolRenderer = {
|
|
|
852
853
|
result: { content: Array<{ type: string; text?: string }>; details?: ReadToolDetails },
|
|
853
854
|
_options: RenderResultOptions,
|
|
854
855
|
uiTheme: Theme,
|
|
855
|
-
|
|
856
|
+
args?: ReadRenderArgs,
|
|
856
857
|
): Component {
|
|
857
858
|
const details = result.details;
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
859
|
+
const contentText = result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
860
|
+
const imageContent = result.content?.find((c) => c.type === "image");
|
|
861
|
+
const rawPath = args?.file_path || args?.path || "";
|
|
862
|
+
const filePath = shortenPath(rawPath);
|
|
863
|
+
const lang = getLanguageFromPath(rawPath);
|
|
861
864
|
|
|
865
|
+
const warningLines: string[] = [];
|
|
862
866
|
const truncation = details?.meta?.truncation;
|
|
863
867
|
const fallback = details?.truncation;
|
|
868
|
+
if (details?.redirectedTo) {
|
|
869
|
+
warningLines.push(uiTheme.fg("warning", wrapBrackets(`Redirected to ${details.redirectedTo}`, uiTheme)));
|
|
870
|
+
}
|
|
871
|
+
if (details?.resolvedPath) {
|
|
872
|
+
warningLines.push(uiTheme.fg("dim", wrapBrackets(`Resolved path: ${details.resolvedPath}`, uiTheme)));
|
|
873
|
+
}
|
|
864
874
|
if (truncation) {
|
|
865
875
|
let warning: string;
|
|
866
876
|
if (fallback?.firstLineExceedsLimit) {
|
|
@@ -874,9 +884,58 @@ export const readToolRenderer = {
|
|
|
874
884
|
if (truncation.artifactId) {
|
|
875
885
|
warning += `. Full output: artifact://${truncation.artifactId}`;
|
|
876
886
|
}
|
|
877
|
-
|
|
887
|
+
warningLines.push(uiTheme.fg("warning", wrapBrackets(warning, uiTheme)));
|
|
878
888
|
}
|
|
879
889
|
|
|
880
|
-
|
|
890
|
+
if (imageContent) {
|
|
891
|
+
const header = renderStatusLine(
|
|
892
|
+
{ icon: "success", title: "Read", description: filePath || rawPath || "image" },
|
|
893
|
+
uiTheme,
|
|
894
|
+
);
|
|
895
|
+
const detailLines = contentText ? contentText.split("\n").map((line) => uiTheme.fg("toolOutput", line)) : [];
|
|
896
|
+
const lines = [...detailLines, ...warningLines];
|
|
897
|
+
return {
|
|
898
|
+
render: (width: number) =>
|
|
899
|
+
renderOutputBlock(
|
|
900
|
+
{
|
|
901
|
+
header,
|
|
902
|
+
state: "success",
|
|
903
|
+
sections: [
|
|
904
|
+
{
|
|
905
|
+
label: uiTheme.fg("toolTitle", "Details"),
|
|
906
|
+
lines: lines.length > 0 ? lines : [uiTheme.fg("dim", "(image)")],
|
|
907
|
+
},
|
|
908
|
+
],
|
|
909
|
+
width,
|
|
910
|
+
},
|
|
911
|
+
uiTheme,
|
|
912
|
+
),
|
|
913
|
+
invalidate: () => {},
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
let title = filePath ? `Read ${filePath}` : "Read";
|
|
918
|
+
if (args?.offset !== undefined || args?.limit !== undefined) {
|
|
919
|
+
const startLine = args.offset ?? 1;
|
|
920
|
+
const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
|
|
921
|
+
title += `:${startLine}${endLine ? `-${endLine}` : ""}`;
|
|
922
|
+
}
|
|
923
|
+
return {
|
|
924
|
+
render: (width: number) =>
|
|
925
|
+
renderCodeCell(
|
|
926
|
+
{
|
|
927
|
+
code: contentText,
|
|
928
|
+
language: lang,
|
|
929
|
+
title,
|
|
930
|
+
status: "complete",
|
|
931
|
+
output: warningLines.length > 0 ? warningLines.join("\n") : undefined,
|
|
932
|
+
expanded: true,
|
|
933
|
+
width,
|
|
934
|
+
},
|
|
935
|
+
uiTheme,
|
|
936
|
+
),
|
|
937
|
+
invalidate: () => {},
|
|
938
|
+
};
|
|
881
939
|
},
|
|
940
|
+
mergeCallAndResult: true,
|
|
882
941
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
10
|
+
import { getTreeBranch } from "@oh-my-pi/pi-coding-agent/tui/utils";
|
|
10
11
|
|
|
11
12
|
// =============================================================================
|
|
12
13
|
// Standardized Display Constants
|
|
@@ -667,21 +668,6 @@ function pluralize(label: string, count: number): string {
|
|
|
667
668
|
// =============================================================================
|
|
668
669
|
// Tree Rendering Utilities
|
|
669
670
|
// =============================================================================
|
|
670
|
-
|
|
671
|
-
/**
|
|
672
|
-
* Get the branch character for a tree item.
|
|
673
|
-
*/
|
|
674
|
-
export function getTreeBranch(isLast: boolean, theme: Theme): string {
|
|
675
|
-
return isLast ? theme.tree.last : theme.tree.branch;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* Get the continuation prefix for nested content under a tree item.
|
|
680
|
-
*/
|
|
681
|
-
export function getTreeContinuePrefix(isLast: boolean, theme: Theme): string {
|
|
682
|
-
return isLast ? " " : `${theme.tree.vertical} `;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
671
|
/**
|
|
686
672
|
* Render a list of items with tree branches, handling truncation.
|
|
687
673
|
*
|
package/src/tools/ssh.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { SSHHost } from "@oh-my-pi/pi-coding-agent/capability/ssh";
|
|
3
3
|
import { sshCapability } from "@oh-my-pi/pi-coding-agent/capability/ssh";
|
|
4
4
|
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
5
|
-
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery
|
|
5
|
+
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
|
6
6
|
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
8
8
|
import sshDescriptionBase from "@oh-my-pi/pi-coding-agent/prompts/tools/ssh.md" with { type: "text" };
|
|
@@ -11,12 +11,13 @@ import { ensureHostInfo, getHostInfoForHost } from "@oh-my-pi/pi-coding-agent/ss
|
|
|
11
11
|
import { executeSSH } from "@oh-my-pi/pi-coding-agent/ssh/ssh-executor";
|
|
12
12
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
13
13
|
import { ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
14
|
+
import { renderOutputBlock, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
|
|
14
15
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
15
16
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
16
17
|
import { Type } from "@sinclair/typebox";
|
|
17
|
-
import type { ToolSession } from "
|
|
18
|
+
import type { ToolSession } from ".";
|
|
18
19
|
import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
|
|
19
|
-
import {
|
|
20
|
+
import { formatBytes, wrapBrackets } from "./render-utils";
|
|
20
21
|
import { toolResult } from "./tool-result";
|
|
21
22
|
import { DEFAULT_MAX_BYTES } from "./truncate";
|
|
22
23
|
|
|
@@ -237,10 +238,9 @@ interface SshRenderContext {
|
|
|
237
238
|
|
|
238
239
|
export const sshToolRenderer = {
|
|
239
240
|
renderCall(args: SshRenderArgs, uiTheme: Theme): Component {
|
|
240
|
-
const ui = new ToolUIKit(uiTheme);
|
|
241
241
|
const host = args.host || uiTheme.format.ellipsis;
|
|
242
242
|
const command = args.command || uiTheme.format.ellipsis;
|
|
243
|
-
const text =
|
|
243
|
+
const text = renderStatusLine({ icon: "pending", title: "SSH", description: `[${host}] $ ${command}` }, uiTheme);
|
|
244
244
|
return new Text(text, 0, 0);
|
|
245
245
|
},
|
|
246
246
|
|
|
@@ -251,42 +251,48 @@ export const sshToolRenderer = {
|
|
|
251
251
|
},
|
|
252
252
|
options: RenderResultOptions & { renderContext?: SshRenderContext },
|
|
253
253
|
uiTheme: Theme,
|
|
254
|
+
args?: SshRenderArgs,
|
|
254
255
|
): Component {
|
|
255
|
-
const ui = new ToolUIKit(uiTheme);
|
|
256
256
|
const { expanded, renderContext } = options;
|
|
257
257
|
const details = result.details;
|
|
258
|
-
const
|
|
258
|
+
const host = args?.host || uiTheme.format.ellipsis;
|
|
259
|
+
const command = args?.command || uiTheme.format.ellipsis;
|
|
260
|
+
const header = renderStatusLine(
|
|
261
|
+
{ icon: "success", title: "SSH", description: `[${host}] $ ${command}` },
|
|
262
|
+
uiTheme,
|
|
263
|
+
);
|
|
264
|
+
const outputLines: string[] = [];
|
|
259
265
|
|
|
260
266
|
const textContent = result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
261
|
-
const output = textContent.
|
|
267
|
+
const output = textContent.trimEnd();
|
|
262
268
|
|
|
263
269
|
if (output) {
|
|
264
270
|
if (expanded) {
|
|
265
|
-
|
|
266
|
-
.split("\n")
|
|
267
|
-
.map((line) => uiTheme.fg("toolOutput", line))
|
|
268
|
-
.join("\n");
|
|
269
|
-
lines.push(styledOutput);
|
|
271
|
+
outputLines.push(...output.split("\n").map((line) => uiTheme.fg("toolOutput", line)));
|
|
270
272
|
} else if (renderContext?.visualLines) {
|
|
271
273
|
const { visualLines, skippedCount = 0, totalVisualLines = visualLines.length } = renderContext;
|
|
272
274
|
if (skippedCount > 0) {
|
|
273
|
-
|
|
275
|
+
outputLines.push(
|
|
274
276
|
uiTheme.fg(
|
|
275
277
|
"dim",
|
|
276
278
|
`${uiTheme.format.ellipsis} (${skippedCount} earlier lines, showing ${visualLines.length} of ${totalVisualLines}) (ctrl+o to expand)`,
|
|
277
279
|
),
|
|
278
280
|
);
|
|
279
281
|
}
|
|
280
|
-
|
|
282
|
+
const styledVisual = visualLines.map((line) =>
|
|
283
|
+
line.includes("\x1b[") ? line : uiTheme.fg("toolOutput", line),
|
|
284
|
+
);
|
|
285
|
+
outputLines.push(...styledVisual);
|
|
281
286
|
} else {
|
|
282
|
-
const
|
|
287
|
+
const outputLinesRaw = output.split("\n");
|
|
283
288
|
const maxLines = 5;
|
|
284
|
-
const displayLines =
|
|
285
|
-
const remaining =
|
|
286
|
-
|
|
287
|
-
lines.push(...displayLines.map((line) => uiTheme.fg("toolOutput", line)));
|
|
289
|
+
const displayLines = outputLinesRaw.slice(0, maxLines);
|
|
290
|
+
const remaining = outputLinesRaw.length - maxLines;
|
|
291
|
+
outputLines.push(...displayLines.map((line) => uiTheme.fg("toolOutput", line)));
|
|
288
292
|
if (remaining > 0) {
|
|
289
|
-
|
|
293
|
+
outputLines.push(
|
|
294
|
+
uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${remaining} more lines) (ctrl+o to expand)`),
|
|
295
|
+
);
|
|
290
296
|
}
|
|
291
297
|
}
|
|
292
298
|
}
|
|
@@ -301,12 +307,25 @@ export const sshToolRenderer = {
|
|
|
301
307
|
warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
|
|
302
308
|
} else {
|
|
303
309
|
warnings.push(
|
|
304
|
-
`Truncated: ${truncation.outputLines} lines shown (${
|
|
310
|
+
`Truncated: ${truncation.outputLines} lines shown (${formatBytes(truncation.outputBytes)} limit)`,
|
|
305
311
|
);
|
|
306
312
|
}
|
|
307
|
-
|
|
313
|
+
outputLines.push(uiTheme.fg("warning", wrapBrackets(warnings.join(". "), uiTheme)));
|
|
308
314
|
}
|
|
309
315
|
|
|
310
|
-
return
|
|
316
|
+
return {
|
|
317
|
+
render: (width: number) =>
|
|
318
|
+
renderOutputBlock(
|
|
319
|
+
{
|
|
320
|
+
header,
|
|
321
|
+
state: "success",
|
|
322
|
+
sections: [{ label: uiTheme.fg("toolTitle", "Output"), lines: outputLines }],
|
|
323
|
+
width,
|
|
324
|
+
},
|
|
325
|
+
uiTheme,
|
|
326
|
+
),
|
|
327
|
+
invalidate: () => {},
|
|
328
|
+
};
|
|
311
329
|
},
|
|
330
|
+
mergeCallAndResult: true,
|
|
312
331
|
};
|
package/src/tools/todo-write.ts
CHANGED
|
@@ -7,11 +7,13 @@ import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibilit
|
|
|
7
7
|
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
8
8
|
import todoWriteDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/todo-write.md" with { type: "text" };
|
|
9
9
|
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/sdk";
|
|
10
|
+
import { renderStatusLine, renderTreeList } from "@oh-my-pi/pi-coding-agent/tui";
|
|
10
11
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
11
12
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
12
13
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
13
14
|
import { Type } from "@sinclair/typebox";
|
|
14
15
|
import chalk from "chalk";
|
|
16
|
+
import { PREVIEW_LIMITS } from "./render-utils";
|
|
15
17
|
|
|
16
18
|
const todoWriteSchema = Type.Object({
|
|
17
19
|
todos: Type.Array(
|
|
@@ -217,29 +219,39 @@ interface TodoWriteRenderArgs {
|
|
|
217
219
|
export const todoWriteToolRenderer = {
|
|
218
220
|
renderCall(args: TodoWriteRenderArgs, uiTheme: Theme): Component {
|
|
219
221
|
const count = args.todos?.length ?? 0;
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
+
const meta = count > 0 ? [`${count} items`] : ["empty"];
|
|
223
|
+
const text = renderStatusLine({ icon: "pending", title: "Todo Write", meta }, uiTheme);
|
|
224
|
+
return new Text(text, 0, 0);
|
|
222
225
|
},
|
|
223
226
|
|
|
224
227
|
renderResult(
|
|
225
228
|
result: { content: Array<{ type: string; text?: string }>; details?: TodoWriteToolDetails },
|
|
226
|
-
|
|
229
|
+
options: RenderResultOptions,
|
|
227
230
|
uiTheme: Theme,
|
|
228
231
|
_args?: TodoWriteRenderArgs,
|
|
229
232
|
): Component {
|
|
233
|
+
const { expanded } = options;
|
|
230
234
|
const todos = result.details?.todos ?? [];
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (todos.length
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
const prefix = `${indent}${index === 0 ? hook : " "} `;
|
|
239
|
-
lines.push(formatTodoLine(todo, uiTheme, prefix));
|
|
240
|
-
});
|
|
235
|
+
const header = renderStatusLine(
|
|
236
|
+
{ icon: "success", title: "Todo Write", meta: [`${todos.length} items`] },
|
|
237
|
+
uiTheme,
|
|
238
|
+
);
|
|
239
|
+
if (todos.length === 0) {
|
|
240
|
+
const fallback = result.content?.find((c) => c.type === "text")?.text ?? "No todos";
|
|
241
|
+
return new Text([header, uiTheme.fg("dim", fallback)].join("\n"), 0, 0);
|
|
241
242
|
}
|
|
242
|
-
|
|
243
|
-
|
|
243
|
+
const lines = renderTreeList(
|
|
244
|
+
{
|
|
245
|
+
items: todos,
|
|
246
|
+
expanded,
|
|
247
|
+
maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
|
|
248
|
+
itemType: "todo",
|
|
249
|
+
renderItem: (todo) => formatTodoLine(todo, uiTheme, ""),
|
|
250
|
+
},
|
|
251
|
+
uiTheme,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return new Text([header, ...lines].join("\n"), 0, 0);
|
|
244
255
|
},
|
|
256
|
+
mergeCallAndResult: true,
|
|
245
257
|
};
|
package/src/tools/write.ts
CHANGED
|
@@ -12,17 +12,18 @@ import {
|
|
|
12
12
|
type FileDiagnosticsResult,
|
|
13
13
|
type WritethroughCallback,
|
|
14
14
|
writethroughNoop,
|
|
15
|
-
} from "@oh-my-pi/pi-coding-agent/lsp
|
|
16
|
-
import { getLanguageFromPath,
|
|
15
|
+
} from "@oh-my-pi/pi-coding-agent/lsp";
|
|
16
|
+
import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
17
17
|
import writeDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/write.md" with { type: "text" };
|
|
18
18
|
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/sdk";
|
|
19
19
|
import { type OutputMeta, outputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
20
|
+
import { renderCodeCell, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
|
|
20
21
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
21
22
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
22
23
|
import { untilAborted } from "@oh-my-pi/pi-utils";
|
|
23
24
|
import { Type } from "@sinclair/typebox";
|
|
24
25
|
import { resolveToCwd } from "./path-utils";
|
|
25
|
-
import { formatDiagnostics,
|
|
26
|
+
import { formatDiagnostics, shortenPath } from "./render-utils";
|
|
26
27
|
import type { RenderCallOptions } from "./renderers";
|
|
27
28
|
|
|
28
29
|
const writeSchema = Type.Object({
|
|
@@ -134,27 +135,6 @@ function countLines(text: string): number {
|
|
|
134
135
|
return text.split("\n").length;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
function formatStreamingContent(content: string, rawPath: string, uiTheme: Theme): string {
|
|
138
|
-
if (!content) return "";
|
|
139
|
-
const lang = getLanguageFromPath(rawPath);
|
|
140
|
-
const lines = content.split("\n");
|
|
141
|
-
const total = lines.length;
|
|
142
|
-
const displayLines = lines.slice(-WRITE_STREAMING_PREVIEW_LINES);
|
|
143
|
-
const hidden = total - displayLines.length;
|
|
144
|
-
|
|
145
|
-
const formattedLines = lang
|
|
146
|
-
? highlightCode(replaceTabs(displayLines.join("\n")), lang)
|
|
147
|
-
: displayLines.map((line: string) => uiTheme.fg("toolOutput", replaceTabs(line)));
|
|
148
|
-
|
|
149
|
-
let text = "\n\n";
|
|
150
|
-
if (hidden > 0) {
|
|
151
|
-
text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${hidden} earlier lines)\n`);
|
|
152
|
-
}
|
|
153
|
-
text += formattedLines.join("\n");
|
|
154
|
-
text += uiTheme.fg("dim", `\n${uiTheme.format.ellipsis} (streaming)`);
|
|
155
|
-
return text;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
138
|
function formatMetadataLine(lineCount: number | null, language: string | undefined, uiTheme: Theme): string {
|
|
159
139
|
const icon = uiTheme.getLangIcon(language);
|
|
160
140
|
if (lineCount !== null) {
|
|
@@ -167,68 +147,117 @@ export const writeToolRenderer = {
|
|
|
167
147
|
renderCall(args: WriteRenderArgs, uiTheme: Theme, options?: RenderCallOptions): Component {
|
|
168
148
|
const rawPath = args.file_path || args.path || "";
|
|
169
149
|
const filePath = shortenPath(rawPath);
|
|
170
|
-
const pathDisplay = filePath
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (args.content) {
|
|
177
|
-
|
|
150
|
+
const pathDisplay = filePath || uiTheme.format.ellipsis;
|
|
151
|
+
const status = options?.spinnerFrame !== undefined ? "running" : "pending";
|
|
152
|
+
const text = renderStatusLine(
|
|
153
|
+
{ icon: status, title: "Write", description: pathDisplay, spinnerFrame: options?.spinnerFrame },
|
|
154
|
+
uiTheme,
|
|
155
|
+
);
|
|
156
|
+
if (!args.content) {
|
|
157
|
+
return new Text(text, 0, 0);
|
|
178
158
|
}
|
|
179
159
|
|
|
180
|
-
|
|
160
|
+
const contentLines = args.content.split("\n");
|
|
161
|
+
const displayLines = contentLines.slice(-WRITE_STREAMING_PREVIEW_LINES);
|
|
162
|
+
const hidden = contentLines.length - displayLines.length;
|
|
163
|
+
const outputLines: string[] = [];
|
|
164
|
+
if (hidden > 0) {
|
|
165
|
+
outputLines.push(uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${hidden} earlier lines)`));
|
|
166
|
+
}
|
|
167
|
+
outputLines.push(uiTheme.fg("dim", `${uiTheme.format.ellipsis} (streaming)`));
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
render: (width: number) =>
|
|
171
|
+
renderCodeCell(
|
|
172
|
+
{
|
|
173
|
+
code: displayLines.join("\n"),
|
|
174
|
+
language: getLanguageFromPath(rawPath),
|
|
175
|
+
title: filePath ? `Write ${filePath}` : "Write",
|
|
176
|
+
status,
|
|
177
|
+
spinnerFrame: options?.spinnerFrame,
|
|
178
|
+
output: outputLines.join("\n"),
|
|
179
|
+
codeMaxLines: WRITE_STREAMING_PREVIEW_LINES,
|
|
180
|
+
expanded: true,
|
|
181
|
+
width,
|
|
182
|
+
},
|
|
183
|
+
uiTheme,
|
|
184
|
+
),
|
|
185
|
+
invalidate: () => {},
|
|
186
|
+
};
|
|
181
187
|
},
|
|
182
188
|
|
|
183
189
|
renderResult(
|
|
184
190
|
result: { content: Array<{ type: string; text?: string }>; details?: WriteToolDetails },
|
|
185
|
-
{ expanded }: RenderResultOptions,
|
|
191
|
+
{ expanded, isPartial, spinnerFrame }: RenderResultOptions,
|
|
186
192
|
uiTheme: Theme,
|
|
187
193
|
args?: WriteRenderArgs,
|
|
188
194
|
): Component {
|
|
189
195
|
const rawPath = args?.file_path || args?.path || "";
|
|
196
|
+
const filePath = shortenPath(rawPath);
|
|
190
197
|
const fileContent = args?.content || "";
|
|
191
198
|
const lang = getLanguageFromPath(rawPath);
|
|
192
|
-
const contentLines = fileContent
|
|
193
|
-
? lang
|
|
194
|
-
? highlightCode(replaceTabs(fileContent), lang)
|
|
195
|
-
: fileContent.split("\n")
|
|
196
|
-
: [];
|
|
197
|
-
const totalLines = contentLines.length;
|
|
198
199
|
const outputLines: string[] = [];
|
|
200
|
+
const lineCount = countLines(fileContent);
|
|
199
201
|
|
|
200
|
-
outputLines.push(formatMetadataLine(
|
|
202
|
+
outputLines.push(formatMetadataLine(lineCount, lang ?? "text", uiTheme));
|
|
201
203
|
|
|
202
|
-
if (fileContent) {
|
|
203
|
-
const
|
|
204
|
-
const displayLines = contentLines.slice(
|
|
205
|
-
const
|
|
204
|
+
if (isPartial && fileContent) {
|
|
205
|
+
const contentLines = fileContent.split("\n");
|
|
206
|
+
const displayLines = contentLines.slice(-WRITE_STREAMING_PREVIEW_LINES);
|
|
207
|
+
const hidden = contentLines.length - displayLines.length;
|
|
208
|
+
if (hidden > 0) {
|
|
209
|
+
outputLines.push(uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${hidden} earlier lines)`));
|
|
210
|
+
}
|
|
211
|
+
outputLines.push(uiTheme.fg("dim", `${uiTheme.format.ellipsis} (streaming)`));
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
return {
|
|
214
|
+
render: (width: number) =>
|
|
215
|
+
renderCodeCell(
|
|
216
|
+
{
|
|
217
|
+
code: displayLines.join("\n"),
|
|
218
|
+
language: lang,
|
|
219
|
+
title: filePath ? `Write ${filePath}` : "Write",
|
|
220
|
+
status: spinnerFrame !== undefined ? "running" : "pending",
|
|
221
|
+
spinnerFrame,
|
|
222
|
+
output: outputLines.join("\n"),
|
|
223
|
+
codeMaxLines: WRITE_STREAMING_PREVIEW_LINES,
|
|
224
|
+
expanded: true,
|
|
225
|
+
width,
|
|
226
|
+
},
|
|
227
|
+
uiTheme,
|
|
218
228
|
),
|
|
219
|
-
)
|
|
220
|
-
}
|
|
229
|
+
invalidate: () => {},
|
|
230
|
+
};
|
|
221
231
|
}
|
|
222
232
|
|
|
223
|
-
// Show LSP diagnostics if available
|
|
224
233
|
if (result.details?.diagnostics) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
228
|
-
),
|
|
234
|
+
const diagText = formatDiagnostics(result.details.diagnostics, expanded, uiTheme, (fp) =>
|
|
235
|
+
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
229
236
|
);
|
|
237
|
+
if (diagText.trim()) {
|
|
238
|
+
const diagLines = diagText.split("\n");
|
|
239
|
+
const firstNonEmpty = diagLines.findIndex((line) => line.trim());
|
|
240
|
+
outputLines.push(...(firstNonEmpty >= 0 ? diagLines.slice(firstNonEmpty) : []));
|
|
241
|
+
}
|
|
230
242
|
}
|
|
231
243
|
|
|
232
|
-
return
|
|
244
|
+
return {
|
|
245
|
+
render: (width: number) =>
|
|
246
|
+
renderCodeCell(
|
|
247
|
+
{
|
|
248
|
+
code: fileContent,
|
|
249
|
+
language: lang,
|
|
250
|
+
title: filePath ? `Write ${filePath}` : "Write",
|
|
251
|
+
status: "complete",
|
|
252
|
+
output: outputLines.join("\n"),
|
|
253
|
+
codeMaxLines: expanded ? Number.POSITIVE_INFINITY : 10,
|
|
254
|
+
expanded,
|
|
255
|
+
width,
|
|
256
|
+
},
|
|
257
|
+
uiTheme,
|
|
258
|
+
),
|
|
259
|
+
invalidate: () => {},
|
|
260
|
+
};
|
|
233
261
|
},
|
|
262
|
+
mergeCallAndResult: true,
|
|
234
263
|
};
|