@oh-my-pi/pi-coding-agent 16.1.14 → 16.1.16
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 +69 -0
- package/dist/cli.js +3705 -4015
- package/dist/types/advisor/runtime.d.ts +3 -0
- package/dist/types/cli/args.d.ts +2 -5
- package/dist/types/cli/flag-tables.d.ts +2 -2
- package/dist/types/cli/session-picker.d.ts +4 -2
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +32 -1
- package/dist/types/eval/agent-bridge.d.ts +19 -0
- package/dist/types/eval/js/shared/helpers.d.ts +1 -13
- package/dist/types/eval/js/shared/types.d.ts +1 -1
- package/dist/types/eval/js/worker-protocol.d.ts +1 -1
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/export/share.d.ts +8 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -1
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/transports/stdio.d.ts +12 -1
- package/dist/types/modes/components/plugin-settings.d.ts +5 -0
- package/dist/types/modes/components/session-selector.d.ts +25 -0
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +4 -3
- package/dist/types/modes/components/status-line/types.d.ts +1 -0
- package/dist/types/secrets/obfuscator.d.ts +3 -3
- package/dist/types/task/isolation-runner.d.ts +128 -0
- package/dist/types/task/worktree.d.ts +14 -1
- package/dist/types/thinking.d.ts +15 -0
- package/dist/types/tools/eval-render.d.ts +3 -0
- package/dist/types/tools/eval.d.ts +11 -17
- package/dist/types/tools/todo.d.ts +26 -28
- package/dist/types/tui/output-block.d.ts +8 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/shell-snapshot.d.ts +10 -0
- package/dist/types/web/search/providers/exa.d.ts +2 -0
- package/dist/types/web/search/providers/perplexity.d.ts +17 -3
- package/package.json +12 -12
- package/scripts/build-binary.ts +18 -4
- package/src/advisor/__tests__/advisor.test.ts +114 -0
- package/src/advisor/runtime.ts +129 -1
- package/src/cli/args.ts +4 -5
- package/src/cli/flag-tables.ts +3 -3
- package/src/cli/gallery-fixtures/interaction.ts +6 -9
- package/src/cli/gallery-fixtures/shell.ts +15 -23
- package/src/cli/session-picker.ts +17 -3
- package/src/commands/launch.ts +3 -3
- package/src/config/model-registry.ts +12 -4
- package/src/config/settings-schema.ts +37 -1
- package/src/edit/renderer.ts +34 -12
- package/src/eval/__tests__/agent-bridge.test.ts +462 -3
- package/src/eval/__tests__/helpers-local-roots.test.ts +2 -5
- package/src/eval/__tests__/julia-prelude.test.ts +1 -30
- package/src/eval/__tests__/prelude-agent.test.ts +42 -8
- package/src/eval/agent-bridge.ts +301 -71
- package/src/eval/jl/prelude.jl +32 -227
- package/src/eval/jl/runner.jl +38 -12
- package/src/eval/js/shared/helpers.ts +1 -114
- package/src/eval/js/shared/prelude.txt +13 -27
- package/src/eval/js/shared/runtime.ts +0 -6
- package/src/eval/js/shared/types.ts +1 -1
- package/src/eval/js/worker-protocol.ts +1 -1
- package/src/eval/py/__tests__/prelude.test.ts +13 -0
- package/src/eval/py/executor.ts +1 -1
- package/src/eval/py/prelude.py +47 -105
- package/src/eval/py/runner.py +0 -6
- package/src/eval/rb/prelude.rb +21 -189
- package/src/eval/rb/runner.rb +116 -9
- package/src/exec/bash-executor.ts +44 -0
- package/src/export/html/tool-views.generated.js +29 -29
- package/src/export/share.ts +51 -28
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/local-protocol.ts +100 -53
- package/src/main.ts +15 -4
- package/src/mcp/transports/stdio.ts +20 -4
- package/src/modes/acp/acp-event-mapper.ts +7 -2
- package/src/modes/components/custom-editor.test.ts +22 -0
- package/src/modes/components/custom-editor.ts +10 -1
- package/src/modes/components/footer.ts +4 -3
- package/src/modes/components/plugin-settings.ts +7 -1
- package/src/modes/components/session-selector.ts +143 -29
- package/src/modes/components/status-line/component.ts +5 -1
- package/src/modes/components/status-line/context-thresholds.ts +11 -3
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/controllers/command-controller.ts +6 -0
- package/src/modes/rpc/rpc-mode.ts +6 -0
- package/src/modes/utils/copy-targets.ts +7 -2
- package/src/prompts/system/system-prompt.md +6 -6
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/bash.md +18 -2
- package/src/prompts/tools/eval.md +19 -19
- package/src/prompts/tools/search.md +1 -0
- package/src/prompts/tools/todo.md +1 -1
- package/src/secrets/obfuscator.ts +3 -9
- package/src/session/agent-session.ts +264 -52
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/task/index.ts +61 -207
- package/src/task/isolation-runner.ts +354 -0
- package/src/task/worktree.ts +46 -9
- package/src/thinking.ts +20 -0
- package/src/tools/ask.ts +44 -38
- package/src/tools/bash.ts +9 -2
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/eval-render.ts +34 -27
- package/src/tools/eval.ts +100 -103
- package/src/tools/index.ts +8 -1
- package/src/tools/read.ts +136 -60
- package/src/tools/todo.ts +60 -64
- package/src/tui/code-cell.ts +1 -1
- package/src/tui/output-block.ts +11 -0
- package/src/utils/image-resize.ts +30 -0
- package/src/utils/shell-snapshot.ts +63 -1
- package/src/web/search/providers/exa.ts +85 -1
- package/src/web/search/providers/perplexity.ts +18 -6
|
@@ -5,17 +5,14 @@ export const interactionFixtures: Record<string, GalleryFixture> = {
|
|
|
5
5
|
todo: {
|
|
6
6
|
label: "Todo",
|
|
7
7
|
streamingArgs: {
|
|
8
|
-
|
|
8
|
+
op: "init",
|
|
9
|
+
list: [{ phase: "Foundation", items: ["Scaffold crate"] }],
|
|
9
10
|
},
|
|
10
11
|
args: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{ phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
|
|
16
|
-
{ phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
|
|
17
|
-
],
|
|
18
|
-
},
|
|
12
|
+
op: "init",
|
|
13
|
+
list: [
|
|
14
|
+
{ phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
|
|
15
|
+
{ phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
|
|
19
16
|
],
|
|
20
17
|
},
|
|
21
18
|
result: {
|
|
@@ -59,31 +59,23 @@ export const shellFixtures: Record<string, GalleryFixture> = {
|
|
|
59
59
|
eval: {
|
|
60
60
|
label: "Eval",
|
|
61
61
|
streamingArgs: {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
code: 'import json\nfrom pathlib import Path\n\ndata = json.loads(Path("package.js',
|
|
66
|
-
title: "load config",
|
|
67
|
-
},
|
|
68
|
-
],
|
|
62
|
+
language: "py",
|
|
63
|
+
code: 'import json\nfrom pathlib import Path\n\ndata = json.loads(Path("package.js',
|
|
64
|
+
title: "load config",
|
|
69
65
|
},
|
|
70
66
|
args: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"display(sorted(deps)[:3])",
|
|
84
|
-
].join("\n"),
|
|
85
|
-
},
|
|
86
|
-
],
|
|
67
|
+
language: "py",
|
|
68
|
+
title: "load config",
|
|
69
|
+
code: [
|
|
70
|
+
"import json",
|
|
71
|
+
"from pathlib import Path",
|
|
72
|
+
"",
|
|
73
|
+
'data = json.loads(Path("package.json").read_text())',
|
|
74
|
+
'deps = data.get("dependencies", {})',
|
|
75
|
+
'print(f"{data[\\"name\\"]} v{data[\\"version\\"]}")',
|
|
76
|
+
'print(f"{len(deps)} dependencies")',
|
|
77
|
+
"display(sorted(deps)[:3])",
|
|
78
|
+
].join("\n"),
|
|
87
79
|
},
|
|
88
80
|
result: {
|
|
89
81
|
content: [
|
|
@@ -8,8 +8,10 @@ import { FileSessionStorage } from "../session/session-storage";
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Show the TUI session selector and return the selected session, or null if
|
|
11
|
-
* cancelled.
|
|
12
|
-
*
|
|
11
|
+
* cancelled. Rendered as a fullscreen overlay on the terminal's alternate
|
|
12
|
+
* screen, so the list scrolls and rows are clickable with the mouse. Tab
|
|
13
|
+
* toggles between current-folder and all-projects scope; the all-projects list
|
|
14
|
+
* is loaded lazily via `SessionManager.listAll`.
|
|
13
15
|
*/
|
|
14
16
|
export async function selectSession(
|
|
15
17
|
sessions: SessionInfo[],
|
|
@@ -65,6 +67,7 @@ export async function selectSession(
|
|
|
65
67
|
loadAllSessions: () => SessionManager.listAll(storage),
|
|
66
68
|
allSessions: options?.allSessions,
|
|
67
69
|
getTerminalRows: () => ui.terminal.rows,
|
|
70
|
+
fillHeight: true,
|
|
68
71
|
},
|
|
69
72
|
);
|
|
70
73
|
return selector;
|
|
@@ -72,7 +75,18 @@ export async function selectSession(
|
|
|
72
75
|
|
|
73
76
|
const selector = showSelector();
|
|
74
77
|
selector.setOnRequestRender(() => ui.requestRender());
|
|
75
|
-
|
|
78
|
+
// Present as a fullscreen overlay so the picker borrows the terminal's
|
|
79
|
+
// alternate screen buffer (vim/less idiom): the list scrolls and rows are
|
|
80
|
+
// clickable via the mouse tracking the overlay enables for its lifetime.
|
|
81
|
+
// Anchored top-left at full size so a mouse row maps directly to a rendered
|
|
82
|
+
// line (the overlay paints from screen row 0).
|
|
83
|
+
ui.showOverlay(selector, {
|
|
84
|
+
anchor: "top-left",
|
|
85
|
+
width: "100%",
|
|
86
|
+
maxHeight: "100%",
|
|
87
|
+
margin: 0,
|
|
88
|
+
fullscreen: true,
|
|
89
|
+
});
|
|
76
90
|
ui.setFocus(selector);
|
|
77
91
|
ui.start();
|
|
78
92
|
return promise;
|
package/src/commands/launch.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Root command for the coding agent CLI.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { THINKING_EFFORTS } from "@oh-my-pi/pi-catalog/effort";
|
|
6
5
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
7
6
|
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
8
7
|
import { parseArgs } from "../cli/args";
|
|
9
8
|
import { runRootCommand } from "../main";
|
|
10
9
|
import { prepareAcpTerminalAuthArgs } from "../modes/acp/terminal-auth";
|
|
10
|
+
import { CLI_THINKING_LEVELS } from "../thinking";
|
|
11
11
|
|
|
12
12
|
export default class Index extends Command {
|
|
13
13
|
static description = "AI coding assistant";
|
|
@@ -100,8 +100,8 @@ export default class Index extends Command {
|
|
|
100
100
|
description: "Comma-separated list of tools to enable (default: all)",
|
|
101
101
|
}),
|
|
102
102
|
thinking: Flags.string({
|
|
103
|
-
description: `Set thinking level: ${
|
|
104
|
-
options: [...
|
|
103
|
+
description: `Set thinking level: ${CLI_THINKING_LEVELS.join(", ")}`,
|
|
104
|
+
options: [...CLI_THINKING_LEVELS],
|
|
105
105
|
}),
|
|
106
106
|
"hide-thinking": Flags.boolean({
|
|
107
107
|
description: "Hide thinking blocks in TUI output (display only, does not disable model thinking)",
|
|
@@ -992,6 +992,7 @@ export class ModelRegistry {
|
|
|
992
992
|
});
|
|
993
993
|
continue;
|
|
994
994
|
}
|
|
995
|
+
const configStale = this.#isDiscoveryCacheOlderThanModelsConfig(cache.updatedAt);
|
|
995
996
|
const models = this.#applyProviderModelOverrides(
|
|
996
997
|
providerConfig.provider,
|
|
997
998
|
this.#normalizeDiscoverableModels(
|
|
@@ -1007,7 +1008,7 @@ export class ModelRegistry {
|
|
|
1007
1008
|
provider: providerConfig.provider,
|
|
1008
1009
|
status: "cached",
|
|
1009
1010
|
optional: providerConfig.optional ?? false,
|
|
1010
|
-
stale: !cache.fresh || !cache.authoritative,
|
|
1011
|
+
stale: !cache.fresh || !cache.authoritative || configStale,
|
|
1011
1012
|
fetchedAt: cache.updatedAt,
|
|
1012
1013
|
models: models.map(model => model.id),
|
|
1013
1014
|
});
|
|
@@ -1259,12 +1260,19 @@ export class ModelRegistry {
|
|
|
1259
1260
|
return providerConfig.provider;
|
|
1260
1261
|
}
|
|
1261
1262
|
|
|
1263
|
+
#isDiscoveryCacheOlderThanModelsConfig(cacheUpdatedAt: number): boolean {
|
|
1264
|
+
const configMtime = this.#modelsConfigFile.getMtimeMs();
|
|
1265
|
+
return configMtime !== null && cacheUpdatedAt < Math.floor(configMtime);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1262
1268
|
async #discoverProviderModels(
|
|
1263
1269
|
providerConfig: DiscoveryProviderConfig,
|
|
1264
1270
|
strategy: ModelRefreshStrategy,
|
|
1265
1271
|
): Promise<Model<Api>[]> {
|
|
1266
1272
|
const cacheProviderId = this.#configuredDiscoveryCacheProviderId(providerConfig);
|
|
1267
1273
|
const cached = readModelCache<Api>(cacheProviderId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
|
|
1274
|
+
const cacheOlderThanConfig = cached !== null && this.#isDiscoveryCacheOlderThanModelsConfig(cached.updatedAt);
|
|
1275
|
+
const effectiveStrategy = strategy === "online-if-uncached" && cacheOlderThanConfig ? "online" : strategy;
|
|
1268
1276
|
const requiresAuth = !this.#keylessProviders.has(providerConfig.provider);
|
|
1269
1277
|
if (requiresAuth) {
|
|
1270
1278
|
const apiKey = await this.#peekApiKeyForProvider(providerConfig.provider);
|
|
@@ -1311,12 +1319,12 @@ export class ModelRegistry {
|
|
|
1311
1319
|
cacheTtlMs: 24 * 60 * 60 * 1000,
|
|
1312
1320
|
fetchDynamicModels,
|
|
1313
1321
|
});
|
|
1314
|
-
const result = await manager.refresh(
|
|
1322
|
+
const result = await manager.refresh(effectiveStrategy);
|
|
1315
1323
|
const status = discoveryError
|
|
1316
1324
|
? result.models.length > 0
|
|
1317
1325
|
? "cached"
|
|
1318
1326
|
: "unavailable"
|
|
1319
|
-
:
|
|
1327
|
+
: effectiveStrategy === "offline"
|
|
1320
1328
|
? cached
|
|
1321
1329
|
? "cached"
|
|
1322
1330
|
: "idle"
|
|
@@ -1327,7 +1335,7 @@ export class ModelRegistry {
|
|
|
1327
1335
|
provider: providerId,
|
|
1328
1336
|
status,
|
|
1329
1337
|
optional: providerConfig.optional ?? false,
|
|
1330
|
-
stale: result.stale || status === "cached",
|
|
1338
|
+
stale: result.stale || status === "cached" || (cacheOlderThanConfig && status !== "ok"),
|
|
1331
1339
|
fetchedAt: discoveryError ? cached?.updatedAt : Date.now(),
|
|
1332
1340
|
models: result.models.map(model => model.id),
|
|
1333
1341
|
error: discoveryError,
|
|
@@ -1592,6 +1592,30 @@ export const SETTINGS_SCHEMA = {
|
|
|
1592
1592
|
},
|
|
1593
1593
|
},
|
|
1594
1594
|
|
|
1595
|
+
"share.store": {
|
|
1596
|
+
type: "enum",
|
|
1597
|
+
values: ["blob", "gist"] as const,
|
|
1598
|
+
default: "blob",
|
|
1599
|
+
ui: {
|
|
1600
|
+
tab: "interaction",
|
|
1601
|
+
group: "Collab",
|
|
1602
|
+
label: "Share Store",
|
|
1603
|
+
description: "Where /share uploads the encrypted session blob",
|
|
1604
|
+
options: [
|
|
1605
|
+
{
|
|
1606
|
+
value: "blob",
|
|
1607
|
+
label: "Encrypted Blob",
|
|
1608
|
+
description: "Upload to the share server (no GitHub account needed; avoids gist API rate limits)",
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
value: "gist",
|
|
1612
|
+
label: "GitHub Gist",
|
|
1613
|
+
description: "Push to a secret gist (needs authenticated gh), falling back to the share server",
|
|
1614
|
+
},
|
|
1615
|
+
],
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
|
|
1595
1619
|
"share.redactSecrets": {
|
|
1596
1620
|
type: "boolean",
|
|
1597
1621
|
default: true,
|
|
@@ -3548,7 +3572,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
3548
3572
|
group: "Discovery & MCP",
|
|
3549
3573
|
label: "Essential Tools Override",
|
|
3550
3574
|
description:
|
|
3551
|
-
"Override the always-loaded built-in tools (default: read, bash, edit). Leave empty to use defaults.",
|
|
3575
|
+
"Override the always-loaded built-in tools (default: read, bash, edit, write, find, eval). Leave empty to use defaults.",
|
|
3552
3576
|
},
|
|
3553
3577
|
},
|
|
3554
3578
|
|
|
@@ -4455,6 +4479,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
4455
4479
|
},
|
|
4456
4480
|
},
|
|
4457
4481
|
|
|
4482
|
+
"exa.searchDelayMs": {
|
|
4483
|
+
type: "number",
|
|
4484
|
+
default: 1_000,
|
|
4485
|
+
ui: {
|
|
4486
|
+
tab: "providers",
|
|
4487
|
+
group: "Services",
|
|
4488
|
+
label: "Exa Search Delay",
|
|
4489
|
+
description: "Minimum delay between Exa web search requests in milliseconds; set 0 to disable pacing",
|
|
4490
|
+
},
|
|
4491
|
+
},
|
|
4492
|
+
|
|
4458
4493
|
"exa.enableResearcher": {
|
|
4459
4494
|
type: "boolean",
|
|
4460
4495
|
default: false,
|
|
@@ -4761,6 +4796,7 @@ export interface TtsrSettings {
|
|
|
4761
4796
|
export interface ExaSettings {
|
|
4762
4797
|
enabled: boolean;
|
|
4763
4798
|
enableSearch: boolean;
|
|
4799
|
+
searchDelayMs: number;
|
|
4764
4800
|
enableResearcher: boolean;
|
|
4765
4801
|
enableWebsets: boolean;
|
|
4766
4802
|
}
|
package/src/edit/renderer.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
invalidateRenderedStringCache,
|
|
23
23
|
type LspBatchRequest,
|
|
24
24
|
PREVIEW_LIMITS,
|
|
25
|
+
previewWindowRows,
|
|
25
26
|
type RenderedStringCache,
|
|
26
27
|
replaceTabs,
|
|
27
28
|
shortenPath,
|
|
@@ -340,6 +341,7 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, _filePath?: string
|
|
|
340
341
|
function formatStreamingDiff(
|
|
341
342
|
diff: string,
|
|
342
343
|
rawPath: string,
|
|
344
|
+
width: number,
|
|
343
345
|
uiTheme: Theme,
|
|
344
346
|
expanded: boolean,
|
|
345
347
|
label = "streaming",
|
|
@@ -347,15 +349,32 @@ function formatStreamingDiff(
|
|
|
347
349
|
cache?: RenderedStringCache,
|
|
348
350
|
): string {
|
|
349
351
|
if (!diff) return "";
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
352
|
+
// Clamp the collapsed tail to the viewport so a tall or fast-growing diff
|
|
353
|
+
// cannot outgrow the live window. Otherwise its mutating tail scrolls above
|
|
354
|
+
// the native-scrollback commit boundary and the engine re-commits a fresh
|
|
355
|
+
// snapshot every streamed frame, stacking duplicate "… more lines above"
|
|
356
|
+
// previews in history. The budget is VISUAL rows (a long wrapped line counts
|
|
357
|
+
// for more than one) at the framed block's inner width (border only —
|
|
358
|
+
// contentPaddingLeft is 0); only the visible suffix is syntax-colored, so the
|
|
359
|
+
// cheap raw-line wrap walk keeps the per-chunk cost bounded. innerWidth/budget
|
|
360
|
+
// are in the cache salt so a resize re-slices.
|
|
361
|
+
const innerWidth = Math.max(1, width - 2);
|
|
362
|
+
const budget = expanded ? Number.POSITIVE_INFINITY : Math.min(EDIT_STREAMING_PREVIEW_LINES, previewWindowRows());
|
|
363
|
+
let text = cachedRenderedString(cache, uiTheme, expanded, `${rawPath}:${innerWidth}:${budget}`, diff, () => {
|
|
364
|
+
// "Cursor" tail window: pin the last rows to the bottom so freshly streamed
|
|
365
|
+
// changes stay on screen. The whole-file diff is recomputed every chunk and
|
|
366
|
+
// its Myers alignment is not monotonic in payload length, so a hunk-aware
|
|
367
|
+
// window stutters as rows move between hunks. Expanded lifts the cap.
|
|
357
368
|
const allLines = diff.replace(/\n+$/u, "").split("\n");
|
|
358
|
-
|
|
369
|
+
let visualUsed = 0;
|
|
370
|
+
let cut = allLines.length;
|
|
371
|
+
for (let i = allLines.length - 1; i >= 0; i--) {
|
|
372
|
+
const lineRows = Math.max(1, wrapTextWithAnsi(replaceTabs(allLines[i]!), innerWidth).length);
|
|
373
|
+
if (visualUsed + lineRows > budget && visualUsed > 0) break;
|
|
374
|
+
visualUsed += lineRows;
|
|
375
|
+
cut = i;
|
|
376
|
+
}
|
|
377
|
+
const hiddenLines = cut;
|
|
359
378
|
const visible = hiddenLines > 0 ? allLines.slice(hiddenLines) : allLines;
|
|
360
379
|
let rendered = "\n\n";
|
|
361
380
|
if (hiddenLines > 0) {
|
|
@@ -384,6 +403,7 @@ function formatStreamingDiff(
|
|
|
384
403
|
|
|
385
404
|
function formatMultiFileStreamingDiff(
|
|
386
405
|
previews: PerFileDiffPreview[],
|
|
406
|
+
width: number,
|
|
387
407
|
uiTheme: Theme,
|
|
388
408
|
expanded: boolean,
|
|
389
409
|
spinnerFrame?: number,
|
|
@@ -405,7 +425,7 @@ function formatMultiFileStreamingDiff(
|
|
|
405
425
|
const isLast = index === previews.length - 1;
|
|
406
426
|
const cache = previewCacheAt(caches, index);
|
|
407
427
|
parts.push(
|
|
408
|
-
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
428
|
+
`${header}${formatStreamingDiff(preview.diff, preview.path, width, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
409
429
|
);
|
|
410
430
|
}
|
|
411
431
|
}
|
|
@@ -415,6 +435,7 @@ function formatMultiFileStreamingDiff(
|
|
|
415
435
|
function getCallPreview(
|
|
416
436
|
args: EditRenderArgs,
|
|
417
437
|
rawPath: string,
|
|
438
|
+
width: number,
|
|
418
439
|
uiTheme: Theme,
|
|
419
440
|
renderContext: EditRenderContext | undefined,
|
|
420
441
|
expanded: boolean,
|
|
@@ -423,14 +444,14 @@ function getCallPreview(
|
|
|
423
444
|
): string {
|
|
424
445
|
const multi = renderContext?.perFileDiffPreview;
|
|
425
446
|
if (multi && multi.length > 1 && multi.some(p => p.diff || p.error)) {
|
|
426
|
-
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame, caches);
|
|
447
|
+
return formatMultiFileStreamingDiff(multi, width, uiTheme, expanded, spinnerFrame, caches);
|
|
427
448
|
}
|
|
428
449
|
const cache = previewCacheAt(caches, 0);
|
|
429
450
|
if (args.previewDiff) {
|
|
430
|
-
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
451
|
+
return formatStreamingDiff(args.previewDiff, rawPath, width, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
431
452
|
}
|
|
432
453
|
if (args.diff && args.op) {
|
|
433
|
-
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
454
|
+
return formatStreamingDiff(args.diff, rawPath, width, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
434
455
|
}
|
|
435
456
|
if (args.diff) {
|
|
436
457
|
return renderPlainTextPreview(args.diff, uiTheme, rawPath);
|
|
@@ -628,6 +649,7 @@ export const editToolRenderer = {
|
|
|
628
649
|
let body = getCallPreview(
|
|
629
650
|
editArgs,
|
|
630
651
|
rawPath,
|
|
652
|
+
width,
|
|
631
653
|
uiTheme,
|
|
632
654
|
renderContext,
|
|
633
655
|
options.expanded,
|