@thoth-agents/pi-thoth-theme 0.1.0 → 0.3.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.
- package/README.md +51 -10
- package/dist/index.ts +3967 -0
- package/package.json +7 -9
- package/src/index.ts +0 -53
- package/src/input-box/decorate.ts +0 -304
- package/src/input-box/frame.ts +0 -182
- package/src/input-box/gradient.ts +0 -92
- package/src/input-box/state.ts +0 -90
- package/src/render-kit/index.ts +0 -305
- package/src/render-kit/working.ts +0 -25
- package/src/shared/cache.ts +0 -21
- package/src/shared/config.ts +0 -86
- package/src/shared/duration.ts +0 -1
- package/src/shared/icons.ts +0 -89
- package/src/status-line/cost.ts +0 -56
- package/src/status-line/index.ts +0 -242
- package/src/status-line/layout.ts +0 -272
- package/src/status-line/snapshot.ts +0 -11
- package/src/status-line/throughput.ts +0 -61
- package/src/status-line/tokens.ts +0 -51
- package/src/tools/bash.ts +0 -237
- package/src/tools/border.ts +0 -21
- package/src/tools/box.ts +0 -258
- package/src/tools/edit.ts +0 -228
- package/src/tools/file-icons.ts +0 -139
- package/src/tools/find.ts +0 -227
- package/src/tools/frame.ts +0 -166
- package/src/tools/generic.ts +0 -195
- package/src/tools/grep.ts +0 -371
- package/src/tools/index.ts +0 -135
- package/src/tools/ls.ts +0 -231
- package/src/tools/ownership.ts +0 -70
- package/src/tools/powershell.ts +0 -1
- package/src/tools/read.ts +0 -194
- package/src/tools/ticker.ts +0 -101
- package/src/tools/write.ts +0 -188
- package/src/welcome/age.ts +0 -11
- package/src/welcome/index.ts +0 -132
- package/src/welcome/logo.ts +0 -35
- package/src/welcome/render.ts +0 -323
- package/src/welcome/resources.ts +0 -251
package/src/tools/edit.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentToolResult,
|
|
3
|
-
Theme,
|
|
4
|
-
ToolRenderResultOptions,
|
|
5
|
-
} from '@earendil-works/pi-coding-agent';
|
|
6
|
-
import { toolFooter } from '../render-kit/index.ts';
|
|
7
|
-
import type { ThemeConfig } from '../shared/config.ts';
|
|
8
|
-
import { icon as semanticIcon, statusIcon } from '../shared/icons.ts';
|
|
9
|
-
import { getToolBorderTone } from './border.ts';
|
|
10
|
-
import { createComponent, getResultText } from './box.ts';
|
|
11
|
-
import { getFileIcon, getToolIcon } from './file-icons.ts';
|
|
12
|
-
import {
|
|
13
|
-
formatDisplayPath,
|
|
14
|
-
hasToolResult,
|
|
15
|
-
renderFrameBottom,
|
|
16
|
-
renderFrameRow,
|
|
17
|
-
renderFrameTop,
|
|
18
|
-
} from './frame.ts';
|
|
19
|
-
import { type ElapsedRenderContext, syncElapsedTicker } from './ticker.ts';
|
|
20
|
-
|
|
21
|
-
const COLLAPSED_DIFF_LINES = 6;
|
|
22
|
-
|
|
23
|
-
interface EditArgs {
|
|
24
|
-
path?: string;
|
|
25
|
-
file_path?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface EditContext extends ElapsedRenderContext {
|
|
29
|
-
cwd?: string;
|
|
30
|
-
lastComponent?: { invalidate?: () => void };
|
|
31
|
-
state?: {
|
|
32
|
-
hasResult?: boolean;
|
|
33
|
-
callComponent?: { invalidate?: () => void };
|
|
34
|
-
[key: string]: unknown;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface EditDetails {
|
|
39
|
-
diff?: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function parseDiffStats(diffText: string): {
|
|
43
|
-
additions: number;
|
|
44
|
-
removals: number;
|
|
45
|
-
} {
|
|
46
|
-
let additions = 0;
|
|
47
|
-
let removals = 0;
|
|
48
|
-
const lines = diffText.split('\n');
|
|
49
|
-
|
|
50
|
-
for (const line of lines) {
|
|
51
|
-
if (line.startsWith('+++') || line.startsWith('---')) continue;
|
|
52
|
-
if (line.startsWith('+')) additions++;
|
|
53
|
-
else if (line.startsWith('-')) removals++;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return { additions, removals };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function colorDiffLine(line: string, theme: Theme): string {
|
|
60
|
-
if (
|
|
61
|
-
line.startsWith('+++') ||
|
|
62
|
-
line.startsWith('---') ||
|
|
63
|
-
line.startsWith('@@')
|
|
64
|
-
) {
|
|
65
|
-
return theme.fg('dim', line);
|
|
66
|
-
}
|
|
67
|
-
if (line.startsWith('+')) {
|
|
68
|
-
return theme.fg('toolDiffAdded', line);
|
|
69
|
-
}
|
|
70
|
-
if (line.startsWith('-')) {
|
|
71
|
-
return theme.fg('toolDiffRemoved', line);
|
|
72
|
-
}
|
|
73
|
-
return theme.fg('toolDiffContext', line);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function createCustomEditTool(cwd: string, config: ThemeConfig) {
|
|
77
|
-
return {
|
|
78
|
-
renderShell: 'self' as const,
|
|
79
|
-
renderCall(rawArgs: unknown, theme: Theme, context: EditContext) {
|
|
80
|
-
syncElapsedTicker(context);
|
|
81
|
-
const args = (rawArgs ?? {}) as EditArgs;
|
|
82
|
-
const rawPath = String(args.path ?? args.file_path ?? '');
|
|
83
|
-
const filePath = formatDisplayPath(rawPath, context?.cwd ?? cwd);
|
|
84
|
-
const icon = getFileIcon(filePath, config.icons);
|
|
85
|
-
const editIcon = getToolIcon('edit', config.icons);
|
|
86
|
-
const borderTone = getToolBorderTone(context);
|
|
87
|
-
|
|
88
|
-
const comp = createComponent((width: number) => {
|
|
89
|
-
const safeWidth = Math.max(0, width);
|
|
90
|
-
const footer =
|
|
91
|
-
context?.executionStarted && context.isPartial
|
|
92
|
-
? toolFooter(theme, { status: 'running', context }, config.icons)
|
|
93
|
-
: undefined;
|
|
94
|
-
const title = `${theme.fg('accent', editIcon)} ${theme.bold ? theme.bold(theme.fg('toolTitle', 'Edit')) : theme.fg('toolTitle', 'Edit')} ${theme.fg('accent', icon)} ${theme.fg('text', filePath)}`;
|
|
95
|
-
|
|
96
|
-
if (hasToolResult(context)) {
|
|
97
|
-
return renderFrameTop(theme, title, safeWidth, borderTone);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return [
|
|
101
|
-
...renderFrameTop(theme, title, safeWidth, borderTone),
|
|
102
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
103
|
-
];
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (context?.state) {
|
|
107
|
-
context.state.callComponent = comp;
|
|
108
|
-
}
|
|
109
|
-
return comp;
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
renderResult(
|
|
113
|
-
result: AgentToolResult<unknown>,
|
|
114
|
-
options: ToolRenderResultOptions,
|
|
115
|
-
theme: Theme,
|
|
116
|
-
context: EditContext,
|
|
117
|
-
) {
|
|
118
|
-
if (context?.state) {
|
|
119
|
-
context.state.hasResult = true;
|
|
120
|
-
if (
|
|
121
|
-
context.state.callComponent &&
|
|
122
|
-
typeof context.state.callComponent.invalidate === 'function'
|
|
123
|
-
) {
|
|
124
|
-
context.state.callComponent.invalidate();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (
|
|
128
|
-
context?.lastComponent &&
|
|
129
|
-
typeof context.lastComponent.invalidate === 'function'
|
|
130
|
-
) {
|
|
131
|
-
context.lastComponent.invalidate();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const isErr = Boolean(context?.isError);
|
|
135
|
-
const status = options?.isPartial
|
|
136
|
-
? 'running'
|
|
137
|
-
: isErr
|
|
138
|
-
? 'failed'
|
|
139
|
-
: 'completed';
|
|
140
|
-
const footerOptions = {
|
|
141
|
-
status,
|
|
142
|
-
context: { ...context, isPartial: options?.isPartial },
|
|
143
|
-
} as const;
|
|
144
|
-
syncElapsedTicker(footerOptions.context);
|
|
145
|
-
const borderTone = getToolBorderTone({
|
|
146
|
-
isError: isErr,
|
|
147
|
-
isPartial: options?.isPartial,
|
|
148
|
-
});
|
|
149
|
-
const details = result.details as EditDetails | undefined;
|
|
150
|
-
const diffText = details?.diff ?? getResultText(result);
|
|
151
|
-
|
|
152
|
-
if (isErr) {
|
|
153
|
-
return createComponent((width: number) => {
|
|
154
|
-
const safeWidth = Math.max(0, width);
|
|
155
|
-
const errText = `${theme.fg('error', `${statusIcon('warning', config.icons)} `)}${theme.fg('error', diffText || 'Edit failed')}`;
|
|
156
|
-
return [
|
|
157
|
-
...renderFrameRow(theme, errText, safeWidth, borderTone),
|
|
158
|
-
...renderFrameBottom(
|
|
159
|
-
theme,
|
|
160
|
-
toolFooter(theme, footerOptions, config.icons),
|
|
161
|
-
safeWidth,
|
|
162
|
-
borderTone,
|
|
163
|
-
),
|
|
164
|
-
];
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const { additions, removals } = parseDiffStats(diffText);
|
|
169
|
-
const diffLines = diffText ? diffText.split('\n') : [];
|
|
170
|
-
|
|
171
|
-
return createComponent((width: number) => {
|
|
172
|
-
const safeWidth = Math.max(0, width);
|
|
173
|
-
const addedStr =
|
|
174
|
-
additions > 0
|
|
175
|
-
? theme.fg('toolDiffAdded', `+${additions}`)
|
|
176
|
-
: theme.fg('dim', '+0');
|
|
177
|
-
const removedStr =
|
|
178
|
-
removals > 0
|
|
179
|
-
? theme.fg('toolDiffRemoved', `-${removals}`)
|
|
180
|
-
: theme.fg('dim', '-0');
|
|
181
|
-
const footer = toolFooter(
|
|
182
|
-
theme,
|
|
183
|
-
{
|
|
184
|
-
...footerOptions,
|
|
185
|
-
summary: [`${addedStr} ${removedStr}`, '1 file'],
|
|
186
|
-
},
|
|
187
|
-
config.icons,
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
if (diffLines.length === 0) {
|
|
191
|
-
return [
|
|
192
|
-
...renderFrameRow(
|
|
193
|
-
theme,
|
|
194
|
-
theme.fg('dim', '(no changes)'),
|
|
195
|
-
safeWidth,
|
|
196
|
-
borderTone,
|
|
197
|
-
),
|
|
198
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
199
|
-
];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const visibleLines = options?.expanded
|
|
203
|
-
? diffLines
|
|
204
|
-
: diffLines.slice(0, COLLAPSED_DIFF_LINES);
|
|
205
|
-
const bodyLines: string[] = visibleLines.map((l) =>
|
|
206
|
-
colorDiffLine(l, theme),
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
if (!options?.expanded && diffLines.length > COLLAPSED_DIFF_LINES) {
|
|
210
|
-
const remaining = diffLines.length - COLLAPSED_DIFF_LINES;
|
|
211
|
-
bodyLines.push(
|
|
212
|
-
theme.fg(
|
|
213
|
-
'dim',
|
|
214
|
-
`${semanticIcon('ellipsis', config.icons)} ${remaining} more lines ${semanticIcon('separator', config.icons)} ctrl+o to expand`,
|
|
215
|
-
),
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return [
|
|
220
|
-
...bodyLines.flatMap((l) =>
|
|
221
|
-
renderFrameRow(theme, l, safeWidth, borderTone),
|
|
222
|
-
),
|
|
223
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
224
|
-
];
|
|
225
|
-
});
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
}
|
package/src/tools/file-icons.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { basename, extname } from 'node:path';
|
|
2
|
-
import type { IconMode } from '../shared/config.ts';
|
|
3
|
-
import { icon } from '../shared/icons.ts';
|
|
4
|
-
|
|
5
|
-
const NERD_FILE_ICONS: Record<string, string> = {
|
|
6
|
-
ts: '\ue628',
|
|
7
|
-
tsx: '\ue7ba',
|
|
8
|
-
js: '\ue74e',
|
|
9
|
-
jsx: '\ue7ba',
|
|
10
|
-
mjs: '\ue74e',
|
|
11
|
-
cjs: '\ue74e',
|
|
12
|
-
py: '\ue73c',
|
|
13
|
-
rs: '\ue7a8',
|
|
14
|
-
go: '\ue724',
|
|
15
|
-
java: '\ue738',
|
|
16
|
-
c: '\ue61e',
|
|
17
|
-
cpp: '\ue61d',
|
|
18
|
-
h: '\ue61e',
|
|
19
|
-
hpp: '\ue61d',
|
|
20
|
-
cs: '\ue648',
|
|
21
|
-
html: '\ue736',
|
|
22
|
-
htm: '\ue736',
|
|
23
|
-
css: '\ue749',
|
|
24
|
-
scss: '\ue749',
|
|
25
|
-
sass: '\ue749',
|
|
26
|
-
less: '\ue749',
|
|
27
|
-
vue: '\ue6a0',
|
|
28
|
-
svelte: '\ue697',
|
|
29
|
-
json: '\ue60b',
|
|
30
|
-
yaml: '\ue6a8',
|
|
31
|
-
yml: '\ue6a8',
|
|
32
|
-
toml: '\ue6b2',
|
|
33
|
-
xml: '\ue619',
|
|
34
|
-
md: '\ue73e',
|
|
35
|
-
mdx: '\ue73e',
|
|
36
|
-
sql: '\ue706',
|
|
37
|
-
sh: '\ue795',
|
|
38
|
-
bash: '\ue795',
|
|
39
|
-
zsh: '\ue795',
|
|
40
|
-
ps1: '\ue70f',
|
|
41
|
-
psm1: '\ue70f',
|
|
42
|
-
lua: '\ue620',
|
|
43
|
-
php: '\ue73d',
|
|
44
|
-
dart: '\ue798',
|
|
45
|
-
png: '\uf1c5',
|
|
46
|
-
jpg: '\uf1c5',
|
|
47
|
-
jpeg: '\uf1c5',
|
|
48
|
-
svg: '\uf1c5',
|
|
49
|
-
webp: '\uf1c5',
|
|
50
|
-
gif: '\uf1c5',
|
|
51
|
-
lock: '\uf023',
|
|
52
|
-
env: '\ue615',
|
|
53
|
-
dockerfile: '\ue7b0',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const NERD_NAME_ICONS: Record<string, string> = {
|
|
57
|
-
'package.json': '\ue71e',
|
|
58
|
-
'package-lock.json': '\ue71e',
|
|
59
|
-
'tsconfig.json': '\ue628',
|
|
60
|
-
'.gitignore': '\ue702',
|
|
61
|
-
'.gitattributes': '\ue702',
|
|
62
|
-
'.env': '\ue615',
|
|
63
|
-
dockerfile: '\ue7b0',
|
|
64
|
-
makefile: '\ue615',
|
|
65
|
-
'readme.md': '\ue73e',
|
|
66
|
-
license: '\ue60a',
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const ASCII_FILE_ICONS: Record<string, string> = {
|
|
70
|
-
ts: '[ts]',
|
|
71
|
-
tsx: '[ts]',
|
|
72
|
-
js: '[js]',
|
|
73
|
-
jsx: '[js]',
|
|
74
|
-
mjs: '[js]',
|
|
75
|
-
cjs: '[js]',
|
|
76
|
-
py: '[py]',
|
|
77
|
-
rs: '[rs]',
|
|
78
|
-
go: '[go]',
|
|
79
|
-
json: '{ }',
|
|
80
|
-
yaml: '{ }',
|
|
81
|
-
yml: '{ }',
|
|
82
|
-
toml: '{ }',
|
|
83
|
-
xml: '< >',
|
|
84
|
-
md: '[md]',
|
|
85
|
-
mdx: '[md]',
|
|
86
|
-
html: '< >',
|
|
87
|
-
htm: '< >',
|
|
88
|
-
css: '{ }',
|
|
89
|
-
sh: '$',
|
|
90
|
-
bash: '$',
|
|
91
|
-
zsh: '$',
|
|
92
|
-
ps1: 'PS',
|
|
93
|
-
psm1: 'PS',
|
|
94
|
-
png: '[img]',
|
|
95
|
-
jpg: '[img]',
|
|
96
|
-
jpeg: '[img]',
|
|
97
|
-
svg: '[img]',
|
|
98
|
-
gif: '[img]',
|
|
99
|
-
webp: '[img]',
|
|
100
|
-
lock: '[lock]',
|
|
101
|
-
env: '[env]',
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export function getFileIcon(filePath: string, mode: IconMode): string {
|
|
105
|
-
if (filePath.endsWith('/') || filePath.endsWith('\\')) {
|
|
106
|
-
return icon('folder', mode);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const name = basename(filePath).toLowerCase();
|
|
110
|
-
const ext = extname(name).replace(/^\./, '').toLowerCase();
|
|
111
|
-
|
|
112
|
-
if (mode === 'ascii') {
|
|
113
|
-
if (ASCII_FILE_ICONS[ext]) return ASCII_FILE_ICONS[ext];
|
|
114
|
-
return icon('file', mode);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (NERD_NAME_ICONS[name]) return NERD_NAME_ICONS[name];
|
|
118
|
-
if (NERD_FILE_ICONS[ext]) return NERD_FILE_ICONS[ext];
|
|
119
|
-
return icon('file', mode);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function getDirIcon(mode: IconMode): string {
|
|
123
|
-
return icon('folder', mode);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function getToolIcon(
|
|
127
|
-
tool:
|
|
128
|
-
| 'read'
|
|
129
|
-
| 'write'
|
|
130
|
-
| 'edit'
|
|
131
|
-
| 'bash'
|
|
132
|
-
| 'powershell'
|
|
133
|
-
| 'search'
|
|
134
|
-
| 'folder'
|
|
135
|
-
| 'file',
|
|
136
|
-
mode: IconMode,
|
|
137
|
-
): string {
|
|
138
|
-
return icon(tool, mode);
|
|
139
|
-
}
|
package/src/tools/find.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentToolResult,
|
|
3
|
-
Theme,
|
|
4
|
-
ToolRenderResultOptions,
|
|
5
|
-
} from '@earendil-works/pi-coding-agent';
|
|
6
|
-
import { truncateToWidth } from '@earendil-works/pi-tui';
|
|
7
|
-
import { toolFooter } from '../render-kit/index.ts';
|
|
8
|
-
import type { ThemeConfig } from '../shared/config.ts';
|
|
9
|
-
import { icon as semanticIcon, statusIcon } from '../shared/icons.ts';
|
|
10
|
-
import { getToolBorderTone } from './border.ts';
|
|
11
|
-
import {
|
|
12
|
-
createComponent,
|
|
13
|
-
escapeControlCharacters,
|
|
14
|
-
getRawResultText,
|
|
15
|
-
renderBox,
|
|
16
|
-
splitResultNotice,
|
|
17
|
-
} from './box.ts';
|
|
18
|
-
import { getFileIcon, getToolIcon } from './file-icons.ts';
|
|
19
|
-
import {
|
|
20
|
-
formatDisplayPath,
|
|
21
|
-
hasToolResult,
|
|
22
|
-
isFramedContext,
|
|
23
|
-
renderFrameBottom,
|
|
24
|
-
renderFrameRow,
|
|
25
|
-
renderFrameTop,
|
|
26
|
-
} from './frame.ts';
|
|
27
|
-
import { type ElapsedRenderContext, syncElapsedTicker } from './ticker.ts';
|
|
28
|
-
|
|
29
|
-
const COLLAPSED_ENTRY_LIMIT = 8;
|
|
30
|
-
|
|
31
|
-
interface FindArgs {
|
|
32
|
-
pattern?: string;
|
|
33
|
-
path?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface FindContext extends ElapsedRenderContext {
|
|
37
|
-
cwd?: string;
|
|
38
|
-
lastComponent?: { invalidate?: () => void };
|
|
39
|
-
state?: {
|
|
40
|
-
hasResult?: boolean;
|
|
41
|
-
callComponent?: { invalidate?: () => void };
|
|
42
|
-
[key: string]: unknown;
|
|
43
|
-
};
|
|
44
|
-
toolCallId?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseFindOutput(
|
|
48
|
-
text: string,
|
|
49
|
-
details: unknown,
|
|
50
|
-
): { entries: string[]; notices: string[] } {
|
|
51
|
-
const output = splitResultNotice(text, details, 'find');
|
|
52
|
-
return {
|
|
53
|
-
entries: output.text.split('\n').filter((line) => line.length > 0),
|
|
54
|
-
notices: output.notices,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createCustomFindTool(cwd: string, config: ThemeConfig) {
|
|
59
|
-
return {
|
|
60
|
-
renderShell: 'self' as const,
|
|
61
|
-
renderCall(rawArgs: unknown, theme: Theme, context: FindContext) {
|
|
62
|
-
syncElapsedTicker(context);
|
|
63
|
-
const args = (rawArgs ?? {}) as FindArgs;
|
|
64
|
-
const pattern = escapeControlCharacters(String(args.pattern ?? '*'));
|
|
65
|
-
const rawPath = String(args.path ?? '.');
|
|
66
|
-
const searchPath = escapeControlCharacters(
|
|
67
|
-
formatDisplayPath(rawPath, context?.cwd ?? cwd),
|
|
68
|
-
);
|
|
69
|
-
const icon = getToolIcon('search', config.icons);
|
|
70
|
-
const borderTone = getToolBorderTone(context);
|
|
71
|
-
|
|
72
|
-
const comp = createComponent((width: number) => {
|
|
73
|
-
const safeWidth = Math.max(0, width);
|
|
74
|
-
const footer =
|
|
75
|
-
context?.executionStarted && context.isPartial
|
|
76
|
-
? toolFooter(theme, { status: 'running', context }, config.icons)
|
|
77
|
-
: undefined;
|
|
78
|
-
const title = `${theme.fg('accent', icon)} ${theme.bold ? theme.bold(theme.fg('toolTitle', 'Find')) : theme.fg('toolTitle', 'Find')} ${theme.fg('syntaxString', `"${pattern}"`)} ${theme.fg('dim', 'in')} ${theme.fg('text', searchPath)}`;
|
|
79
|
-
|
|
80
|
-
if (!isFramedContext(context)) {
|
|
81
|
-
return renderBox(theme, [], safeWidth, {
|
|
82
|
-
title,
|
|
83
|
-
footer,
|
|
84
|
-
isError: borderTone === 'error',
|
|
85
|
-
isSuccess: borderTone === 'success',
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (hasToolResult(context)) {
|
|
90
|
-
return renderFrameTop(theme, title, safeWidth, borderTone);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return [
|
|
94
|
-
...renderFrameTop(theme, title, safeWidth, borderTone),
|
|
95
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
96
|
-
];
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (context?.state) {
|
|
100
|
-
context.state.callComponent = comp;
|
|
101
|
-
}
|
|
102
|
-
return comp;
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
renderResult(
|
|
106
|
-
result: AgentToolResult<unknown>,
|
|
107
|
-
options: ToolRenderResultOptions,
|
|
108
|
-
theme: Theme,
|
|
109
|
-
context: FindContext,
|
|
110
|
-
) {
|
|
111
|
-
if (context?.state) {
|
|
112
|
-
context.state.hasResult = true;
|
|
113
|
-
if (
|
|
114
|
-
context.state.callComponent &&
|
|
115
|
-
typeof context.state.callComponent.invalidate === 'function'
|
|
116
|
-
) {
|
|
117
|
-
context.state.callComponent.invalidate();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (
|
|
121
|
-
context?.lastComponent &&
|
|
122
|
-
typeof context.lastComponent.invalidate === 'function'
|
|
123
|
-
) {
|
|
124
|
-
context.lastComponent.invalidate();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const isErr = Boolean(context?.isError);
|
|
128
|
-
const status = options?.isPartial
|
|
129
|
-
? 'running'
|
|
130
|
-
: isErr
|
|
131
|
-
? 'failed'
|
|
132
|
-
: 'completed';
|
|
133
|
-
const footerOptions = {
|
|
134
|
-
status,
|
|
135
|
-
context: { ...context, isPartial: options?.isPartial },
|
|
136
|
-
} as const;
|
|
137
|
-
syncElapsedTicker(footerOptions.context);
|
|
138
|
-
const borderTone = getToolBorderTone({
|
|
139
|
-
isError: isErr,
|
|
140
|
-
isPartial: options?.isPartial,
|
|
141
|
-
});
|
|
142
|
-
const textOutput = getRawResultText(result);
|
|
143
|
-
|
|
144
|
-
if (isErr) {
|
|
145
|
-
return createComponent((width: number) => {
|
|
146
|
-
const safeWidth = Math.max(0, width);
|
|
147
|
-
const footer = toolFooter(theme, footerOptions, config.icons);
|
|
148
|
-
const errText = `${theme.fg('error', `${statusIcon('warning', config.icons)} `)}${theme.fg('error', escapeControlCharacters(textOutput || 'Find failed'))}`;
|
|
149
|
-
if (!isFramedContext(context)) {
|
|
150
|
-
return [
|
|
151
|
-
truncateToWidth(errText, safeWidth),
|
|
152
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
153
|
-
];
|
|
154
|
-
}
|
|
155
|
-
return [
|
|
156
|
-
...renderFrameRow(theme, errText, safeWidth, borderTone),
|
|
157
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
158
|
-
];
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const { entries, notices } = parseFindOutput(textOutput, result.details);
|
|
163
|
-
const total = entries.length;
|
|
164
|
-
|
|
165
|
-
return createComponent((width: number) => {
|
|
166
|
-
const safeWidth = Math.max(0, width);
|
|
167
|
-
const footer = toolFooter(theme, footerOptions, config.icons);
|
|
168
|
-
if (safeWidth === 0) return [];
|
|
169
|
-
if (total === 0 && notices.length === 0) {
|
|
170
|
-
if (!isFramedContext(context)) {
|
|
171
|
-
return [
|
|
172
|
-
truncateToWidth(theme.fg('dim', 'no matches found'), safeWidth),
|
|
173
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
174
|
-
];
|
|
175
|
-
}
|
|
176
|
-
return [
|
|
177
|
-
...renderFrameRow(
|
|
178
|
-
theme,
|
|
179
|
-
theme.fg('dim', 'no matches found'),
|
|
180
|
-
safeWidth,
|
|
181
|
-
borderTone,
|
|
182
|
-
),
|
|
183
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
184
|
-
];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const rawLines: string[] = [];
|
|
188
|
-
const visible = options?.expanded
|
|
189
|
-
? entries
|
|
190
|
-
: entries.slice(0, COLLAPSED_ENTRY_LIMIT);
|
|
191
|
-
const remaining = total - visible.length;
|
|
192
|
-
|
|
193
|
-
for (let i = 0; i < visible.length; i++) {
|
|
194
|
-
const entry = visible[i];
|
|
195
|
-
const isLast = i === visible.length - 1 && remaining === 0;
|
|
196
|
-
const branch = isLast ? '└─' : '├─';
|
|
197
|
-
const icon = getFileIcon(entry, config.icons);
|
|
198
|
-
const line = `${theme.fg('dim', branch)} ${theme.fg('accent', icon)} ${theme.fg('toolOutput', escapeControlCharacters(entry))}`;
|
|
199
|
-
rawLines.push(line);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (remaining > 0) {
|
|
203
|
-
const more = `${theme.fg('dim', '└─')} ${theme.fg('dim', `${semanticIcon('ellipsis', config.icons)} ${remaining} more matches ${semanticIcon('separator', config.icons)} ctrl+o to expand`)}`;
|
|
204
|
-
rawLines.push(more);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
for (const notice of notices) {
|
|
208
|
-
rawLines.push(theme.fg('warning', notice));
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (!isFramedContext(context)) {
|
|
212
|
-
return [
|
|
213
|
-
...rawLines.map((line) => truncateToWidth(` ${line}`, safeWidth)),
|
|
214
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
215
|
-
];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return [
|
|
219
|
-
...rawLines.flatMap((line) =>
|
|
220
|
-
renderFrameRow(theme, line, safeWidth, borderTone),
|
|
221
|
-
),
|
|
222
|
-
...renderFrameBottom(theme, footer, safeWidth, borderTone),
|
|
223
|
-
];
|
|
224
|
-
});
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
}
|