@oh-my-pi/pi-coding-agent 15.13.1 → 15.13.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 +25 -0
- package/dist/cli.js +957 -214
- package/dist/types/config/model-registry.d.ts +1 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/models-config.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +66 -0
- package/dist/types/edit/hashline/block-resolver.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +4 -4
- package/dist/types/modes/rpc/rpc-types.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/session-dump-format.d.ts +2 -1
- package/dist/types/system-prompt.d.ts +11 -0
- package/dist/types/tools/ask.d.ts +2 -0
- package/dist/types/tools/ast-edit.d.ts +2 -0
- package/dist/types/tools/ast-grep.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +2 -0
- package/dist/types/tools/find.d.ts +2 -0
- package/dist/types/tools/inspect-image.d.ts +2 -1
- package/dist/types/tools/irc.d.ts +2 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo.d.ts +2 -0
- package/dist/types/tui/tree-list.d.ts +1 -0
- package/package.json +12 -12
- package/src/config/model-registry.ts +10 -0
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/models-config.ts +1 -0
- package/src/config/settings-schema.ts +53 -0
- package/src/edit/hashline/block-resolver.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -6
- package/src/edit/index.ts +48 -0
- package/src/eval/__tests__/js-context-manager.test.ts +41 -1
- package/src/eval/js/context-manager.ts +92 -26
- package/src/eval/js/worker-core.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +9 -2
- package/src/modes/components/welcome.ts +14 -4
- package/src/modes/controllers/input-controller.ts +21 -38
- package/src/modes/rpc/rpc-mode.ts +1 -0
- package/src/modes/rpc/rpc-types.ts +2 -2
- package/src/prompts/system/system-prompt.md +17 -21
- package/src/prompts/tools/ask.md +0 -8
- package/src/prompts/tools/ast-edit.md +0 -15
- package/src/prompts/tools/ast-grep.md +0 -13
- package/src/prompts/tools/browser.md +0 -21
- package/src/prompts/tools/debug.md +0 -13
- package/src/prompts/tools/eval.md +0 -9
- package/src/prompts/tools/find.md +0 -13
- package/src/prompts/tools/inspect-image.md +0 -9
- package/src/prompts/tools/irc.md +0 -15
- package/src/prompts/tools/patch.md +0 -13
- package/src/prompts/tools/ssh.md +0 -9
- package/src/prompts/tools/todo.md +1 -19
- package/src/sdk.ts +19 -0
- package/src/session/agent-session.ts +125 -19
- package/src/session/session-dump-format.ts +10 -31
- package/src/system-prompt.ts +31 -0
- package/src/tools/ask.ts +41 -0
- package/src/tools/ast-edit.ts +46 -0
- package/src/tools/ast-grep.ts +24 -0
- package/src/tools/browser.ts +52 -0
- package/src/tools/debug.ts +17 -0
- package/src/tools/eval.ts +20 -1
- package/src/tools/find.ts +24 -0
- package/src/tools/inspect-image.ts +27 -1
- package/src/tools/irc.ts +41 -0
- package/src/tools/ssh.ts +16 -0
- package/src/tools/todo.ts +82 -3
- package/src/tui/tree-list.ts +68 -19
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { instrumentedCompleteSimple, resolveTelemetry } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { type Api, completeSimple, type Model, type ToolExample } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { z } from "zod/v4";
|
|
6
6
|
import { extractTextContent } from "../commit/utils";
|
|
@@ -43,6 +43,32 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
43
43
|
readonly parameters = inspectImageSchema;
|
|
44
44
|
readonly strict = false;
|
|
45
45
|
|
|
46
|
+
readonly examples: readonly ToolExample<z.input<typeof inspectImageSchema>>[] = [
|
|
47
|
+
{
|
|
48
|
+
caption: "OCR with strict formatting",
|
|
49
|
+
call: {
|
|
50
|
+
path: "screenshots/error.png",
|
|
51
|
+
question: "Extract all visible text verbatim. Return as bullet list in reading order.",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
caption: "Screenshot debugging",
|
|
56
|
+
call: {
|
|
57
|
+
path: "screenshots/settings.png",
|
|
58
|
+
question:
|
|
59
|
+
"Identify the likely cause of the disabled Save button. Return: (1) observations, (2) likely cause, (3) confidence.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
caption: "Scene/object question",
|
|
64
|
+
call: {
|
|
65
|
+
path: "photos/shelf.jpg",
|
|
66
|
+
question:
|
|
67
|
+
"List all clearly visible product labels and their shelf positions (top/middle/bottom). If unreadable, say unreadable.",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
46
72
|
constructor(
|
|
47
73
|
private readonly session: ToolSession,
|
|
48
74
|
private readonly completeImageRequest: typeof completeSimple = completeSimple,
|
package/src/tools/irc.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
13
|
+
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
13
14
|
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
14
15
|
import { formatAge, formatDuration, prompt } from "@oh-my-pi/pi-utils";
|
|
15
16
|
import { z } from "zod/v4";
|
|
@@ -96,6 +97,46 @@ export class IrcTool implements AgentTool<typeof ircSchema, IrcDetails> {
|
|
|
96
97
|
readonly description: string;
|
|
97
98
|
readonly parameters = ircSchema;
|
|
98
99
|
readonly strict = true;
|
|
100
|
+
|
|
101
|
+
readonly examples: readonly ToolExample<z.input<typeof ircSchema>>[] = [
|
|
102
|
+
{
|
|
103
|
+
caption: "List peers",
|
|
104
|
+
call: { op: "list" },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
caption: "Fire-and-forget DM — same send wakes idle/parked peers",
|
|
108
|
+
call: {
|
|
109
|
+
op: "send",
|
|
110
|
+
to: "AuthLoader",
|
|
111
|
+
message: "Still touching src/server/auth.ts? I need to add a 401 path.",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
caption: "Round-trip when you cannot proceed without the answer",
|
|
116
|
+
call: {
|
|
117
|
+
op: "send",
|
|
118
|
+
to: "Main",
|
|
119
|
+
message: "JWT or session cookies for the auth flow?",
|
|
120
|
+
await: true,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
caption: "Block until a specific peer answers",
|
|
125
|
+
call: { op: "wait", from: "AuthLoader", timeoutMs: 60000 },
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
caption: "Drain pending messages",
|
|
129
|
+
call: { op: "inbox" },
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
caption: "Broadcast to live peers (no replies expected)",
|
|
133
|
+
call: {
|
|
134
|
+
op: "send",
|
|
135
|
+
to: "all",
|
|
136
|
+
message: "About to refactor src/server/middleware/*. Anyone already in there?",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
];
|
|
99
140
|
readonly loadMode = "discoverable";
|
|
100
141
|
constructor(private readonly session: ToolSession) {
|
|
101
142
|
this.description = prompt.render(ircDescription);
|
package/src/tools/ssh.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
2
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { z } from "zod/v4";
|
|
@@ -135,6 +136,21 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
|
135
136
|
readonly concurrency = "exclusive";
|
|
136
137
|
readonly strict = true;
|
|
137
138
|
|
|
139
|
+
readonly examples: readonly ToolExample<z.input<typeof sshSchema>>[] = [
|
|
140
|
+
{
|
|
141
|
+
caption: "List files: Linux (on server1 (10.0.0.1) | linux/bash)",
|
|
142
|
+
call: { host: "server1", command: "ls -la /home/user" },
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
caption: "Show running processes: Windows cmd (on winbox (192.168.1.5) | windows/cmd)",
|
|
146
|
+
call: { host: "winbox", command: "tasklist /v" },
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
caption: "Get system info: macOS (on macbook (10.0.0.20) | macos/zsh)",
|
|
150
|
+
call: { host: "macbook", command: "uname -a && sw_vers" },
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
|
|
138
154
|
readonly #allowedHosts: Set<string>;
|
|
139
155
|
|
|
140
156
|
constructor(
|
package/src/tools/todo.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
2
3
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
4
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
5
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
@@ -270,8 +271,20 @@ function getTaskTargets(phases: TodoPhase[], entry: TodoOpEntryValue, errors: st
|
|
|
270
271
|
return phases.flatMap(phase => phase.tasks);
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
/** Phase name for `init` given a flat `items` list with no explicit `phase`. */
|
|
275
|
+
const DEFAULT_INIT_PHASE = "Tasks";
|
|
276
|
+
|
|
273
277
|
function initPhases(entry: TodoOpEntryValue, errors: string[]): TodoPhase[] {
|
|
274
|
-
|
|
278
|
+
// Models routinely flatten the single-phase init into `{op:"init", items:[...]}`
|
|
279
|
+
// (optionally with a bare `phase`) instead of the canonical
|
|
280
|
+
// `list: [{phase, items}]`. Accept that shape by synthesizing a one-phase list
|
|
281
|
+
// so a common, recoverable mistake isn't a hard error.
|
|
282
|
+
const list =
|
|
283
|
+
entry.list ??
|
|
284
|
+
(entry.items && entry.items.length > 0
|
|
285
|
+
? [{ phase: entry.phase ?? DEFAULT_INIT_PHASE, items: entry.items }]
|
|
286
|
+
: undefined);
|
|
287
|
+
if (!list) {
|
|
275
288
|
errors.push("Missing list for init operation");
|
|
276
289
|
return [];
|
|
277
290
|
}
|
|
@@ -279,7 +292,7 @@ function initPhases(entry: TodoOpEntryValue, errors: string[]): TodoPhase[] {
|
|
|
279
292
|
// (every targeting op resolves the first match), so reject them up front.
|
|
280
293
|
const seenPhases = new Set<string>();
|
|
281
294
|
const seenTasks = new Set<string>();
|
|
282
|
-
for (const listEntry of
|
|
295
|
+
for (const listEntry of list) {
|
|
283
296
|
if (seenPhases.has(listEntry.phase)) {
|
|
284
297
|
errors.push(`Duplicate phase "${listEntry.phase}" in init list`);
|
|
285
298
|
}
|
|
@@ -291,7 +304,7 @@ function initPhases(entry: TodoOpEntryValue, errors: string[]): TodoPhase[] {
|
|
|
291
304
|
seenTasks.add(content);
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
|
-
return
|
|
307
|
+
return list.map(listEntry => ({
|
|
295
308
|
name: listEntry.phase,
|
|
296
309
|
tasks: listEntry.items.map<TodoItem>(content => ({ content, status: "pending" })),
|
|
297
310
|
}));
|
|
@@ -551,6 +564,71 @@ export class TodoTool implements AgentTool<typeof todoSchema, TodoToolDetails> {
|
|
|
551
564
|
readonly parameters = todoSchema;
|
|
552
565
|
readonly concurrency = "exclusive";
|
|
553
566
|
readonly strict = true;
|
|
567
|
+
|
|
568
|
+
readonly examples: readonly ToolExample<z.input<typeof todoSchema>>[] = [
|
|
569
|
+
{
|
|
570
|
+
caption: "Initial setup (multi-phase)",
|
|
571
|
+
call: {
|
|
572
|
+
ops: [
|
|
573
|
+
{
|
|
574
|
+
op: "init",
|
|
575
|
+
list: [
|
|
576
|
+
{ phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
|
|
577
|
+
{ phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
|
|
578
|
+
{ phase: "Verification", items: ["Run cargo test"] },
|
|
579
|
+
],
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
caption: "View current state (read-only)",
|
|
586
|
+
call: {
|
|
587
|
+
ops: [{ op: "view" }],
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
caption: "Initial setup (single phase)",
|
|
592
|
+
call: {
|
|
593
|
+
ops: [
|
|
594
|
+
{
|
|
595
|
+
op: "init",
|
|
596
|
+
list: [{ phase: "Implementation", items: ["Apply fix", "Run tests"] }],
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
caption: "Complete one task",
|
|
603
|
+
call: {
|
|
604
|
+
ops: [{ op: "done", task: "Wire workspace" }],
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
caption: "Complete a whole phase",
|
|
609
|
+
call: {
|
|
610
|
+
ops: [{ op: "done", phase: "Auth" }],
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
caption: "Remove all tasks",
|
|
615
|
+
call: {
|
|
616
|
+
ops: [{ op: "rm" }],
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
caption: "Drop one task",
|
|
621
|
+
call: {
|
|
622
|
+
ops: [{ op: "drop", task: "Run cargo test" }],
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
caption: "Append tasks to a phase",
|
|
627
|
+
call: {
|
|
628
|
+
ops: [{ op: "append", phase: "Auth", items: ["Handle retries", "Run tests"] }],
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
];
|
|
554
632
|
readonly loadMode = "discoverable";
|
|
555
633
|
constructor(private readonly session: ToolSession) {
|
|
556
634
|
this.description = prompt.render(todoDescription);
|
|
@@ -839,6 +917,7 @@ export const todoToolRenderer = {
|
|
|
839
917
|
expanded,
|
|
840
918
|
maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
|
|
841
919
|
itemType: "todo",
|
|
920
|
+
truncateFrom: "start",
|
|
842
921
|
renderItem: todo => formatTodoLine(todo, uiTheme, "", completionKeys, spinnerFrame),
|
|
843
922
|
},
|
|
844
923
|
uiTheme,
|
package/src/tui/tree-list.ts
CHANGED
|
@@ -17,23 +17,45 @@ export interface TreeListOptions<T> {
|
|
|
17
17
|
*/
|
|
18
18
|
maxCollapsedLines?: number;
|
|
19
19
|
itemType?: string;
|
|
20
|
+
truncateFrom?: "start" | "end";
|
|
20
21
|
/** Called once per item with `isLast: false` during budget calculation;
|
|
21
22
|
* line count MUST NOT vary based on `isLast`. */
|
|
22
23
|
renderItem: (item: T, context: TreeContext) => string | string[];
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): string[] {
|
|
26
|
-
const {
|
|
27
|
+
const {
|
|
28
|
+
items,
|
|
29
|
+
expanded = false,
|
|
30
|
+
maxCollapsed = 8,
|
|
31
|
+
maxCollapsedLines,
|
|
32
|
+
itemType = "item",
|
|
33
|
+
truncateFrom = "end",
|
|
34
|
+
renderItem,
|
|
35
|
+
} = options;
|
|
27
36
|
const maxItems = expanded ? items.length : Math.min(items.length, maxCollapsed);
|
|
28
37
|
const linesBudget = !expanded && maxCollapsedLines !== undefined ? maxCollapsedLines : Infinity;
|
|
29
38
|
|
|
39
|
+
const candidateIndices: number[] = [];
|
|
40
|
+
if (truncateFrom === "start") {
|
|
41
|
+
const startCandidateIdx = Math.max(0, items.length - maxItems);
|
|
42
|
+
for (let i = startCandidateIdx; i < items.length; i++) {
|
|
43
|
+
candidateIndices.push(i);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
for (let i = 0; i < maxItems; i++) {
|
|
47
|
+
candidateIndices.push(i);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
// Pre-render each candidate item once.
|
|
31
52
|
// isLast cannot be known at this point (fittingCount is not yet determined);
|
|
32
53
|
// renderItem implementations MUST NOT vary line count based on isLast.
|
|
33
54
|
const preRendered: string[][] = [];
|
|
34
|
-
for (let i = 0; i <
|
|
35
|
-
const
|
|
36
|
-
|
|
55
|
+
for (let i = 0; i < candidateIndices.length; i++) {
|
|
56
|
+
const itemIdx = candidateIndices[i];
|
|
57
|
+
const rendered = renderItem(items[itemIdx], {
|
|
58
|
+
index: itemIdx,
|
|
37
59
|
isLast: false,
|
|
38
60
|
depth: 0,
|
|
39
61
|
theme,
|
|
@@ -43,28 +65,55 @@ export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): st
|
|
|
43
65
|
preRendered.push(Array.isArray(rendered) ? rendered : rendered ? [rendered] : []);
|
|
44
66
|
}
|
|
45
67
|
|
|
46
|
-
|
|
47
|
-
let
|
|
68
|
+
let displayedSlice: { start: number; end: number };
|
|
69
|
+
let remaining: number;
|
|
48
70
|
let fittedLineCount = 0;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
|
|
72
|
+
if (truncateFrom === "start") {
|
|
73
|
+
let fittingCount = candidateIndices.length;
|
|
74
|
+
if (linesBudget !== Infinity) {
|
|
75
|
+
fittingCount = 0;
|
|
76
|
+
for (let i = candidateIndices.length - 1; i >= 0; i--) {
|
|
77
|
+
const count = preRendered[i].length;
|
|
78
|
+
const remainingBefore = candidateIndices[i];
|
|
79
|
+
const reservedSummaryLines = remainingBefore > 0 ? 1 : 0;
|
|
80
|
+
if (fittedLineCount + count + reservedSummaryLines > linesBudget) break;
|
|
81
|
+
fittedLineCount += count;
|
|
82
|
+
fittingCount++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const start = candidateIndices.length - fittingCount;
|
|
86
|
+
displayedSlice = { start, end: candidateIndices.length };
|
|
87
|
+
remaining = candidateIndices.length > 0 ? candidateIndices[start] : 0;
|
|
88
|
+
} else {
|
|
89
|
+
let fittingCount = candidateIndices.length;
|
|
90
|
+
if (linesBudget !== Infinity) {
|
|
91
|
+
fittingCount = 0;
|
|
92
|
+
for (let i = 0; i < candidateIndices.length; i++) {
|
|
93
|
+
const count = preRendered[i].length;
|
|
94
|
+
const remainingAfter = items.length - (i + 1);
|
|
95
|
+
const reservedSummaryLines = remainingAfter > 0 ? 1 : 0;
|
|
96
|
+
if (fittedLineCount + count + reservedSummaryLines > linesBudget) break;
|
|
97
|
+
fittedLineCount += count;
|
|
98
|
+
fittingCount = i + 1;
|
|
99
|
+
}
|
|
58
100
|
}
|
|
101
|
+
displayedSlice = { start: 0, end: fittingCount };
|
|
102
|
+
remaining = items.length - fittingCount;
|
|
59
103
|
}
|
|
60
104
|
|
|
61
|
-
const remaining = items.length - fittingCount;
|
|
62
105
|
const hasSummary = !expanded && remaining > 0 && (linesBudget === Infinity || fittedLineCount < linesBudget);
|
|
63
106
|
|
|
64
107
|
// Emit pre-rendered content with correct isLast-based branch prefixes.
|
|
65
108
|
const lines: string[] = [];
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
|
|
110
|
+
if (truncateFrom === "start" && hasSummary) {
|
|
111
|
+
lines.push(`${theme.fg("dim", theme.tree.branch)} ${theme.fg("muted", formatMoreItems(remaining, itemType))}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (let i = displayedSlice.start; i < displayedSlice.end; i++) {
|
|
115
|
+
const isLast =
|
|
116
|
+
truncateFrom === "start" ? i === displayedSlice.end - 1 : !hasSummary && i === displayedSlice.end - 1;
|
|
68
117
|
const branch = getTreeBranch(isLast, theme);
|
|
69
118
|
const prefix = `${theme.fg("dim", branch)} `;
|
|
70
119
|
const continuePrefix = `${theme.fg("dim", getTreeContinuePrefix(isLast, theme))}`;
|
|
@@ -76,7 +125,7 @@ export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): st
|
|
|
76
125
|
}
|
|
77
126
|
}
|
|
78
127
|
|
|
79
|
-
if (hasSummary) {
|
|
128
|
+
if (truncateFrom === "end" && hasSummary) {
|
|
80
129
|
lines.push(`${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", formatMoreItems(remaining, itemType))}`);
|
|
81
130
|
}
|
|
82
131
|
|