snow-ai 0.3.5 → 0.3.7
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/dist/agents/reviewAgent.d.ts +50 -0
- package/dist/agents/reviewAgent.js +264 -0
- package/dist/api/anthropic.js +104 -71
- package/dist/api/chat.d.ts +1 -1
- package/dist/api/chat.js +60 -41
- package/dist/api/gemini.js +97 -57
- package/dist/api/responses.d.ts +9 -1
- package/dist/api/responses.js +110 -70
- package/dist/api/systemPrompt.d.ts +1 -1
- package/dist/api/systemPrompt.js +36 -7
- package/dist/api/types.d.ts +8 -0
- package/dist/hooks/useCommandHandler.d.ts +1 -0
- package/dist/hooks/useCommandHandler.js +44 -1
- package/dist/hooks/useCommandPanel.js +13 -0
- package/dist/hooks/useConversation.d.ts +4 -1
- package/dist/hooks/useConversation.js +65 -9
- package/dist/hooks/useKeyboardInput.js +19 -0
- package/dist/hooks/useTerminalFocus.js +13 -3
- package/dist/mcp/aceCodeSearch.d.ts +2 -76
- package/dist/mcp/aceCodeSearch.js +31 -467
- package/dist/mcp/bash.d.ts +1 -8
- package/dist/mcp/bash.js +20 -40
- package/dist/mcp/filesystem.d.ts +3 -68
- package/dist/mcp/filesystem.js +32 -348
- package/dist/mcp/ideDiagnostics.js +2 -4
- package/dist/mcp/todo.d.ts +1 -17
- package/dist/mcp/todo.js +11 -15
- package/dist/mcp/types/aceCodeSearch.types.d.ts +92 -0
- package/dist/mcp/types/aceCodeSearch.types.js +4 -0
- package/dist/mcp/types/bash.types.d.ts +13 -0
- package/dist/mcp/types/bash.types.js +4 -0
- package/dist/mcp/types/filesystem.types.d.ts +44 -0
- package/dist/mcp/types/filesystem.types.js +4 -0
- package/dist/mcp/types/todo.types.d.ts +27 -0
- package/dist/mcp/types/todo.types.js +4 -0
- package/dist/mcp/types/websearch.types.d.ts +30 -0
- package/dist/mcp/types/websearch.types.js +4 -0
- package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +34 -0
- package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +146 -0
- package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +14 -0
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +99 -0
- package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +31 -0
- package/dist/mcp/utils/aceCodeSearch/search.utils.js +136 -0
- package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +20 -0
- package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +141 -0
- package/dist/mcp/utils/bash/security.utils.d.ts +20 -0
- package/dist/mcp/utils/bash/security.utils.js +34 -0
- package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +18 -0
- package/dist/mcp/utils/filesystem/code-analysis.utils.js +165 -0
- package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +16 -0
- package/dist/mcp/utils/filesystem/match-finder.utils.js +85 -0
- package/dist/mcp/utils/filesystem/similarity.utils.d.ts +22 -0
- package/dist/mcp/utils/filesystem/similarity.utils.js +75 -0
- package/dist/mcp/utils/todo/date.utils.d.ts +9 -0
- package/dist/mcp/utils/todo/date.utils.js +14 -0
- package/dist/mcp/utils/websearch/browser.utils.d.ts +8 -0
- package/dist/mcp/utils/websearch/browser.utils.js +58 -0
- package/dist/mcp/utils/websearch/text.utils.d.ts +16 -0
- package/dist/mcp/utils/websearch/text.utils.js +39 -0
- package/dist/mcp/websearch.d.ts +1 -31
- package/dist/mcp/websearch.js +21 -97
- package/dist/ui/components/ChatInput.d.ts +2 -1
- package/dist/ui/components/ChatInput.js +10 -3
- package/dist/ui/components/MarkdownRenderer.d.ts +1 -2
- package/dist/ui/components/MarkdownRenderer.js +16 -153
- package/dist/ui/components/MessageList.js +4 -4
- package/dist/ui/components/SessionListScreen.js +37 -17
- package/dist/ui/components/ToolResultPreview.js +27 -7
- package/dist/ui/components/UsagePanel.d.ts +2 -0
- package/dist/ui/components/UsagePanel.js +360 -0
- package/dist/ui/pages/ChatScreen.d.ts +4 -0
- package/dist/ui/pages/ChatScreen.js +70 -30
- package/dist/ui/pages/ConfigScreen.js +23 -19
- package/dist/ui/pages/HeadlessModeScreen.js +2 -4
- package/dist/ui/pages/SubAgentConfigScreen.js +17 -17
- package/dist/ui/pages/SystemPromptConfigScreen.js +7 -6
- package/dist/utils/commandExecutor.d.ts +3 -3
- package/dist/utils/commandExecutor.js +4 -4
- package/dist/utils/commands/home.d.ts +2 -0
- package/dist/utils/commands/home.js +12 -0
- package/dist/utils/commands/review.d.ts +2 -0
- package/dist/utils/commands/review.js +81 -0
- package/dist/utils/commands/role.d.ts +2 -0
- package/dist/utils/commands/role.js +37 -0
- package/dist/utils/commands/usage.d.ts +2 -0
- package/dist/utils/commands/usage.js +12 -0
- package/dist/utils/contextCompressor.js +99 -367
- package/dist/utils/fileUtils.js +3 -3
- package/dist/utils/mcpToolsManager.js +12 -12
- package/dist/utils/proxyUtils.d.ts +15 -0
- package/dist/utils/proxyUtils.js +50 -0
- package/dist/utils/retryUtils.d.ts +27 -0
- package/dist/utils/retryUtils.js +114 -2
- package/dist/utils/sessionManager.d.ts +2 -5
- package/dist/utils/sessionManager.js +16 -83
- package/dist/utils/terminal.js +4 -3
- package/dist/utils/usageLogger.d.ts +11 -0
- package/dist/utils/usageLogger.js +99 -0
- package/package.json +3 -7
- package/dist/agents/summaryAgent.d.ts +0 -31
- package/dist/agents/summaryAgent.js +0 -256
|
@@ -1,159 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Text } from 'ink';
|
|
3
3
|
import { highlight } from 'cli-highlight';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (block.type === 'heading') {
|
|
16
|
-
const headingColors = ['cyan', 'blue', 'magenta', 'yellow'];
|
|
17
|
-
const headingColor = headingColors[block.level - 1] || 'white';
|
|
18
|
-
return (React.createElement(Box, { key: index, marginY: 0 },
|
|
19
|
-
React.createElement(Text, { bold: true, color: headingColor }, renderInlineFormatting(block.content))));
|
|
20
|
-
}
|
|
21
|
-
// Render list
|
|
22
|
-
if (block.type === 'list') {
|
|
23
|
-
return (React.createElement(Box, { key: index, flexDirection: "column", marginY: 0 }, block.items.map((item, itemIndex) => (React.createElement(Box, { key: itemIndex },
|
|
24
|
-
React.createElement(Text, { color: "yellow" }, "\u2022 "),
|
|
25
|
-
React.createElement(Text, { color: color }, renderInlineFormatting(item)))))));
|
|
26
|
-
}
|
|
27
|
-
// Render text with inline formatting
|
|
28
|
-
return (React.createElement(Box, { key: index, flexDirection: "column" }, block.content
|
|
29
|
-
.split('\n')
|
|
30
|
-
.map((line, lineIndex) => (React.createElement(Text, { key: lineIndex, color: color }, line === '' ? ' ' : renderInlineFormatting(line))))));
|
|
31
|
-
})));
|
|
32
|
-
}
|
|
33
|
-
function parseMarkdown(content) {
|
|
34
|
-
const blocks = [];
|
|
35
|
-
const lines = content.split('\n');
|
|
36
|
-
let i = 0;
|
|
37
|
-
while (i < lines.length) {
|
|
38
|
-
const line = lines[i] ?? '';
|
|
39
|
-
// Check for code block - support ```language or just ```
|
|
40
|
-
const codeBlockMatch = line.match(/^```(.*)$/);
|
|
41
|
-
if (codeBlockMatch) {
|
|
42
|
-
const language = codeBlockMatch[1]?.trim() || '';
|
|
43
|
-
const codeLines = [];
|
|
44
|
-
i++;
|
|
45
|
-
// Collect code block lines
|
|
46
|
-
while (i < lines.length) {
|
|
47
|
-
const currentLine = lines[i] ?? '';
|
|
48
|
-
if (currentLine.trim().startsWith('```')) {
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
codeLines.push(currentLine);
|
|
52
|
-
i++;
|
|
53
|
-
}
|
|
54
|
-
blocks.push({
|
|
55
|
-
type: 'code',
|
|
56
|
-
language,
|
|
57
|
-
code: codeLines.join('\n'),
|
|
58
|
-
});
|
|
59
|
-
i++; // Skip closing ```
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
// Check for heading (# ## ### ####)
|
|
63
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
64
|
-
if (headingMatch) {
|
|
65
|
-
blocks.push({
|
|
66
|
-
type: 'heading',
|
|
67
|
-
level: headingMatch[1].length,
|
|
68
|
-
content: headingMatch[2].trim(),
|
|
69
|
-
});
|
|
70
|
-
i++;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
// Check for list item (* or -)
|
|
74
|
-
const listMatch = line.match(/^[\s]*[*\-]\s+(.+)$/);
|
|
75
|
-
if (listMatch) {
|
|
76
|
-
const listItems = [listMatch[1].trim()];
|
|
77
|
-
i++;
|
|
78
|
-
// Collect consecutive list items
|
|
79
|
-
while (i < lines.length) {
|
|
80
|
-
const currentLine = lines[i] ?? '';
|
|
81
|
-
const nextListMatch = currentLine.match(/^[\s]*[*\-]\s+(.+)$/);
|
|
82
|
-
if (!nextListMatch) {
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
listItems.push(nextListMatch[1].trim());
|
|
86
|
-
i++;
|
|
4
|
+
// @ts-expect-error - cli-markdown doesn't have TypeScript definitions
|
|
5
|
+
import cliMarkdown from 'cli-markdown';
|
|
6
|
+
export default function MarkdownRenderer({ content }) {
|
|
7
|
+
// Use cli-markdown for elegant markdown rendering with syntax highlighting
|
|
8
|
+
const rendered = cliMarkdown(content, {
|
|
9
|
+
// Enable syntax highlighting for code blocks
|
|
10
|
+
code: (code, language) => {
|
|
11
|
+
if (!language)
|
|
12
|
+
return code;
|
|
13
|
+
try {
|
|
14
|
+
return highlight(code, { language, ignoreIllegals: true });
|
|
87
15
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
items: listItems,
|
|
91
|
-
});
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
// Collect text lines until next code block, heading, or list
|
|
95
|
-
const textLines = [];
|
|
96
|
-
while (i < lines.length) {
|
|
97
|
-
const currentLine = lines[i] ?? '';
|
|
98
|
-
if (currentLine.trim().startsWith('```') ||
|
|
99
|
-
currentLine.match(/^#{1,6}\s+/) ||
|
|
100
|
-
currentLine.match(/^[\s]*[*\-]\s+/)) {
|
|
101
|
-
break;
|
|
16
|
+
catch {
|
|
17
|
+
return code;
|
|
102
18
|
}
|
|
103
|
-
|
|
104
|
-
i++;
|
|
105
|
-
}
|
|
106
|
-
if (textLines.length > 0) {
|
|
107
|
-
blocks.push({
|
|
108
|
-
type: 'text',
|
|
109
|
-
content: textLines.join('\n'),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return blocks;
|
|
114
|
-
}
|
|
115
|
-
function highlightCode(code, language) {
|
|
116
|
-
try {
|
|
117
|
-
// If no language specified, try to auto-detect or just return the code
|
|
118
|
-
if (!language) {
|
|
119
|
-
return code;
|
|
120
|
-
}
|
|
121
|
-
// Map common language aliases to cli-highlight supported names
|
|
122
|
-
const languageMap = {
|
|
123
|
-
js: 'javascript',
|
|
124
|
-
ts: 'typescript',
|
|
125
|
-
py: 'python',
|
|
126
|
-
rb: 'ruby',
|
|
127
|
-
sh: 'bash',
|
|
128
|
-
shell: 'bash',
|
|
129
|
-
cs: 'csharp',
|
|
130
|
-
'c#': 'csharp',
|
|
131
|
-
cpp: 'cpp',
|
|
132
|
-
'c++': 'cpp',
|
|
133
|
-
yml: 'yaml',
|
|
134
|
-
md: 'markdown',
|
|
135
|
-
};
|
|
136
|
-
const mappedLanguage = languageMap[language.toLowerCase()] || language.toLowerCase();
|
|
137
|
-
return highlight(code, { language: mappedLanguage, ignoreIllegals: true });
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
// If highlighting fails, return the code as-is
|
|
141
|
-
return code;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function renderInlineFormatting(text) {
|
|
145
|
-
// Handle inline code `code` - remove backticks
|
|
146
|
-
text = text.replace(/`([^`]+)`/g, (_, code) => {
|
|
147
|
-
// Use ANSI codes for inline code styling
|
|
148
|
-
return `\x1b[36m${code}\x1b[0m`;
|
|
149
|
-
});
|
|
150
|
-
// Handle bold **text** or __text__ - remove markers
|
|
151
|
-
text = text.replace(/(\*\*|__)([^*_]+)\1/g, (_, __, content) => {
|
|
152
|
-
return `\x1b[1m${content}\x1b[0m`;
|
|
153
|
-
});
|
|
154
|
-
// Handle italic *text* or _text_ (but not part of bold) - remove markers
|
|
155
|
-
text = text.replace(/(?<!\*)(\*)(?!\*)([^*]+)\1(?!\*)/g, (_, __, content) => {
|
|
156
|
-
return `\x1b[3m${content}\x1b[0m`;
|
|
19
|
+
},
|
|
157
20
|
});
|
|
158
|
-
return
|
|
21
|
+
return React.createElement(Text, null, rendered);
|
|
159
22
|
}
|
|
@@ -24,7 +24,7 @@ const MessageList = memo(({ messages, animationFrame, maxMessages = 6 }) => {
|
|
|
24
24
|
: message.role === 'subagent'
|
|
25
25
|
? '◈'
|
|
26
26
|
: '❆'),
|
|
27
|
-
React.createElement(Box, { marginLeft: 1,
|
|
27
|
+
React.createElement(Box, { marginLeft: 1, flexDirection: "column" }, message.role === 'command' ? (React.createElement(Text, { color: "gray" },
|
|
28
28
|
"\u2514\u2500 ",
|
|
29
29
|
message.commandName)) : message.role === 'subagent' ? (React.createElement(React.Fragment, null,
|
|
30
30
|
React.createElement(Text, { color: "magenta", dimColor: true },
|
|
@@ -32,12 +32,12 @@ const MessageList = memo(({ messages, animationFrame, maxMessages = 6 }) => {
|
|
|
32
32
|
message.subAgent?.agentName,
|
|
33
33
|
message.subAgent?.isComplete ? ' ✓' : ' ...'),
|
|
34
34
|
React.createElement(Box, { marginLeft: 2 },
|
|
35
|
-
React.createElement(
|
|
36
|
-
React.createElement(
|
|
35
|
+
React.createElement(Text, { color: "gray" }, message.content || ' ')))) : (React.createElement(React.Fragment, null,
|
|
36
|
+
message.role === 'user' ? (React.createElement(Text, { color: "gray" }, message.content || ' ')) : (React.createElement(MarkdownRenderer, { content: message.content || ' ' })),
|
|
37
37
|
(message.systemInfo ||
|
|
38
38
|
message.files ||
|
|
39
39
|
message.files ||
|
|
40
|
-
message.images) && (React.createElement(Box, {
|
|
40
|
+
message.images) && (React.createElement(Box, { flexDirection: "column" },
|
|
41
41
|
message.systemInfo && (React.createElement(React.Fragment, null,
|
|
42
42
|
React.createElement(Text, { color: "gray", dimColor: true },
|
|
43
43
|
"\u2514\u2500 Platform: ",
|
|
@@ -3,7 +3,7 @@ import { Box, Text, useInput, useStdout } from 'ink';
|
|
|
3
3
|
import Gradient from 'ink-gradient';
|
|
4
4
|
import { Alert } from '@inkjs/ui';
|
|
5
5
|
import ScrollableSelectInput from './ScrollableSelectInput.js';
|
|
6
|
-
import { sessionManager } from '../../utils/sessionManager.js';
|
|
6
|
+
import { sessionManager, } from '../../utils/sessionManager.js';
|
|
7
7
|
export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
8
8
|
const [sessions, setSessions] = useState([]);
|
|
9
9
|
const [loading, setLoading] = useState(true);
|
|
@@ -53,22 +53,35 @@ export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
|
53
53
|
// Create select items with truncated labels
|
|
54
54
|
const selectItems = useMemo(() => {
|
|
55
55
|
const terminalWidth = stdout?.columns || 80;
|
|
56
|
-
//
|
|
57
|
-
const
|
|
56
|
+
// Reserve space for indicators (✔ + ❯ + spacing) and margins
|
|
57
|
+
const reservedSpace = 30;
|
|
58
|
+
const maxLabelWidth = Math.max(30, terminalWidth - reservedSpace);
|
|
58
59
|
return sessions.map(session => {
|
|
59
60
|
const timeString = formatDate(session.updatedAt);
|
|
60
61
|
const title = session.title || 'Untitled';
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// Format: "Title • 5 msgs • 2h ago"
|
|
63
|
+
const messageInfo = `${session.messageCount} msg${session.messageCount !== 1 ? 's' : ''}`;
|
|
64
|
+
const fullLabel = `${title} • ${messageInfo} • ${timeString}`;
|
|
65
|
+
// Truncate if too long - prioritize showing the title
|
|
66
|
+
let truncatedLabel = fullLabel;
|
|
67
|
+
if (fullLabel.length > maxLabelWidth) {
|
|
68
|
+
const ellipsis = '...';
|
|
69
|
+
const suffixLength = messageInfo.length + timeString.length + 6; // " • " x2 + "..."
|
|
70
|
+
const availableForTitle = maxLabelWidth - suffixLength - ellipsis.length;
|
|
71
|
+
if (availableForTitle > 10) {
|
|
72
|
+
const truncatedTitle = title.substring(0, availableForTitle);
|
|
73
|
+
truncatedLabel = `${truncatedTitle}${ellipsis} • ${messageInfo} • ${timeString}`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// If terminal is too narrow, just truncate the whole thing
|
|
77
|
+
truncatedLabel =
|
|
78
|
+
fullLabel.substring(0, maxLabelWidth - ellipsis.length) + ellipsis;
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
return {
|
|
69
82
|
label: truncatedLabel,
|
|
70
83
|
value: session.id,
|
|
71
|
-
isMarked: selectedSessions.has(session.id)
|
|
84
|
+
isMarked: selectedSessions.has(session.id),
|
|
72
85
|
};
|
|
73
86
|
});
|
|
74
87
|
}, [sessions, formatDate, stdout?.columns, selectedSessions]);
|
|
@@ -96,10 +109,14 @@ export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
|
96
109
|
const ids = Array.from(selectedSessions);
|
|
97
110
|
const deletionResults = await Promise.all(ids.map(async (id) => ({
|
|
98
111
|
id,
|
|
99
|
-
success: await sessionManager.deleteSession(id)
|
|
112
|
+
success: await sessionManager.deleteSession(id),
|
|
100
113
|
})));
|
|
101
|
-
const succeededIds = deletionResults
|
|
102
|
-
|
|
114
|
+
const succeededIds = deletionResults
|
|
115
|
+
.filter(result => result.success)
|
|
116
|
+
.map(result => result.id);
|
|
117
|
+
const failedIds = deletionResults
|
|
118
|
+
.filter(result => !result.success)
|
|
119
|
+
.map(result => result.id);
|
|
103
120
|
if (succeededIds.length > 0) {
|
|
104
121
|
setSessions(previous => previous.filter(session => !succeededIds.includes(session.id)));
|
|
105
122
|
setSelectedSessions(previous => {
|
|
@@ -113,13 +130,13 @@ export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
|
113
130
|
if (failedIds.length > 0) {
|
|
114
131
|
setActionMessage({
|
|
115
132
|
type: 'error',
|
|
116
|
-
text: `Failed to delete ${failedIds.length} conversation${failedIds.length > 1 ? 's' : ''}
|
|
133
|
+
text: `Failed to delete ${failedIds.length} conversation${failedIds.length > 1 ? 's' : ''}.`,
|
|
117
134
|
});
|
|
118
135
|
}
|
|
119
136
|
else if (succeededIds.length > 0) {
|
|
120
137
|
setActionMessage({
|
|
121
138
|
type: 'info',
|
|
122
|
-
text: `Deleted ${succeededIds.length} conversation${succeededIds.length > 1 ? 's' : ''}
|
|
139
|
+
text: `Deleted ${succeededIds.length} conversation${succeededIds.length > 1 ? 's' : ''}.`,
|
|
123
140
|
});
|
|
124
141
|
}
|
|
125
142
|
else {
|
|
@@ -182,7 +199,8 @@ export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
|
182
199
|
sessions.length,
|
|
183
200
|
" conversation",
|
|
184
201
|
sessions.length !== 1 ? 's' : '',
|
|
185
|
-
|
|
202
|
+
' ',
|
|
203
|
+
"available"))),
|
|
186
204
|
React.createElement(Box, { marginBottom: 1, flexShrink: 0 },
|
|
187
205
|
React.createElement(ScrollableSelectInput, { items: selectItems, limit: listLimit, onSelect: handleSelect, onToggleItem: handleToggleItem, onDeleteSelection: handleDeleteSelected, selectedValues: selectedSessions, indicator: ({ isSelected }) => (React.createElement(Text, { color: isSelected ? 'green' : 'gray' }, isSelected ? '❯ ' : ' ')), renderItem: ({ isSelected, isMarked, label }) => (React.createElement(Text, null,
|
|
188
206
|
React.createElement(Text, { color: isMarked ? 'green' : 'gray' }, isMarked ? '✔ ' : ' '),
|
|
@@ -192,5 +210,7 @@ export default function SessionListScreen({ onBack, onSelectSession }) {
|
|
|
192
210
|
React.createElement(Alert, { variant: actionMessage.type }, actionMessage.text))) : null,
|
|
193
211
|
React.createElement(Alert, { variant: "info" },
|
|
194
212
|
"\u2191\u2193 navigate \u2022 Space mark \u2022 D delete \u2022 Enter select \u2022 Esc return \u2022 R refresh",
|
|
195
|
-
selectedSessions.size > 0
|
|
213
|
+
selectedSessions.size > 0
|
|
214
|
+
? ` • ${selectedSessions.size} selected`
|
|
215
|
+
: ''))));
|
|
196
216
|
}
|
|
@@ -9,7 +9,10 @@ export default function ToolResultPreview({ toolName, result, maxLines = 5 }) {
|
|
|
9
9
|
// Try to parse JSON result
|
|
10
10
|
const data = JSON.parse(result);
|
|
11
11
|
// Handle different tool types
|
|
12
|
-
if (toolName
|
|
12
|
+
if (toolName.startsWith('subagent-')) {
|
|
13
|
+
return renderSubAgentPreview(data, maxLines);
|
|
14
|
+
}
|
|
15
|
+
else if (toolName === 'filesystem-read') {
|
|
13
16
|
return renderReadPreview(data, maxLines);
|
|
14
17
|
}
|
|
15
18
|
else if (toolName === 'filesystem-list') {
|
|
@@ -40,6 +43,23 @@ export default function ToolResultPreview({ toolName, result, maxLines = 5 }) {
|
|
|
40
43
|
return null;
|
|
41
44
|
}
|
|
42
45
|
}
|
|
46
|
+
function renderSubAgentPreview(data, maxLines) {
|
|
47
|
+
// Sub-agent results have format: { success: boolean, result: string }
|
|
48
|
+
if (!data.result)
|
|
49
|
+
return null;
|
|
50
|
+
// Split the result into lines
|
|
51
|
+
const lines = data.result.split('\n').filter((line) => line.trim());
|
|
52
|
+
const previewLines = lines.slice(0, maxLines);
|
|
53
|
+
const hasMore = lines.length > maxLines;
|
|
54
|
+
return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
55
|
+
previewLines.map((line, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
|
|
56
|
+
idx === previewLines.length - 1 && !hasMore ? '└─ ' : '├─ ',
|
|
57
|
+
line.length > 80 ? line.slice(0, 80) + '...' : line))),
|
|
58
|
+
hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
59
|
+
"\u2514\u2500 ... (",
|
|
60
|
+
lines.length - maxLines,
|
|
61
|
+
" more lines)"))));
|
|
62
|
+
}
|
|
43
63
|
function renderReadPreview(data, maxLines) {
|
|
44
64
|
if (!data.content)
|
|
45
65
|
return null;
|
|
@@ -77,7 +97,7 @@ function renderListPreview(data, maxLines) {
|
|
|
77
97
|
}
|
|
78
98
|
function renderACEPreview(toolName, data, maxLines) {
|
|
79
99
|
// Handle ace-text-search results
|
|
80
|
-
if (toolName === 'ace-text-search' || toolName === '
|
|
100
|
+
if (toolName === 'ace-text-search' || toolName === 'ace-text_search') {
|
|
81
101
|
if (!data || data.length === 0) {
|
|
82
102
|
return (React.createElement(Box, { marginLeft: 2 },
|
|
83
103
|
React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No matches found")));
|
|
@@ -100,7 +120,7 @@ function renderACEPreview(toolName, data, maxLines) {
|
|
|
100
120
|
" more matches)"))));
|
|
101
121
|
}
|
|
102
122
|
// Handle ace-search-symbols results
|
|
103
|
-
if (toolName === 'ace-search-symbols' || toolName === '
|
|
123
|
+
if (toolName === 'ace-search-symbols' || toolName === 'ace-search_symbols') {
|
|
104
124
|
const symbols = data.symbols || [];
|
|
105
125
|
if (symbols.length === 0) {
|
|
106
126
|
return (React.createElement(Box, { marginLeft: 2 },
|
|
@@ -124,7 +144,7 @@ function renderACEPreview(toolName, data, maxLines) {
|
|
|
124
144
|
" more symbols)"))));
|
|
125
145
|
}
|
|
126
146
|
// Handle ace-find-references results
|
|
127
|
-
if (toolName === 'ace-find-references' || toolName === '
|
|
147
|
+
if (toolName === 'ace-find-references' || toolName === 'ace-find_references') {
|
|
128
148
|
const references = Array.isArray(data) ? data : [];
|
|
129
149
|
if (references.length === 0) {
|
|
130
150
|
return (React.createElement(Box, { marginLeft: 2 },
|
|
@@ -146,7 +166,7 @@ function renderACEPreview(toolName, data, maxLines) {
|
|
|
146
166
|
" more references)"))));
|
|
147
167
|
}
|
|
148
168
|
// Handle ace-find-definition result
|
|
149
|
-
if (toolName === 'ace-find-definition' || toolName === '
|
|
169
|
+
if (toolName === 'ace-find-definition' || toolName === 'ace-find_definition') {
|
|
150
170
|
if (!data) {
|
|
151
171
|
return (React.createElement(Box, { marginLeft: 2 },
|
|
152
172
|
React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 Definition not found")));
|
|
@@ -163,7 +183,7 @@ function renderACEPreview(toolName, data, maxLines) {
|
|
|
163
183
|
data.line)));
|
|
164
184
|
}
|
|
165
185
|
// Handle ace-file-outline result
|
|
166
|
-
if (toolName === 'ace-file-outline' || toolName === '
|
|
186
|
+
if (toolName === 'ace-file-outline' || toolName === 'ace-file_outline') {
|
|
167
187
|
const symbols = Array.isArray(data) ? data : [];
|
|
168
188
|
if (symbols.length === 0) {
|
|
169
189
|
return (React.createElement(Box, { marginLeft: 2 },
|
|
@@ -186,7 +206,7 @@ function renderACEPreview(toolName, data, maxLines) {
|
|
|
186
206
|
" more symbols)"))));
|
|
187
207
|
}
|
|
188
208
|
// Handle ace-semantic-search result
|
|
189
|
-
if (toolName === 'ace-semantic-search' || toolName === '
|
|
209
|
+
if (toolName === 'ace-semantic-search' || toolName === 'ace-semantic_search') {
|
|
190
210
|
const totalResults = (data.symbols?.length || 0) + (data.references?.length || 0);
|
|
191
211
|
if (totalResults === 0) {
|
|
192
212
|
return (React.createElement(Box, { marginLeft: 2 },
|