@oh-my-pi/pi-coding-agent 8.0.20 → 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/bash.ts
CHANGED
|
@@ -8,15 +8,15 @@ import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
|
8
8
|
import bashDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/bash.md" with { type: "text" };
|
|
9
9
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
10
10
|
import { ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
11
|
+
import { renderOutputBlock, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
|
|
11
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
12
|
-
import { Text
|
|
13
|
+
import { Text } from "@oh-my-pi/pi-tui";
|
|
13
14
|
import { Type } from "@sinclair/typebox";
|
|
14
|
-
|
|
15
|
+
import type { ToolSession } from ".";
|
|
15
16
|
import { checkBashInterception, checkSimpleLsInterception } from "./bash-interceptor";
|
|
16
|
-
import type { ToolSession } from "./index";
|
|
17
17
|
import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
|
|
18
18
|
import { resolveToCwd } from "./path-utils";
|
|
19
|
-
import {
|
|
19
|
+
import { formatBytes, wrapBrackets } from "./render-utils";
|
|
20
20
|
import { toolResult } from "./tool-result";
|
|
21
21
|
import { DEFAULT_MAX_BYTES } from "./truncate";
|
|
22
22
|
|
|
@@ -158,35 +158,36 @@ interface BashRenderContext {
|
|
|
158
158
|
timeout?: number;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
function formatBashCommand(args: BashRenderArgs, uiTheme: Theme): string {
|
|
162
|
+
const command = args.command || uiTheme.format.ellipsis;
|
|
163
|
+
const prompt = "$";
|
|
164
|
+
const cwd = process.cwd();
|
|
165
|
+
let displayWorkdir = args.cwd;
|
|
166
|
+
|
|
167
|
+
if (displayWorkdir) {
|
|
168
|
+
const resolvedCwd = resolve(cwd);
|
|
169
|
+
const resolvedWorkdir = resolve(displayWorkdir);
|
|
170
|
+
if (resolvedWorkdir === resolvedCwd) {
|
|
171
|
+
displayWorkdir = undefined;
|
|
172
|
+
} else {
|
|
173
|
+
const relativePath = relative(resolvedCwd, resolvedWorkdir);
|
|
174
|
+
const isWithinCwd = relativePath && !relativePath.startsWith("..") && !relativePath.startsWith(`..${sep}`);
|
|
175
|
+
if (isWithinCwd) {
|
|
176
|
+
displayWorkdir = relativePath;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return displayWorkdir ? `${prompt} cd ${displayWorkdir} && ${command}` : `${prompt} ${command}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
161
184
|
// Preview line limit when not expanded (matches tool-execution behavior)
|
|
162
185
|
export const BASH_PREVIEW_LINES = 10;
|
|
163
186
|
|
|
164
187
|
export const bashToolRenderer = {
|
|
165
188
|
renderCall(args: BashRenderArgs, uiTheme: Theme): Component {
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const prompt = uiTheme.fg("accent", "$");
|
|
169
|
-
const cwd = process.cwd();
|
|
170
|
-
let displayWorkdir = args.cwd;
|
|
171
|
-
|
|
172
|
-
if (displayWorkdir) {
|
|
173
|
-
const resolvedCwd = resolve(cwd);
|
|
174
|
-
const resolvedWorkdir = resolve(displayWorkdir);
|
|
175
|
-
if (resolvedWorkdir === resolvedCwd) {
|
|
176
|
-
displayWorkdir = undefined;
|
|
177
|
-
} else {
|
|
178
|
-
const relativePath = relative(resolvedCwd, resolvedWorkdir);
|
|
179
|
-
const isWithinCwd = relativePath && !relativePath.startsWith("..") && !relativePath.startsWith(`..${sep}`);
|
|
180
|
-
if (isWithinCwd) {
|
|
181
|
-
displayWorkdir = relativePath;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const cmdText = displayWorkdir
|
|
187
|
-
? `${prompt} ${uiTheme.fg("dim", `cd ${displayWorkdir} &&`)} ${command}`
|
|
188
|
-
: `${prompt} ${command}`;
|
|
189
|
-
const text = ui.title(cmdText);
|
|
189
|
+
const cmdText = formatBashCommand(args, uiTheme);
|
|
190
|
+
const text = renderStatusLine({ icon: "pending", title: "Bash", description: cmdText }, uiTheme);
|
|
190
191
|
return new Text(text, 0, 0);
|
|
191
192
|
},
|
|
192
193
|
|
|
@@ -194,19 +195,23 @@ export const bashToolRenderer = {
|
|
|
194
195
|
result: {
|
|
195
196
|
content: Array<{ type: string; text?: string }>;
|
|
196
197
|
details?: BashToolDetails;
|
|
198
|
+
isError?: boolean;
|
|
197
199
|
},
|
|
198
200
|
options: RenderResultOptions & { renderContext?: BashRenderContext },
|
|
199
201
|
uiTheme: Theme,
|
|
202
|
+
args?: BashRenderArgs,
|
|
200
203
|
): Component {
|
|
201
|
-
const
|
|
204
|
+
const cmdText = args ? formatBashCommand(args, uiTheme) : undefined;
|
|
205
|
+
const isError = result.isError === true;
|
|
206
|
+
const header = renderStatusLine({ icon: isError ? "error" : "success", title: "Bash" }, uiTheme);
|
|
202
207
|
const { renderContext } = options;
|
|
203
208
|
const details = result.details;
|
|
204
209
|
const expanded = renderContext?.expanded ?? options.expanded;
|
|
205
210
|
const previewLines = renderContext?.previewLines ?? BASH_DEFAULT_PREVIEW_LINES;
|
|
206
211
|
|
|
207
212
|
// Get output from context (preferred) or fall back to result content
|
|
208
|
-
const output = renderContext?.output ??
|
|
209
|
-
const displayOutput = output;
|
|
213
|
+
const output = renderContext?.output ?? result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
214
|
+
const displayOutput = output.trimEnd();
|
|
210
215
|
const showingFullOutput = expanded && renderContext?.isFullOutput === true;
|
|
211
216
|
|
|
212
217
|
// Build truncation warning lines (static, doesn't depend on width)
|
|
@@ -214,7 +219,10 @@ export const bashToolRenderer = {
|
|
|
214
219
|
const timeoutSeconds = renderContext?.timeout;
|
|
215
220
|
const timeoutLine =
|
|
216
221
|
typeof timeoutSeconds === "number"
|
|
217
|
-
? uiTheme.fg(
|
|
222
|
+
? uiTheme.fg(
|
|
223
|
+
"dim",
|
|
224
|
+
`${uiTheme.format.bracketLeft}Timeout: ${timeoutSeconds}s${uiTheme.format.bracketRight}`,
|
|
225
|
+
)
|
|
218
226
|
: undefined;
|
|
219
227
|
let warningLine: string | undefined;
|
|
220
228
|
if (truncation && !showingFullOutput) {
|
|
@@ -226,72 +234,57 @@ export const bashToolRenderer = {
|
|
|
226
234
|
warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
|
|
227
235
|
} else {
|
|
228
236
|
warnings.push(
|
|
229
|
-
`Truncated: ${truncation.outputLines} lines shown (${
|
|
237
|
+
`Truncated: ${truncation.outputLines} lines shown (${formatBytes(truncation.outputBytes)} limit)`,
|
|
230
238
|
);
|
|
231
239
|
}
|
|
232
240
|
if (warnings.length > 0) {
|
|
233
|
-
warningLine = uiTheme.fg("warning",
|
|
241
|
+
warningLine = uiTheme.fg("warning", wrapBrackets(warnings.join(". "), uiTheme));
|
|
234
242
|
}
|
|
235
243
|
}
|
|
236
244
|
|
|
237
|
-
if (!displayOutput) {
|
|
238
|
-
// No output - just show warning if any
|
|
239
|
-
const lines = [timeoutLine, warningLine].filter(Boolean) as string[];
|
|
240
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (expanded) {
|
|
244
|
-
// Show all lines when expanded
|
|
245
|
-
const styledOutput = displayOutput
|
|
246
|
-
.split("\n")
|
|
247
|
-
.map((line) => uiTheme.fg("toolOutput", line))
|
|
248
|
-
.join("\n");
|
|
249
|
-
const lines = [styledOutput, timeoutLine, warningLine].filter(Boolean) as string[];
|
|
250
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Collapsed: use width-aware caching component
|
|
254
|
-
const styledOutput = displayOutput
|
|
255
|
-
.split("\n")
|
|
256
|
-
.map((line) => uiTheme.fg("toolOutput", line))
|
|
257
|
-
.join("\n");
|
|
258
|
-
const textContent = `\n${styledOutput}`;
|
|
259
|
-
|
|
260
|
-
let cachedWidth: number | undefined;
|
|
261
|
-
let cachedLines: string[] | undefined;
|
|
262
|
-
let cachedSkipped: number | undefined;
|
|
263
|
-
|
|
264
245
|
return {
|
|
265
246
|
render: (width: number): string[] => {
|
|
266
|
-
if (cachedLines === undefined || cachedWidth !== width) {
|
|
267
|
-
const result = truncateToVisualLines(textContent, previewLines, width);
|
|
268
|
-
cachedLines = result.visualLines;
|
|
269
|
-
cachedSkipped = result.skippedCount;
|
|
270
|
-
cachedWidth = width;
|
|
271
|
-
}
|
|
272
247
|
const outputLines: string[] = [];
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
248
|
+
const hasOutput = displayOutput.trim().length > 0;
|
|
249
|
+
if (hasOutput) {
|
|
250
|
+
if (expanded) {
|
|
251
|
+
outputLines.push(...displayOutput.split("\n").map((line) => uiTheme.fg("toolOutput", line)));
|
|
252
|
+
} else {
|
|
253
|
+
const styledOutput = displayOutput
|
|
254
|
+
.split("\n")
|
|
255
|
+
.map((line) => uiTheme.fg("toolOutput", line))
|
|
256
|
+
.join("\n");
|
|
257
|
+
const textContent = styledOutput;
|
|
258
|
+
const result = truncateToVisualLines(textContent, previewLines, width);
|
|
259
|
+
if (result.skippedCount > 0) {
|
|
260
|
+
outputLines.push(
|
|
261
|
+
uiTheme.fg(
|
|
262
|
+
"dim",
|
|
263
|
+
`${uiTheme.format.ellipsis} (${result.skippedCount} earlier lines, showing ${result.visualLines.length} of ${result.skippedCount + result.visualLines.length}) (ctrl+o to expand)`,
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
outputLines.push(...result.visualLines);
|
|
268
|
+
}
|
|
284
269
|
}
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
270
|
+
if (timeoutLine) outputLines.push(timeoutLine);
|
|
271
|
+
if (warningLine) outputLines.push(warningLine);
|
|
272
|
+
|
|
273
|
+
return renderOutputBlock(
|
|
274
|
+
{
|
|
275
|
+
header,
|
|
276
|
+
state: isError ? "error" : "success",
|
|
277
|
+
sections: [
|
|
278
|
+
{ lines: cmdText ? [uiTheme.fg("dim", cmdText)] : [] },
|
|
279
|
+
{ label: uiTheme.fg("toolTitle", "Output"), lines: outputLines },
|
|
280
|
+
],
|
|
281
|
+
width,
|
|
282
|
+
},
|
|
283
|
+
uiTheme,
|
|
284
|
+
);
|
|
294
285
|
},
|
|
286
|
+
invalidate: () => {},
|
|
295
287
|
};
|
|
296
288
|
},
|
|
289
|
+
mergeCallAndResult: true,
|
|
297
290
|
};
|
package/src/tools/calculator.ts
CHANGED
|
@@ -3,17 +3,16 @@ import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-te
|
|
|
3
3
|
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
4
4
|
import type { Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
5
5
|
import calculatorDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/calculator.md" with { type: "text" };
|
|
6
|
+
import { renderStatusLine, renderTreeList } from "@oh-my-pi/pi-coding-agent/tui";
|
|
6
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
8
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
8
9
|
import { untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
10
|
import { Type } from "@sinclair/typebox";
|
|
10
|
-
import type { ToolSession } from "
|
|
11
|
+
import type { ToolSession } from ".";
|
|
11
12
|
import {
|
|
12
13
|
formatCount,
|
|
13
14
|
formatEmptyMessage,
|
|
14
|
-
|
|
15
|
-
formatMeta,
|
|
16
|
-
formatMoreItems,
|
|
15
|
+
formatErrorMessage,
|
|
17
16
|
PREVIEW_LIMITS,
|
|
18
17
|
TRUNCATE_LENGTHS,
|
|
19
18
|
truncate,
|
|
@@ -453,16 +452,11 @@ export const calculatorToolRenderer = {
|
|
|
453
452
|
* Format: "Calc <expression> (N calcs)"
|
|
454
453
|
*/
|
|
455
454
|
renderCall(args: CalculatorRenderArgs, uiTheme: Theme): Component {
|
|
456
|
-
const label = uiTheme.fg("toolTitle", uiTheme.bold("Calc"));
|
|
457
455
|
const count = args.calculations?.length ?? 0;
|
|
458
456
|
const firstExpression = args.calculations?.[0]?.expression;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
const meta: string[] = [];
|
|
464
|
-
if (count > 0) meta.push(formatCount("calc", count));
|
|
465
|
-
text += formatMeta(meta, uiTheme);
|
|
457
|
+
const description = firstExpression ? truncate(firstExpression, TRUNCATE_LENGTHS.TITLE, "...") : undefined;
|
|
458
|
+
const meta = count > 0 ? [formatCount("calc", count)] : [];
|
|
459
|
+
const text = renderStatusLine({ icon: "pending", title: "Calc", description, meta }, uiTheme);
|
|
466
460
|
return new Text(text, 0, 0);
|
|
467
461
|
},
|
|
468
462
|
|
|
@@ -471,46 +465,54 @@ export const calculatorToolRenderer = {
|
|
|
471
465
|
* Collapsed mode shows first N items with expand hint; expanded shows all.
|
|
472
466
|
*/
|
|
473
467
|
renderResult(
|
|
474
|
-
result: { content: Array<{ type: string; text?: string }>; details?: CalculatorToolDetails },
|
|
468
|
+
result: { content: Array<{ type: string; text?: string }>; details?: CalculatorToolDetails; isError?: boolean },
|
|
475
469
|
{ expanded }: RenderResultOptions,
|
|
476
470
|
uiTheme: Theme,
|
|
471
|
+
args?: CalculatorRenderArgs,
|
|
477
472
|
): Component {
|
|
478
473
|
const details = result.details;
|
|
479
474
|
const textContent = result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
475
|
+
if (result.isError) {
|
|
476
|
+
const header = renderStatusLine({ icon: "error", title: "Calc" }, uiTheme);
|
|
477
|
+
return new Text([header, formatErrorMessage(textContent, uiTheme)].join("\n"), 0, 0);
|
|
478
|
+
}
|
|
480
479
|
|
|
481
480
|
// Prefer structured details; fall back to parsing text content
|
|
482
|
-
let outputs = details?.results?.map((entry) => entry.output) ?? [];
|
|
481
|
+
let outputs = details?.results?.map((entry) => `${entry.expression} = ${entry.output}`) ?? [];
|
|
483
482
|
if (outputs.length === 0 && textContent.trim()) {
|
|
484
|
-
|
|
483
|
+
const rawOutputs = textContent.split("\n").filter((line) => line.trim().length > 0);
|
|
484
|
+
const expressions = args?.calculations?.map((calc) => calc.expression) ?? [];
|
|
485
|
+
if (expressions.length === rawOutputs.length && expressions.length > 0) {
|
|
486
|
+
outputs = rawOutputs.map((output, index) => `${expressions[index]} = ${output}`);
|
|
487
|
+
} else {
|
|
488
|
+
outputs = rawOutputs;
|
|
489
|
+
}
|
|
485
490
|
}
|
|
486
491
|
|
|
487
492
|
if (outputs.length === 0) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// Limit visible items in collapsed mode
|
|
492
|
-
const maxItems = expanded ? outputs.length : Math.min(outputs.length, COLLAPSED_LIST_LIMIT);
|
|
493
|
-
const hasMore = outputs.length > maxItems;
|
|
494
|
-
const icon = uiTheme.styledSymbol("status.success", "success");
|
|
495
|
-
const summary = uiTheme.fg("dim", formatCount("result", outputs.length));
|
|
496
|
-
const expandHint = formatExpandHint(uiTheme, expanded, hasMore);
|
|
497
|
-
let text = `${icon} ${summary}${expandHint}`;
|
|
498
|
-
|
|
499
|
-
// Render each result as a tree branch
|
|
500
|
-
for (let i = 0; i < maxItems; i += 1) {
|
|
501
|
-
const isLast = i === maxItems - 1 && !hasMore;
|
|
502
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
503
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg("toolOutput", outputs[i])}`;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Show overflow indicator for collapsed mode
|
|
507
|
-
if (hasMore) {
|
|
508
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg(
|
|
509
|
-
"muted",
|
|
510
|
-
formatMoreItems(outputs.length - maxItems, "result", uiTheme),
|
|
511
|
-
)}`;
|
|
493
|
+
const header = renderStatusLine({ icon: "warning", title: "Calc" }, uiTheme);
|
|
494
|
+
return new Text([header, formatEmptyMessage("No results", uiTheme)].join("\n"), 0, 0);
|
|
512
495
|
}
|
|
513
496
|
|
|
514
|
-
|
|
497
|
+
const description = args?.calculations?.[0]?.expression
|
|
498
|
+
? truncate(args.calculations[0].expression, TRUNCATE_LENGTHS.TITLE, "...")
|
|
499
|
+
: undefined;
|
|
500
|
+
const header = renderStatusLine(
|
|
501
|
+
{ icon: "success", title: "Calc", description, meta: [formatCount("result", outputs.length)] },
|
|
502
|
+
uiTheme,
|
|
503
|
+
);
|
|
504
|
+
const lines = renderTreeList(
|
|
505
|
+
{
|
|
506
|
+
items: outputs,
|
|
507
|
+
expanded,
|
|
508
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
509
|
+
itemType: "result",
|
|
510
|
+
renderItem: (output) => uiTheme.fg("toolOutput", output),
|
|
511
|
+
},
|
|
512
|
+
uiTheme,
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
return new Text([header, ...lines].join("\n"), 0, 0);
|
|
515
516
|
},
|
|
517
|
+
mergeCallAndResult: true,
|
|
516
518
|
};
|
package/src/tools/complete.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { subprocessToolRegistry } from "@oh-my-pi/pi-coding-agent/task/subproces
|
|
|
10
10
|
import type { Static, TObject } from "@sinclair/typebox";
|
|
11
11
|
import { Type } from "@sinclair/typebox";
|
|
12
12
|
import Ajv, { type ErrorObject, type ValidateFunction } from "ajv";
|
|
13
|
-
import type { ToolSession } from "
|
|
13
|
+
import type { ToolSession } from ".";
|
|
14
14
|
import { jtdToJsonSchema } from "./jtd-to-json-schema";
|
|
15
15
|
|
|
16
16
|
export interface CompleteDetails {
|
package/src/tools/fetch.ts
CHANGED
|
@@ -7,8 +7,9 @@ import { type Theme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
|
7
7
|
import fetchDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/fetch.md" with { type: "text" };
|
|
8
8
|
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
9
9
|
import { ToolAbortError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
10
|
+
import { renderOutputBlock, renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
|
|
10
11
|
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
11
|
-
import { specialHandlers } from "@oh-my-pi/pi-coding-agent/web/scrapers
|
|
12
|
+
import { specialHandlers } from "@oh-my-pi/pi-coding-agent/web/scrapers";
|
|
12
13
|
import type { RenderResult } from "@oh-my-pi/pi-coding-agent/web/scrapers/types";
|
|
13
14
|
import { finalizeOutput, loadPage, MAX_OUTPUT_CHARS } from "@oh-my-pi/pi-coding-agent/web/scrapers/types";
|
|
14
15
|
import { convertWithMarkitdown, fetchBinary } from "@oh-my-pi/pi-coding-agent/web/scrapers/utils";
|
|
@@ -18,7 +19,7 @@ import { ptree } from "@oh-my-pi/pi-utils";
|
|
|
18
19
|
import { type Static, Type } from "@sinclair/typebox";
|
|
19
20
|
import { nanoid } from "nanoid";
|
|
20
21
|
import { parse as parseHtml } from "node-html-parser";
|
|
21
|
-
import type { ToolSession } from "
|
|
22
|
+
import type { ToolSession } from ".";
|
|
22
23
|
import { applyListLimit } from "./list-limit";
|
|
23
24
|
import { formatExpandHint } from "./render-utils";
|
|
24
25
|
import { toolResult } from "./tool-result";
|
|
@@ -993,8 +994,11 @@ export function renderFetchCall(
|
|
|
993
994
|
): Component {
|
|
994
995
|
const domain = getDomain(args.url);
|
|
995
996
|
const path = truncate(args.url.replace(/^https?:\/\/[^/]+/, ""), 50, uiTheme.format.ellipsis);
|
|
996
|
-
const
|
|
997
|
-
const
|
|
997
|
+
const description = `${domain}${path ? ` ${path}` : ""}`.trim();
|
|
998
|
+
const meta: string[] = [];
|
|
999
|
+
if (args.raw) meta.push("raw");
|
|
1000
|
+
if (args.timeout !== undefined) meta.push(`timeout:${args.timeout}s`);
|
|
1001
|
+
const text = renderStatusLine({ icon: "pending", title: "Fetch", description, meta }, uiTheme);
|
|
998
1002
|
return new Text(text, 0, 0);
|
|
999
1003
|
}
|
|
1000
1004
|
|
|
@@ -1012,20 +1016,22 @@ export function renderFetchResult(
|
|
|
1012
1016
|
}
|
|
1013
1017
|
|
|
1014
1018
|
const domain = getDomain(details.finalUrl);
|
|
1019
|
+
const path = truncate(details.finalUrl.replace(/^https?:\/\/[^/]+/, ""), 50, uiTheme.format.ellipsis);
|
|
1015
1020
|
const hasRedirect = details.url !== details.finalUrl;
|
|
1016
1021
|
const hasNotes = details.notes.length > 0;
|
|
1017
1022
|
const truncation = details.meta?.truncation;
|
|
1018
1023
|
const truncated = Boolean(details.truncated || truncation);
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1024
|
+
|
|
1025
|
+
const header = renderStatusLine(
|
|
1026
|
+
{
|
|
1027
|
+
icon: truncated ? "warning" : "success",
|
|
1028
|
+
title: "Fetch",
|
|
1029
|
+
description: `${domain}${path ? ` ${path}` : ""}`,
|
|
1030
|
+
},
|
|
1031
|
+
uiTheme,
|
|
1032
|
+
);
|
|
1033
|
+
|
|
1027
1034
|
const contentText = result.content[0]?.text ?? "";
|
|
1028
|
-
// Extract just the content part (after the --- separator)
|
|
1029
1035
|
const contentBody = contentText.includes("---\n\n")
|
|
1030
1036
|
? contentText.split("---\n\n").slice(1).join("---\n\n")
|
|
1031
1037
|
: contentText;
|
|
@@ -1033,104 +1039,61 @@ export function renderFetchResult(
|
|
|
1033
1039
|
const charCount = contentBody.trim().length;
|
|
1034
1040
|
const contentLines = contentBody.split("\n").filter((l) => l.trim());
|
|
1035
1041
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
if (hasNotes) {
|
|
1052
|
-
metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
const previewLimit = 3;
|
|
1056
|
-
const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
|
|
1057
|
-
const previewLines = previewList.items.map((line) => truncate(line.trim(), 100, uiTheme.format.ellipsis));
|
|
1058
|
-
const detailLines: string[] = [...metaLines];
|
|
1059
|
-
|
|
1060
|
-
if (previewLines.length === 0) {
|
|
1061
|
-
detailLines.push(uiTheme.fg("dim", "(no content)"));
|
|
1062
|
-
} else {
|
|
1063
|
-
for (const line of previewLines) {
|
|
1064
|
-
detailLines.push(uiTheme.fg("dim", line));
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const remaining = Math.max(0, contentLines.length - previewLines.length);
|
|
1069
|
-
if (remaining > 0) {
|
|
1070
|
-
detailLines.push(uiTheme.fg("muted", `${uiTheme.format.ellipsis} ${remaining} more lines`));
|
|
1071
|
-
} else {
|
|
1072
|
-
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1073
|
-
detailLines.push(uiTheme.fg("muted", `${lineLabel}${uiTheme.sep.dot}${charCount} chars`));
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
for (let i = 0; i < detailLines.length; i++) {
|
|
1077
|
-
const isLast = i === detailLines.length - 1;
|
|
1078
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.vertical;
|
|
1079
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${detailLines[i]}`;
|
|
1080
|
-
}
|
|
1081
|
-
} else {
|
|
1082
|
-
// Expanded view: structured metadata + bounded content preview
|
|
1083
|
-
const metaLines: string[] = [
|
|
1084
|
-
`${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`,
|
|
1085
|
-
`${uiTheme.fg("muted", "Method:")} ${details.method}`,
|
|
1086
|
-
];
|
|
1087
|
-
if (hasRedirect) {
|
|
1088
|
-
metaLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
|
|
1089
|
-
}
|
|
1090
|
-
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1091
|
-
metaLines.push(`${uiTheme.fg("muted", "Lines:")} ${lineLabel}`);
|
|
1092
|
-
metaLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
|
|
1093
|
-
if (truncated) {
|
|
1094
|
-
metaLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1095
|
-
if (truncation?.artifactId) {
|
|
1096
|
-
metaLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
if (hasNotes) {
|
|
1100
|
-
metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.branch)} ${uiTheme.fg("accent", "Metadata")}`;
|
|
1104
|
-
for (let i = 0; i < metaLines.length; i++) {
|
|
1105
|
-
const isLast = i === metaLines.length - 1;
|
|
1106
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
1107
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.vertical)} ${uiTheme.fg("dim", branch)} ${metaLines[i]}`;
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("accent", "Content Preview")}`;
|
|
1111
|
-
const previewLimit = 12;
|
|
1112
|
-
const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
|
|
1113
|
-
const previewLines = previewList.items.map((line) => truncate(line.trim(), 120, uiTheme.format.ellipsis));
|
|
1114
|
-
const remaining = Math.max(0, contentLines.length - previewLines.length);
|
|
1115
|
-
const contentPrefix = uiTheme.fg("dim", " ");
|
|
1116
|
-
|
|
1117
|
-
if (previewLines.length === 0) {
|
|
1118
|
-
text += `\n ${contentPrefix} ${uiTheme.fg("dim", "(no content)")}`;
|
|
1119
|
-
} else {
|
|
1120
|
-
for (const line of previewLines) {
|
|
1121
|
-
text += `\n ${contentPrefix} ${uiTheme.fg("dim", line)}`;
|
|
1122
|
-
}
|
|
1042
|
+
const metadataLines: string[] = [
|
|
1043
|
+
`${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`,
|
|
1044
|
+
`${uiTheme.fg("muted", "Method:")} ${details.method}`,
|
|
1045
|
+
];
|
|
1046
|
+
if (hasRedirect) {
|
|
1047
|
+
metadataLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
|
|
1048
|
+
}
|
|
1049
|
+
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1050
|
+
metadataLines.push(`${uiTheme.fg("muted", "Lines:")} ${lineLabel}`);
|
|
1051
|
+
metadataLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
|
|
1052
|
+
if (truncated) {
|
|
1053
|
+
metadataLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1054
|
+
if (truncation?.artifactId) {
|
|
1055
|
+
metadataLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
|
|
1123
1056
|
}
|
|
1057
|
+
}
|
|
1058
|
+
if (hasNotes) {
|
|
1059
|
+
metadataLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1060
|
+
}
|
|
1124
1061
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1062
|
+
const previewLimit = expanded ? 12 : 3;
|
|
1063
|
+
const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
|
|
1064
|
+
const previewLines = previewList.items.map((line) => truncate(line.trimEnd(), 120, uiTheme.format.ellipsis));
|
|
1065
|
+
const remaining = Math.max(0, contentLines.length - previewLines.length);
|
|
1066
|
+
const contentPreviewLines =
|
|
1067
|
+
previewLines.length > 0
|
|
1068
|
+
? previewLines.map((line) => uiTheme.fg("dim", line))
|
|
1069
|
+
: [uiTheme.fg("dim", "(no content)")];
|
|
1070
|
+
if (remaining > 0) {
|
|
1071
|
+
const hint = formatExpandHint(uiTheme, expanded, true);
|
|
1072
|
+
contentPreviewLines.push(
|
|
1073
|
+
uiTheme.fg("muted", `${uiTheme.format.ellipsis} ${remaining} more lines${hint ? ` ${hint}` : ""}`),
|
|
1074
|
+
);
|
|
1128
1075
|
}
|
|
1129
1076
|
|
|
1130
|
-
return
|
|
1077
|
+
return {
|
|
1078
|
+
render: (width: number) =>
|
|
1079
|
+
renderOutputBlock(
|
|
1080
|
+
{
|
|
1081
|
+
header,
|
|
1082
|
+
state: truncated ? "warning" : "success",
|
|
1083
|
+
sections: [
|
|
1084
|
+
{ label: uiTheme.fg("toolTitle", "Metadata"), lines: metadataLines },
|
|
1085
|
+
{ label: uiTheme.fg("toolTitle", "Content Preview"), lines: contentPreviewLines },
|
|
1086
|
+
],
|
|
1087
|
+
width,
|
|
1088
|
+
},
|
|
1089
|
+
uiTheme,
|
|
1090
|
+
),
|
|
1091
|
+
invalidate: () => {},
|
|
1092
|
+
};
|
|
1131
1093
|
}
|
|
1132
1094
|
|
|
1133
1095
|
export const fetchToolRenderer = {
|
|
1134
1096
|
renderCall: renderFetchCall,
|
|
1135
1097
|
renderResult: renderFetchResult,
|
|
1098
|
+
mergeCallAndResult: true,
|
|
1136
1099
|
};
|