@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
package/src/tools/ask.ts
CHANGED
|
@@ -16,22 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
19
|
-
import {
|
|
20
|
-
type Component,
|
|
21
|
-
Container,
|
|
22
|
-
Markdown,
|
|
23
|
-
type MarkdownTheme,
|
|
24
|
-
renderInlineMarkdown,
|
|
25
|
-
TERMINAL,
|
|
26
|
-
Text,
|
|
27
|
-
} from "@oh-my-pi/pi-tui";
|
|
19
|
+
import { type Component, Markdown, type MarkdownTheme, renderInlineMarkdown, TERMINAL, Text } from "@oh-my-pi/pi-tui";
|
|
28
20
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
29
21
|
import * as z from "zod/v4";
|
|
30
22
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
31
23
|
import type { ExtensionUISelectItem } from "../extensibility/extensions";
|
|
32
24
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
33
25
|
import askDescription from "../prompts/tools/ask.md" with { type: "text" };
|
|
34
|
-
import { renderStatusLine } from "../tui";
|
|
26
|
+
import { framedBlock, renderStatusLine } from "../tui";
|
|
35
27
|
import type { ToolSession } from ".";
|
|
36
28
|
import { formatErrorMessage, formatMeta, formatTitle } from "./render-utils";
|
|
37
29
|
import { ToolAbortError } from "./tool-errors";
|
|
@@ -626,23 +618,14 @@ interface AskRenderArgs {
|
|
|
626
618
|
}>;
|
|
627
619
|
}
|
|
628
620
|
|
|
629
|
-
/** Render custom
|
|
630
|
-
function
|
|
631
|
-
uiTheme: Theme,
|
|
632
|
-
prefix: string,
|
|
633
|
-
customInput: string,
|
|
634
|
-
isLastEntry: boolean,
|
|
635
|
-
includeLeadingNewline = true,
|
|
636
|
-
): string {
|
|
621
|
+
/** Render a custom free-text answer as a status line plus indented continuation rows. */
|
|
622
|
+
function renderCustomInputLines(uiTheme: Theme, customInput: string): string[] {
|
|
637
623
|
const lines = customInput.split("\n");
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
text += `\n${prefix}${continuationIndent} ${uiTheme.fg("toolOutput", lines[i])}`;
|
|
644
|
-
}
|
|
645
|
-
return text;
|
|
624
|
+
const out: string[] = [
|
|
625
|
+
` ${uiTheme.styledSymbol("status.success", "success")} ${uiTheme.fg("toolOutput", lines[0] ?? "")}`,
|
|
626
|
+
];
|
|
627
|
+
for (let i = 1; i < lines.length; i++) out.push(` ${uiTheme.fg("toolOutput", lines[i])}`);
|
|
628
|
+
return out;
|
|
646
629
|
}
|
|
647
630
|
|
|
648
631
|
/**
|
|
@@ -654,25 +637,38 @@ function optionMarker(uiTheme: Theme, multi: boolean | undefined, selected: bool
|
|
|
654
637
|
return selected ? uiTheme.radio.selected : uiTheme.radio.unselected;
|
|
655
638
|
}
|
|
656
639
|
|
|
640
|
+
/** Render the offered options for a question form as flat marker bullets (no tree guides). */
|
|
641
|
+
function renderQuestionOptionLines(
|
|
642
|
+
uiTheme: Theme,
|
|
643
|
+
mdTheme: MarkdownTheme,
|
|
644
|
+
options: AskRenderOption[],
|
|
645
|
+
multi: boolean | undefined,
|
|
646
|
+
): string[] {
|
|
647
|
+
const out: string[] = [];
|
|
648
|
+
for (const opt of options) {
|
|
649
|
+
const optLabel = renderInlineMarkdown(opt.label, mdTheme, t => uiTheme.fg("muted", t));
|
|
650
|
+
out.push(` ${uiTheme.fg("dim", optionMarker(uiTheme, multi, false))} ${optLabel}`);
|
|
651
|
+
if (opt.description?.trim()) {
|
|
652
|
+
const description = renderInlineMarkdown(opt.description.trim(), mdTheme, t => uiTheme.fg("dim", t));
|
|
653
|
+
out.push(` ${uiTheme.fg("dim", "↳")} ${description}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return out;
|
|
657
|
+
}
|
|
658
|
+
|
|
657
659
|
/**
|
|
658
660
|
* Render the answered option list for a question: every offered option with its
|
|
659
|
-
* selection marker filled in, plus any custom free-text answer.
|
|
660
|
-
*
|
|
661
|
-
* reads in place rather than as a detached summary block.
|
|
662
|
-
*
|
|
663
|
-
* `linePrefix` is the indent that precedes each entry's tree branch — a single
|
|
664
|
-
* leading space for top-level (single-question) entries, or the question's
|
|
665
|
-
* vertical continuation for nested (multi-question) entries.
|
|
661
|
+
* selection marker filled in, plus any custom free-text answer. Flat marker
|
|
662
|
+
* bullets — the frame is the container, so no tree guides are drawn.
|
|
666
663
|
*/
|
|
667
|
-
function
|
|
664
|
+
function renderAnswerOptionLines(
|
|
668
665
|
uiTheme: Theme,
|
|
669
666
|
mdTheme: MarkdownTheme,
|
|
670
|
-
linePrefix: string,
|
|
671
667
|
options: string[] | undefined,
|
|
672
668
|
selectedOptions: string[] | undefined,
|
|
673
669
|
multi: boolean | undefined,
|
|
674
670
|
customInput: string | undefined,
|
|
675
|
-
): string {
|
|
671
|
+
): string[] {
|
|
676
672
|
const selected = new Set(selectedOptions ?? []);
|
|
677
673
|
// Prefer the full recorded option set; fall back to the selected labels when
|
|
678
674
|
// details omit the options array.
|
|
@@ -680,26 +676,21 @@ function renderAnswerOptions(
|
|
|
680
676
|
|
|
681
677
|
// Nothing was chosen (and no custom answer) → a lone cancelled marker.
|
|
682
678
|
if (selected.size === 0 && customInput === undefined) {
|
|
683
|
-
return
|
|
679
|
+
return [` ${uiTheme.styledSymbol("status.warning", "warning")} ${uiTheme.fg("warning", "Cancelled")}`];
|
|
684
680
|
}
|
|
685
681
|
|
|
686
|
-
|
|
687
|
-
for (
|
|
688
|
-
const label = list[i];
|
|
682
|
+
const out: string[] = [];
|
|
683
|
+
for (const label of list) {
|
|
689
684
|
const isSelected = selected.has(label);
|
|
690
|
-
const isLastEntry = i === list.length - 1 && customInput === undefined;
|
|
691
|
-
const branch = isLastEntry ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
692
685
|
const marker = optionMarker(uiTheme, multi, isSelected);
|
|
693
686
|
const markerStyled = isSelected ? uiTheme.fg("success", marker) : uiTheme.fg("dim", marker);
|
|
694
687
|
const labelStyled = renderInlineMarkdown(label, mdTheme, t =>
|
|
695
688
|
isSelected ? uiTheme.fg("toolOutput", t) : uiTheme.fg("muted", t),
|
|
696
689
|
);
|
|
697
|
-
|
|
690
|
+
out.push(` ${markerStyled} ${labelStyled}`);
|
|
698
691
|
}
|
|
699
|
-
if (customInput !== undefined)
|
|
700
|
-
|
|
701
|
-
}
|
|
702
|
-
return text;
|
|
692
|
+
if (customInput !== undefined) out.push(...renderCustomInputLines(uiTheme, customInput));
|
|
693
|
+
return out;
|
|
703
694
|
}
|
|
704
695
|
|
|
705
696
|
export const askToolRenderer = {
|
|
@@ -708,80 +699,58 @@ export const askToolRenderer = {
|
|
|
708
699
|
const label = formatTitle("Ask", uiTheme);
|
|
709
700
|
const mdTheme = getMarkdownTheme();
|
|
710
701
|
const accentStyle = { color: (t: string) => uiTheme.fg("accent", t) };
|
|
702
|
+
const md = (text: string, width: number) =>
|
|
703
|
+
new Markdown(text, 1, 0, mdTheme, accentStyle).render(Math.max(1, width - 3 + 1));
|
|
711
704
|
|
|
712
|
-
// Multi-part questions
|
|
705
|
+
// Multi-part questions: one divider-labelled section per question.
|
|
713
706
|
if (args.questions && args.questions.length > 0) {
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
container.addChild(
|
|
729
|
-
new Text(` ${uiTheme.fg("dim", qBranch)} ${uiTheme.fg("dim", `[${q.id}]`)}${metaStr}`, 0, 0),
|
|
730
|
-
);
|
|
731
|
-
container.addChild(new Markdown(q.question, 3, 0, mdTheme, accentStyle));
|
|
732
|
-
|
|
733
|
-
if (q.options?.length) {
|
|
734
|
-
let optText = "";
|
|
735
|
-
for (let j = 0; j < q.options.length; j++) {
|
|
736
|
-
const opt = q.options[j];
|
|
737
|
-
const isLastOpt = j === q.options.length - 1;
|
|
738
|
-
const optBranch = isLastOpt ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
739
|
-
const optLabel = renderInlineMarkdown(opt.label, mdTheme, t => uiTheme.fg("muted", t));
|
|
740
|
-
optText += `\n ${uiTheme.fg("dim", continuation)} ${uiTheme.fg("dim", optBranch)} ${uiTheme.fg("dim", optionMarker(uiTheme, q.multi, false))} ${optLabel}`;
|
|
741
|
-
if (opt.description?.trim()) {
|
|
742
|
-
const optContinuation = isLastOpt ? " " : uiTheme.tree.vertical;
|
|
743
|
-
const description = renderInlineMarkdown(opt.description.trim(), mdTheme, t =>
|
|
744
|
-
uiTheme.fg("dim", t),
|
|
745
|
-
);
|
|
746
|
-
optText += `\n ${uiTheme.fg("dim", continuation)} ${uiTheme.fg("dim", optContinuation)} ${uiTheme.fg("dim", "↳")} ${description}`;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
container.addChild(new Text(optText, 0, 0));
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return container;
|
|
707
|
+
const questions = args.questions;
|
|
708
|
+
const header = `${label} ${uiTheme.fg("muted", `${questions.length} questions`)}`;
|
|
709
|
+
return framedBlock(uiTheme, width => {
|
|
710
|
+
const sections = questions.map(q => {
|
|
711
|
+
const meta: string[] = [];
|
|
712
|
+
if (q.multi) meta.push("multi");
|
|
713
|
+
if (q.options?.length) meta.push(`options:${q.options.length}`);
|
|
714
|
+
const metaStr = meta.length > 0 ? uiTheme.fg("dim", ` · ${meta.join(" · ")}`) : "";
|
|
715
|
+
const lines = md(q.question, width);
|
|
716
|
+
if (q.options?.length) lines.push(...renderQuestionOptionLines(uiTheme, mdTheme, q.options, q.multi));
|
|
717
|
+
return { label: `${uiTheme.fg("dim", `[${q.id}]`)}${metaStr}`, lines };
|
|
718
|
+
});
|
|
719
|
+
return { header, sections, state: "pending", borderColor: "borderMuted", width };
|
|
720
|
+
});
|
|
753
721
|
}
|
|
754
722
|
|
|
755
723
|
// Single question
|
|
756
724
|
if (!args.question) {
|
|
757
|
-
|
|
725
|
+
const errorLine = formatErrorMessage("No question provided", uiTheme);
|
|
726
|
+
return framedBlock(uiTheme, width => ({
|
|
727
|
+
header: errorLine,
|
|
728
|
+
sections: [],
|
|
729
|
+
state: "error",
|
|
730
|
+
borderColor: "error",
|
|
731
|
+
width,
|
|
732
|
+
}));
|
|
758
733
|
}
|
|
759
734
|
|
|
760
|
-
const
|
|
735
|
+
const question = args.question;
|
|
761
736
|
const meta: string[] = [];
|
|
762
737
|
if (args.multi) meta.push("multi");
|
|
763
738
|
if (args.options?.length) meta.push(`options:${args.options.length}`);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
container.addChild(new Text(optText, 0, 0));
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
return container;
|
|
739
|
+
const header = `${label}${formatMeta(meta, uiTheme)}`;
|
|
740
|
+
const questionOptions = args.options;
|
|
741
|
+
const multi = args.multi;
|
|
742
|
+
return framedBlock(uiTheme, width => {
|
|
743
|
+
const bodyLines = md(question, width);
|
|
744
|
+
if (questionOptions?.length)
|
|
745
|
+
bodyLines.push(...renderQuestionOptionLines(uiTheme, mdTheme, questionOptions, multi));
|
|
746
|
+
return {
|
|
747
|
+
header,
|
|
748
|
+
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
749
|
+
state: "pending",
|
|
750
|
+
borderColor: "borderMuted",
|
|
751
|
+
width,
|
|
752
|
+
};
|
|
753
|
+
});
|
|
785
754
|
},
|
|
786
755
|
|
|
787
756
|
renderResult(
|
|
@@ -792,56 +761,47 @@ export const askToolRenderer = {
|
|
|
792
761
|
const { details } = result;
|
|
793
762
|
const mdTheme = getMarkdownTheme();
|
|
794
763
|
const accentStyle = { color: (t: string) => uiTheme.fg("accent", t) };
|
|
764
|
+
const md = (text: string, width: number) =>
|
|
765
|
+
new Markdown(text, 1, 0, mdTheme, accentStyle).render(Math.max(1, width - 3 + 1));
|
|
795
766
|
|
|
796
767
|
if (!details) {
|
|
797
768
|
const txt = result.content[0];
|
|
798
769
|
const fallback = txt?.type === "text" && txt.text ? txt.text : "";
|
|
799
770
|
const header = renderStatusLine({ icon: "warning", title: "Ask" }, uiTheme);
|
|
800
|
-
|
|
771
|
+
const body = fallback ? `\n${uiTheme.fg("dim", fallback)}` : "";
|
|
772
|
+
return new Text(`${header}${body}`, 0, 0);
|
|
801
773
|
}
|
|
802
774
|
|
|
803
|
-
// Multi-part results
|
|
775
|
+
// Multi-part results: one divider-labelled section per question.
|
|
804
776
|
if (details.results && details.results.length > 0) {
|
|
805
|
-
const
|
|
777
|
+
const results = details.results;
|
|
778
|
+
const hasAnySelection = results.some(
|
|
806
779
|
r => r.customInput !== undefined || (r.selectedOptions && r.selectedOptions.length > 0),
|
|
807
780
|
);
|
|
808
781
|
const header = renderStatusLine(
|
|
809
782
|
{
|
|
810
783
|
icon: hasAnySelection ? "success" : "warning",
|
|
811
784
|
title: "Ask",
|
|
812
|
-
meta: [`${
|
|
785
|
+
meta: [`${results.length} questions`],
|
|
813
786
|
},
|
|
814
787
|
uiTheme,
|
|
815
788
|
);
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
mdTheme,
|
|
833
|
-
linePrefix,
|
|
834
|
-
r.options,
|
|
835
|
-
r.selectedOptions,
|
|
836
|
-
r.multi,
|
|
837
|
-
r.customInput,
|
|
838
|
-
),
|
|
839
|
-
0,
|
|
840
|
-
0,
|
|
841
|
-
),
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
return container;
|
|
789
|
+
return framedBlock(uiTheme, width => {
|
|
790
|
+
const sections = results.map(r => {
|
|
791
|
+
const lines = md(r.question, width);
|
|
792
|
+
lines.push(
|
|
793
|
+
...renderAnswerOptionLines(uiTheme, mdTheme, r.options, r.selectedOptions, r.multi, r.customInput),
|
|
794
|
+
);
|
|
795
|
+
return { label: uiTheme.fg("dim", `[${r.id}]`), lines };
|
|
796
|
+
});
|
|
797
|
+
return {
|
|
798
|
+
header,
|
|
799
|
+
sections,
|
|
800
|
+
state: hasAnySelection ? "success" : "warning",
|
|
801
|
+
borderColor: "borderMuted",
|
|
802
|
+
width,
|
|
803
|
+
};
|
|
804
|
+
});
|
|
845
805
|
}
|
|
846
806
|
|
|
847
807
|
// Single question result
|
|
@@ -851,29 +811,24 @@ export const askToolRenderer = {
|
|
|
851
811
|
return new Text(fallback, 0, 0);
|
|
852
812
|
}
|
|
853
813
|
|
|
814
|
+
const question = details.question;
|
|
854
815
|
const hasSelection =
|
|
855
816
|
details.customInput !== undefined || (details.selectedOptions && details.selectedOptions.length > 0);
|
|
856
817
|
const header = renderStatusLine({ icon: hasSelection ? "success" : "warning", title: "Ask" }, uiTheme);
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
0,
|
|
873
|
-
0,
|
|
874
|
-
),
|
|
875
|
-
);
|
|
876
|
-
|
|
877
|
-
return container;
|
|
818
|
+
const dOptions = details.options;
|
|
819
|
+
const dSelected = details.selectedOptions;
|
|
820
|
+
const dMulti = details.multi;
|
|
821
|
+
const dCustom = details.customInput;
|
|
822
|
+
return framedBlock(uiTheme, width => {
|
|
823
|
+
const bodyLines = md(question, width);
|
|
824
|
+
bodyLines.push(...renderAnswerOptionLines(uiTheme, mdTheme, dOptions, dSelected, dMulti, dCustom));
|
|
825
|
+
return {
|
|
826
|
+
header,
|
|
827
|
+
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
828
|
+
state: hasSelection ? "success" : "warning",
|
|
829
|
+
borderColor: "borderMuted",
|
|
830
|
+
width,
|
|
831
|
+
};
|
|
832
|
+
});
|
|
878
833
|
},
|
|
879
834
|
};
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { formatHashlineHeader } from "@oh-my-pi/hashline";
|
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import { type AstReplaceChange, type AstReplaceFileChange, astEdit } from "@oh-my-pi/pi-natives";
|
|
5
5
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
6
|
-
import { Text } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import { replaceTabs, Text } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { $envpos, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import * as z from "zod/v4";
|
|
9
9
|
import { getFileSnapshotStore } from "../edit/file-snapshot-store";
|
|
@@ -11,26 +11,24 @@ import { normalizeToLF } from "../edit/normalize";
|
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import type { Theme } from "../modes/theme/theme";
|
|
13
13
|
import astEditDescription from "../prompts/tools/ast-edit.md" with { type: "text" };
|
|
14
|
-
import { Ellipsis, fileHyperlink,
|
|
14
|
+
import { Ellipsis, fileHyperlink, framedBlock, renderStatusLine, truncateToWidth } from "../tui";
|
|
15
15
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
16
16
|
import type { ToolSession } from ".";
|
|
17
17
|
import { truncateForPrompt } from "./approval";
|
|
18
18
|
import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
19
|
-
import { formatGroupedFiles } from "./grouped-file-output";
|
|
19
|
+
import { classifyGroupedLines, formatGroupedFiles, groupLineIndicesByBlank } from "./grouped-file-output";
|
|
20
20
|
import type { OutputMeta } from "./output-meta";
|
|
21
21
|
import { isInternalUrlPath, resolveToolSearchScope } from "./path-utils";
|
|
22
22
|
import {
|
|
23
23
|
appendParseErrorsBulletList,
|
|
24
24
|
capParseErrors,
|
|
25
|
-
createCachedComponent,
|
|
26
25
|
formatCodeFrameLine,
|
|
27
26
|
formatCount,
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
formatErrorDetail,
|
|
28
|
+
formatMoreItems,
|
|
30
29
|
formatParseErrors,
|
|
31
30
|
formatParseErrorsCountLabel,
|
|
32
31
|
PREVIEW_LIMITS,
|
|
33
|
-
splitGroupsByBlankLine,
|
|
34
32
|
} from "./render-utils";
|
|
35
33
|
import { queueResolveHandler } from "./resolve";
|
|
36
34
|
import { ToolError } from "./tool-errors";
|
|
@@ -161,6 +159,9 @@ export interface AstEditToolDetails {
|
|
|
161
159
|
/** Absolute base directory used during the edit. Used by the renderer to resolve
|
|
162
160
|
* display-relative paths to absolute paths for OSC 8 hyperlinks. */
|
|
163
161
|
searchPath?: string;
|
|
162
|
+
/** Session cwd at edit time. Display header paths are cwd-relative, so the
|
|
163
|
+
* renderer resolves them against this; `searchPath` is the scope target. */
|
|
164
|
+
cwd?: string;
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolDetails> {
|
|
@@ -274,6 +275,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
274
275
|
...(cappedParseErrors.length > 0 ? { parseErrors: cappedParseErrors, parseErrorsTotal } : {}),
|
|
275
276
|
scopePath,
|
|
276
277
|
searchPath: resolvedSearchPath,
|
|
278
|
+
cwd: this.session.cwd,
|
|
277
279
|
files: fileList,
|
|
278
280
|
fileReplacements: [],
|
|
279
281
|
};
|
|
@@ -464,6 +466,32 @@ interface AstEditRenderArgs {
|
|
|
464
466
|
|
|
465
467
|
const COLLAPSED_CHANGE_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
466
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Flatten pre-styled change groups into frame body lines. Groups are separated
|
|
471
|
+
* by a blank line and carry no tree guides — the frame border is the container,
|
|
472
|
+
* so nested `├─ │` gutters would just be noise. Collapsed mode always shows at
|
|
473
|
+
* least the first group, then fills up to `budget` lines before summarizing the
|
|
474
|
+
* rest as `… N more changes`.
|
|
475
|
+
*/
|
|
476
|
+
function buildChangeBody(groups: string[][], expanded: boolean, budget: number, theme: Theme): string[] {
|
|
477
|
+
const lines: string[] = [];
|
|
478
|
+
let shown = 0;
|
|
479
|
+
for (let i = 0; i < groups.length; i++) {
|
|
480
|
+
const group = groups[i]!;
|
|
481
|
+
const separator = shown > 0 ? 1 : 0;
|
|
482
|
+
const remainingAfter = groups.length - (i + 1);
|
|
483
|
+
const reserved = !expanded && remainingAfter > 0 ? 1 : 0;
|
|
484
|
+
// Always emit the first group; budget only gates subsequent ones.
|
|
485
|
+
if (!expanded && shown > 0 && lines.length + separator + group.length + reserved > budget) break;
|
|
486
|
+
if (separator) lines.push("");
|
|
487
|
+
lines.push(...group);
|
|
488
|
+
shown++;
|
|
489
|
+
}
|
|
490
|
+
const remaining = groups.length - shown;
|
|
491
|
+
if (!expanded && remaining > 0) lines.push(theme.fg("muted", formatMoreItems(remaining, "change")));
|
|
492
|
+
return lines;
|
|
493
|
+
}
|
|
494
|
+
|
|
467
495
|
export const astEditToolRenderer = {
|
|
468
496
|
inline: true,
|
|
469
497
|
renderCall(args: AstEditRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
@@ -473,8 +501,9 @@ export const astEditToolRenderer = {
|
|
|
473
501
|
if (rewriteCount > 1) meta.push(`${rewriteCount} rewrites`);
|
|
474
502
|
|
|
475
503
|
const description = rewriteCount === 1 ? args.ops?.[0]?.pat : rewriteCount ? `${rewriteCount} rewrites` : "?";
|
|
476
|
-
const
|
|
477
|
-
|
|
504
|
+
const header = renderStatusLine({ icon: "pending", title: "AST Edit", description, meta }, uiTheme);
|
|
505
|
+
// Pending call has no body yet — a lone status line is sleeker than an empty frame.
|
|
506
|
+
return new Text(header, 0, 0);
|
|
478
507
|
},
|
|
479
508
|
|
|
480
509
|
renderResult(
|
|
@@ -487,7 +516,14 @@ export const astEditToolRenderer = {
|
|
|
487
516
|
|
|
488
517
|
if (result.isError) {
|
|
489
518
|
const errorText = result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
490
|
-
|
|
519
|
+
const header = renderStatusLine({ icon: "error", title: "AST Edit" }, uiTheme);
|
|
520
|
+
return framedBlock(uiTheme, width => ({
|
|
521
|
+
header,
|
|
522
|
+
sections: [{ lines: formatErrorDetail(errorText, uiTheme).split("\n") }],
|
|
523
|
+
state: "error",
|
|
524
|
+
borderColor: "error",
|
|
525
|
+
width,
|
|
526
|
+
}));
|
|
491
527
|
}
|
|
492
528
|
|
|
493
529
|
const totalReplacements = details?.totalReplacements ?? 0;
|
|
@@ -502,9 +538,18 @@ export const astEditToolRenderer = {
|
|
|
502
538
|
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
503
539
|
if (filesSearched > 0) meta.push(`searched ${filesSearched}`);
|
|
504
540
|
const header = renderStatusLine({ icon: "warning", title: "AST Edit", description, meta }, uiTheme);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
541
|
+
// The "0 replacements" count already rides on the status line; only parse
|
|
542
|
+
// errors are worth a body, so frame solely when there are some.
|
|
543
|
+
const bodyLines: string[] = [];
|
|
544
|
+
appendParseErrorsBulletList(bodyLines, details?.parseErrors, uiTheme, details?.parseErrorsTotal);
|
|
545
|
+
if (bodyLines.length === 0) return new Text(header, 0, 0);
|
|
546
|
+
return framedBlock(uiTheme, width => ({
|
|
547
|
+
header,
|
|
548
|
+
sections: [{ lines: bodyLines }],
|
|
549
|
+
state: "warning",
|
|
550
|
+
borderColor: "borderMuted",
|
|
551
|
+
width,
|
|
552
|
+
}));
|
|
508
553
|
}
|
|
509
554
|
|
|
510
555
|
const summaryParts = [formatCount("replacement", totalReplacements), formatCount("file", filesTouched)];
|
|
@@ -516,10 +561,33 @@ export const astEditToolRenderer = {
|
|
|
516
561
|
const description = rewriteCount === 1 ? args?.ops?.[0]?.pat : undefined;
|
|
517
562
|
|
|
518
563
|
const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
);
|
|
564
|
+
const allLines = textContent.split("\n");
|
|
565
|
+
// Resolve hyperlinks over the whole output so nested directory headers
|
|
566
|
+
// reconstruct across the blank-line groups the tree list collapses by.
|
|
567
|
+
const contexts = classifyGroupedLines(allLines, details?.cwd ?? details?.searchPath, details?.searchPath);
|
|
568
|
+
const styledLines = allLines.map((line, index) => {
|
|
569
|
+
const ctx = contexts[index]!;
|
|
570
|
+
// Swap the inner code-frame gutter `│` for a space so it does not nest a
|
|
571
|
+
// second vertical bar inside the frame border.
|
|
572
|
+
const display = replaceTabs(line.replace("│", " "));
|
|
573
|
+
if (ctx.kind === "dir") {
|
|
574
|
+
const styled = uiTheme.fg("accent", display);
|
|
575
|
+
return ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled;
|
|
576
|
+
}
|
|
577
|
+
if (ctx.kind === "file") {
|
|
578
|
+
const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", display);
|
|
579
|
+
return ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled;
|
|
580
|
+
}
|
|
581
|
+
if (display.startsWith("+")) return uiTheme.fg("toolDiffAdded", display);
|
|
582
|
+
if (display.startsWith("-")) return uiTheme.fg("toolDiffRemoved", display);
|
|
583
|
+
return uiTheme.fg("toolOutput", display);
|
|
584
|
+
});
|
|
585
|
+
const changeGroups = groupLineIndicesByBlank(allLines)
|
|
586
|
+
.filter(indices => {
|
|
587
|
+
const first = allLines[indices[0]!]!;
|
|
588
|
+
return !first.startsWith("Safety cap reached") && !first.startsWith("Parse issues:");
|
|
589
|
+
})
|
|
590
|
+
.map(indices => indices.map(index => styledLines[index]!));
|
|
523
591
|
|
|
524
592
|
const badge = { label: "proposed", color: "warning" as const };
|
|
525
593
|
const header = renderStatusLine(
|
|
@@ -536,60 +604,19 @@ export const astEditToolRenderer = {
|
|
|
536
604
|
uiTheme.fg("warning", formatParseErrorsCountLabel(details.parseErrors, details.parseErrorsTotal)),
|
|
537
605
|
);
|
|
538
606
|
}
|
|
539
|
-
return
|
|
540
|
-
(
|
|
541
|
-
width
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
return group.map(line => {
|
|
553
|
-
if (line.startsWith("## ")) {
|
|
554
|
-
// Strip ` (3 replacements)` and `#hash` suffixes from formatGroupedFiles.
|
|
555
|
-
const fileName = line
|
|
556
|
-
.slice(3)
|
|
557
|
-
.trimEnd()
|
|
558
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
559
|
-
.replace(/#[0-9a-f]+$/, "");
|
|
560
|
-
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
561
|
-
const styled = uiTheme.fg("dim", line);
|
|
562
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
563
|
-
}
|
|
564
|
-
if (line.startsWith("# ")) {
|
|
565
|
-
const raw = line
|
|
566
|
-
.slice(2)
|
|
567
|
-
.trimEnd()
|
|
568
|
-
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
569
|
-
const isDirectory = raw.endsWith("/");
|
|
570
|
-
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
571
|
-
if (isDirectory) {
|
|
572
|
-
if (searchBase) {
|
|
573
|
-
contextDir = name === "." ? searchBase : path.join(searchBase, name);
|
|
574
|
-
}
|
|
575
|
-
return uiTheme.fg("accent", line);
|
|
576
|
-
}
|
|
577
|
-
// Root-level file with optional `#hash` and ` (3 replacements)` suffixes.
|
|
578
|
-
const absPath = searchBase && name ? path.join(searchBase, name) : undefined;
|
|
579
|
-
const styled = uiTheme.fg("accent", line);
|
|
580
|
-
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
581
|
-
}
|
|
582
|
-
if (line.startsWith("+")) return uiTheme.fg("toolDiffAdded", line);
|
|
583
|
-
if (line.startsWith("-")) return uiTheme.fg("toolDiffRemoved", line);
|
|
584
|
-
return uiTheme.fg("toolOutput", line);
|
|
585
|
-
});
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
uiTheme,
|
|
589
|
-
);
|
|
590
|
-
return [header, ...changeLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
591
|
-
},
|
|
592
|
-
);
|
|
607
|
+
return framedBlock(uiTheme, width => {
|
|
608
|
+
const changeLines = buildChangeBody(changeGroups, Boolean(options.expanded), COLLAPSED_CHANGE_LIMIT, uiTheme);
|
|
609
|
+
const innerWidth = Math.max(1, width - 3);
|
|
610
|
+
const bodyLines = [...changeLines, ...extraLines].map(l => truncateToWidth(l, innerWidth, Ellipsis.Omit));
|
|
611
|
+
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
612
|
+
return {
|
|
613
|
+
header,
|
|
614
|
+
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
615
|
+
state: options.isPartial ? "pending" : "success",
|
|
616
|
+
borderColor: "borderMuted",
|
|
617
|
+
width,
|
|
618
|
+
};
|
|
619
|
+
});
|
|
593
620
|
},
|
|
594
621
|
mergeCallAndResult: true,
|
|
595
622
|
};
|