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((input, key) => {
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: 2 },
103
- React.createElement(Text, { color: "gray" }, agent.description || 'No description'),
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
- return JSON.stringify(mcpConfig.mcpServers);
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 '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {