@danypops/pi-lector 0.13.11 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import type { EditOutcome } from "@danypops/lector";
2
2
  import { renderDiffLines, renderTruncatedList, type TextMeasure } from "malevich-tui-components";
3
3
  import type { LectorTheme } from "../lector-tui-theme.ts";
4
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
4
5
 
5
6
  /**
6
7
  * The applied result (EditOutcome) only carries a hash transition, not the
@@ -15,7 +16,7 @@ const DEFAULT_VISIBLE_PATCH_LINES = 12;
15
16
 
16
17
  export function formatApplyPatchCall(args: { path?: unknown; patchText?: unknown }, theme: LectorTheme, measure?: TextMeasure): string {
17
18
  const path = typeof args.path === "string" ? args.path : "";
18
- const header = `${theme.fg("toolTitle", theme.bold("apply_patch"))} ${theme.fg("accent", path)}`;
19
+ const header = `${theme.fg("toolTitle", theme.bold(presentationTitle("apply_patch")))} ${theme.fg("accent", path)}`;
19
20
  if (typeof args.patchText !== "string" || args.patchText.length === 0) return header;
20
21
 
21
22
  const styledLines = renderDiffLines(
@@ -14,6 +14,7 @@ import type {
14
14
  import { keyHint, type ThemeColor } from "@earendil-works/pi-coding-agent";
15
15
  import { renderTruncatedList } from "malevich-tui-components";
16
16
  import { colorForKind, formatLocation, type LectorTheme } from "../lector-tui-theme.ts";
17
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
17
18
 
18
19
  /**
19
20
  * Custom TUI rendering for the code-intelligence tools: go_to_definition,
@@ -41,7 +42,7 @@ function formatPositionalCall(toolName: string, args: { path?: unknown; line?: u
41
42
  const path = typeof args.path === "string" ? args.path : "";
42
43
  const line = typeof args.line === "number" ? args.line : "?";
43
44
  const character = typeof args.character === "number" ? args.character : "?";
44
- return `${theme.fg("toolTitle", theme.bold(toolName))} ${theme.fg("accent", `${path}:${line}:${character}`)}`;
45
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle(toolName)))} ${theme.fg("accent", `${path}:${line}:${character}`)}`;
45
46
  }
46
47
 
47
48
  function moreLine(theme: LectorTheme): (hidden: number) => string {
@@ -110,7 +111,7 @@ export function formatHoverResult(hover: Hover | undefined, expanded: boolean, t
110
111
 
111
112
  export function formatDocumentSymbolsCall(args: { path?: unknown }, theme: LectorTheme): string {
112
113
  const path = typeof args.path === "string" ? args.path : "";
113
- return `${theme.fg("toolTitle", theme.bold("document_symbols"))} ${theme.fg("accent", path)}`;
114
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("document_symbols")))} ${theme.fg("accent", path)}`;
114
115
  }
115
116
 
116
117
  /** Flattens a hierarchical DocumentSymbolEntry[] into (depth, entry) pairs, depth-first, for bounded rendering. */
@@ -149,7 +150,7 @@ export function formatDocumentSymbolsResult(symbols: readonly DocumentSymbolEntr
149
150
 
150
151
  export function formatDiagnosticsCall(args: { path?: unknown }, theme: LectorTheme): string {
151
152
  const path = typeof args.path === "string" ? args.path : "";
152
- return `${theme.fg("toolTitle", theme.bold("diagnostics"))} ${theme.fg("accent", path)}`;
153
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("diagnostics")))} ${theme.fg("accent", path)}`;
153
154
  }
154
155
 
155
156
  export function formatDiagnosticsResult(diagnostics: readonly Diagnostic[] | undefined, expanded: boolean, theme: LectorTheme): string {
@@ -175,7 +176,7 @@ export function formatDiagnosticsResult(diagnostics: readonly Diagnostic[] | und
175
176
 
176
177
  function formatPathCall(toolName: string, args: { path?: unknown }, theme: LectorTheme, qualifier = ""): string {
177
178
  const path = typeof args.path === "string" ? args.path : "";
178
- return `${theme.fg("toolTitle", theme.bold(toolName))}${qualifier ? ` ${theme.fg("muted", qualifier)}` : ""} ${theme.fg("accent", path)}`;
179
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle(toolName)))}${qualifier ? ` ${theme.fg("muted", qualifier)}` : ""} ${theme.fg("accent", path)}`;
179
180
  }
180
181
 
181
182
  export function formatCodeActionPreviewCall(
@@ -249,7 +250,11 @@ export function formatDiagnosticDeltaResult(result: OperationOutputs["workspace.
249
250
  }
250
251
 
251
252
  export function formatTypeHierarchyCall(args: { direction?: unknown; path?: unknown; line?: unknown; character?: unknown }, theme: LectorTheme): string {
252
- return `${formatPositionalCall("type_hierarchy", args, theme)} ${theme.fg("muted", typeof args.direction === "string" ? args.direction : "")}`;
253
+ const direction = typeof args.direction === "string" ? args.direction : undefined;
254
+ const path = typeof args.path === "string" ? args.path : "";
255
+ const line = typeof args.line === "number" ? args.line : "?";
256
+ const character = typeof args.character === "number" ? args.character : "?";
257
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("type_hierarchy", direction)))} ${theme.fg("accent", `${path}:${line}:${character}`)}`;
253
258
  }
254
259
 
255
260
  export function formatTypeHierarchyResult(
@@ -311,7 +316,7 @@ export function formatCallHierarchyCall(args: { direction?: unknown; path?: unkn
311
316
  const path = typeof args.path === "string" ? args.path : "";
312
317
  const line = typeof args.line === "number" ? args.line : "?";
313
318
  const character = typeof args.character === "number" ? args.character : "?";
314
- return `${theme.fg("toolTitle", theme.bold("call_hierarchy"))} ${theme.fg("muted", direction)} ${theme.fg("accent", `${path}:${line}:${character}`)}`;
319
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("call_hierarchy", direction)))} ${theme.fg("accent", `${path}:${line}:${character}`)}`;
315
320
  }
316
321
 
317
322
  function formatPrepareCallHierarchyResult(items: readonly CallHierarchyEntry[] | undefined, theme: LectorTheme): string {
@@ -384,7 +389,7 @@ export function formatReachableFromResult(symbols: readonly SymbolNode[] | undef
384
389
  export function formatWorkspaceMapCall(args: { path?: unknown; maxEntries?: unknown }, theme: LectorTheme): string {
385
390
  const path = typeof args.path === "string" ? args.path : "";
386
391
  const maxEntries = typeof args.maxEntries === "number" ? ` (top ${args.maxEntries})` : "";
387
- return `${theme.fg("toolTitle", theme.bold("workspace_map"))} ${theme.fg("dim", path)}${theme.fg("muted", maxEntries)}`;
392
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("workspace_map")))} ${theme.fg("dim", path)}${theme.fg("muted", maxEntries)}`;
388
393
  }
389
394
 
390
395
  export function formatWorkspaceMapResult(result: WorkspaceMapResult | undefined, expanded: boolean, theme: LectorTheme): string {
@@ -0,0 +1,69 @@
1
+ import type { SymbolSearchResult, TextSearchResult } from "@danypops/lector";
2
+ import { boundModelContentText, DEFAULT_MODEL_CONTENT_BYTES } from "../presentation/model-content.ts";
3
+ import type { CrossWorkspaceOutcome } from "./operations.ts";
4
+
5
+ const MAX_RESULTS_PER_PROJECT = 10;
6
+
7
+ function appendOutcomeHeader<T>(lines: string[], entry: CrossWorkspaceOutcome<T>): boolean {
8
+ const { outcome } = entry;
9
+ switch (outcome.status) {
10
+ case "ready":
11
+ lines.push(`${entry.directory} -- ready`);
12
+ break;
13
+ case "loading":
14
+ lines.push(`${entry.directory} -- loading: ${outcome.message}`);
15
+ return false;
16
+ case "error":
17
+ lines.push(`${entry.directory} -- error: ${outcome.message}`);
18
+ return false;
19
+ default: {
20
+ const exhaustive: never = outcome;
21
+ throw new Error(`unhandled workspace query outcome: ${JSON.stringify(exhaustive)}`);
22
+ }
23
+ }
24
+ if (entry.collapsedWith.length > 0) lines.push(` same workspace as: ${entry.collapsedWith.join(", ")}`);
25
+ return true;
26
+ }
27
+
28
+ /** Formats cross-project symbol outcomes with concrete, independently bounded results for model consumption. */
29
+ export function formatFindSymbolsAcrossProjectsModelContent(
30
+ results: readonly CrossWorkspaceOutcome<SymbolSearchResult>[],
31
+ maxBytes = DEFAULT_MODEL_CONTENT_BYTES,
32
+ ): string {
33
+ const lines = ["Find Symbols Across Projects", `projects: ${results.length}`];
34
+ for (const entry of results) {
35
+ if (!appendOutcomeHeader(lines, entry)) continue;
36
+ if (entry.outcome.status !== "ready") continue;
37
+ const { result } = entry.outcome;
38
+ lines.push(` provenance: ${result.provenance.fidelity} via ${result.provenance.backend}`);
39
+ lines.push(` upstream truncated: ${result.truncated}`);
40
+ for (const item of result.symbols.slice(0, MAX_RESULTS_PER_PROJECT)) {
41
+ lines.push(` ${item.kind} ${item.name} -- ${item.location.path}:${item.location.line}:${item.location.character}`);
42
+ }
43
+ if (result.symbols.length > MAX_RESULTS_PER_PROJECT) lines.push(` ${result.symbols.length - MAX_RESULTS_PER_PROJECT} more symbols omitted`);
44
+ if (result.symbols.length === 0) lines.push(" no symbols matched");
45
+ }
46
+ return boundModelContentText(lines.join("\n"), maxBytes);
47
+ }
48
+
49
+ /** Formats cross-project text outcomes with concrete, independently bounded matches for model consumption. */
50
+ export function formatSearchTextAcrossProjectsModelContent(
51
+ results: readonly CrossWorkspaceOutcome<TextSearchResult>[],
52
+ maxBytes = DEFAULT_MODEL_CONTENT_BYTES,
53
+ ): string {
54
+ const lines = ["Search Code Across Projects", `projects: ${results.length}`];
55
+ for (const entry of results) {
56
+ if (!appendOutcomeHeader(lines, entry)) continue;
57
+ if (entry.outcome.status !== "ready") continue;
58
+ const { result } = entry.outcome;
59
+ if (result.provenance) lines.push(` provenance: lexical via ${result.provenance.backend} (${result.provenance.indexState})`);
60
+ lines.push(` upstream truncated: ${result.truncated}`);
61
+ for (const item of result.matches.slice(0, MAX_RESULTS_PER_PROJECT)) {
62
+ const line = item.line.replace(/\n$/, "");
63
+ lines.push(` ${item.path}:${item.lineNumber}: ${line}${item.lineTruncated ? " (line truncated)" : ""}`);
64
+ }
65
+ if (result.matches.length > MAX_RESULTS_PER_PROJECT) lines.push(` ${result.matches.length - MAX_RESULTS_PER_PROJECT} more matches omitted`);
66
+ if (result.matches.length === 0) lines.push(" no matches");
67
+ }
68
+ return boundModelContentText(lines.join("\n"), maxBytes);
69
+ }
@@ -3,14 +3,19 @@ import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { renderTruncatedList } from "malevich-tui-components";
4
4
  import { describeFindSymbolSources } from "../find-symbols/rendering.ts";
5
5
  import type { LectorTheme } from "../lector-tui-theme.ts";
6
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
6
7
  import type { CrossWorkspaceOutcome } from "./operations.ts";
7
8
 
8
9
  const DEFAULT_VISIBLE_PER_WORKSPACE = 10;
9
10
 
10
- export function formatCrossWorkspaceCall(args: { directories?: unknown; query?: unknown }, theme: LectorTheme): string {
11
+ export function formatCrossWorkspaceCall(
12
+ toolName: "find_symbols_across_projects" | "search_code_across_projects",
13
+ args: { directories?: unknown; query?: unknown },
14
+ theme: LectorTheme,
15
+ ): string {
11
16
  const directories = Array.isArray(args.directories) ? args.directories.filter((d): d is string => typeof d === "string") : [];
12
17
  const query = typeof args.query === "string" ? args.query : "";
13
- return `${theme.fg("accent", `"${query}"`)} ${theme.fg("dim", `across ${directories.length} project(s)`)}`;
18
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle(toolName)))} ${theme.fg("accent", `"${query}"`)} ${theme.fg("dim", `across ${directories.length} project(s)`)}`;
14
19
  }
15
20
 
16
21
  function formatOutcomeHeader(entry: CrossWorkspaceOutcome<unknown>, theme: LectorTheme): string {
@@ -1,27 +1,61 @@
1
1
  import type { GithubRepoSearchResult, NpmPackageCandidate, SourcegraphCodeCandidate } from "@danypops/lector";
2
2
  import type { LectorTheme } from "../lector-tui-theme.ts";
3
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
3
4
 
4
5
  type ExternalSearchAction = "github_repos" | "npm_packages" | "sourcegraph_code";
5
6
 
6
7
  export function formatExternalSearchCall(action: ExternalSearchAction, args: { query?: unknown }, theme: LectorTheme): string {
7
- const label = theme.fg("toolTitle", theme.bold("external_search"));
8
+ const label = theme.fg("toolTitle", theme.bold(presentationTitle("external_search", action)));
8
9
  const query = typeof args.query === "string" ? args.query : "";
9
- return `${label} ${theme.fg("accent", action)} ${theme.fg("dim", query)}`;
10
+ return `${label} ${theme.fg("accent", `"${query}"`)}`;
10
11
  }
11
12
 
12
- export function formatGithubRepoSearchResult(result: GithubRepoSearchResult | undefined, theme: LectorTheme): string {
13
+ const VISIBLE_CANDIDATES = 8;
14
+
15
+ function boundedCandidates<T>(candidates: readonly T[], expanded: boolean): { visible: readonly T[]; more: number } {
16
+ const visible = expanded ? candidates : candidates.slice(0, VISIBLE_CANDIDATES);
17
+ return { visible, more: candidates.length - visible.length };
18
+ }
19
+
20
+ export function formatGithubRepoSearchResult(result: GithubRepoSearchResult | undefined, expanded: boolean, theme: LectorTheme): string {
13
21
  if (!result) return theme.fg("dim", "No result.");
14
- const count = result.candidates.length;
15
- const summary = count === 0 ? theme.fg("dim", "no repositories matched") : theme.fg("success", `${count} repositor${count === 1 ? "y" : "ies"}`);
16
- return result.authenticated ? summary : `${summary} ${theme.fg("warning", "(unauthenticated -- lower rate limit)")}`;
22
+ if (result.candidates.length === 0) return theme.fg("dim", "no repositories matched");
23
+ const { visible, more } = boundedCandidates(result.candidates, expanded);
24
+ const lines = visible.map(
25
+ (candidate) =>
26
+ `${theme.fg("accent", `${candidate.host}/${candidate.owner}/${candidate.repo}`)} · ${candidate.stars} stars${candidate.language ? ` · ${candidate.language}` : ""}${candidate.description ? `\n ${candidate.description}` : ""}`,
27
+ );
28
+ if (more > 0) lines.push(theme.fg("muted", `… ${more} more (expand to show)`));
29
+ if (!result.authenticated) lines.push(theme.fg("warning", "unauthenticated -- lower rate limit"));
30
+ return lines.join("\n");
17
31
  }
18
32
 
19
- export function formatNpmPackageSearchResult(result: { candidates: readonly NpmPackageCandidate[] } | undefined, theme: LectorTheme): string {
20
- const count = result?.candidates.length ?? 0;
21
- return count === 0 ? theme.fg("dim", "no packages matched") : theme.fg("success", `${count} package${count === 1 ? "" : "s"}`);
33
+ export function formatNpmPackageSearchResult(
34
+ result: { candidates: readonly NpmPackageCandidate[] } | undefined,
35
+ expanded: boolean,
36
+ theme: LectorTheme,
37
+ ): string {
38
+ if (!result || result.candidates.length === 0) return theme.fg("dim", "no packages matched");
39
+ const { visible, more } = boundedCandidates(result.candidates, expanded);
40
+ const lines = visible.map(
41
+ (candidate) =>
42
+ `${theme.fg("accent", `${candidate.name}@${candidate.version}`)} · score ${candidate.score.toFixed(3)}${candidate.description ? `\n ${candidate.description}` : ""}`,
43
+ );
44
+ if (more > 0) lines.push(theme.fg("muted", `… ${more} more (expand to show)`));
45
+ return lines.join("\n");
22
46
  }
23
47
 
24
- export function formatSourcegraphCodeSearchResult(result: { candidates: readonly SourcegraphCodeCandidate[] } | undefined, theme: LectorTheme): string {
25
- const count = result?.candidates.length ?? 0;
26
- return count === 0 ? theme.fg("dim", "no code matches") : theme.fg("success", `${count} match${count === 1 ? "" : "es"}`);
48
+ export function formatSourcegraphCodeSearchResult(
49
+ result: { candidates: readonly SourcegraphCodeCandidate[] } | undefined,
50
+ expanded: boolean,
51
+ theme: LectorTheme,
52
+ ): string {
53
+ if (!result || result.candidates.length === 0) return theme.fg("dim", "no code matches");
54
+ const { visible, more } = boundedCandidates(result.candidates, expanded);
55
+ const lines = visible.map((candidate) => {
56
+ const matches = candidate.lineMatches.slice(0, expanded ? candidate.lineMatches.length : 3);
57
+ return `${theme.fg("accent", `${candidate.repository}/${candidate.path}`)}\n${matches.map((match) => ` ${match.line}: ${match.preview}`).join("\n")}`;
58
+ });
59
+ if (more > 0) lines.push(theme.fg("muted", `… ${more} more (expand to show)`));
60
+ return lines.join("\n");
27
61
  }
@@ -2,13 +2,14 @@ import type { FindFilesResult } from "@danypops/lector";
2
2
  import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { renderTruncatedList } from "malevich-tui-components";
4
4
  import type { LectorTheme } from "../lector-tui-theme.ts";
5
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
5
6
 
6
7
  const DEFAULT_VISIBLE_PATHS = 40;
7
8
 
8
9
  export function formatFindFilesCall(args: { directory?: unknown; patterns?: unknown }, theme: LectorTheme): string {
9
10
  const directory = typeof args.directory === "string" ? args.directory : "";
10
11
  const patterns = Array.isArray(args.patterns) ? args.patterns.filter((p): p is string => typeof p === "string") : [];
11
- return `${theme.fg("toolTitle", theme.bold("find_files"))} ${theme.fg("accent", patterns.map((p) => `"${p}"`).join(", "))} ${theme.fg("dim", directory)}`;
12
+ return `${theme.fg("toolTitle", theme.bold(presentationTitle("find_files")))} ${theme.fg("accent", patterns.map((p) => `"${p}"`).join(", "))} ${theme.fg("dim", directory)}`;
12
13
  }
13
14
 
14
15
  export function formatFindFilesResult(result: FindFilesResult | undefined, expanded: boolean, theme: LectorTheme): string {
@@ -2,6 +2,7 @@ import type { SymbolSearchResult, WorkspaceSymbol } from "@danypops/lector";
2
2
  import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { renderTruncatedList } from "malevich-tui-components";
4
4
  import { colorForKind, formatLocation, type LectorTheme } from "../lector-tui-theme.ts";
5
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
5
6
 
6
7
  /**
7
8
  * Custom TUI rendering for find_symbols -- the one Lector-backed tool with
@@ -18,7 +19,7 @@ const DEFAULT_VISIBLE_RESULTS = 8;
18
19
 
19
20
  export function formatFindSymbolsCall(args: { query?: unknown; directory?: unknown }, theme: FindSymbolsTheme): string {
20
21
  const query = typeof args.query === "string" ? args.query : "";
21
- let content = `${theme.fg("toolTitle", theme.bold("find_symbols"))} ${theme.fg("accent", `"${query}"`)}`;
22
+ let content = `${theme.fg("toolTitle", theme.bold(presentationTitle("find_symbols")))} ${theme.fg("accent", `"${query}"`)}`;
22
23
  if (typeof args.directory === "string" && args.directory.length > 0) {
23
24
  content += ` ${theme.fg("muted", `in ${args.directory}`)}`;
24
25
  }
@@ -3,6 +3,7 @@ import { keyHint } from "@earendil-works/pi-coding-agent";
3
3
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import { renderDiffLines, renderTruncatedList, type TextMeasure } from "malevich-tui-components";
5
5
  import type { LectorTheme } from "../lector-tui-theme.ts";
6
+ import { presentationTitle } from "../presentation/tool-presentation.ts";
6
7
 
7
8
  /** Real ANSI-aware measurement, not Malevich's own ASCII-only default -- every diff line renderDiffLines receives is already theme.fg-styled. */
8
9
  const measure: TextMeasure = { visibleWidth, truncateToWidth };
@@ -61,31 +62,32 @@ export function formatGitCall(
61
62
  theme: LectorTheme,
62
63
  ): string {
63
64
  const action = typeof args.action === "string" ? args.action : "";
65
+ const label = theme.fg("toolTitle", theme.bold(presentationTitle("git", action)));
64
66
  const directory = typeof args.directory === "string" ? args.directory : "";
65
67
  if (action === "compare-symbol") {
66
68
  const path = typeof args.path === "string" ? args.path : "";
67
69
  const symbol = typeof args.symbol === "string" ? args.symbol : "";
68
70
  const fromRef = typeof args.fromRef === "string" ? args.fromRef : "";
69
71
  const toRef = typeof args.toRef === "string" ? ` -> ${args.toRef}` : "";
70
- return `${theme.fg("toolTitle", theme.bold("git"))} ${theme.fg("muted", action)} ${theme.fg("accent", `${directory}/${path}`)} (${symbol}) ${fromRef}${toRef}`;
72
+ return `${label} ${theme.fg("accent", `${directory}/${path}`)} (${symbol}) ${fromRef}${toRef}`;
71
73
  }
72
74
  if (action === "is-ancestor") {
73
75
  const ancestorRef = typeof args.ancestorRef === "string" ? args.ancestorRef : "";
74
76
  const ref = typeof args.ref === "string" ? args.ref : "";
75
- return `${theme.fg("toolTitle", theme.bold("git"))} ${theme.fg("muted", action)} ${theme.fg("accent", directory)} ${ancestorRef} -> ${ref}`;
77
+ return `${label} ${theme.fg("accent", directory)} ${ancestorRef} -> ${ref}`;
76
78
  }
77
79
  if (action === "grep-ref" || action === "grep-history") {
78
80
  const ref = action === "grep-ref" && typeof args.ref === "string" ? `${args.ref} ` : "";
79
81
  const pattern = typeof args.pattern === "string" ? args.pattern : "";
80
- return `${theme.fg("toolTitle", theme.bold("git"))} ${theme.fg("muted", action)} ${theme.fg("accent", directory)} ${ref}"${pattern}"`;
82
+ return `${label} ${theme.fg("accent", directory)} ${ref}"${pattern}"`;
81
83
  }
82
84
  if (action === "show") {
83
85
  const ref = typeof args.ref === "string" ? args.ref : "";
84
86
  const path = typeof args.path === "string" ? args.path : "";
85
- return `${theme.fg("toolTitle", theme.bold("git"))} ${theme.fg("muted", action)} ${theme.fg("accent", `${directory}/${path}`)} @ ${ref}`;
87
+ return `${label} ${theme.fg("accent", `${directory}/${path}`)} @ ${ref}`;
86
88
  }
87
89
  const ref = typeof args.ref === "string" ? ` ${args.ref}` : "";
88
- return `${theme.fg("toolTitle", theme.bold("git"))} ${theme.fg("muted", action)} ${theme.fg("accent", directory)}${ref}`;
90
+ return `${label} ${theme.fg("accent", directory)}${ref}`;
89
91
  }
90
92
 
91
93
  function formatGitWorktreeAddResult(result: GitWorktreeAddResult | undefined, theme: LectorTheme): string {