snow-ai 0.3.14 → 0.3.15
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.
|
@@ -4,6 +4,39 @@ import TextInput from 'ink-text-input';
|
|
|
4
4
|
import { Alert, Spinner } from '@inkjs/ui';
|
|
5
5
|
import { getMCPServicesInfo } from '../../utils/mcpToolsManager.js';
|
|
6
6
|
import { createSubAgent, updateSubAgent, getSubAgent, validateSubAgent, } from '../../utils/subAgentConfig.js';
|
|
7
|
+
// Focus event handling - prevent terminal focus events from appearing as input
|
|
8
|
+
const focusEventTokenRegex = /(?:\x1b)?\[[0-9;]*[IO]/g;
|
|
9
|
+
const isFocusEventInput = (value) => {
|
|
10
|
+
if (!value) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (value === '\x1b[I' ||
|
|
14
|
+
value === '\x1b[O' ||
|
|
15
|
+
value === '[I' ||
|
|
16
|
+
value === '[O') {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const trimmed = value.trim();
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const tokens = trimmed.match(focusEventTokenRegex);
|
|
24
|
+
if (!tokens) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const normalized = trimmed.replace(/\s+/g, '');
|
|
28
|
+
const tokensCombined = tokens.join('');
|
|
29
|
+
return tokensCombined === normalized;
|
|
30
|
+
};
|
|
31
|
+
const stripFocusArtifacts = (value) => {
|
|
32
|
+
if (!value) {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
return value
|
|
36
|
+
.replace(/\x1b\[[0-9;]*[IO]/g, '')
|
|
37
|
+
.replace(/\[[0-9;]*[IO]/g, '')
|
|
38
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
|
39
|
+
};
|
|
7
40
|
const toolCategories = [
|
|
8
41
|
{
|
|
9
42
|
name: 'Filesystem Tools',
|
|
@@ -191,7 +224,15 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
191
224
|
setSaveError(error instanceof Error ? error.message : 'Failed to save sub-agent');
|
|
192
225
|
}
|
|
193
226
|
}, [agentName, description, selectedTools, onSave, isEditMode, agentId]);
|
|
194
|
-
useInput((
|
|
227
|
+
useInput((rawInput, key) => {
|
|
228
|
+
const input = stripFocusArtifacts(rawInput);
|
|
229
|
+
// Ignore focus events completely
|
|
230
|
+
if (!input && isFocusEventInput(rawInput)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (isFocusEventInput(rawInput)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
195
236
|
if (key.escape) {
|
|
196
237
|
onBack();
|
|
197
238
|
return;
|
|
@@ -341,11 +382,11 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
341
382
|
React.createElement(Box, { flexDirection: "column" },
|
|
342
383
|
React.createElement(Text, { bold: true, color: currentField === 'name' ? 'green' : 'white' }, "Agent Name:"),
|
|
343
384
|
React.createElement(Box, { marginLeft: 2 },
|
|
344
|
-
React.createElement(TextInput, { value: agentName, onChange: setAgentName, placeholder: "Enter agent name...", focus: currentField === 'name' }))),
|
|
385
|
+
React.createElement(TextInput, { value: agentName, onChange: value => setAgentName(stripFocusArtifacts(value)), placeholder: "Enter agent name...", focus: currentField === 'name' }))),
|
|
345
386
|
React.createElement(Box, { flexDirection: "column" },
|
|
346
387
|
React.createElement(Text, { bold: true, color: currentField === 'description' ? 'green' : 'white' }, "Description:"),
|
|
347
388
|
React.createElement(Box, { marginLeft: 2 },
|
|
348
|
-
React.createElement(TextInput, { value: description, onChange: setDescription, placeholder: "Enter agent description...", focus: currentField === 'description' }))),
|
|
389
|
+
React.createElement(TextInput, { value: description, onChange: value => setDescription(stripFocusArtifacts(value)), placeholder: "Enter agent description...", focus: currentField === 'description' }))),
|
|
349
390
|
renderToolSelection(),
|
|
350
391
|
React.createElement(Box, { marginTop: 1 },
|
|
351
392
|
React.createElement(Text, { color: "gray", dimColor: true }, "\u2191\u2193: Navigate | \u2190\u2192: Switch category | Space: Toggle | A: Toggle all | Enter: Save | Esc: Back")))));
|
|
@@ -2,11 +2,23 @@ import React, { useState, useCallback, useEffect } from 'react';
|
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
4
|
import { getSubAgents, deleteSubAgent, } from '../../utils/subAgentConfig.js';
|
|
5
|
+
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
|
5
6
|
export default function SubAgentListScreen({ onBack, onAdd, onEdit, inlineMode = false, }) {
|
|
7
|
+
const { columns } = useTerminalSize();
|
|
6
8
|
const [agents, setAgents] = useState([]);
|
|
7
9
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
8
10
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
9
11
|
const [deleteSuccess, setDeleteSuccess] = useState(false);
|
|
12
|
+
// Truncate text based on terminal width
|
|
13
|
+
const truncateText = useCallback((text, prefixLength = 0) => {
|
|
14
|
+
if (!text)
|
|
15
|
+
return text;
|
|
16
|
+
// Reserve space for indentation (3), prefix text, padding (5), and ellipsis (3)
|
|
17
|
+
const maxLength = Math.max(20, columns - prefixLength - 3 - 5 - 3);
|
|
18
|
+
if (text.length <= maxLength)
|
|
19
|
+
return text;
|
|
20
|
+
return text.substring(0, maxLength) + '...';
|
|
21
|
+
}, [columns]);
|
|
10
22
|
// Load agents on mount
|
|
11
23
|
useEffect(() => {
|
|
12
24
|
loadAgents();
|
|
@@ -99,8 +111,11 @@ export default function SubAgentListScreen({ onBack, onAdd, onEdit, inlineMode =
|
|
|
99
111
|
React.createElement(Text, { color: isSelected ? 'green' : 'white', bold: isSelected },
|
|
100
112
|
isSelected ? '❯ ' : ' ',
|
|
101
113
|
agent.name)),
|
|
102
|
-
isSelected && (React.createElement(Box, { flexDirection: "column", marginLeft:
|
|
103
|
-
React.createElement(Text, { color: "gray" },
|
|
114
|
+
isSelected && (React.createElement(Box, { flexDirection: "column", marginLeft: 3 },
|
|
115
|
+
React.createElement(Text, { color: "gray" },
|
|
116
|
+
"Description:",
|
|
117
|
+
' ',
|
|
118
|
+
truncateText(agent.description || 'No description', 'Description: '.length)),
|
|
104
119
|
React.createElement(Text, { color: "gray" },
|
|
105
120
|
"Tools: ",
|
|
106
121
|
agent.tools.length,
|
|
@@ -29,12 +29,16 @@ export function getTodoService() {
|
|
|
29
29
|
return todoService;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* Generate a hash of the current MCP configuration
|
|
32
|
+
* Generate a hash of the current MCP configuration and sub-agents
|
|
33
33
|
*/
|
|
34
34
|
function generateConfigHash() {
|
|
35
35
|
try {
|
|
36
36
|
const mcpConfig = getMCPConfig();
|
|
37
|
-
|
|
37
|
+
const subAgents = getSubAgentTools(); // Include sub-agents in hash
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
mcpServers: mcpConfig.mcpServers,
|
|
40
|
+
subAgents: subAgents.map(t => t.name), // Only track agent names for hash
|
|
41
|
+
});
|
|
38
42
|
}
|
|
39
43
|
catch {
|
|
40
44
|
return '';
|