@iaforged/context-code 1.1.9 → 1.2.1

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.
Files changed (78) hide show
  1. package/README.md +24 -0
  2. package/dist/src/commands/model/model.js +9 -5
  3. package/dist/src/commands/timeline/index.js +8 -0
  4. package/dist/src/commands/timeline/timeline.js +194 -0
  5. package/dist/src/commands.js +2 -0
  6. package/dist/src/components/AgentActivitySidebar.js +50 -0
  7. package/dist/src/components/AgentProgressLine.js +5 -5
  8. package/dist/src/components/ModelPicker.js +252 -441
  9. package/dist/src/components/PromptInput/PromptInputFooter.js +10 -29
  10. package/dist/src/components/Spinner/TeammateSpinnerLine.js +20 -62
  11. package/dist/src/components/Spinner/TeammateSpinnerTree.js +16 -258
  12. package/dist/src/components/Spinner/teammateSelectHint.js +1 -1
  13. package/dist/src/components/Spinner/utils.js +3 -6
  14. package/dist/src/components/ThemeBrowser.js +120 -0
  15. package/dist/src/components/ThemePicker.js +113 -321
  16. package/dist/src/components/design-system/ThemeProvider.js +3 -0
  17. package/dist/src/components/mcp/MCPListPanel.js +138 -444
  18. package/dist/src/components/permissions/SandboxPermissionRequest.js +5 -5
  19. package/dist/src/components/teams/TeamStatus.js +7 -71
  20. package/dist/src/constants/spinnerVerbs.js +80 -180
  21. package/dist/src/context/modalStackContext.js +12 -0
  22. package/dist/src/hooks/useTextInput.js +28 -18
  23. package/dist/src/main.js +12 -0
  24. package/dist/src/screens/REPL.js +386 -320
  25. package/dist/src/skills/loadSkillsDir.js +1 -0
  26. package/dist/src/tools/AgentTool/UI.js +8 -8
  27. package/dist/src/tools/BashTool/bashSecurity.js +1 -1
  28. package/dist/src/utils/handlePromptSubmit.js +12 -2
  29. package/dist/src/utils/processUserInput/processSlashCommand.js +9 -5
  30. package/dist/src/utils/sembleMcp/common.js +5 -0
  31. package/dist/src/utils/sembleMcp/setup.js +119 -0
  32. package/dist/src/utils/theme.js +24 -3
  33. package/dist/src/utils/themes/bootstrap.js +109 -0
  34. package/dist/src/utils/themes/builtin/opencode/_index.json +41 -0
  35. package/dist/src/utils/themes/builtin/opencode/amoled.json +49 -0
  36. package/dist/src/utils/themes/builtin/opencode/aura.json +51 -0
  37. package/dist/src/utils/themes/builtin/opencode/ayu.json +51 -0
  38. package/dist/src/utils/themes/builtin/opencode/carbonfox.json +53 -0
  39. package/dist/src/utils/themes/builtin/opencode/catppuccin-frappe.json +85 -0
  40. package/dist/src/utils/themes/builtin/opencode/catppuccin-macchiato.json +85 -0
  41. package/dist/src/utils/themes/builtin/opencode/catppuccin.json +45 -0
  42. package/dist/src/utils/themes/builtin/opencode/cobalt2.json +87 -0
  43. package/dist/src/utils/themes/builtin/opencode/cursor.json +91 -0
  44. package/dist/src/utils/themes/builtin/opencode/dracula.json +49 -0
  45. package/dist/src/utils/themes/builtin/opencode/everforest.json +89 -0
  46. package/dist/src/utils/themes/builtin/opencode/flexoki.json +86 -0
  47. package/dist/src/utils/themes/builtin/opencode/github.json +85 -0
  48. package/dist/src/utils/themes/builtin/opencode/gruvbox.json +45 -0
  49. package/dist/src/utils/themes/builtin/opencode/kanagawa.json +89 -0
  50. package/dist/src/utils/themes/builtin/opencode/lucent-orng.json +87 -0
  51. package/dist/src/utils/themes/builtin/opencode/material.json +87 -0
  52. package/dist/src/utils/themes/builtin/opencode/matrix.json +91 -0
  53. package/dist/src/utils/themes/builtin/opencode/mercury.json +86 -0
  54. package/dist/src/utils/themes/builtin/opencode/monokai.json +49 -0
  55. package/dist/src/utils/themes/builtin/opencode/nightowl.json +46 -0
  56. package/dist/src/utils/themes/builtin/opencode/nord.json +46 -0
  57. package/dist/src/utils/themes/builtin/opencode/oc-2.json +88 -0
  58. package/dist/src/utils/themes/builtin/opencode/one-dark.json +89 -0
  59. package/dist/src/utils/themes/builtin/opencode/onedarkpro.json +45 -0
  60. package/dist/src/utils/themes/builtin/opencode/opencode.json +89 -0
  61. package/dist/src/utils/themes/builtin/opencode/orng.json +87 -0
  62. package/dist/src/utils/themes/builtin/opencode/osaka-jade.json +88 -0
  63. package/dist/src/utils/themes/builtin/opencode/palenight.json +85 -0
  64. package/dist/src/utils/themes/builtin/opencode/rosepine.json +85 -0
  65. package/dist/src/utils/themes/builtin/opencode/shadesofpurple.json +51 -0
  66. package/dist/src/utils/themes/builtin/opencode/solarized.json +49 -0
  67. package/dist/src/utils/themes/builtin/opencode/synthwave84.json +87 -0
  68. package/dist/src/utils/themes/builtin/opencode/tokyonight.json +47 -0
  69. package/dist/src/utils/themes/builtin/opencode/vercel.json +90 -0
  70. package/dist/src/utils/themes/builtin/opencode/vesper.json +51 -0
  71. package/dist/src/utils/themes/builtin/opencode/zenburn.json +87 -0
  72. package/dist/src/utils/themes/index.js +4 -0
  73. package/dist/src/utils/themes/loader.js +147 -0
  74. package/dist/src/utils/themes/opencodeMapper.js +124 -0
  75. package/dist/src/utils/themes/resolver.js +66 -0
  76. package/dist/src/utils/themes/types.js +1 -0
  77. package/docs/MCP_SERVERS.md +27 -1
  78. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { feature } from '../../recovery/bunBundleShim.js';
1
+ import { MACRO, feature } from '../../recovery/bunBundleShim.js';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { memo, useMemo, useRef } from 'react';
4
4
  import { isBridgeEnabled } from '../../bridge/bridgeEnabled.js';
@@ -23,27 +23,15 @@ function PromptInputFooter({ apiKeyStatus, debug, exitMessage, vimMode, mode, au
23
23
  messagesRef.current = messages;
24
24
  const lastAssistantMessageId = useMemo(() => getLastAssistantMessageId(messages), [messages]);
25
25
  const isNarrow = columns < 80;
26
- // In fullscreen the bottom slot is flexShrink:0, so every row here is a row
27
- // stolen from the ScrollBox. Drop the optional StatusLine first. Non-fullscreen
28
- // has terminal scrollback to absorb overflow, so we never hide StatusLine there.
29
26
  const isFullscreen = isFullscreenEnvEnabled();
30
27
  const isShort = isFullscreen && rows < 24;
31
- // Pill highlights when tasks is the active footer item AND no specific
32
- // agent row is selected. When coordinatorTaskIndex >= 0 the pointer has
33
- // moved into CoordinatorTaskPanel, so the pill should un-highlight.
34
- // coordinatorTaskCount === 0 covers the bash-only case (no agent rows
35
- // exist, pill is the only selectable item).
36
28
  const coordinatorTaskCount = useCoordinatorTaskCount();
37
29
  const coordinatorTaskIndex = useAppState(s => s.coordinatorTaskIndex);
30
+ const mainLoopModel = useAppState(s => s.mainLoopModelForSession ?? s.mainLoopModel);
31
+ const modelStatusText = isNarrow ? `Modelo: ${mainLoopModel}` : `ContextCode v${MACRO.VERSION} · Modelo activo: ${mainLoopModel}`;
38
32
  const pillSelected = tasksSelected && (coordinatorTaskCount === 0 || coordinatorTaskIndex < 0);
39
- // Hide `? for shortcuts` if the user has a custom status line, or during ctrl-r
40
33
  const suppressHint = suppressHintFromProps || statusLineShouldDisplay(settings) || isSearching;
41
- // Fullscreen: portal data to FullscreenLayout see promptOverlayContext.tsx
42
- const overlayData = useMemo(() => isFullscreen && suggestions.length ? {
43
- suggestions,
44
- selectedSuggestion,
45
- maxColumnWidth
46
- } : null, [isFullscreen, suggestions, selectedSuggestion, maxColumnWidth]);
34
+ const overlayData = useMemo(() => isFullscreen && suggestions.length ? { suggestions, selectedSuggestion, maxColumnWidth } : null, [isFullscreen, suggestions, selectedSuggestion, maxColumnWidth]);
47
35
  useSetPromptOverlay(overlayData);
48
36
  if (suggestions.length && !isFullscreen) {
49
37
  return _jsx(Box, { paddingX: 2, paddingY: 0, children: _jsx(PromptInputFooterSuggestions, { suggestions: suggestions, selectedSuggestion: selectedSuggestion, maxColumnWidth: maxColumnWidth }) });
@@ -51,7 +39,7 @@ function PromptInputFooter({ apiKeyStatus, debug, exitMessage, vimMode, mode, au
51
39
  if (helpOpen) {
52
40
  return _jsx(PromptInputHelpMenu, { dimColor: true, fixedWidth: true, paddingX: 2 });
53
41
  }
54
- return _jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: isNarrow ? 'column' : 'row', justifyContent: isNarrow ? 'flex-start' : 'space-between', paddingX: 2, gap: isNarrow ? 0 : 1, children: [_jsxs(Box, { flexDirection: "column", flexShrink: isNarrow ? 0 : 1, children: [mode === 'prompt' && !isShort && !exitMessage.show && !isPasting && statusLineShouldDisplay(settings) && _jsx(StatusLine, { messagesRef: messagesRef, lastAssistantMessageId: lastAssistantMessageId, vimMode: vimMode }), _jsx(PromptInputFooterLeftSide, { exitMessage: exitMessage, vimMode: vimMode, mode: mode, toolPermissionContext: toolPermissionContext, suppressHint: suppressHint, isLoading: isLoading, tasksSelected: pillSelected, teamsSelected: teamsSelected, teammateFooterIndex: teammateFooterIndex, tmuxSelected: tmuxSelected, isPasting: isPasting, isSearching: isSearching, historyQuery: historyQuery, setHistoryQuery: setHistoryQuery, historyFailedMatch: historyFailedMatch, onOpenTasksDialog: onOpenTasksDialog })] }), _jsxs(Box, { flexShrink: 1, gap: 1, children: [isFullscreen ? null : _jsx(Notifications, { apiKeyStatus: apiKeyStatus, autoUpdaterResult: autoUpdaterResult, debug: debug, isAutoUpdating: isAutoUpdating, verbose: verbose, messages: messages, onAutoUpdaterResult: onAutoUpdaterResult, onChangeIsUpdating: onChangeIsUpdating, ideSelection: ideSelection, mcpClients: mcpClients, isInputWrapped: isInputWrapped, isNarrow: isNarrow }), process.env.USER_TYPE === 'ant' && isUndercover() && _jsx(Text, { dimColor: true, children: "undercover" }), _jsx(BridgeStatusIndicator, { bridgeSelected: bridgeSelected })] })] }), process.env.USER_TYPE === 'ant' && _jsx(CoordinatorTaskPanel, {})] });
42
+ return _jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: isNarrow ? 'column' : 'row', justifyContent: isNarrow ? 'flex-start' : 'space-between', paddingX: 2, gap: isNarrow ? 0 : 1, children: [_jsxs(Box, { flexDirection: "column", flexShrink: isNarrow ? 0 : 1, children: [mode === 'prompt' && !isShort && !exitMessage.show && !isPasting && statusLineShouldDisplay(settings) && _jsx(StatusLine, { messagesRef: messagesRef, lastAssistantMessageId: lastAssistantMessageId, vimMode: vimMode }), _jsx(PromptInputFooterLeftSide, { exitMessage: exitMessage, vimMode: vimMode, mode: mode, toolPermissionContext: toolPermissionContext, suppressHint: suppressHint, isLoading: isLoading, tasksSelected: pillSelected, teamsSelected: teamsSelected, teammateFooterIndex: teammateFooterIndex, tmuxSelected: tmuxSelected, isPasting: isPasting, isSearching: isSearching, historyQuery: historyQuery, setHistoryQuery: setHistoryQuery, historyFailedMatch: historyFailedMatch, onOpenTasksDialog: onOpenTasksDialog })] }), _jsxs(Box, { flexShrink: 1, gap: 1, children: [isFullscreen ? null : _jsx(Notifications, { apiKeyStatus: apiKeyStatus, autoUpdaterResult: autoUpdaterResult, debug: debug, isAutoUpdating: isAutoUpdating, verbose: verbose, messages: messages, onAutoUpdaterResult: onAutoUpdaterResult, onChangeIsUpdating: onChangeIsUpdating, ideSelection: ideSelection, mcpClients: mcpClients, isInputWrapped: isInputWrapped, isNarrow: isNarrow }), process.env.USER_TYPE === 'ant' && isUndercover() && _jsx(Text, { dimColor: true, children: "undercover" }), _jsx(BridgeStatusIndicator, { bridgeSelected: bridgeSelected })] })] }), _jsx(Box, { paddingX: 2, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: modelStatusText }) }), process.env.USER_TYPE === 'ant' && _jsx(CoordinatorTaskPanel, {})] });
55
43
  }
56
44
  export default memo(PromptInputFooter);
57
45
  function BridgeStatusIndicator({ bridgeSelected }) {
@@ -60,23 +48,16 @@ function BridgeStatusIndicator({ bridgeSelected }) {
60
48
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
61
49
  const enabled = useAppState(s => s.replBridgeEnabled);
62
50
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
63
- const connected = useAppState(s_0 => s_0.replBridgeConnected);
51
+ const connected = useAppState(s => s.replBridgeConnected);
64
52
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
65
- const sessionActive = useAppState(s_1 => s_1.replBridgeSessionActive);
53
+ const sessionActive = useAppState(s => s.replBridgeSessionActive);
66
54
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
67
- const reconnecting = useAppState(s_2 => s_2.replBridgeReconnecting);
55
+ const reconnecting = useAppState(s => s.replBridgeReconnecting);
68
56
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
69
- const explicit = useAppState(s_3 => s_3.replBridgeExplicit);
70
- // Failed state is surfaced via notification (useReplBridge), not a footer pill.
57
+ const explicit = useAppState(s => s.replBridgeExplicit);
71
58
  if (!isBridgeEnabled() || !enabled)
72
59
  return null;
73
- const status = getBridgeStatus({
74
- error: undefined,
75
- connected,
76
- sessionActive,
77
- reconnecting
78
- });
79
- // For implicit (config-driven) remote, only show the reconnecting state
60
+ const status = getBridgeStatus({ error: undefined, connected, sessionActive, reconnecting });
80
61
  if (!explicit && status.label !== 'Reconectando Control Remoto') {
81
62
  return null;
82
63
  }
@@ -6,26 +6,20 @@ import { getSpinnerVerbs } from '../../constants/spinnerVerbs.js';
6
6
  import { TURN_COMPLETION_VERBS } from '../../constants/turnCompletionVerbs.js';
7
7
  import { useElapsedTime } from '../../hooks/useElapsedTime.js';
8
8
  import { useTerminalSize } from '../../hooks/useTerminalSize.js';
9
- import { stringWidth } from '../../ink/stringWidth.js';
10
9
  import { Box, Text } from '../../ink.js';
10
+ import { stringWidth } from '../../ink/stringWidth.js';
11
11
  import { summarizeRecentActivities } from '../../utils/collapseReadSearch.js';
12
12
  import { formatDuration, formatNumber, truncateToWidth } from '../../utils/format.js';
13
13
  import { toInkColor } from '../../utils/ink.js';
14
14
  import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js';
15
- /**
16
- * Extract the last 3 lines of content from a teammate's conversation.
17
- * Shows recent activity from any message type (user or assistant).
18
- */
19
15
  function getMessagePreview(messages) {
20
16
  if (!messages?.length)
21
17
  return [];
22
18
  const allLines = [];
23
19
  const maxLineLength = 80;
24
- // Collect lines from recent messages (newest first)
25
20
  for (let i = messages.length - 1; i >= 0 && allLines.length < 3; i--) {
26
21
  const msg = messages[i];
27
- // Only process messages that have content (user/assistant messages)
28
- if (!msg || msg.type !== 'user' && msg.type !== 'assistant' || !msg.message?.content?.length) {
22
+ if (!msg || (msg.type !== 'user' && msg.type !== 'assistant') || !msg.message?.content?.length) {
29
23
  continue;
30
24
  }
31
25
  const content = msg.message.content;
@@ -35,11 +29,9 @@ function getMessagePreview(messages) {
35
29
  if (!block || typeof block !== 'object')
36
30
  continue;
37
31
  if ('type' in block && block.type === 'tool_use' && 'name' in block) {
38
- // Try to show meaningful info from tool input
39
32
  const input = 'input' in block ? block.input : null;
40
- let toolLine = `Using ${block.name}…`;
33
+ let toolLine = `Usando ${block.name}...`;
41
34
  if (input) {
42
- // Look for common descriptive fields
43
35
  const desc = input.description || input.prompt || input.command || input.query || input.pattern;
44
36
  if (desc) {
45
37
  toolLine = desc.split('\n')[0] ?? toolLine;
@@ -49,7 +41,6 @@ function getMessagePreview(messages) {
49
41
  }
50
42
  else if ('type' in block && block.type === 'text' && 'text' in block) {
51
43
  const textLines = block.text.split('\n').filter(l => l.trim());
52
- // Take from end of text (most recent lines)
53
44
  for (let j = textLines.length - 1; j >= 0 && allLines.length < 3; j--) {
54
45
  const line = textLines[j];
55
46
  if (!line)
@@ -59,76 +50,52 @@ function getMessagePreview(messages) {
59
50
  }
60
51
  }
61
52
  }
62
- // Reverse so oldest of the 3 is first (reading order)
63
53
  return allLines.reverse();
64
54
  }
65
55
  export function TeammateSpinnerLine({ teammate, isLast, isSelected, isForegrounded, allIdle, showPreview }) {
66
56
  const [randomVerb] = useState(() => teammate.spinnerVerb ?? sample(getSpinnerVerbs()));
67
57
  const [pastTenseVerb] = useState(() => teammate.pastTenseVerb ?? sample(TURN_COMPLETION_VERBS));
68
58
  const isHighlighted = isSelected || isForegrounded;
69
- const treeChar = isHighlighted ? isLast ? '╘═' : '╞═' : isLast ? '└─' : '├─';
59
+ const treeChar = isHighlighted ? (isLast ? '╘═' : '╞═') : isLast ? '└─' : '├─';
70
60
  const nameColor = toInkColor(teammate.identity.color);
71
61
  const { columns } = useTerminalSize();
72
- // Track when teammate became idle (for "Idle for X..." display)
73
62
  const idleStartRef = useRef(null);
74
- // Freeze elapsed time when entering all-idle state
75
63
  const frozenDurationRef = useRef(null);
76
- // Track idle start time
77
64
  if (teammate.isIdle && idleStartRef.current === null) {
78
65
  idleStartRef.current = Date.now();
79
66
  }
80
67
  else if (!teammate.isIdle) {
81
68
  idleStartRef.current = null;
82
69
  }
83
- // Reset frozen duration when leaving all-idle state
84
70
  if (!allIdle && frozenDurationRef.current !== null) {
85
71
  frozenDurationRef.current = null;
86
72
  }
87
- // Get elapsed idle time (how long they've been idle) - for "Idle for X..." display
88
73
  const idleElapsedTime = useElapsedTime(idleStartRef.current ?? Date.now(), teammate.isIdle && !allIdle);
89
- // Freeze the duration when we first detect all idle
90
- // Use the teammate's actual work time (since task started) for the past-tense display
91
74
  if (allIdle && frozenDurationRef.current === null) {
92
75
  frozenDurationRef.current = formatDuration(Math.max(0, Date.now() - teammate.startTime - (teammate.totalPausedMs ?? 0)));
93
76
  }
94
- // Use frozen work duration when all idle, otherwise use idle elapsed time
95
- const displayTime = allIdle ? frozenDurationRef.current ?? (() => {
96
- throw new Error(`frozenDurationRef is null for idle teammate ${teammate.identity.agentName}`);
97
- })() : idleElapsedTime;
98
- // Layout: paddingLeft(3) + pointer(1) + space(1) + treeChar(2) + space(1) = 8 fixed chars
99
- // Then optionally: @name + ": " OR just ": "
100
- // Then: activity text + optional extras (stats, hints)
77
+ const displayTime = allIdle ? frozenDurationRef.current ?? '0s' : idleElapsedTime;
101
78
  const basePrefix = 8;
102
79
  const fullAgentName = `@${teammate.identity.agentName}`;
103
80
  const fullNameWidth = stringWidth(fullAgentName);
104
- // Get stats from progress
105
81
  const toolUseCount = teammate.progress?.toolUseCount ?? 0;
106
82
  const tokenCount = teammate.progress?.tokenCount ?? 0;
107
- const statsText = ` · ${toolUseCount} tool ${toolUseCount === 1 ? 'use' : 'uses'} · ${formatNumber(tokenCount)} tokens`;
83
+ const statsText = ` · ${toolUseCount} uso${toolUseCount === 1 ? '' : 's'} de herramienta · ${formatNumber(tokenCount)} tokens`;
108
84
  const statsWidth = stringWidth(statsText);
109
85
  const selectHintText = ` · ${TEAMMATE_SELECT_HINT}`;
110
86
  const selectHintWidth = stringWidth(selectHintText);
111
- const viewHintText = ' · enter to view';
87
+ const viewHintText = ' · Enter para ver';
112
88
  const viewHintWidth = stringWidth(viewHintText);
113
- // Progressive responsive layout:
114
- // Wide (80+): full name + activity + stats + hint
115
- // Medium (60-80): full name + activity
116
- // Narrow (<60): hide name, just show activity
117
89
  const minActivityWidth = 25;
118
- // Hide name on narrow terminals (< 60 cols) or if there's not enough room
119
90
  const spaceWithFullName = columns - basePrefix - fullNameWidth - 2;
120
91
  const showName = columns >= 60 && spaceWithFullName >= minActivityWidth;
121
- const nameWidth = showName ? fullNameWidth + 2 : 0; // +2 for ": " when name shown
92
+ const nameWidth = showName ? fullNameWidth + 2 : 0;
122
93
  const availableForActivity = columns - basePrefix - nameWidth;
123
- // Progressive hiding: view hint select hint stats
124
- // Stats always visible (dimmed when not selected); hints only when highlighted/selected
125
- const showViewHint = isSelected && !isForegrounded && availableForActivity > viewHintWidth + statsWidth + minActivityWidth + 5;
126
- const showSelectHint = isHighlighted && availableForActivity > selectHintWidth + (showViewHint ? viewHintWidth : 0) + statsWidth + minActivityWidth + 5;
94
+ const showViewHint = !!(isSelected && !isForegrounded && availableForActivity > viewHintWidth + statsWidth + minActivityWidth + 5);
95
+ const showSelectHint = !!(isHighlighted && availableForActivity > selectHintWidth + (showViewHint ? viewHintWidth : 0) + statsWidth + minActivityWidth + 5);
127
96
  const showStats = availableForActivity > statsWidth + minActivityWidth + 5;
128
- // Activity text gets remaining space
129
97
  const extrasCost = (showStats ? statsWidth : 0) + (showSelectHint ? selectHintWidth : 0) + (showViewHint ? viewHintWidth : 0);
130
98
  const activityMaxWidth = Math.max(minActivityWidth, availableForActivity - extrasCost - 1);
131
- // Format the activity text for active teammates, rolling up search/read ops
132
99
  const activityText = (() => {
133
100
  const activities = teammate.progress?.recentActivities;
134
101
  if (activities && activities.length > 0) {
@@ -141,30 +108,21 @@ export function TeammateSpinnerLine({ teammate, isLast, isSelected, isForeground
141
108
  return truncateToWidth(desc, activityMaxWidth);
142
109
  return randomVerb;
143
110
  })();
144
- // Status rendering logic
145
111
  const renderStatus = () => {
146
- if (teammate.shutdownRequested) {
147
- return _jsx(Text, { dimColor: true, children: "[stopping]" });
148
- }
149
- if (teammate.awaitingPlanApproval) {
150
- return _jsx(Text, { color: "warning", children: "[awaiting approval]" });
151
- }
112
+ if (teammate.shutdownRequested)
113
+ return _jsx(Text, { dimColor: true, children: "[deteniendo]" });
114
+ if (teammate.awaitingPlanApproval)
115
+ return _jsx(Text, { color: "warning", children: "[esperando aprobaci\u00F3n]" });
152
116
  if (teammate.isIdle) {
153
- if (allIdle) {
154
- return _jsxs(Text, { dimColor: true, children: [pastTenseVerb, " for ", displayTime] });
155
- }
156
- return _jsxs(Text, { dimColor: true, children: ["Idle for ", idleElapsedTime] });
117
+ if (allIdle)
118
+ return _jsxs(Text, { dimColor: true, children: [pastTenseVerb, " durante ", displayTime] });
119
+ return _jsxs(Text, { dimColor: true, children: ["Inactivo durante ", idleElapsedTime] });
157
120
  }
158
- // Active - show spinner glyph + activity description (only when not highlighted;
159
- // when highlighted, the main spinner above already shows the verb)
160
- if (isHighlighted) {
121
+ if (isHighlighted)
161
122
  return null;
162
- }
163
- return _jsx(Text, { dimColor: true, children: activityText?.endsWith('…') ? activityText : `${activityText}…` });
123
+ return _jsx(Text, { dimColor: true, children: activityText?.endsWith('...') ? activityText : `${activityText}...` });
164
124
  };
165
- // Get preview lines if enabled
166
125
  const previewLines = showPreview ? getMessagePreview(teammate.messages) : [];
167
- // Tree continuation character for preview lines
168
126
  const previewTreeChar = isLast ? ' ' : '│ ';
169
- return _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { color: isSelected ? 'suggestion' : undefined, bold: isSelected, children: isSelected ? figures.pointer : ' ' }), _jsxs(Text, { dimColor: !isSelected, children: [treeChar, " "] }), showName && _jsxs(Text, { color: isSelected ? 'suggestion' : nameColor, children: ["@", teammate.identity.agentName] }), showName && _jsx(Text, { dimColor: !isSelected, children: ": " }), renderStatus(), showStats && _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", toolUseCount, " tool ", toolUseCount === 1 ? 'use' : 'uses', " \u00B7", ' ', formatNumber(tokenCount), " tokens"] }), showSelectHint && _jsxs(Text, { dimColor: true, children: [" \u00B7 ", TEAMMATE_SELECT_HINT] }), showViewHint && _jsx(Text, { dimColor: true, children: " \u00B7 enter to view" })] }), previewLines.map((line, idx) => _jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { dimColor: true, children: " " }), _jsxs(Text, { dimColor: true, children: [previewTreeChar, " "] }), _jsx(Text, { dimColor: true, children: line })] }, idx))] });
127
+ return _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { color: isSelected ? 'suggestion' : undefined, bold: isSelected, children: isSelected ? figures.pointer : ' ' }), _jsxs(Text, { dimColor: !isSelected, children: [treeChar, " "] }), showName && _jsxs(Text, { color: isSelected ? 'suggestion' : nameColor, children: ["@", teammate.identity.agentName] }), showName && _jsx(Text, { dimColor: !isSelected, children: ": " }), renderStatus(), showStats && _jsx(Text, { dimColor: true, children: statsText }), showSelectHint && _jsx(Text, { dimColor: true, children: selectHintText }), showViewHint && _jsx(Text, { dimColor: true, children: viewHintText })] }), previewLines.map((line, idx) => _jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { dimColor: true, children: " " }), _jsxs(Text, { dimColor: true, children: [previewTreeChar, " "] }), _jsx(Text, { dimColor: true, children: line })] }, idx))] });
170
128
  }
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { c as _c } from "react/compiler-runtime";
3
2
  import figures from 'figures';
4
3
  import { Box, Text } from '../../ink.js';
5
4
  import { useAppState } from '../../state/AppState.js';
@@ -7,262 +6,21 @@ import { getRunningTeammatesSorted } from '../../tasks/InProcessTeammateTask/InP
7
6
  import { formatNumber } from '../../utils/format.js';
8
7
  import { TeammateSpinnerLine } from './TeammateSpinnerLine.js';
9
8
  import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js';
10
- export function TeammateSpinnerTree(t0) {
11
- const $ = _c(61);
12
- const { selectedIndex, isInSelectionMode, allIdle, leaderVerb, leaderTokenCount, leaderIdleText } = t0;
13
- const tasks = useAppState(_temp);
14
- const viewingAgentTaskId = useAppState(_temp2);
15
- const showTeammateMessagePreview = useAppState(_temp3);
16
- let T0;
17
- let isHideSelected;
18
- let t1;
19
- let t2;
20
- let t3;
21
- let t4;
22
- let t5;
23
- if ($[0] !== allIdle || $[1] !== isInSelectionMode || $[2] !== leaderIdleText || $[3] !== leaderTokenCount || $[4] !== leaderVerb || $[5] !== selectedIndex || $[6] !== showTeammateMessagePreview || $[7] !== tasks || $[8] !== viewingAgentTaskId) {
24
- t5 = Symbol.for("react.early_return_sentinel");
25
- bb0: {
26
- const teammateTasks = getRunningTeammatesSorted(tasks);
27
- if (teammateTasks.length === 0) {
28
- t5 = null;
29
- break bb0;
30
- }
31
- const isLeaderForegrounded = viewingAgentTaskId === undefined;
32
- const isLeaderSelected = isInSelectionMode && selectedIndex === -1;
33
- const isLeaderHighlighted = isLeaderForegrounded || isLeaderSelected;
34
- isHideSelected = isInSelectionMode === true && selectedIndex === teammateTasks.length;
35
- T0 = Box;
36
- t1 = "column";
37
- t2 = 1;
38
- const t6 = isLeaderSelected ? "suggestion" : undefined;
39
- const t7 = isLeaderSelected ? figures.pointer : " ";
40
- let t8;
41
- if ($[16] !== isLeaderHighlighted || $[17] !== t6 || $[18] !== t7) {
42
- t8 = _jsx(Text, { color: t6, bold: isLeaderHighlighted, children: t7 });
43
- $[16] = isLeaderHighlighted;
44
- $[17] = t6;
45
- $[18] = t7;
46
- $[19] = t8;
47
- }
48
- else {
49
- t8 = $[19];
50
- }
51
- const t9 = !isLeaderHighlighted;
52
- const t10 = isLeaderHighlighted ? "\u2552\u2550" : "\u250C\u2500";
53
- let t11;
54
- if ($[20] !== isLeaderHighlighted || $[21] !== t10 || $[22] !== t9) {
55
- t11 = _jsxs(Text, { dimColor: t9, bold: isLeaderHighlighted, children: [t10, " "] });
56
- $[20] = isLeaderHighlighted;
57
- $[21] = t10;
58
- $[22] = t9;
59
- $[23] = t11;
60
- }
61
- else {
62
- t11 = $[23];
63
- }
64
- const t12 = isLeaderSelected ? "suggestion" : "cyan_FOR_SUBAGENTS_ONLY";
65
- let t13;
66
- if ($[24] !== isLeaderHighlighted || $[25] !== t12) {
67
- t13 = _jsx(Text, { bold: isLeaderHighlighted, color: t12, children: "team-lead" });
68
- $[24] = isLeaderHighlighted;
69
- $[25] = t12;
70
- $[26] = t13;
71
- }
72
- else {
73
- t13 = $[26];
74
- }
75
- let t14;
76
- if ($[27] !== isLeaderForegrounded || $[28] !== leaderVerb) {
77
- t14 = !isLeaderForegrounded && leaderVerb && _jsxs(Text, { dimColor: true, children: [": ", leaderVerb, "\u2026"] });
78
- $[27] = isLeaderForegrounded;
79
- $[28] = leaderVerb;
80
- $[29] = t14;
81
- }
82
- else {
83
- t14 = $[29];
84
- }
85
- let t15;
86
- if ($[30] !== isLeaderForegrounded || $[31] !== leaderIdleText || $[32] !== leaderVerb) {
87
- t15 = !isLeaderForegrounded && !leaderVerb && leaderIdleText && _jsxs(Text, { dimColor: true, children: [": ", leaderIdleText] });
88
- $[30] = isLeaderForegrounded;
89
- $[31] = leaderIdleText;
90
- $[32] = leaderVerb;
91
- $[33] = t15;
92
- }
93
- else {
94
- t15 = $[33];
95
- }
96
- let t16;
97
- if ($[34] !== isLeaderHighlighted || $[35] !== leaderTokenCount) {
98
- t16 = leaderTokenCount !== undefined && leaderTokenCount > 0 && _jsxs(Text, { dimColor: !isLeaderHighlighted, children: [" ", "\u00B7 ", formatNumber(leaderTokenCount), " tokens"] });
99
- $[34] = isLeaderHighlighted;
100
- $[35] = leaderTokenCount;
101
- $[36] = t16;
102
- }
103
- else {
104
- t16 = $[36];
105
- }
106
- let t17;
107
- if ($[37] !== isLeaderHighlighted) {
108
- t17 = isLeaderHighlighted && _jsxs(Text, { dimColor: true, children: [" \u00B7 ", TEAMMATE_SELECT_HINT] });
109
- $[37] = isLeaderHighlighted;
110
- $[38] = t17;
111
- }
112
- else {
113
- t17 = $[38];
114
- }
115
- let t18;
116
- if ($[39] !== isLeaderForegrounded || $[40] !== isLeaderSelected) {
117
- t18 = isLeaderSelected && !isLeaderForegrounded && _jsx(Text, { dimColor: true, children: " \u00B7 enter to view" });
118
- $[39] = isLeaderForegrounded;
119
- $[40] = isLeaderSelected;
120
- $[41] = t18;
121
- }
122
- else {
123
- t18 = $[41];
124
- }
125
- if ($[42] !== t11 || $[43] !== t13 || $[44] !== t14 || $[45] !== t15 || $[46] !== t16 || $[47] !== t17 || $[48] !== t18 || $[49] !== t8) {
126
- t3 = _jsxs(Box, { paddingLeft: 3, children: [t8, t11, t13, t14, t15, t16, t17, t18] });
127
- $[42] = t11;
128
- $[43] = t13;
129
- $[44] = t14;
130
- $[45] = t15;
131
- $[46] = t16;
132
- $[47] = t17;
133
- $[48] = t18;
134
- $[49] = t8;
135
- $[50] = t3;
136
- }
137
- else {
138
- t3 = $[50];
139
- }
140
- t4 = teammateTasks.map((teammate, index) => _jsx(TeammateSpinnerLine, { teammate: teammate, isLast: !isInSelectionMode && index === teammateTasks.length - 1, isSelected: isInSelectionMode && selectedIndex === index, isForegrounded: viewingAgentTaskId === teammate.id, allIdle: allIdle, showPreview: showTeammateMessagePreview }, teammate.id));
141
- }
142
- $[0] = allIdle;
143
- $[1] = isInSelectionMode;
144
- $[2] = leaderIdleText;
145
- $[3] = leaderTokenCount;
146
- $[4] = leaderVerb;
147
- $[5] = selectedIndex;
148
- $[6] = showTeammateMessagePreview;
149
- $[7] = tasks;
150
- $[8] = viewingAgentTaskId;
151
- $[9] = T0;
152
- $[10] = isHideSelected;
153
- $[11] = t1;
154
- $[12] = t2;
155
- $[13] = t3;
156
- $[14] = t4;
157
- $[15] = t5;
158
- }
159
- else {
160
- T0 = $[9];
161
- isHideSelected = $[10];
162
- t1 = $[11];
163
- t2 = $[12];
164
- t3 = $[13];
165
- t4 = $[14];
166
- t5 = $[15];
167
- }
168
- if (t5 !== Symbol.for("react.early_return_sentinel")) {
169
- return t5;
170
- }
171
- let t6;
172
- if ($[51] !== isHideSelected || $[52] !== isInSelectionMode) {
173
- t6 = isInSelectionMode && _jsx(HideRow, { isSelected: isHideSelected });
174
- $[51] = isHideSelected;
175
- $[52] = isInSelectionMode;
176
- $[53] = t6;
177
- }
178
- else {
179
- t6 = $[53];
180
- }
181
- let t7;
182
- if ($[54] !== T0 || $[55] !== t1 || $[56] !== t2 || $[57] !== t3 || $[58] !== t4 || $[59] !== t6) {
183
- t7 = _jsxs(T0, { flexDirection: t1, marginTop: t2, children: [t3, t4, t6] });
184
- $[54] = T0;
185
- $[55] = t1;
186
- $[56] = t2;
187
- $[57] = t3;
188
- $[58] = t4;
189
- $[59] = t6;
190
- $[60] = t7;
191
- }
192
- else {
193
- t7 = $[60];
194
- }
195
- return t7;
9
+ export function TeammateSpinnerTree({ selectedIndex, isInSelectionMode, allIdle, leaderVerb, leaderTokenCount, leaderIdleText }) {
10
+ const tasks = useAppState(s => s.tasks);
11
+ const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId);
12
+ const showTeammateMessagePreview = useAppState(s => s.showTeammateMessagePreview);
13
+ const teammateTasks = getRunningTeammatesSorted(tasks);
14
+ if (teammateTasks.length === 0)
15
+ return null;
16
+ const isLeaderForegrounded = viewingAgentTaskId === undefined;
17
+ const isLeaderSelected = !!isInSelectionMode && selectedIndex === -1;
18
+ const isLeaderHighlighted = isLeaderForegrounded || isLeaderSelected;
19
+ const hideSelected = isInSelectionMode === true && selectedIndex === teammateTasks.length;
20
+ const leaderConnector = isLeaderHighlighted ? '╒═' : '┌─';
21
+ return _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { color: isLeaderSelected ? 'suggestion' : undefined, bold: isLeaderHighlighted, children: isLeaderSelected ? figures.pointer : ' ' }), _jsxs(Text, { dimColor: !isLeaderHighlighted, bold: isLeaderHighlighted, children: [leaderConnector, " "] }), _jsx(Text, { bold: isLeaderHighlighted, color: isLeaderSelected ? 'suggestion' : 'cyan_FOR_SUBAGENTS_ONLY', children: "l\u00EDder" }), !isLeaderForegrounded && leaderVerb && _jsxs(Text, { dimColor: true, children: [": ", leaderVerb, "..."] }), !isLeaderForegrounded && !leaderVerb && leaderIdleText && _jsxs(Text, { dimColor: true, children: [": ", leaderIdleText] }), leaderTokenCount !== undefined && leaderTokenCount > 0 && _jsxs(Text, { dimColor: !isLeaderHighlighted, children: [" \u00B7 ", formatNumber(leaderTokenCount), " tokens"] }), isLeaderHighlighted && _jsxs(Text, { dimColor: true, children: [" \u00B7 ", TEAMMATE_SELECT_HINT] }), isLeaderSelected && !isLeaderForegrounded && _jsx(Text, { dimColor: true, children: " \u00B7 Enter para ver" })] }), teammateTasks.map((teammate, index) => _jsx(TeammateSpinnerLine, { teammate: teammate, isLast: !isInSelectionMode && index === teammateTasks.length - 1, isSelected: !!isInSelectionMode && selectedIndex === index, isForegrounded: viewingAgentTaskId === teammate.id, allIdle: allIdle, showPreview: showTeammateMessagePreview }, teammate.id)), isInSelectionMode && _jsx(HideRow, { isSelected: hideSelected })] });
196
22
  }
197
- function _temp3(s_1) {
198
- return s_1.showTeammateMessagePreview;
199
- }
200
- function _temp2(s_0) {
201
- return s_0.viewingAgentTaskId;
202
- }
203
- function _temp(s) {
204
- return s.tasks;
205
- }
206
- function HideRow(t0) {
207
- const $ = _c(18);
208
- const { isSelected } = t0;
209
- const t1 = isSelected ? "suggestion" : undefined;
210
- const t2 = isSelected ? figures.pointer : " ";
211
- let t3;
212
- if ($[0] !== isSelected || $[1] !== t1 || $[2] !== t2) {
213
- t3 = _jsx(Text, { color: t1, bold: isSelected, children: t2 });
214
- $[0] = isSelected;
215
- $[1] = t1;
216
- $[2] = t2;
217
- $[3] = t3;
218
- }
219
- else {
220
- t3 = $[3];
221
- }
222
- const t4 = !isSelected;
223
- const t5 = isSelected ? "\u2558\u2550" : "\u2514\u2500";
224
- let t6;
225
- if ($[4] !== isSelected || $[5] !== t4 || $[6] !== t5) {
226
- t6 = _jsxs(Text, { dimColor: t4, bold: isSelected, children: [t5, " "] });
227
- $[4] = isSelected;
228
- $[5] = t4;
229
- $[6] = t5;
230
- $[7] = t6;
231
- }
232
- else {
233
- t6 = $[7];
234
- }
235
- const t7 = !isSelected;
236
- let t8;
237
- if ($[8] !== isSelected || $[9] !== t7) {
238
- t8 = _jsx(Text, { dimColor: t7, bold: isSelected, children: "hide" });
239
- $[8] = isSelected;
240
- $[9] = t7;
241
- $[10] = t8;
242
- }
243
- else {
244
- t8 = $[10];
245
- }
246
- let t9;
247
- if ($[11] !== isSelected) {
248
- t9 = isSelected && _jsx(Text, { dimColor: true, children: " \u00B7 enter to collapse" });
249
- $[11] = isSelected;
250
- $[12] = t9;
251
- }
252
- else {
253
- t9 = $[12];
254
- }
255
- let t10;
256
- if ($[13] !== t3 || $[14] !== t6 || $[15] !== t8 || $[16] !== t9) {
257
- t10 = _jsxs(Box, { paddingLeft: 3, children: [t3, t6, t8, t9] });
258
- $[13] = t3;
259
- $[14] = t6;
260
- $[15] = t8;
261
- $[16] = t9;
262
- $[17] = t10;
263
- }
264
- else {
265
- t10 = $[17];
266
- }
267
- return t10;
23
+ function HideRow({ isSelected }) {
24
+ const connector = isSelected ? '╘═' : '└─';
25
+ return _jsxs(Box, { paddingLeft: 3, children: [_jsx(Text, { color: isSelected ? 'suggestion' : undefined, bold: isSelected, children: isSelected ? figures.pointer : ' ' }), _jsxs(Text, { dimColor: !isSelected, bold: isSelected, children: [connector, " "] }), _jsx(Text, { dimColor: !isSelected, bold: isSelected, children: "ocultar" }), isSelected && _jsx(Text, { dimColor: true, children: " \u00B7 Enter para colapsar" })] });
268
26
  }
@@ -1 +1 @@
1
- export const TEAMMATE_SELECT_HINT = 'shift + ↑/↓ to select';
1
+ export const TEAMMATE_SELECT_HINT = 'Shift + ↑/↓ para seleccionar';
@@ -1,10 +1,7 @@
1
1
  export function getDefaultCharacters() {
2
- if (process.env.TERM === 'xterm-ghostty') {
3
- return ['·', '✢', '✳', '✶', '✻', '*']; // Use * instead of for Ghostty because the latter renders in a way that's slightly offset
4
- }
5
- return process.platform === 'darwin'
6
- ? ['·', '✢', '✳', '✶', '✻', '✽']
7
- : ['·', '✢', '*', '✶', '✻', '✽'];
2
+ // Unified, clean spinner glyphs across OS/terminals.
3
+ // Keep symbols simple to avoid odd fallback rendering.
4
+ return ['.', 'o', 'O', 'o'];
8
5
  }
9
6
  // Interpolate between two RGB colors
10
7
  export function interpolateColor(color1, color2, t) {