wave-code 0.14.0 → 0.14.2

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 (71) hide show
  1. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  2. package/dist/components/BackgroundTaskManager.js +36 -25
  3. package/dist/components/ChatInterface.js +2 -2
  4. package/dist/components/CompactDisplay.d.ts +9 -0
  5. package/dist/components/CompactDisplay.d.ts.map +1 -0
  6. package/dist/components/{CompressDisplay.js → CompactDisplay.js} +2 -2
  7. package/dist/components/InputBox.d.ts.map +1 -1
  8. package/dist/components/InputBox.js +2 -2
  9. package/dist/components/LoadingIndicator.d.ts +2 -2
  10. package/dist/components/LoadingIndicator.d.ts.map +1 -1
  11. package/dist/components/LoadingIndicator.js +2 -2
  12. package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
  13. package/dist/components/MarketplaceAddForm.js +48 -17
  14. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  15. package/dist/components/MarketplaceDetail.js +2 -1
  16. package/dist/components/MarketplaceList.d.ts.map +1 -1
  17. package/dist/components/MarketplaceList.js +2 -1
  18. package/dist/components/McpManager.d.ts.map +1 -1
  19. package/dist/components/McpManager.js +22 -27
  20. package/dist/components/MessageBlockItem.js +2 -2
  21. package/dist/components/PluginDetail.d.ts.map +1 -1
  22. package/dist/components/PluginDetail.js +22 -22
  23. package/dist/components/PluginManagerShell.d.ts +1 -0
  24. package/dist/components/PluginManagerShell.d.ts.map +1 -1
  25. package/dist/components/PluginManagerShell.js +2 -2
  26. package/dist/components/PluginManagerTypes.d.ts +2 -2
  27. package/dist/components/PluginManagerTypes.d.ts.map +1 -1
  28. package/dist/contexts/useChat.d.ts +2 -1
  29. package/dist/contexts/useChat.d.ts.map +1 -1
  30. package/dist/contexts/useChat.js +127 -100
  31. package/dist/hooks/usePluginManager.d.ts +3 -1
  32. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  33. package/dist/hooks/usePluginManager.js +16 -7
  34. package/dist/reducers/backgroundTaskManagerReducer.d.ts +43 -0
  35. package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -0
  36. package/dist/reducers/backgroundTaskManagerReducer.js +23 -0
  37. package/dist/reducers/marketplaceAddFormReducer.d.ts +24 -0
  38. package/dist/reducers/marketplaceAddFormReducer.d.ts.map +1 -0
  39. package/dist/reducers/marketplaceAddFormReducer.js +18 -0
  40. package/dist/reducers/mcpManagerReducer.d.ts +16 -0
  41. package/dist/reducers/mcpManagerReducer.d.ts.map +1 -0
  42. package/dist/reducers/mcpManagerReducer.js +15 -0
  43. package/dist/reducers/pluginDetailReducer.d.ts +25 -0
  44. package/dist/reducers/pluginDetailReducer.d.ts.map +1 -0
  45. package/dist/reducers/pluginDetailReducer.js +38 -0
  46. package/dist/utils/usageSummary.d.ts +1 -1
  47. package/dist/utils/usageSummary.d.ts.map +1 -1
  48. package/dist/utils/usageSummary.js +7 -7
  49. package/package.json +2 -2
  50. package/src/components/BackgroundTaskManager.tsx +32 -34
  51. package/src/components/ChatInterface.tsx +3 -3
  52. package/src/components/{CompressDisplay.tsx → CompactDisplay.tsx} +5 -5
  53. package/src/components/InputBox.tsx +7 -1
  54. package/src/components/LoadingIndicator.tsx +5 -5
  55. package/src/components/MarketplaceAddForm.tsx +76 -22
  56. package/src/components/MarketplaceDetail.tsx +4 -0
  57. package/src/components/MarketplaceList.tsx +4 -0
  58. package/src/components/McpManager.tsx +30 -34
  59. package/src/components/MessageBlockItem.tsx +3 -3
  60. package/src/components/PluginDetail.tsx +25 -33
  61. package/src/components/PluginManagerShell.tsx +3 -2
  62. package/src/components/PluginManagerTypes.ts +8 -2
  63. package/src/contexts/useChat.tsx +60 -26
  64. package/src/hooks/usePluginManager.ts +18 -7
  65. package/src/reducers/backgroundTaskManagerReducer.ts +60 -0
  66. package/src/reducers/marketplaceAddFormReducer.ts +35 -0
  67. package/src/reducers/mcpManagerReducer.ts +31 -0
  68. package/src/reducers/pluginDetailReducer.ts +58 -0
  69. package/src/utils/usageSummary.ts +8 -8
  70. package/dist/components/CompressDisplay.d.ts +0 -9
  71. package/dist/components/CompressDisplay.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"BackgroundTaskManager.d.ts","sourceRoot":"","sources":["../../src/components/BackgroundTaskManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAgBnD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAyUtE,CAAC"}
1
+ {"version":3,"file":"BackgroundTaskManager.d.ts","sourceRoot":"","sources":["../../src/components/BackgroundTaskManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CA8UtE,CAAC"}
@@ -1,34 +1,41 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
2
+ import { useEffect, useReducer } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
4
  import { useChat } from "../contexts/useChat.js";
5
5
  import { getLastLines } from "wave-agent-sdk";
6
+ import { backgroundTaskManagerReducer, } from "../reducers/backgroundTaskManagerReducer.js";
6
7
  export const BackgroundTaskManager = ({ onCancel, }) => {
7
8
  const { backgroundTasks, getBackgroundTaskOutput, stopBackgroundTask } = useChat();
8
- const [tasks, setTasks] = useState([]);
9
- const [selectedIndex, setSelectedIndex] = useState(0);
10
9
  const MAX_VISIBLE_ITEMS = 3;
11
- const [viewMode, setViewMode] = useState("list");
12
- const [detailTaskId, setDetailTaskId] = useState(null);
13
- const [detailOutput, setDetailOutput] = useState(null);
10
+ const [state, dispatch] = useReducer(backgroundTaskManagerReducer, {
11
+ tasks: [],
12
+ selectedIndex: 0,
13
+ viewMode: "list",
14
+ detailTaskId: null,
15
+ detailOutput: null,
16
+ });
17
+ const { tasks, selectedIndex, viewMode, detailTaskId, detailOutput } = state;
14
18
  // Convert backgroundTasks to local Task format
15
19
  useEffect(() => {
16
- setTasks(backgroundTasks.map((task) => ({
17
- id: task.id,
18
- type: task.type,
19
- description: task.description,
20
- status: task.status,
21
- startTime: task.startTime,
22
- exitCode: task.exitCode,
23
- runtime: task.runtime,
24
- outputPath: task.outputPath,
25
- })));
20
+ dispatch({
21
+ type: "SET_TASKS",
22
+ tasks: backgroundTasks.map((task) => ({
23
+ id: task.id,
24
+ type: task.type,
25
+ description: task.description,
26
+ status: task.status,
27
+ startTime: task.startTime,
28
+ exitCode: task.exitCode,
29
+ runtime: task.runtime,
30
+ outputPath: task.outputPath,
31
+ })),
32
+ });
26
33
  }, [backgroundTasks]);
27
34
  // Load detail output for selected task
28
35
  useEffect(() => {
29
36
  if (viewMode === "detail" && detailTaskId) {
30
37
  const output = getBackgroundTaskOutput(detailTaskId);
31
- setDetailOutput(output);
38
+ dispatch({ type: "SET_DETAIL_OUTPUT", output });
32
39
  }
33
40
  }, [viewMode, detailTaskId, getBackgroundTaskOutput]);
34
41
  const formatDuration = (ms) => {
@@ -55,8 +62,8 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
55
62
  if (key.return) {
56
63
  if (tasks.length > 0 && selectedIndex < tasks.length) {
57
64
  const selectedTask = tasks[selectedIndex];
58
- setDetailTaskId(selectedTask.id);
59
- setViewMode("detail");
65
+ dispatch({ type: "SET_DETAIL_TASK_ID", id: selectedTask.id });
66
+ dispatch({ type: "SET_VIEW_MODE", mode: "detail" });
60
67
  }
61
68
  return;
62
69
  }
@@ -65,11 +72,17 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
65
72
  return;
66
73
  }
67
74
  if (key.upArrow) {
68
- setSelectedIndex(Math.max(0, selectedIndex - 1));
75
+ dispatch({
76
+ type: "SELECT_INDEX",
77
+ index: Math.max(0, selectedIndex - 1),
78
+ });
69
79
  return;
70
80
  }
71
81
  if (key.downArrow) {
72
- setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
82
+ dispatch({
83
+ type: "SELECT_INDEX",
84
+ index: Math.min(tasks.length - 1, selectedIndex + 1),
85
+ });
73
86
  return;
74
87
  }
75
88
  if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
@@ -83,9 +96,7 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
83
96
  else if (viewMode === "detail") {
84
97
  // Detail mode navigation
85
98
  if (key.escape) {
86
- setViewMode("list");
87
- setDetailTaskId(null);
88
- setDetailOutput(null);
99
+ dispatch({ type: "RESET_DETAIL" });
89
100
  return;
90
101
  }
91
102
  if (input === "k" && detailTaskId) {
@@ -100,7 +111,7 @@ export const BackgroundTaskManager = ({ onCancel, }) => {
100
111
  if (viewMode === "detail" && detailTaskId && detailOutput) {
101
112
  const task = tasks.find((t) => t.id === detailTaskId);
102
113
  if (!task) {
103
- setViewMode("list");
114
+ dispatch({ type: "RESET_DETAIL" });
104
115
  return null;
105
116
  }
106
117
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "cyan", bold: true, children: ["Background Task Details: ", task.id] }) }), _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Type:" }), " ", task.type] }) }), task.description && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Description:" }), " ", task.description] }) })), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Status:" }), " ", task.status, task.exitCode !== undefined && ` (exit code: ${task.exitCode})`] }) }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Started:" }), " ", formatTime(task.startTime), task.runtime !== undefined && (_jsxs(Text, { children: [" ", "| ", _jsx(Text, { color: "blue", children: "Runtime:" }), " ", formatDuration(task.runtime)] }))] }) }), task.outputPath && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Log File:" }), " ", task.outputPath] }) }))] }), detailOutput.stdout && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "green", bold: true, children: "OUTPUT (last 10 lines):" }), _jsx(Box, { borderStyle: "single", borderColor: "green", padding: 1, children: _jsx(Text, { children: getLastLines(detailOutput.stdout, 10) }) })] })), detailOutput.stderr && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "red", bold: true, children: "ERRORS:" }), _jsx(Box, { borderStyle: "single", borderColor: "red", padding: 1, children: _jsx(Text, { color: "red", children: getLastLines(detailOutput.stderr, 10) }) })] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [task.status === "running" ? "k to stop · " : "", "Esc to go back"] }) })] }));
@@ -10,7 +10,7 @@ import { ConfirmationDetails } from "./ConfirmationDetails.js";
10
10
  import { ConfirmationSelector } from "./ConfirmationSelector.js";
11
11
  import { useChat } from "../contexts/useChat.js";
12
12
  export const ChatInterface = () => {
13
- const { messages, isLoading, isCommandRunning, isCompressing, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, } = useChat();
13
+ const { messages, isLoading, isCommandRunning, isCompacting, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, } = useChat();
14
14
  const displayMessages = messages;
15
15
  const [forceStatic, setForceStatic] = useState(false);
16
16
  const { stdout } = useStdout();
@@ -37,5 +37,5 @@ export const ChatInterface = () => {
37
37
  ]);
38
38
  if (!sessionId)
39
39
  return null;
40
- return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading || isCommandRunning || isCompressing) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }, "confirmation-details")) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel })] }))] }));
40
+ return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading || isCommandRunning || isCompacting) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompacting: isCompacting, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }, "confirmation-details")) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel })] }))] }));
41
41
  };
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { CompactBlock } from "wave-agent-sdk";
3
+ interface CompactDisplayProps {
4
+ block: CompactBlock;
5
+ isExpanded?: boolean;
6
+ }
7
+ export declare const CompactDisplay: React.FC<CompactDisplayProps>;
8
+ export {};
9
+ //# sourceMappingURL=CompactDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompactDisplay.d.ts","sourceRoot":"","sources":["../../src/components/CompactDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,UAAU,mBAAmB;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA+BxD,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
3
  import { Box, Text } from "ink";
4
- export const CompressDisplay = ({ block }) => {
4
+ export const CompactDisplay = ({ block }) => {
5
5
  const { content } = block;
6
6
  const { displayContent } = useMemo(() => {
7
7
  if (!content) {
@@ -9,5 +9,5 @@ export const CompressDisplay = ({ block }) => {
9
9
  }
10
10
  return { displayContent: content };
11
11
  }, [content]);
12
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { children: "Compressed Messages" }) }), content && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: _jsx(Text, { color: "white", children: displayContent }) }) }))] }));
12
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { children: "Compacted Messages" }) }), content && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: _jsx(Text, { color: "white", children: displayContent }) }) }))] }));
13
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAkBzC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAuQ5C,CAAC"}
1
+ {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAkBzC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA6Q5C,CAAC"}
@@ -19,7 +19,7 @@ import { useChat } from "../contexts/useChat.js";
19
19
  export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
20
20
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
21
21
  export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, }) => {
22
- const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, allowBypassInCycle: chatAllowBypassInCycle, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, } = useChat();
22
+ const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, allowBypassInCycle: chatAllowBypassInCycle, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, } = useChat();
23
23
  // Input manager with all input state and functionality (including images)
24
24
  const { inputText, cursorPosition,
25
25
  // Image management
@@ -98,7 +98,7 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
98
98
  return _jsx(StatusCommand, { onCancel: () => setShowStatusCommand(false) });
99
99
  }
100
100
  if (showPluginManager) {
101
- return _jsx(PluginManagerShell, { onCancel: () => setShowPluginManager(false) });
101
+ return (_jsx(PluginManagerShell, { onCancel: () => setShowPluginManager(false), onPluginInstalled: recreateAgent }));
102
102
  }
103
103
  if (showModelSelector) {
104
104
  return (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel }));
@@ -1,11 +1,11 @@
1
1
  export interface LoadingIndicatorProps {
2
2
  isLoading?: boolean;
3
3
  isCommandRunning?: boolean;
4
- isCompressing?: boolean;
4
+ isCompacting?: boolean;
5
5
  latestTotalTokens?: number;
6
6
  }
7
7
  export declare const LoadingIndicator: {
8
- ({ isLoading, isCommandRunning, isCompressing, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
8
+ ({ isLoading, isCommandRunning, isCompacting, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
9
9
  displayName: string;
10
10
  };
11
11
  //# sourceMappingURL=LoadingIndicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;yEAK1B,qBAAqB;;CAsCvB,CAAC"}
1
+ {"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;wEAK1B,qBAAqB;;CAsCvB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
- export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompressing = false, latestTotalTokens = 0, }) => {
4
- return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompressing && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompressing && (_jsx(Text, { color: "magenta", children: "\u273B Compressing message history..." }))] }));
3
+ export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompacting = false, latestTotalTokens = 0, }) => {
4
+ return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompacting && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompacting && (_jsx(Text, { color: "magenta", children: "\u273B Compacting message history..." }))] }));
5
5
  };
6
6
  LoadingIndicator.displayName = "LoadingIndicator";
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceAddForm.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceAddForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAI3D,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAkDtC,CAAC"}
1
+ {"version":3,"file":"MarketplaceAddForm.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceAddForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAW1C,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAiGtC,CAAC"}
@@ -1,35 +1,66 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useRef, useEffect } from "react";
2
+ import { useReducer } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
4
  import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
5
+ import { marketplaceAddFormReducer } from "../reducers/marketplaceAddFormReducer.js";
6
+ const SCOPES = [
7
+ { value: "user", label: "user" },
8
+ { value: "project", label: "project" },
9
+ { value: "local", label: "local" },
10
+ ];
5
11
  export const MarketplaceAddForm = () => {
6
12
  const { state, actions } = usePluginManagerContext();
7
- const [source, setSource] = useState("");
8
- const sourceRef = useRef(source);
9
- // Keep ref in sync with state
10
- useEffect(() => {
11
- sourceRef.current = source;
12
- }, [source]);
13
+ const [{ source, scopeIndex, step }, dispatch] = useReducer(marketplaceAddFormReducer, { source: "", scopeIndex: 0, step: "source" });
13
14
  useInput((input, key) => {
14
15
  if (key.escape) {
15
- actions.setView("MARKETPLACES");
16
+ if (step === "scope") {
17
+ dispatch({ type: "BACK_TO_SOURCE" });
18
+ }
19
+ else {
20
+ actions.setView("MARKETPLACES");
21
+ }
16
22
  }
17
23
  else if (state.isLoading) {
18
24
  return;
19
25
  }
20
- else if (key.return) {
21
- if (sourceRef.current.trim()) {
22
- actions.addMarketplace(sourceRef.current.trim());
26
+ else if (step === "source" && key.return) {
27
+ if (source.trim()) {
28
+ dispatch({ type: "SET_STEP", step: "scope" });
23
29
  }
24
30
  }
25
- else if (key.backspace || key.delete) {
26
- setSource((prev) => prev.slice(0, -1));
31
+ else if (step === "source" && (key.backspace || key.delete)) {
32
+ dispatch({ type: "DELETE_CHAR" });
33
+ }
34
+ else if (step === "source" &&
35
+ input &&
36
+ !key.ctrl &&
37
+ !key.meta &&
38
+ !("alt" in key && key.alt)) {
39
+ dispatch({ type: "INSERT_CHAR", text: input });
27
40
  }
28
- else if (input.length === 1) {
29
- setSource((prev) => prev + input);
41
+ else if (step === "scope") {
42
+ if (key.upArrow) {
43
+ dispatch({
44
+ type: "SET_SCOPE_INDEX",
45
+ index: Math.max(0, scopeIndex - 1),
46
+ });
47
+ }
48
+ else if (key.downArrow) {
49
+ dispatch({
50
+ type: "SET_SCOPE_INDEX",
51
+ index: Math.min(SCOPES.length - 1, scopeIndex + 1),
52
+ });
53
+ }
54
+ else if (key.return) {
55
+ const scope = SCOPES[scopeIndex].value;
56
+ actions.addMarketplace(source.trim(), scope);
57
+ }
30
58
  }
31
59
  });
32
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Add Marketplace" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Source (URL or Path): " }), _jsx(Text, { color: state.isLoading ? "gray" : "yellow", children: source }), !state.isLoading && _jsx(Text, { color: "yellow", children: "_" })] }), state.isLoading && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u231B Adding marketplace..." }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: state.isLoading
60
+ if (step === "source") {
61
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Step 1/2: Enter marketplace source" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Source: " }), _jsx(Text, { color: "yellow", children: source }), !state.isLoading && _jsx(Text, { color: "yellow", children: "_" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to continue, Esc to cancel" }) })] }));
62
+ }
63
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Step 2/2: Select scope" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Source: " }), _jsx(Text, { dimColor: true, children: source })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: SCOPES.map((s, i) => (_jsxs(Text, { color: i === scopeIndex ? "yellow" : "dim", children: [i === scopeIndex ? "> " : " ", s.label] }, s.value))) }), state.isLoading && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "Adding marketplace..." }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: state.isLoading
33
64
  ? "Please wait..."
34
- : "Press Enter to add, Esc to cancel" }) })] }));
65
+ : "Enter to confirm, \u2191/\u2193 to navigate, Esc to go back" }) })] }));
35
66
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceDetail.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAmHrC,CAAC"}
1
+ {"version":3,"file":"MarketplaceDetail.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAuHrC,CAAC"}
@@ -40,7 +40,8 @@ export const MarketplaceDetail = () => {
40
40
  if (!marketplace) {
41
41
  return (_jsx(Box, { children: _jsx(Text, { color: "red", children: "Marketplace not found." }) }));
42
42
  }
43
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: marketplace.name }), marketplace.isBuiltin && _jsx(Text, { dimColor: true, children: " (Built-in)" })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Source: ", JSON.stringify(marketplace.source)] }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Auto-update:", " ", _jsx(Text, { color: marketplace.autoUpdate ? "green" : "red", children: marketplace.autoUpdate ? "Enabled" : "Disabled" })] }) }), marketplace.lastUpdated && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Last updated:", " ", _jsx(Text, { color: "cyan", children: new Date(marketplace.lastUpdated).toLocaleString() })] }) })), state.isLoading && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u231B Processing operation..." }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Marketplace Actions:" }), ACTIONS.map((action, index) => (_jsxs(Text, { color: index === selectedActionIndex
43
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: marketplace.name }), marketplace.isBuiltin && _jsx(Text, { dimColor: true, children: " (Built-in)" }), marketplace.declaredScope &&
44
+ marketplace.declaredScope !== "builtin" && (_jsxs(Text, { dimColor: true, children: [" (", marketplace.declaredScope, " scope)"] }))] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Source: ", JSON.stringify(marketplace.source)] }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Auto-update:", " ", _jsx(Text, { color: marketplace.autoUpdate ? "green" : "red", children: marketplace.autoUpdate ? "Enabled" : "Disabled" })] }) }), marketplace.lastUpdated && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Last updated:", " ", _jsx(Text, { color: "cyan", children: new Date(marketplace.lastUpdated).toLocaleString() })] }) })), state.isLoading && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u231B Processing operation..." }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Marketplace Actions:" }), ACTIONS.map((action, index) => (_jsxs(Text, { color: index === selectedActionIndex
44
45
  ? state.isLoading
45
46
  ? "gray"
46
47
  : "yellow"
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceList.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,oBAAoB;IAC5B,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAgD1D,CAAC"}
1
+ {"version":3,"file":"MarketplaceList.d.ts","sourceRoot":"","sources":["../../src/components/MarketplaceList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,oBAAoB;IAC5B,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAoD1D,CAAC"}
@@ -11,6 +11,7 @@ export const MarketplaceList = ({ marketplaces, selectedIndex, }) => {
11
11
  : marketplace.source.source === "github"
12
12
  ? marketplace.source.repo
13
13
  : marketplace.source.url;
14
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "cyan" : undefined, children: [isSelected ? "> " : " ", _jsx(Text, { bold: true, children: marketplace.name }), marketplace.isBuiltin && (_jsx(Text, { color: "yellow", children: " [Built-in]" }))] }) }), _jsxs(Box, { marginLeft: 4, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Source: ", sourceStr] }), marketplace.lastUpdated && (_jsxs(Text, { dimColor: true, children: ["Last updated:", " ", new Date(marketplace.lastUpdated).toLocaleString()] }))] })] }, marketplace.name));
14
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "cyan" : undefined, children: [isSelected ? "> " : " ", _jsx(Text, { bold: true, children: marketplace.name }), marketplace.isBuiltin && (_jsx(Text, { color: "yellow", children: " [Built-in]" })), marketplace.declaredScope &&
15
+ marketplace.declaredScope !== "builtin" && (_jsxs(Text, { color: "magenta", children: [" [", marketplace.declaredScope, "]"] }))] }) }), _jsxs(Box, { marginLeft: 4, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Source: ", sourceStr] }), marketplace.lastUpdated && (_jsxs(Text, { dimColor: true, children: ["Last updated:", " ", new Date(marketplace.lastUpdated).toLocaleString()] }))] })] }, marketplace.name));
15
16
  }) }));
16
17
  };
@@ -1 +1 @@
1
- {"version":3,"file":"McpManager.d.ts","sourceRoot":"","sources":["../../src/components/McpManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAoThD,CAAC"}
1
+ {"version":3,"file":"McpManager.d.ts","sourceRoot":"","sources":["../../src/components/McpManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMjD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAOD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuShD,CAAC"}
@@ -1,23 +1,18 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useState, useRef, useEffect } from "react";
2
+ import { useReducer } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
+ import { mcpManagerReducer, } from "../reducers/mcpManagerReducer.js";
5
+ const initialState = {
6
+ selectedIndex: 0,
7
+ viewMode: "list",
8
+ };
4
9
  export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectServer, }) => {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- const [viewMode, setViewMode] = useState("list");
7
- // Keep ref in sync with state to avoid stale closures in useInput
8
- const selectedIndexRef = useRef(selectedIndex);
9
- const viewModeRef = useRef(viewMode);
10
- useEffect(() => {
11
- selectedIndexRef.current = selectedIndex;
12
- }, [selectedIndex]);
13
- useEffect(() => {
14
- viewModeRef.current = viewMode;
15
- }, [viewMode]);
10
+ const [state, dispatch] = useReducer(mcpManagerReducer, initialState);
16
11
  // Dynamically calculate selectedServer based on selectedIndex and servers
17
- const selectedServer = viewMode === "detail" &&
12
+ const selectedServer = state.viewMode === "detail" &&
18
13
  servers.length > 0 &&
19
- selectedIndex < servers.length
20
- ? servers[selectedIndex]
14
+ state.selectedIndex < servers.length
15
+ ? servers[state.selectedIndex]
21
16
  : null;
22
17
  const formatTime = (timestamp) => {
23
18
  return new Date(timestamp).toLocaleTimeString();
@@ -54,14 +49,14 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
54
49
  };
55
50
  useInput((input, key) => {
56
51
  if (key.return) {
57
- if (viewModeRef.current === "list") {
58
- setViewMode("detail");
52
+ if (state.viewMode === "list") {
53
+ dispatch({ type: "SET_VIEW_MODE", viewMode: "detail" });
59
54
  }
60
55
  return;
61
56
  }
62
57
  if (key.escape) {
63
- if (viewModeRef.current === "detail") {
64
- setViewMode("list");
58
+ if (state.viewMode === "detail") {
59
+ dispatch({ type: "SET_VIEW_MODE", viewMode: "list" });
65
60
  }
66
61
  else {
67
62
  onCancel();
@@ -69,16 +64,16 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
69
64
  return;
70
65
  }
71
66
  if (key.upArrow) {
72
- setSelectedIndex((prev) => Math.max(0, prev - 1));
67
+ dispatch({ type: "MOVE_UP", serverCount: servers.length });
73
68
  return;
74
69
  }
75
70
  if (key.downArrow) {
76
- setSelectedIndex((prev) => Math.min(servers.length - 1, prev + 1));
71
+ dispatch({ type: "MOVE_DOWN", serverCount: servers.length });
77
72
  return;
78
73
  }
79
74
  // Hotkeys for server actions
80
75
  if (input === "c") {
81
- const server = servers[selectedIndexRef.current];
76
+ const server = servers[state.selectedIndex];
82
77
  if (server &&
83
78
  (server.status === "disconnected" || server.status === "error")) {
84
79
  handleConnect(server.name);
@@ -86,14 +81,14 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
86
81
  return;
87
82
  }
88
83
  if (input === "d") {
89
- const server = servers[selectedIndexRef.current];
84
+ const server = servers[state.selectedIndex];
90
85
  if (server && server.status === "connected") {
91
86
  handleDisconnect(server.name);
92
87
  }
93
88
  return;
94
89
  }
95
90
  });
96
- if (viewMode === "detail" && selectedServer) {
91
+ if (state.viewMode === "detail" && selectedServer) {
97
92
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "cyan", bold: true, children: ["MCP Server Details: ", selectedServer.name] }) }), _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Status:" }), " ", _jsxs(Text, { color: getStatusColor(selectedServer.status), children: [getStatusIcon(selectedServer.status), " ", selectedServer.status] })] }) }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Command:" }), " ", selectedServer.config.command] }) }), selectedServer.config.args && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Args:" }), " ", selectedServer.config.args.join(" ")] }) })), selectedServer.toolCount !== undefined && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Tools:" }), " ", selectedServer.toolCount, " ", "tools"] }) })), selectedServer.capabilities && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Capabilities:" }), " ", selectedServer.capabilities.join(", ")] }) })), selectedServer.lastConnected && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Last Connected:" }), " ", formatTime(selectedServer.lastConnected)] }) })), selectedServer.error && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "red", children: "Error:" }), " ", selectedServer.error] }) }))] }), selectedServer.config.env &&
98
93
  Object.keys(selectedServer.config.env).length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "blue", bold: true, children: "Environment Variables:" }), _jsx(Box, { borderStyle: "single", borderColor: "blue", padding: 1, children: Object.entries(selectedServer.config.env).map(([key, value]) => (_jsxs(Text, { children: [key, "=", value] }, key))) })] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [selectedServer.status === "disconnected" ||
99
94
  selectedServer.status === "error"
@@ -103,10 +98,10 @@ export const McpManager = ({ onCancel, servers, onConnectServer, onDisconnectSer
103
98
  if (servers.length === 0) {
104
99
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "Manage MCP servers" }), _jsx(Text, { children: "No MCP servers configured" }), _jsx(Text, { dimColor: true, children: "Create a .mcp.json file in your project root to add servers" }), _jsx(Text, { dimColor: true, children: "Press Escape to close" })] }));
105
100
  }
106
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "Manage MCP servers" }) }), _jsx(Text, { dimColor: true, children: "Select a server to view details" }), servers.map((server, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: index === selectedIndex ? "black" : "white", backgroundColor: index === selectedIndex ? "cyan" : undefined, children: [index === selectedIndex ? "▶ " : " ", index + 1, ".", " ", _jsx(Text, { color: getStatusColor(server.status), children: getStatusIcon(server.status) }), " ", server.name, server.status === "connected" && server.toolCount && (_jsxs(Text, { color: "green", children: [" \u00B7 ", server.toolCount, " tools"] }))] }), index === selectedIndex && (_jsxs(Box, { marginLeft: 4, flexDirection: "column", children: [_jsxs(Text, { color: "gray", dimColor: true, children: [server.config.command, server.config.args ? ` ${server.config.args.join(" ")}` : ""] }), server.lastConnected && (_jsxs(Text, { color: "gray", dimColor: true, children: ["Last connected: ", formatTime(server.lastConnected)] }))] }))] }, server.name))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2191/\u2193 to select \u00B7 Enter to view \u00B7", " ", servers[selectedIndex]?.status === "disconnected" ||
107
- servers[selectedIndex]?.status === "error"
101
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "Manage MCP servers" }) }), _jsx(Text, { dimColor: true, children: "Select a server to view details" }), servers.map((server, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: index === state.selectedIndex ? "black" : "white", backgroundColor: index === state.selectedIndex ? "cyan" : undefined, children: [index === state.selectedIndex ? "▶ " : " ", index + 1, ".", " ", _jsx(Text, { color: getStatusColor(server.status), children: getStatusIcon(server.status) }), " ", server.name, server.status === "connected" && server.toolCount && (_jsxs(Text, { color: "green", children: [" \u00B7 ", server.toolCount, " tools"] }))] }), index === state.selectedIndex && (_jsxs(Box, { marginLeft: 4, flexDirection: "column", children: [_jsxs(Text, { color: "gray", dimColor: true, children: [server.config.command, server.config.args ? ` ${server.config.args.join(" ")}` : ""] }), server.lastConnected && (_jsxs(Text, { color: "gray", dimColor: true, children: ["Last connected: ", formatTime(server.lastConnected)] }))] }))] }, server.name))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2191/\u2193 to select \u00B7 Enter to view \u00B7", " ", servers[state.selectedIndex]?.status === "disconnected" ||
102
+ servers[state.selectedIndex]?.status === "error"
108
103
  ? "c to connect · "
109
- : "", servers[selectedIndex]?.status === "connected"
104
+ : "", servers[state.selectedIndex]?.status === "connected"
110
105
  ? "d to disconnect · "
111
106
  : "", "Esc to close"] }) })] }));
112
107
  };
@@ -3,7 +3,7 @@ import { Box, Text } from "ink";
3
3
  import { MessageSource } from "wave-agent-sdk";
4
4
  import { BangDisplay } from "./BangDisplay.js";
5
5
  import { ToolDisplay } from "./ToolDisplay.js";
6
- import { CompressDisplay } from "./CompressDisplay.js";
6
+ import { CompactDisplay } from "./CompactDisplay.js";
7
7
  import { ReasoningDisplay } from "./ReasoningDisplay.js";
8
8
  import { Markdown } from "./Markdown.js";
9
9
  import { TaskNotificationMessage } from "./TaskNotificationMessage.js";
@@ -11,5 +11,5 @@ export const MessageBlockItem = ({ block, message, isExpanded, paddingTop = 0, }
11
11
  return (_jsxs(Box, { flexDirection: "column", paddingTop: paddingTop, children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["~", " "] })), message.role === "user" || isExpanded ? (_jsx(Text, { backgroundColor: message.role === "user" ? "gray" : undefined, color: "white", children: block.content })) : block.stage === "streaming" ? (_jsx(Text, { color: "gray", wrap: "truncate-end", children: (() => {
12
12
  const flat = block.content.replace(/\n/g, "\\n");
13
13
  return flat.length > 30 ? `…${flat.slice(-30)}` : flat;
14
- })() })) : (_jsx(Markdown, { children: block.content }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", block.content] }) })), block.type === "bang" && (_jsx(BangDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "# Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "reasoning" && (_jsx(ReasoningDisplay, { block: block, isExpanded: isExpanded })), block.type === "task_notification" && (_jsx(TaskNotificationMessage, { block: block }))] }));
14
+ })() })) : (_jsx(Markdown, { children: block.content }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", block.content] }) })), block.type === "bang" && (_jsx(BangDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "# Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "compact" && (_jsx(CompactDisplay, { block: block, isExpanded: isExpanded })), block.type === "reasoning" && (_jsx(ReasoningDisplay, { block: block, isExpanded: isExpanded })), block.type === "task_notification" && (_jsx(TaskNotificationMessage, { block: block }))] }));
15
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"PluginDetail.d.ts","sourceRoot":"","sources":["../../src/components/PluginDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAU3D,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAqKhC,CAAC"}
1
+ {"version":3,"file":"PluginDetail.d.ts","sourceRoot":"","sources":["../../src/components/PluginDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAc1C,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAyJhC,CAAC"}