@oh-my-pi/pi-coding-agent 12.18.3 → 12.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/package.json +35 -27
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +341 -0
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +3 -17
- package/src/cli/stats-cli.ts +3 -22
- package/src/cli/web-search-cli.ts +8 -16
- package/src/commit/agentic/agent.ts +6 -9
- package/src/commit/agentic/index.ts +44 -50
- package/src/commit/agentic/state.ts +0 -9
- package/src/commit/agentic/tools/propose-commit.ts +1 -30
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +1 -30
- package/src/commit/agentic/validation.ts +1 -18
- package/src/commit/analysis/conventional.ts +3 -50
- package/src/commit/analysis/summary.ts +2 -13
- package/src/commit/changelog/detect.ts +4 -1
- package/src/commit/changelog/generate.ts +2 -25
- package/src/commit/changelog/index.ts +1 -2
- package/src/commit/cli.ts +4 -12
- package/src/commit/map-reduce/reduce-phase.ts +2 -43
- package/src/commit/pipeline.ts +7 -15
- package/src/commit/utils.ts +44 -0
- package/src/config/prompt-templates.ts +1 -81
- package/src/config/settings-schema.ts +20 -1
- package/src/config.ts +2 -3
- package/src/debug/index.ts +1 -6
- package/src/debug/system-info.ts +2 -6
- package/src/discovery/builtin.ts +5 -9
- package/src/discovery/helpers.ts +0 -26
- package/src/discovery/ssh.ts +1 -8
- package/src/exa/company.ts +8 -39
- package/src/exa/factory.ts +64 -0
- package/src/exa/index.ts +0 -16
- package/src/exa/linkedin.ts +8 -39
- package/src/exa/mcp-client.ts +0 -64
- package/src/exa/researcher.ts +17 -59
- package/src/exa/search.ts +30 -154
- package/src/extensibility/custom-tools/loader.ts +3 -41
- package/src/extensibility/extensions/loader.ts +2 -9
- package/src/extensibility/hooks/loader.ts +3 -20
- package/src/extensibility/hooks/runner.ts +3 -19
- package/src/extensibility/plugins/installer.ts +2 -1
- package/src/extensibility/plugins/loader.ts +29 -117
- package/src/extensibility/skills.ts +2 -89
- package/src/extensibility/slash-commands.ts +1 -63
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +9 -25
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/jobs-protocol.ts +118 -0
- package/src/ipy/kernel.ts +2 -0
- package/src/lsp/config.ts +1 -5
- package/src/lsp/lspmux.ts +0 -17
- package/src/lsp/utils.ts +2 -24
- package/src/main.ts +16 -24
- package/src/mcp/client.ts +1 -46
- package/src/mcp/render.ts +8 -1
- package/src/mcp/tool-cache.ts +1 -5
- package/src/mcp/transports/http.ts +2 -7
- package/src/mcp/transports/stdio.ts +2 -7
- package/src/modes/components/bash-execution.ts +2 -16
- package/src/modes/components/extensions/inspector-panel.ts +8 -18
- package/src/modes/components/footer.ts +10 -50
- package/src/modes/components/model-selector.ts +2 -21
- package/src/modes/components/python-execution.ts +2 -16
- package/src/modes/components/settings-selector.ts +1 -10
- package/src/modes/components/status-line/segments.ts +8 -25
- package/src/modes/components/status-line.ts +14 -31
- package/src/modes/components/tool-execution.ts +8 -2
- package/src/modes/controllers/command-controller.ts +71 -30
- package/src/modes/controllers/event-controller.ts +34 -4
- package/src/modes/controllers/mcp-command-controller.ts +3 -34
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/controllers/ssh-command-controller.ts +3 -34
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-client.ts +1 -5
- package/src/modes/shared.ts +73 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +26 -2
- package/src/patch/hashline.ts +6 -286
- package/src/patch/index.ts +6 -57
- package/src/patch/normalize.ts +22 -65
- package/src/patch/shared.ts +16 -16
- package/src/prompts/system/custom-system-prompt.md +0 -10
- package/src/prompts/system/system-prompt.md +69 -89
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +5 -0
- package/src/prompts/tools/cancel-job.md +7 -0
- package/src/prompts/tools/hashline.md +0 -16
- package/src/prompts/tools/poll-jobs.md +7 -0
- package/src/prompts/tools/task.md +4 -0
- package/src/sdk.ts +70 -6
- package/src/session/agent-session.ts +43 -6
- package/src/session/agent-storage.ts +69 -278
- package/src/session/auth-storage.ts +14 -1430
- package/src/session/session-manager.ts +69 -5
- package/src/session/session-storage.ts +1 -5
- package/src/session/streaming-output.ts +637 -76
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/ssh/connection-manager.ts +4 -12
- package/src/ssh/sshfs-mount.ts +3 -7
- package/src/ssh/utils.ts +8 -0
- package/src/system-prompt.ts +24 -90
- package/src/task/executor.ts +11 -1
- package/src/task/index.ts +258 -13
- package/src/task/parallel.ts +32 -0
- package/src/task/render.ts +15 -7
- package/src/task/types.ts +5 -0
- package/src/tools/ask.ts +4 -7
- package/src/tools/bash-interactive.ts +4 -5
- package/src/tools/bash.ts +125 -41
- package/src/tools/cancel-job.ts +93 -0
- package/src/tools/fetch.ts +7 -27
- package/src/tools/find.ts +3 -3
- package/src/tools/gemini-image.ts +15 -14
- package/src/tools/grep.ts +3 -3
- package/src/tools/index.ts +13 -29
- package/src/tools/json-tree.ts +12 -1
- package/src/tools/jtd-to-json-schema.ts +10 -74
- package/src/tools/jtd-to-typescript.ts +10 -72
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/notebook.ts +4 -9
- package/src/tools/output-meta.ts +52 -26
- package/src/tools/path-utils.ts +13 -7
- package/src/tools/poll-jobs.ts +178 -0
- package/src/tools/python.ts +32 -35
- package/src/tools/read.ts +61 -82
- package/src/tools/render-utils.ts +8 -159
- package/src/tools/ssh.ts +7 -20
- package/src/tools/submit-result.ts +1 -1
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/tool-result.ts +1 -2
- package/src/tools/write.ts +8 -10
- package/src/tui/code-cell.ts +8 -3
- package/src/tui/status-line.ts +4 -4
- package/src/tui/types.ts +0 -1
- package/src/tui/utils.ts +1 -14
- package/src/utils/command-args.ts +76 -0
- package/src/utils/file-mentions.ts +15 -19
- package/src/utils/frontmatter.ts +5 -10
- package/src/utils/shell-snapshot.ts +0 -11
- package/src/utils/title-generator.ts +0 -12
- package/src/web/scrapers/artifacthub.ts +7 -16
- package/src/web/scrapers/arxiv.ts +3 -8
- package/src/web/scrapers/aur.ts +8 -22
- package/src/web/scrapers/biorxiv.ts +5 -14
- package/src/web/scrapers/bluesky.ts +13 -36
- package/src/web/scrapers/brew.ts +5 -10
- package/src/web/scrapers/cheatsh.ts +2 -12
- package/src/web/scrapers/chocolatey.ts +63 -26
- package/src/web/scrapers/choosealicense.ts +3 -18
- package/src/web/scrapers/cisa-kev.ts +4 -18
- package/src/web/scrapers/clojars.ts +6 -33
- package/src/web/scrapers/coingecko.ts +25 -33
- package/src/web/scrapers/crates-io.ts +7 -26
- package/src/web/scrapers/crossref.ts +4 -18
- package/src/web/scrapers/devto.ts +11 -41
- package/src/web/scrapers/discogs.ts +7 -10
- package/src/web/scrapers/discourse.ts +6 -31
- package/src/web/scrapers/dockerhub.ts +12 -35
- package/src/web/scrapers/fdroid.ts +8 -33
- package/src/web/scrapers/firefox-addons.ts +10 -34
- package/src/web/scrapers/flathub.ts +7 -24
- package/src/web/scrapers/github-gist.ts +2 -12
- package/src/web/scrapers/github.ts +9 -47
- package/src/web/scrapers/gitlab.ts +130 -185
- package/src/web/scrapers/go-pkg.ts +12 -22
- package/src/web/scrapers/hackage.ts +88 -43
- package/src/web/scrapers/hackernews.ts +25 -45
- package/src/web/scrapers/hex.ts +19 -36
- package/src/web/scrapers/huggingface.ts +26 -91
- package/src/web/scrapers/iacr.ts +3 -8
- package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
- package/src/web/scrapers/lemmy.ts +5 -23
- package/src/web/scrapers/lobsters.ts +16 -28
- package/src/web/scrapers/mastodon.ts +24 -43
- package/src/web/scrapers/maven.ts +6 -21
- package/src/web/scrapers/mdn.ts +7 -11
- package/src/web/scrapers/metacpan.ts +9 -41
- package/src/web/scrapers/musicbrainz.ts +4 -28
- package/src/web/scrapers/npm.ts +8 -25
- package/src/web/scrapers/nuget.ts +14 -37
- package/src/web/scrapers/nvd.ts +6 -28
- package/src/web/scrapers/ollama.ts +7 -34
- package/src/web/scrapers/open-vsx.ts +5 -19
- package/src/web/scrapers/opencorporates.ts +30 -14
- package/src/web/scrapers/openlibrary.ts +49 -33
- package/src/web/scrapers/orcid.ts +4 -18
- package/src/web/scrapers/osv.ts +7 -24
- package/src/web/scrapers/packagist.ts +9 -24
- package/src/web/scrapers/pub-dev.ts +7 -50
- package/src/web/scrapers/pubmed.ts +54 -21
- package/src/web/scrapers/pypi.ts +8 -26
- package/src/web/scrapers/rawg.ts +11 -19
- package/src/web/scrapers/readthedocs.ts +4 -9
- package/src/web/scrapers/reddit.ts +5 -15
- package/src/web/scrapers/repology.ts +8 -20
- package/src/web/scrapers/rfc.ts +5 -14
- package/src/web/scrapers/rubygems.ts +6 -21
- package/src/web/scrapers/searchcode.ts +8 -36
- package/src/web/scrapers/sec-edgar.ts +4 -18
- package/src/web/scrapers/semantic-scholar.ts +15 -35
- package/src/web/scrapers/snapcraft.ts +5 -19
- package/src/web/scrapers/sourcegraph.ts +5 -43
- package/src/web/scrapers/spdx.ts +4 -18
- package/src/web/scrapers/spotify.ts +4 -23
- package/src/web/scrapers/stackoverflow.ts +8 -13
- package/src/web/scrapers/terraform.ts +9 -37
- package/src/web/scrapers/tldr.ts +3 -7
- package/src/web/scrapers/twitter.ts +3 -7
- package/src/web/scrapers/types.ts +105 -27
- package/src/web/scrapers/utils.ts +97 -103
- package/src/web/scrapers/vimeo.ts +7 -27
- package/src/web/scrapers/vscode-marketplace.ts +8 -17
- package/src/web/scrapers/w3c.ts +6 -14
- package/src/web/scrapers/wikidata.ts +5 -19
- package/src/web/scrapers/wikipedia.ts +2 -12
- package/src/web/scrapers/youtube.ts +5 -34
- package/src/web/search/index.ts +0 -9
- package/src/web/search/providers/anthropic.ts +3 -2
- package/src/web/search/providers/brave.ts +3 -18
- package/src/web/search/providers/exa.ts +1 -12
- package/src/web/search/providers/kimi.ts +5 -44
- package/src/web/search/providers/perplexity.ts +1 -12
- package/src/web/search/providers/synthetic.ts +3 -26
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +9 -50
- package/src/web/search/types.ts +0 -28
- package/src/web/search/utils.ts +17 -0
- package/src/tools/output-utils.ts +0 -63
- package/src/tools/truncate.ts +0 -385
- package/src/web/search/auth.ts +0 -178
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import { type Ellipsis, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
9
|
+
import { pluralize } from "@oh-my-pi/pi-utils";
|
|
9
10
|
import type { Theme } from "../modes/theme/theme";
|
|
10
11
|
|
|
11
12
|
export { Ellipsis, replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
@@ -81,60 +82,7 @@ export function getDomain(url: string): string {
|
|
|
81
82
|
// Formatting Utilities
|
|
82
83
|
// =============================================================================
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
* Format byte count for display (e.g., "1.5KB", "2.3MB").
|
|
86
|
-
*/
|
|
87
|
-
export function formatBytes(bytes: number): string {
|
|
88
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
89
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
90
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Format token count for display (e.g., "1.5k", "25k").
|
|
95
|
-
*/
|
|
96
|
-
export function formatTokens(tokens: number): string {
|
|
97
|
-
if (tokens >= 1000) {
|
|
98
|
-
return `${(tokens / 1000).toFixed(1)}k`;
|
|
99
|
-
}
|
|
100
|
-
return String(tokens);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Format duration for display (e.g., "500ms", "2.5s", "1.2m").
|
|
105
|
-
*/
|
|
106
|
-
export function formatDuration(ms: number): string {
|
|
107
|
-
if (ms < 1000) return `${ms}ms`;
|
|
108
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
109
|
-
return `${(ms / 60000).toFixed(1)}m`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Format count with pluralized label (e.g., "3 files", "1 error").
|
|
114
|
-
*/
|
|
115
|
-
export function formatCount(label: string, count: number): string {
|
|
116
|
-
const safeCount = Number.isFinite(count) ? count : 0;
|
|
117
|
-
return `${safeCount} ${pluralize(label, safeCount)}`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Format age from seconds to human-readable string.
|
|
122
|
-
*/
|
|
123
|
-
export function formatAge(ageSeconds: number | null | undefined): string {
|
|
124
|
-
if (!ageSeconds) return "";
|
|
125
|
-
const mins = Math.floor(ageSeconds / 60);
|
|
126
|
-
const hours = Math.floor(mins / 60);
|
|
127
|
-
const days = Math.floor(hours / 24);
|
|
128
|
-
const weeks = Math.floor(days / 7);
|
|
129
|
-
const months = Math.floor(days / 30);
|
|
130
|
-
|
|
131
|
-
if (months > 0) return `${months}mo ago`;
|
|
132
|
-
if (weeks > 0) return `${weeks}w ago`;
|
|
133
|
-
if (days > 0) return `${days}d ago`;
|
|
134
|
-
if (hours > 0) return `${hours}h ago`;
|
|
135
|
-
if (mins > 0) return `${mins}m ago`;
|
|
136
|
-
return "just now";
|
|
137
|
-
}
|
|
85
|
+
export { formatAge, formatBytes, formatCount, formatDuration, pluralize } from "@oh-my-pi/pi-utils";
|
|
138
86
|
|
|
139
87
|
// =============================================================================
|
|
140
88
|
// Theme Helper Utilities
|
|
@@ -199,14 +147,6 @@ export function formatMeta(meta: string[], theme: Theme): string {
|
|
|
199
147
|
return meta.length > 0 ? ` ${theme.fg("muted", meta.join(theme.sep.dot))}` : "";
|
|
200
148
|
}
|
|
201
149
|
|
|
202
|
-
export function formatScope(scopePath: string | undefined, theme: Theme): string {
|
|
203
|
-
return scopePath ? ` ${theme.fg("muted", `in ${scopePath}`)}` : "";
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function formatTruncationSuffix(truncated: boolean, theme: Theme): string {
|
|
207
|
-
return truncated ? theme.fg("warning", " (truncated)") : "";
|
|
208
|
-
}
|
|
209
|
-
|
|
210
150
|
export function formatErrorMessage(message: string | undefined, theme: Theme): string {
|
|
211
151
|
const clean = (message ?? "").replace(/^Error:\s*/, "").trim();
|
|
212
152
|
return `${theme.styledSymbol("status.error", "error")} ${theme.fg("error", `Error: ${clean || "Unknown error"}`)}`;
|
|
@@ -217,7 +157,7 @@ export function formatEmptyMessage(message: string, theme: Theme): string {
|
|
|
217
157
|
}
|
|
218
158
|
|
|
219
159
|
// =============================================================================
|
|
220
|
-
// Tool UI
|
|
160
|
+
// Tool UI Helpers
|
|
221
161
|
// =============================================================================
|
|
222
162
|
|
|
223
163
|
export type ToolUIStatus = "success" | "error" | "warning" | "info" | "pending" | "running" | "aborted";
|
|
@@ -227,99 +167,15 @@ export interface ToolUITitleOptions {
|
|
|
227
167
|
bold?: boolean;
|
|
228
168
|
}
|
|
229
169
|
|
|
170
|
+
export function formatTitle(label: string, theme: Theme, options?: ToolUITitleOptions): string {
|
|
171
|
+
const content = options?.bold === false ? label : theme.bold(label);
|
|
172
|
+
return theme.fg("toolTitle", content);
|
|
173
|
+
}
|
|
174
|
+
|
|
230
175
|
// =============================================================================
|
|
231
176
|
// Diagnostic Formatting
|
|
232
177
|
// =============================================================================
|
|
233
178
|
|
|
234
|
-
export class ToolUIKit {
|
|
235
|
-
constructor(public theme: Theme) {}
|
|
236
|
-
|
|
237
|
-
title(label: string, options?: ToolUITitleOptions): string {
|
|
238
|
-
const content = options?.bold === false ? label : this.theme.bold(label);
|
|
239
|
-
return this.theme.fg("toolTitle", content);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
meta(meta: string[]): string {
|
|
243
|
-
return formatMeta(meta, this.theme);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
count(label: string, count: number): string {
|
|
247
|
-
return formatCount(label, count);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
moreItems(remaining: number, itemType: string): string {
|
|
251
|
-
return formatMoreItems(remaining, itemType);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
expandHint(expanded: boolean, hasMore: boolean): string {
|
|
255
|
-
return formatExpandHint(this.theme, expanded, hasMore);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
scope(scopePath?: string): string {
|
|
259
|
-
return formatScope(scopePath, this.theme);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
truncationSuffix(truncated: boolean): string {
|
|
263
|
-
return formatTruncationSuffix(truncated, this.theme);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
errorMessage(message: string | undefined): string {
|
|
267
|
-
return formatErrorMessage(message, this.theme);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
emptyMessage(message: string): string {
|
|
271
|
-
return formatEmptyMessage(message, this.theme);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
badge(label: string, color: ToolUIColor): string {
|
|
275
|
-
return formatBadge(label, color, this.theme);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
statusIcon(status: ToolUIStatus, spinnerFrame?: number): string {
|
|
279
|
-
return formatStatusIcon(status, this.theme, spinnerFrame);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
wrapBrackets(text: string): string {
|
|
283
|
-
return wrapBrackets(text, this.theme);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
truncate(text: string, maxLen: number): string {
|
|
287
|
-
return truncateToWidth(text, maxLen);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
previewLines(text: string, maxLines: number, maxLineLen: number): string[] {
|
|
291
|
-
return getPreviewLines(text, maxLines, maxLineLen);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
formatBytes(bytes: number): string {
|
|
295
|
-
return formatBytes(bytes);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
formatTokens(tokens: number): string {
|
|
299
|
-
return formatTokens(tokens);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
formatDuration(ms: number): string {
|
|
303
|
-
return formatDuration(ms);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
formatAge(ageSeconds: number | null | undefined): string {
|
|
307
|
-
return formatAge(ageSeconds);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
formatDiagnostics(
|
|
311
|
-
diag: { errored: boolean; summary: string; messages: string[] },
|
|
312
|
-
expanded: boolean,
|
|
313
|
-
getLangIcon: (filePath: string) => string,
|
|
314
|
-
): string {
|
|
315
|
-
return formatDiagnostics(diag, expanded, this.theme, getLangIcon);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
formatDiffStats(added: number, removed: number, hunks: number): string {
|
|
319
|
-
return formatDiffStats(added, removed, hunks, this.theme);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
179
|
interface ParsedDiagnostic {
|
|
324
180
|
filePath: string;
|
|
325
181
|
line: number;
|
|
@@ -671,10 +527,3 @@ export function shortenPath(filePath: string, homeDir?: string): string {
|
|
|
671
527
|
export function wrapBrackets(text: string, theme: Theme): string {
|
|
672
528
|
return `${theme.format.bracketLeft}${text}${theme.format.bracketRight}`;
|
|
673
529
|
}
|
|
674
|
-
|
|
675
|
-
function pluralize(label: string, count: number): string {
|
|
676
|
-
if (count === 1) return label;
|
|
677
|
-
if (/(?:ch|sh|s|x|z)$/i.test(label)) return `${label}es`;
|
|
678
|
-
if (/[^aeiou]y$/i.test(label)) return `${label.slice(0, -1)}ies`;
|
|
679
|
-
return `${label}s`;
|
|
680
|
-
}
|
package/src/tools/ssh.ts
CHANGED
|
@@ -9,18 +9,16 @@ import { loadCapability } from "../discovery";
|
|
|
9
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
10
|
import type { Theme } from "../modes/theme/theme";
|
|
11
11
|
import sshDescriptionBase from "../prompts/tools/ssh.md" with { type: "text" };
|
|
12
|
+
import { DEFAULT_MAX_BYTES, TailBuffer } from "../session/streaming-output";
|
|
12
13
|
import type { SSHHostInfo } from "../ssh/connection-manager";
|
|
13
14
|
import { ensureHostInfo, getHostInfoForHost } from "../ssh/connection-manager";
|
|
14
15
|
import { executeSSH } from "../ssh/ssh-executor";
|
|
15
16
|
import { renderStatusLine } from "../tui";
|
|
16
17
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
17
18
|
import type { ToolSession } from ".";
|
|
18
|
-
import type
|
|
19
|
-
import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
|
|
20
|
-
import { formatBytes, wrapBrackets } from "./render-utils";
|
|
19
|
+
import { formatStyledTruncationWarning, type OutputMeta } from "./output-meta";
|
|
21
20
|
import { ToolError } from "./tool-errors";
|
|
22
21
|
import { toolResult } from "./tool-result";
|
|
23
|
-
import { DEFAULT_MAX_BYTES } from "./truncate";
|
|
24
22
|
|
|
25
23
|
const sshSchema = Type.Object({
|
|
26
24
|
host: Type.String({ description: "Host name from managed SSH config or discovered ssh.json files" }),
|
|
@@ -159,8 +157,8 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
|
159
157
|
const timeoutSec = Math.max(1, Math.min(3600, rawTimeout));
|
|
160
158
|
const timeoutMs = timeoutSec * 1000;
|
|
161
159
|
|
|
162
|
-
const tailBuffer =
|
|
163
|
-
const { artifactPath, artifactId } = await
|
|
160
|
+
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
|
|
161
|
+
const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("ssh")) ?? {};
|
|
164
162
|
|
|
165
163
|
const result = await executeSSH(hostConfig, remoteCommand, {
|
|
166
164
|
timeout: timeoutMs,
|
|
@@ -253,7 +251,6 @@ export const sshToolRenderer = {
|
|
|
253
251
|
uiTheme,
|
|
254
252
|
);
|
|
255
253
|
const textContent = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
256
|
-
const truncation = details?.meta?.truncation;
|
|
257
254
|
const outputBlock = new CachedOutputBlock();
|
|
258
255
|
|
|
259
256
|
return {
|
|
@@ -292,19 +289,9 @@ export const sshToolRenderer = {
|
|
|
292
289
|
}
|
|
293
290
|
}
|
|
294
291
|
|
|
295
|
-
if (truncation) {
|
|
296
|
-
const
|
|
297
|
-
if (
|
|
298
|
-
warnings.push(`Full output: artifact://${truncation.artifactId}`);
|
|
299
|
-
}
|
|
300
|
-
if (truncation.truncatedBy === "lines") {
|
|
301
|
-
warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
|
|
302
|
-
} else {
|
|
303
|
-
warnings.push(
|
|
304
|
-
`Truncated: ${truncation.outputLines} lines shown (${formatBytes(truncation.outputBytes)} limit)`,
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
outputLines.push(uiTheme.fg("warning", wrapBrackets(warnings.join(". "), uiTheme)));
|
|
292
|
+
if (details?.meta?.truncation) {
|
|
293
|
+
const warning = formatStyledTruncationWarning(details.meta, uiTheme);
|
|
294
|
+
if (warning) outputLines.push(warning);
|
|
308
295
|
}
|
|
309
296
|
|
|
310
297
|
return outputBlock.render(
|
|
@@ -89,7 +89,7 @@ export class SubmitResultTool implements AgentTool<TObject, SubmitResultDetails>
|
|
|
89
89
|
...(normalizedSchema as object),
|
|
90
90
|
description: `Structured output matching the schema:\n${schemaHint}`,
|
|
91
91
|
})
|
|
92
|
-
: Type.
|
|
92
|
+
: Type.Object({}, { additionalProperties: true, description: "Structured JSON output (no schema specified)" });
|
|
93
93
|
|
|
94
94
|
this.parameters = Type.Object({
|
|
95
95
|
data: Type.Optional(dataSchema),
|
package/src/tools/tool-errors.ts
CHANGED
|
@@ -24,36 +24,6 @@ export class ToolError extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Error entry for MultiError.
|
|
29
|
-
*/
|
|
30
|
-
export interface ErrorEntry {
|
|
31
|
-
message: string;
|
|
32
|
-
context?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Error with multiple entries (e.g., multiple validation failures, batch errors).
|
|
37
|
-
*/
|
|
38
|
-
export class MultiError extends ToolError {
|
|
39
|
-
constructor(readonly errors: ErrorEntry[]) {
|
|
40
|
-
super(errors.map(e => e.message).join("; "));
|
|
41
|
-
this.name = "MultiError";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
render(): string {
|
|
45
|
-
if (this.errors.length === 1) {
|
|
46
|
-
const e = this.errors[0];
|
|
47
|
-
return e.context ? `${e.context}: ${e.message}` : e.message;
|
|
48
|
-
}
|
|
49
|
-
return this.errors.map(e => (e.context ? `${e.context}: ${e.message}` : e.message)).join("\n");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
static from(errors: Array<string | ErrorEntry>): MultiError {
|
|
53
|
-
return new MultiError(errors.map(e => (typeof e === "string" ? { message: e } : e)));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
27
|
/**
|
|
58
28
|
* Error thrown when a tool operation is aborted (e.g., via AbortSignal).
|
|
59
29
|
*/
|
package/src/tools/tool-result.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import type { OutputSummary } from "../session/streaming-output";
|
|
3
|
+
import type { OutputSummary, TruncationResult } from "../session/streaming-output";
|
|
4
4
|
import type { OutputMeta, TruncationOptions, TruncationSummaryOptions, TruncationTextOptions } from "./output-meta";
|
|
5
5
|
import { outputMeta } from "./output-meta";
|
|
6
|
-
import type { TruncationResult } from "./truncate";
|
|
7
6
|
|
|
8
7
|
type ToolContent = Array<TextContent | ImageContent>;
|
|
9
8
|
|
package/src/tools/write.ts
CHANGED
|
@@ -24,9 +24,9 @@ import {
|
|
|
24
24
|
formatExpandHint,
|
|
25
25
|
formatMoreItems,
|
|
26
26
|
formatStatusIcon,
|
|
27
|
+
formatTitle,
|
|
27
28
|
replaceTabs,
|
|
28
29
|
shortenPath,
|
|
29
|
-
ToolUIKit,
|
|
30
30
|
} from "./render-utils";
|
|
31
31
|
|
|
32
32
|
const writeSchema = Type.Object({
|
|
@@ -151,7 +151,7 @@ function formatMetadataLine(lineCount: number | null, language: string | undefin
|
|
|
151
151
|
return uiTheme.fg("dim", `${icon}`);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
function formatStreamingContent(content: string, uiTheme: Theme
|
|
154
|
+
function formatStreamingContent(content: string, uiTheme: Theme): string {
|
|
155
155
|
if (!content) return "";
|
|
156
156
|
const lines = content.split("\n");
|
|
157
157
|
const displayLines = lines.slice(-WRITE_STREAMING_PREVIEW_LINES);
|
|
@@ -162,13 +162,13 @@ function formatStreamingContent(content: string, uiTheme: Theme, ui: ToolUIKit):
|
|
|
162
162
|
text += uiTheme.fg("dim", `… (${hidden} earlier lines)\n`);
|
|
163
163
|
}
|
|
164
164
|
for (const line of displayLines) {
|
|
165
|
-
text += `${uiTheme.fg("toolOutput",
|
|
165
|
+
text += `${uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), 80))}\n`;
|
|
166
166
|
}
|
|
167
167
|
text += uiTheme.fg("dim", `… (streaming)`);
|
|
168
168
|
return text;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
function renderContentPreview(content: string, expanded: boolean, uiTheme: Theme
|
|
171
|
+
function renderContentPreview(content: string, expanded: boolean, uiTheme: Theme): string {
|
|
172
172
|
if (!content) return "";
|
|
173
173
|
const lines = content.split("\n");
|
|
174
174
|
const maxLines = expanded ? lines.length : Math.min(lines.length, WRITE_PREVIEW_LINES);
|
|
@@ -177,7 +177,7 @@ function renderContentPreview(content: string, expanded: boolean, uiTheme: Theme
|
|
|
177
177
|
|
|
178
178
|
let text = "\n\n";
|
|
179
179
|
for (const line of displayLines) {
|
|
180
|
-
text += `${uiTheme.fg("toolOutput",
|
|
180
|
+
text += `${uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), 80))}\n`;
|
|
181
181
|
}
|
|
182
182
|
if (!expanded && hidden > 0) {
|
|
183
183
|
const hint = formatExpandHint(uiTheme, expanded, hidden > 0);
|
|
@@ -189,7 +189,6 @@ function renderContentPreview(content: string, expanded: boolean, uiTheme: Theme
|
|
|
189
189
|
|
|
190
190
|
export const writeToolRenderer = {
|
|
191
191
|
renderCall(args: WriteRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
192
|
-
const ui = new ToolUIKit(uiTheme);
|
|
193
192
|
const rawPath = args.file_path || args.path || "";
|
|
194
193
|
const filePath = shortenPath(rawPath);
|
|
195
194
|
const lang = getLanguageFromPath(rawPath) ?? "text";
|
|
@@ -198,14 +197,14 @@ export const writeToolRenderer = {
|
|
|
198
197
|
const spinner =
|
|
199
198
|
options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
|
|
200
199
|
|
|
201
|
-
let text = `${
|
|
200
|
+
let text = `${formatTitle("Write", uiTheme)} ${spinner ? `${spinner} ` : ""}${langIcon} ${pathDisplay}`;
|
|
202
201
|
|
|
203
202
|
if (!args.content) {
|
|
204
203
|
return new Text(text, 0, 0);
|
|
205
204
|
}
|
|
206
205
|
|
|
207
206
|
// Show streaming preview of content (tail)
|
|
208
|
-
text += formatStreamingContent(args.content, uiTheme
|
|
207
|
+
text += formatStreamingContent(args.content, uiTheme);
|
|
209
208
|
|
|
210
209
|
return new Text(text, 0, 0);
|
|
211
210
|
},
|
|
@@ -216,7 +215,6 @@ export const writeToolRenderer = {
|
|
|
216
215
|
uiTheme: Theme,
|
|
217
216
|
args?: WriteRenderArgs,
|
|
218
217
|
): Component {
|
|
219
|
-
const ui = new ToolUIKit(uiTheme);
|
|
220
218
|
const rawPath = args?.file_path || args?.path || "";
|
|
221
219
|
const filePath = shortenPath(rawPath);
|
|
222
220
|
const fileContent = args?.content || "";
|
|
@@ -247,7 +245,7 @@ export const writeToolRenderer = {
|
|
|
247
245
|
|
|
248
246
|
let text = header;
|
|
249
247
|
text += `\n${metadataLine}`;
|
|
250
|
-
text += renderContentPreview(fileContent, expanded, uiTheme
|
|
248
|
+
text += renderContentPreview(fileContent, expanded, uiTheme);
|
|
251
249
|
|
|
252
250
|
if (diagnostics) {
|
|
253
251
|
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
package/src/tui/code-cell.ts
CHANGED
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
* Render a code cell with optional output section.
|
|
3
3
|
*/
|
|
4
4
|
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
formatDuration,
|
|
7
|
+
formatExpandHint,
|
|
8
|
+
formatMoreItems,
|
|
9
|
+
formatStatusIcon,
|
|
10
|
+
replaceTabs,
|
|
11
|
+
} from "../tools/render-utils";
|
|
6
12
|
import { renderOutputBlock } from "./output-block";
|
|
7
13
|
import type { State } from "./types";
|
|
8
|
-
import { getStateIcon } from "./utils";
|
|
9
14
|
|
|
10
15
|
export interface CodeCellOptions {
|
|
11
16
|
code: string;
|
|
@@ -35,7 +40,7 @@ function formatHeader(options: CodeCellOptions, theme: Theme): { title: string;
|
|
|
35
40
|
const { index, total, title, status, spinnerFrame, duration } = options;
|
|
36
41
|
const parts: string[] = [];
|
|
37
42
|
if (status) {
|
|
38
|
-
const icon =
|
|
43
|
+
const icon = formatStatusIcon(
|
|
39
44
|
status === "complete"
|
|
40
45
|
? "success"
|
|
41
46
|
: status === "error"
|
package/src/tui/status-line.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Standardized status header rendering for tool output.
|
|
3
3
|
*/
|
|
4
4
|
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
5
|
+
import type { ToolUIStatus } from "../tools/render-utils";
|
|
6
|
+
import { formatStatusIcon } from "../tools/render-utils";
|
|
7
7
|
|
|
8
8
|
export interface StatusLineOptions {
|
|
9
|
-
icon?:
|
|
9
|
+
icon?: ToolUIStatus;
|
|
10
10
|
spinnerFrame?: number;
|
|
11
11
|
title: string;
|
|
12
12
|
titleColor?: ThemeColor;
|
|
@@ -16,7 +16,7 @@ export interface StatusLineOptions {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function renderStatusLine(options: StatusLineOptions, theme: Theme): string {
|
|
19
|
-
const icon = options.icon ?
|
|
19
|
+
const icon = options.icon ? formatStatusIcon(options.icon, theme, options.spinnerFrame) : "";
|
|
20
20
|
const titleColor = options.titleColor ?? "accent";
|
|
21
21
|
const title = theme.fg(titleColor, options.title);
|
|
22
22
|
let line = icon ? `${icon} ${title}` : title;
|
package/src/tui/types.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import type { Theme } from "../modes/theme/theme";
|
|
5
5
|
|
|
6
6
|
export type State = "pending" | "running" | "success" | "error" | "warning";
|
|
7
|
-
export type IconType = "success" | "error" | "running" | "pending" | "warning" | "info";
|
|
8
7
|
|
|
9
8
|
export interface TreeContext {
|
|
10
9
|
index: number;
|
package/src/tui/utils.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { padding, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import type { Theme, ThemeBg } from "../modes/theme/theme";
|
|
6
|
-
import type {
|
|
6
|
+
import type { State } from "./types";
|
|
7
7
|
|
|
8
8
|
export { Ellipsis, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
9
9
|
|
|
@@ -101,16 +101,3 @@ export function getStateBgColor(state: State): ThemeBg {
|
|
|
101
101
|
if (state === "error") return "toolErrorBg";
|
|
102
102
|
return "toolPendingBg";
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
export function getStateIcon(icon: IconType, theme: Theme, spinnerFrame?: number): string {
|
|
106
|
-
if (icon === "success") return theme.styledSymbol("status.success", "success");
|
|
107
|
-
if (icon === "error") return theme.styledSymbol("status.error", "error");
|
|
108
|
-
if (icon === "warning") return theme.styledSymbol("status.warning", "warning");
|
|
109
|
-
if (icon === "info") return theme.styledSymbol("status.info", "accent");
|
|
110
|
-
if (icon === "pending") return theme.styledSymbol("status.pending", "accent");
|
|
111
|
-
if (spinnerFrame !== undefined) {
|
|
112
|
-
const frames = theme.spinnerFrames;
|
|
113
|
-
return frames[spinnerFrame % frames.length];
|
|
114
|
-
}
|
|
115
|
-
return theme.styledSymbol("status.running", "accent");
|
|
116
|
-
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function parseCommandArgs(argsString: string): string[] {
|
|
2
|
+
const args: string[] = [];
|
|
3
|
+
let current = "";
|
|
4
|
+
let inQuote: string | null = null;
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < argsString.length; i++) {
|
|
7
|
+
const char = argsString[i];
|
|
8
|
+
|
|
9
|
+
if (inQuote) {
|
|
10
|
+
if (char === inQuote) {
|
|
11
|
+
inQuote = null;
|
|
12
|
+
} else {
|
|
13
|
+
current += char;
|
|
14
|
+
}
|
|
15
|
+
} else if (char === '"' || char === "'") {
|
|
16
|
+
inQuote = char;
|
|
17
|
+
} else if (char === " " || char === "\t") {
|
|
18
|
+
if (current) {
|
|
19
|
+
args.push(current);
|
|
20
|
+
current = "";
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
current += char;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (current) {
|
|
28
|
+
args.push(current);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Substitute argument placeholders in template content
|
|
36
|
+
* Supports $1, $2, ... for positional args, $@ and $ARGUMENTS for all args
|
|
37
|
+
*
|
|
38
|
+
* Note: Replacement happens on the template string only. Argument values
|
|
39
|
+
* containing patterns like $1, $@, or $ARGUMENTS are NOT recursively substituted.
|
|
40
|
+
*/
|
|
41
|
+
export function substituteArgs(content: string, args: string[]): string {
|
|
42
|
+
let result = content;
|
|
43
|
+
|
|
44
|
+
// Replace $1, $2, etc. with positional args FIRST (before wildcards)
|
|
45
|
+
// This prevents wildcard replacement values containing $<digit> patterns from being re-substituted
|
|
46
|
+
result = result.replace(/\$(\d+)/g, (_, num) => {
|
|
47
|
+
const index = parseInt(num, 10) - 1;
|
|
48
|
+
return args[index] ?? "";
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
result = result.replace(/\$@\[(\d+)(?::(\d*)?)?\]/g, (_, startRaw: string, lengthRaw?: string) => {
|
|
52
|
+
const start = Number.parseInt(startRaw, 10);
|
|
53
|
+
if (!Number.isFinite(start) || start < 1) return "";
|
|
54
|
+
const startIndex = start - 1;
|
|
55
|
+
if (startIndex >= args.length) return "";
|
|
56
|
+
|
|
57
|
+
if (lengthRaw === undefined || lengthRaw === "") {
|
|
58
|
+
return args.slice(startIndex).join(" ");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const length = Number.parseInt(lengthRaw, 10);
|
|
62
|
+
if (!Number.isFinite(length) || length <= 0) return "";
|
|
63
|
+
return args.slice(startIndex, startIndex + length).join(" ");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Pre-compute all args joined (optimization)
|
|
67
|
+
const allArgs = args.join(" ");
|
|
68
|
+
|
|
69
|
+
// Replace $ARGUMENTS with all args joined (new syntax, aligns with Claude, Codex, OpenCode)
|
|
70
|
+
result = result.replaceAll("$ARGUMENTS", allArgs);
|
|
71
|
+
|
|
72
|
+
// Replace $@ with all args joined (existing syntax)
|
|
73
|
+
result = result.replaceAll("$@", allArgs);
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
@@ -11,9 +11,14 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
|
11
11
|
import { glob } from "@oh-my-pi/pi-natives";
|
|
12
12
|
import { formatHashLines } from "../patch/hashline";
|
|
13
13
|
import type { FileMentionMessage } from "../session/messages";
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_MAX_BYTES,
|
|
16
|
+
formatHeadTruncationNotice,
|
|
17
|
+
truncateHead,
|
|
18
|
+
truncateHeadBytes,
|
|
19
|
+
} from "../session/streaming-output";
|
|
14
20
|
import { resolveReadPath } from "../tools/path-utils";
|
|
15
|
-
import { formatAge } from "../tools/render-utils";
|
|
16
|
-
import { DEFAULT_MAX_BYTES, formatSize, truncateHead, truncateStringToBytesFromStart } from "../tools/truncate";
|
|
21
|
+
import { formatAge, formatBytes } from "../tools/render-utils";
|
|
17
22
|
import { fuzzyMatch } from "./fuzzy";
|
|
18
23
|
import { formatDimensionNote, resizeImage } from "./image-resize";
|
|
19
24
|
import { detectSupportedImageMimeTypeFromFile } from "./mime";
|
|
@@ -162,15 +167,15 @@ function buildTextOutput(textContent: string): { output: string; lineCount: numb
|
|
|
162
167
|
if (truncation.firstLineExceedsLimit) {
|
|
163
168
|
const firstLine = allLines[0] ?? "";
|
|
164
169
|
const firstLineBytes = Buffer.byteLength(firstLine, "utf-8");
|
|
165
|
-
const snippet =
|
|
170
|
+
const snippet = truncateHeadBytes(firstLine, DEFAULT_MAX_BYTES);
|
|
166
171
|
let outputText = snippet.text;
|
|
167
172
|
|
|
168
173
|
if (outputText.length > 0) {
|
|
169
|
-
outputText += `\n\n[Line 1 is ${
|
|
174
|
+
outputText += `\n\n[Line 1 is ${formatBytes(firstLineBytes)}, exceeds ${formatBytes(
|
|
170
175
|
DEFAULT_MAX_BYTES,
|
|
171
|
-
)} limit. Showing first ${
|
|
176
|
+
)} limit. Showing first ${formatBytes(snippet.bytes)} of the line.]`;
|
|
172
177
|
} else {
|
|
173
|
-
outputText = `[Line 1 is ${
|
|
178
|
+
outputText = `[Line 1 is ${formatBytes(firstLineBytes)}, exceeds ${formatBytes(
|
|
174
179
|
DEFAULT_MAX_BYTES,
|
|
175
180
|
)} limit. Unable to display a valid UTF-8 snippet.]`;
|
|
176
181
|
}
|
|
@@ -181,16 +186,7 @@ function buildTextOutput(textContent: string): { output: string; lineCount: numb
|
|
|
181
186
|
let outputText = truncation.content;
|
|
182
187
|
|
|
183
188
|
if (truncation.truncated) {
|
|
184
|
-
|
|
185
|
-
const nextOffset = endLineDisplay + 1;
|
|
186
|
-
|
|
187
|
-
if (truncation.truncatedBy === "lines") {
|
|
188
|
-
outputText += `\n\n[Showing lines 1-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue]`;
|
|
189
|
-
} else {
|
|
190
|
-
outputText += `\n\n[Showing lines 1-${endLineDisplay} of ${totalFileLines} (${formatSize(
|
|
191
|
-
DEFAULT_MAX_BYTES,
|
|
192
|
-
)} limit). Use offset=${nextOffset} to continue]`;
|
|
193
|
-
}
|
|
189
|
+
outputText += formatHeadTruncationNotice(truncation, { startLine: 1, totalFileLines });
|
|
194
190
|
}
|
|
195
191
|
|
|
196
192
|
return { output: outputText, lineCount: totalFileLines };
|
|
@@ -247,7 +243,7 @@ async function buildDirectoryListing(absolutePath: string): Promise<{ output: st
|
|
|
247
243
|
notices.push(`${DEFAULT_DIR_LIMIT} entries limit reached. Use limit=${DEFAULT_DIR_LIMIT * 2} for more`);
|
|
248
244
|
}
|
|
249
245
|
if (truncation.truncated) {
|
|
250
|
-
notices.push(`${
|
|
246
|
+
notices.push(`${formatBytes(DEFAULT_MAX_BYTES)} limit reached`);
|
|
251
247
|
}
|
|
252
248
|
if (notices.length > 0) {
|
|
253
249
|
output += `\n\n[${notices.join(". ")}]`;
|
|
@@ -313,7 +309,7 @@ export async function generateFileMentionMessages(
|
|
|
313
309
|
if (stat.size > MAX_AUTO_READ_IMAGE_BYTES) {
|
|
314
310
|
files.push({
|
|
315
311
|
path: resolvedPath,
|
|
316
|
-
content: `(skipped auto-read: too large, ${
|
|
312
|
+
content: `(skipped auto-read: too large, ${formatBytes(stat.size)})`,
|
|
317
313
|
byteSize: stat.size,
|
|
318
314
|
skippedReason: "tooLarge",
|
|
319
315
|
});
|
|
@@ -349,7 +345,7 @@ export async function generateFileMentionMessages(
|
|
|
349
345
|
if (stat.size > MAX_AUTO_READ_TEXT_BYTES) {
|
|
350
346
|
files.push({
|
|
351
347
|
path: resolvedPath,
|
|
352
|
-
content: `(skipped auto-read: too large, ${
|
|
348
|
+
content: `(skipped auto-read: too large, ${formatBytes(stat.size)})`,
|
|
353
349
|
byteSize: stat.size,
|
|
354
350
|
skippedReason: "tooLarge",
|
|
355
351
|
});
|