dexto 1.5.5 → 1.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +13 -0
  2. package/dist/agents/agent-template.yml +1 -1
  3. package/dist/agents/coding-agent/coding-agent.yml +17 -2
  4. package/dist/agents/coding-agent/skills/code-review.md +46 -0
  5. package/dist/agents/explore-agent/explore-agent.yml +2 -0
  6. package/dist/agents/podcast-agent/podcast-agent.yml +1 -1
  7. package/dist/analytics/events.d.ts +1 -1
  8. package/dist/analytics/events.d.ts.map +1 -1
  9. package/dist/api/server-hono.d.ts.map +1 -1
  10. package/dist/api/server-hono.js +55 -10
  11. package/dist/cli/assets/dexto-logo.svg +31 -0
  12. package/dist/cli/auth/api-client.d.ts +49 -0
  13. package/dist/cli/auth/api-client.d.ts.map +1 -0
  14. package/dist/cli/auth/api-client.js +127 -0
  15. package/dist/cli/auth/constants.d.ts +23 -0
  16. package/dist/cli/auth/constants.d.ts.map +1 -0
  17. package/dist/cli/auth/constants.js +24 -0
  18. package/dist/cli/auth/index.d.ts +5 -0
  19. package/dist/cli/auth/index.d.ts.map +1 -0
  20. package/dist/cli/auth/index.js +6 -0
  21. package/dist/cli/auth/oauth.d.ts +26 -0
  22. package/dist/cli/auth/oauth.d.ts.map +1 -0
  23. package/dist/cli/auth/oauth.js +327 -0
  24. package/dist/cli/auth/service.d.ts +20 -0
  25. package/dist/cli/auth/service.d.ts.map +1 -0
  26. package/dist/cli/auth/service.js +147 -0
  27. package/dist/cli/commands/auth/index.d.ts +4 -0
  28. package/dist/cli/commands/auth/index.d.ts.map +1 -0
  29. package/dist/cli/commands/auth/index.js +4 -0
  30. package/dist/cli/commands/auth/login.d.ts +9 -0
  31. package/dist/cli/commands/auth/login.d.ts.map +1 -0
  32. package/dist/cli/commands/auth/login.js +255 -0
  33. package/dist/cli/commands/auth/logout.d.ts +5 -0
  34. package/dist/cli/commands/auth/logout.d.ts.map +1 -0
  35. package/dist/cli/commands/auth/logout.js +51 -0
  36. package/dist/cli/commands/auth/status.d.ts +2 -0
  37. package/dist/cli/commands/auth/status.d.ts.map +1 -0
  38. package/dist/cli/commands/auth/status.js +22 -0
  39. package/dist/cli/commands/billing/index.d.ts +2 -0
  40. package/dist/cli/commands/billing/index.d.ts.map +1 -0
  41. package/dist/cli/commands/billing/index.js +2 -0
  42. package/dist/cli/commands/billing/status.d.ts +6 -0
  43. package/dist/cli/commands/billing/status.d.ts.map +1 -0
  44. package/dist/cli/commands/billing/status.js +60 -0
  45. package/dist/cli/commands/index.d.ts +4 -0
  46. package/dist/cli/commands/index.d.ts.map +1 -1
  47. package/dist/cli/commands/index.js +9 -0
  48. package/dist/cli/commands/interactive-commands/auth/index.d.ts +12 -0
  49. package/dist/cli/commands/interactive-commands/auth/index.d.ts.map +1 -0
  50. package/dist/cli/commands/interactive-commands/auth/index.js +20 -0
  51. package/dist/cli/commands/interactive-commands/command-parser.d.ts +5 -0
  52. package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
  53. package/dist/cli/commands/interactive-commands/command-parser.js +6 -0
  54. package/dist/cli/commands/interactive-commands/commands.d.ts +1 -0
  55. package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
  56. package/dist/cli/commands/interactive-commands/commands.js +10 -0
  57. package/dist/cli/commands/interactive-commands/export/index.d.ts +13 -0
  58. package/dist/cli/commands/interactive-commands/export/index.d.ts.map +1 -0
  59. package/dist/cli/commands/interactive-commands/export/index.js +21 -0
  60. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  61. package/dist/cli/commands/interactive-commands/general-commands.js +1 -0
  62. package/dist/cli/commands/interactive-commands/mcp/index.d.ts +2 -2
  63. package/dist/cli/commands/interactive-commands/mcp/index.d.ts.map +1 -1
  64. package/dist/cli/commands/interactive-commands/mcp/index.js +4 -7
  65. package/dist/cli/commands/interactive-commands/model/index.d.ts +2 -2
  66. package/dist/cli/commands/interactive-commands/model/index.d.ts.map +1 -1
  67. package/dist/cli/commands/interactive-commands/model/index.js +4 -7
  68. package/dist/cli/commands/interactive-commands/plugin/index.d.ts +13 -0
  69. package/dist/cli/commands/interactive-commands/plugin/index.d.ts.map +1 -0
  70. package/dist/cli/commands/interactive-commands/plugin/index.js +18 -0
  71. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts +3 -1
  72. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
  73. package/dist/cli/commands/interactive-commands/prompt-commands.js +72 -36
  74. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
  75. package/dist/cli/commands/interactive-commands/system/system-commands.js +2 -3
  76. package/dist/cli/commands/plugin.d.ts +161 -0
  77. package/dist/cli/commands/plugin.d.ts.map +1 -0
  78. package/dist/cli/commands/plugin.js +376 -0
  79. package/dist/cli/commands/setup.d.ts +9 -9
  80. package/dist/cli/commands/setup.d.ts.map +1 -1
  81. package/dist/cli/commands/setup.js +325 -37
  82. package/dist/cli/commands/sync-agents.d.ts +44 -0
  83. package/dist/cli/commands/sync-agents.d.ts.map +1 -0
  84. package/dist/cli/commands/sync-agents.js +483 -0
  85. package/dist/cli/ink-cli/InkCLIRefactored.d.ts +14 -1
  86. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  87. package/dist/cli/ink-cli/InkCLIRefactored.js +8 -2
  88. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +1 -1
  89. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  90. package/dist/cli/ink-cli/components/ApprovalPrompt.js +80 -12
  91. package/dist/cli/ink-cli/components/Footer.d.ts +2 -1
  92. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  93. package/dist/cli/ink-cli/components/Footer.js +6 -2
  94. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
  95. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +15 -7
  96. package/dist/cli/ink-cli/components/StatusBar.d.ts +9 -1
  97. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  98. package/dist/cli/ink-cli/components/StatusBar.js +17 -5
  99. package/dist/cli/ink-cli/components/TodoPanel.d.ts +11 -8
  100. package/dist/cli/ink-cli/components/TodoPanel.d.ts.map +1 -1
  101. package/dist/cli/ink-cli/components/TodoPanel.js +38 -36
  102. package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
  103. package/dist/cli/ink-cli/components/chat/Header.js +1 -1
  104. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  105. package/dist/cli/ink-cli/components/chat/MessageItem.js +14 -1
  106. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
  107. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  108. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +16 -4
  109. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  110. package/dist/cli/ink-cli/components/modes/StaticCLI.js +4 -1
  111. package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts +22 -0
  112. package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts.map +1 -0
  113. package/dist/cli/ink-cli/components/overlays/ExportWizard.js +308 -0
  114. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts +1 -0
  115. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts.map +1 -1
  116. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +31 -20
  117. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts +20 -0
  118. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts.map +1 -0
  119. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.js +81 -0
  120. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts +31 -0
  121. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts.map +1 -0
  122. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.js +297 -0
  123. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.d.ts.map +1 -1
  124. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.js +7 -1
  125. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts +1 -1
  126. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts.map +1 -1
  127. package/dist/cli/ink-cli/components/overlays/McpServerActions.js +9 -0
  128. package/dist/cli/ink-cli/components/overlays/McpServerList.d.ts.map +1 -1
  129. package/dist/cli/ink-cli/components/overlays/McpServerList.js +9 -2
  130. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  131. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +15 -2
  132. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts +27 -0
  133. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts.map +1 -0
  134. package/dist/cli/ink-cli/components/overlays/PluginActions.js +66 -0
  135. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts +21 -0
  136. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts.map +1 -0
  137. package/dist/cli/ink-cli/components/overlays/PluginList.js +70 -0
  138. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts +21 -0
  139. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts.map +1 -0
  140. package/dist/cli/ink-cli/components/overlays/PluginManager.js +63 -0
  141. package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
  142. package/dist/cli/ink-cli/components/overlays/PromptList.js +4 -1
  143. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts +2 -1
  144. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
  145. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +61 -2
  146. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +4 -2
  147. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
  148. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +4 -4
  149. package/dist/cli/ink-cli/constants/tips.js +2 -2
  150. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  151. package/dist/cli/ink-cli/containers/InputContainer.js +31 -3
  152. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  153. package/dist/cli/ink-cli/containers/OverlayContainer.js +260 -11
  154. package/dist/cli/ink-cli/hooks/index.d.ts +1 -0
  155. package/dist/cli/ink-cli/hooks/index.d.ts.map +1 -1
  156. package/dist/cli/ink-cli/hooks/index.js +1 -0
  157. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  158. package/dist/cli/ink-cli/hooks/useCLIState.js +3 -0
  159. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts +13 -0
  160. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts.map +1 -0
  161. package/dist/cli/ink-cli/hooks/useGitBranch.js +35 -0
  162. package/dist/cli/ink-cli/hooks/useInputOrchestrator.d.ts.map +1 -1
  163. package/dist/cli/ink-cli/hooks/useInputOrchestrator.js +50 -6
  164. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  165. package/dist/cli/ink-cli/services/processStream.js +42 -10
  166. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  167. package/dist/cli/ink-cli/state/initialState.js +3 -0
  168. package/dist/cli/ink-cli/state/types.d.ts +16 -1
  169. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  170. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  171. package/dist/cli/ink-cli/utils/commandOverlays.js +2 -0
  172. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +14 -1
  173. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  174. package/dist/cli/ink-cli/utils/messageFormatting.js +68 -8
  175. package/dist/cli/ink-cli/utils/toolUtils.d.ts +11 -0
  176. package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
  177. package/dist/cli/ink-cli/utils/toolUtils.js +17 -0
  178. package/dist/cli/mcp/index.d.ts +8 -0
  179. package/dist/cli/mcp/index.d.ts.map +1 -0
  180. package/dist/cli/mcp/index.js +7 -0
  181. package/dist/cli/mcp/oauth-factory.d.ts +6 -0
  182. package/dist/cli/mcp/oauth-factory.d.ts.map +1 -0
  183. package/dist/cli/mcp/oauth-factory.js +25 -0
  184. package/dist/cli/mcp/oauth-provider.d.ts +10 -0
  185. package/dist/cli/mcp/oauth-provider.d.ts.map +1 -0
  186. package/dist/cli/mcp/oauth-provider.js +77 -0
  187. package/dist/cli/mcp/oauth-redirect.d.ts +3 -0
  188. package/dist/cli/mcp/oauth-redirect.d.ts.map +1 -0
  189. package/dist/cli/mcp/oauth-redirect.js +4 -0
  190. package/dist/cli/mcp/oauth-server.d.ts +2 -0
  191. package/dist/cli/mcp/oauth-server.d.ts.map +1 -0
  192. package/dist/cli/mcp/oauth-server.js +70 -0
  193. package/dist/cli/mcp/oauth-store.d.ts +10 -0
  194. package/dist/cli/mcp/oauth-store.d.ts.map +1 -0
  195. package/dist/cli/mcp/oauth-store.js +27 -0
  196. package/dist/cli/mcp/oauth-ui.d.ts +2 -0
  197. package/dist/cli/mcp/oauth-ui.d.ts.map +1 -0
  198. package/dist/cli/mcp/oauth-ui.js +12 -0
  199. package/dist/cli/mcp/oauth-utils.d.ts +2 -0
  200. package/dist/cli/mcp/oauth-utils.d.ts.map +1 -0
  201. package/dist/cli/mcp/oauth-utils.js +17 -0
  202. package/dist/cli/utils/api-key-setup.d.ts.map +1 -1
  203. package/dist/cli/utils/api-key-setup.js +13 -90
  204. package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
  205. package/dist/cli/utils/api-key-verification.js +36 -0
  206. package/dist/cli/utils/config-validation.d.ts +3 -1
  207. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  208. package/dist/cli/utils/config-validation.js +42 -19
  209. package/dist/cli/utils/dexto-auth-check.d.ts +53 -0
  210. package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -0
  211. package/dist/cli/utils/dexto-auth-check.js +104 -0
  212. package/dist/cli/utils/dexto-setup.d.ts +8 -0
  213. package/dist/cli/utils/dexto-setup.d.ts.map +1 -0
  214. package/dist/cli/utils/dexto-setup.js +17 -0
  215. package/dist/cli/utils/options.d.ts.map +1 -1
  216. package/dist/cli/utils/options.js +5 -1
  217. package/dist/cli/utils/provider-setup.d.ts +4 -0
  218. package/dist/cli/utils/provider-setup.d.ts.map +1 -1
  219. package/dist/cli/utils/provider-setup.js +20 -0
  220. package/dist/cli/utils/version-check.d.ts +45 -0
  221. package/dist/cli/utils/version-check.d.ts.map +1 -0
  222. package/dist/cli/utils/version-check.js +195 -0
  223. package/dist/config/cli-overrides.d.ts +17 -8
  224. package/dist/config/cli-overrides.d.ts.map +1 -1
  225. package/dist/config/cli-overrides.js +36 -22
  226. package/dist/config/effective-llm.d.ts +123 -0
  227. package/dist/config/effective-llm.d.ts.map +1 -0
  228. package/dist/config/effective-llm.js +171 -0
  229. package/dist/index.js +451 -126
  230. package/dist/webui/assets/index-C9JXwpvo.css +1 -0
  231. package/dist/webui/assets/{index-DVQWNLpT.js → index-Dl3mj53P.js} +217 -217
  232. package/dist/webui/index.html +2 -2
  233. package/package.json +9 -8
  234. package/dist/webui/assets/index-BglIVTSG.css +0 -1
@@ -15,6 +15,7 @@ import { Box, Text } from 'ink';
15
15
  // Hooks
16
16
  import { useTerminalSize } from '../../hooks/index.js';
17
17
  import { useCLIState } from '../../hooks/useCLIState.js';
18
+ import { useGitBranch } from '../../hooks/useGitBranch.js';
18
19
  import { useScrollable } from '../../contexts/index.js';
19
20
  // Components
20
21
  import { Header } from '../chat/Header.js';
@@ -48,6 +49,8 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
48
49
  startupInfo,
49
50
  onKeyboardScroll: handleKeyboardScroll,
50
51
  });
52
+ // Get current git branch name
53
+ const branchName = useGitBranch();
51
54
  // Register the VirtualizedList as scrollable so ScrollProvider can handle mouse scroll
52
55
  const getScrollState = useCallback(() => {
53
56
  const scrollState = listRef.current?.getScrollState();
@@ -99,19 +102,28 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
99
102
  // Build list data: header as first item, then finalized + pending + dequeued buffer
100
103
  // In alternate buffer mode, everything is re-rendered anyway, so we combine all
101
104
  // Order: finalized messages → pending/streaming → dequeued user messages (guarantees order)
105
+ // IMPORTANT: Deduplicate by ID to prevent race condition where a message appears in both
106
+ // finalized (messages) and pending during the brief window between setState calls
102
107
  const listData = useMemo(() => {
103
108
  const items = [{ type: 'header' }];
109
+ const seenIds = new Set();
104
110
  for (const msg of visibleMessages) {
105
111
  items.push({ type: 'message', message: msg });
112
+ seenIds.add(msg.id);
106
113
  }
107
- // Add pending/streaming messages
114
+ // Add pending/streaming messages (skip if already in finalized - race condition guard)
108
115
  for (const msg of pendingMessages) {
109
- items.push({ type: 'message', message: msg });
116
+ if (!seenIds.has(msg.id)) {
117
+ items.push({ type: 'message', message: msg });
118
+ seenIds.add(msg.id);
119
+ }
110
120
  }
111
121
  // Add dequeued buffer (user messages waiting to be flushed to finalized)
112
122
  // These render AFTER pending to guarantee correct visual order
113
123
  for (const msg of dequeuedBuffer) {
114
- items.push({ type: 'message', message: msg });
124
+ if (!seenIds.has(msg.id)) {
125
+ items.push({ type: 'message', message: msg });
126
+ }
115
127
  }
116
128
  return items;
117
129
  }, [visibleMessages, pendingMessages, dequeuedBuffer]);
@@ -163,5 +175,5 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
163
175
  return 'header';
164
176
  return item.message.id;
165
177
  }, []);
166
- return (_jsxs(Box, { flexDirection: "column", height: terminalHeight, children: [_jsx(Box, { ref: listContainerRef, flexGrow: 1, flexShrink: 1, minHeight: 0, children: _jsx(VirtualizedList, { ref: listRef, data: listData, renderItem: renderListItem, estimatedItemHeight: estimateItemHeight, keyExtractor: getItemKey, initialScrollIndex: SCROLL_TO_ITEM_END, initialScrollOffsetInIndex: SCROLL_TO_ITEM_END }) }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(TodoPanel, { todos: todos }), _jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, isCompacting: ui.isCompacting, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null }), selectionHintVisible && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: "yellowBright", children: "\uD83D\uDCA1 Tip: Hold Option (\u2325) and click to select text, or press Ctrl+S to toggle copy mode" }) })), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, setTodos: setTodos, agent: agent, inputService: inputService, onKeyboardScroll: handleKeyboardScroll, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { agent: agent, sessionId: session.id, modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits, isShellMode: buffer.text.startsWith('!') }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
178
+ return (_jsxs(Box, { flexDirection: "column", height: terminalHeight, children: [_jsx(Box, { ref: listContainerRef, flexGrow: 1, flexShrink: 1, minHeight: 0, children: _jsx(VirtualizedList, { ref: listRef, data: listData, renderItem: renderListItem, estimatedItemHeight: estimateItemHeight, keyExtractor: getItemKey, initialScrollIndex: SCROLL_TO_ITEM_END, initialScrollOffsetInIndex: SCROLL_TO_ITEM_END }) }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, isCompacting: ui.isCompacting, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null, todoExpanded: ui.todoExpanded, hasTodos: todos.some((t) => t.status !== 'completed'), planModeActive: ui.planModeActive, autoApproveEdits: ui.autoApproveEdits }), _jsx(TodoPanel, { todos: todos, isExpanded: ui.todoExpanded, isProcessing: ui.isProcessing }), selectionHintVisible && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: "yellowBright", children: "\uD83D\uDCA1 Tip: Hold Option (\u2325) and click to select text, or press Ctrl+S to toggle copy mode" }) })), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, setTodos: setTodos, agent: agent, inputService: inputService, onKeyboardScroll: handleKeyboardScroll, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { agent: agent, sessionId: session.id, modelName: session.modelName, cwd: process.cwd(), ...(branchName ? { branchName } : {}), autoApproveEdits: ui.autoApproveEdits, planModeActive: ui.planModeActive, isShellMode: buffer.text.startsWith('!') }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
167
179
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAmBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,YAAmB,GACtB,EAAE,cAAc,2CA8NhB"}
1
+ {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,YAAmB,GACtB,EAAE,cAAc,2CA2OhB"}
@@ -21,6 +21,7 @@ const CLEAR_TERMINAL = '\x1B[2J\x1B[3J\x1B[H';
21
21
  // Hooks
22
22
  import { useCLIState } from '../../hooks/useCLIState.js';
23
23
  import { useTerminalSize } from '../../hooks/useTerminalSize.js';
24
+ import { useGitBranch } from '../../hooks/useGitBranch.js';
24
25
  // Components
25
26
  import { Header } from '../chat/Header.js';
26
27
  import { MessageItem } from '../chat/MessageItem.js';
@@ -40,6 +41,8 @@ export function StaticCLI({ agent, initialSessionId, startupInfo, useStreaming =
40
41
  startupInfo,
41
42
  // No keyboard scroll handler - let terminal handle scrollback
42
43
  });
44
+ // Get current git branch name
45
+ const branchName = useGitBranch();
43
46
  // Terminal resize handling - clear and re-render Static content
44
47
  const { write: stdoutWrite } = useStdout();
45
48
  const { columns: terminalWidth } = useTerminalSize();
@@ -99,5 +102,5 @@ export function StaticCLI({ agent, initialSessionId, startupInfo, useStreaming =
99
102
  startupInfo,
100
103
  terminalWidth,
101
104
  ]);
102
- return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Static, { items: staticItems, children: (item) => item }, staticRemountKey), pendingMessages.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), dequeuedBuffer.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(TodoPanel, { todos: todos }), _jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, isCompacting: ui.isCompacting, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null }), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, setTodos: setTodos, agent: agent, inputService: inputService, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, refreshStatic: refreshStatic, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { agent: agent, sessionId: session.id, modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits, isShellMode: buffer.text.startsWith('!') }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
105
+ return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Static, { items: staticItems, children: (item) => item }, staticRemountKey), pendingMessages.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), dequeuedBuffer.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, isCompacting: ui.isCompacting, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null, todoExpanded: ui.todoExpanded, hasTodos: todos.some((t) => t.status !== 'completed'), planModeActive: ui.planModeActive, autoApproveEdits: ui.autoApproveEdits }), _jsx(TodoPanel, { todos: todos, isExpanded: ui.todoExpanded, isProcessing: ui.isProcessing }), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, setTodos: setTodos, agent: agent, inputService: inputService, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, refreshStatic: refreshStatic, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { agent: agent, sessionId: session.id, modelName: session.modelName, cwd: process.cwd(), ...(branchName ? { branchName } : {}), autoApproveEdits: ui.autoApproveEdits, planModeActive: ui.planModeActive, isShellMode: buffer.text.startsWith('!') }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
103
106
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * ExportWizard Component
3
+ * Interactive wizard for exporting conversation to markdown or JSON
4
+ */
5
+ import React from 'react';
6
+ import type { Key } from '../../hooks/useInputOrchestrator.js';
7
+ import type { DextoAgent } from '@dexto/core';
8
+ interface ExportWizardProps {
9
+ isVisible: boolean;
10
+ agent: DextoAgent;
11
+ sessionId: string | null;
12
+ onClose: () => void;
13
+ }
14
+ export interface ExportWizardHandle {
15
+ handleInput: (input: string, key: Key) => boolean;
16
+ }
17
+ /**
18
+ * Interactive wizard for exporting conversation
19
+ */
20
+ declare const ExportWizard: React.ForwardRefExoticComponent<ExportWizardProps & React.RefAttributes<ExportWizardHandle>>;
21
+ export default ExportWizard;
22
+ //# sourceMappingURL=ExportWizard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExportWizard.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/ExportWizard.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA4E,MAAM,OAAO,CAAC;AAIjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAY9C,UAAU,iBAAiB;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AA4JD;;GAEG;AACH,QAAA,MAAM,YAAY,8FA+UhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -0,0 +1,308 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ExportWizard Component
4
+ * Interactive wizard for exporting conversation to markdown or JSON
5
+ */
6
+ import { useState, useEffect, forwardRef, useImperativeHandle, useCallback } from 'react';
7
+ import { Box, Text } from 'ink';
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ /**
11
+ * Generate default filename based on date and session ID
12
+ */
13
+ function generateDefaultFilename(sessionId, format) {
14
+ const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
15
+ const shortId = sessionId ? sessionId.slice(0, 6) : 'unknown';
16
+ const ext = format === 'markdown' ? 'md' : 'json';
17
+ return `conversation-${date}-${shortId}.${ext}`;
18
+ }
19
+ /**
20
+ * Format conversation history as Markdown
21
+ */
22
+ function formatAsMarkdown(messages, metadata, includeToolCalls) {
23
+ const lines = [];
24
+ // Header
25
+ lines.push('# Conversation Export');
26
+ lines.push('');
27
+ lines.push(`- **Session**: ${metadata.sessionId}`);
28
+ if (metadata.title) {
29
+ lines.push(`- **Title**: ${metadata.title}`);
30
+ }
31
+ if (metadata.createdAt) {
32
+ lines.push(`- **Created**: ${metadata.createdAt}`);
33
+ }
34
+ lines.push(`- **Exported**: ${new Date().toISOString()}`);
35
+ lines.push('');
36
+ lines.push('---');
37
+ lines.push('');
38
+ // Messages
39
+ for (const msg of messages) {
40
+ const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
41
+ // Skip tool messages if not including tool calls
42
+ if (!includeToolCalls && msg.role === 'tool') {
43
+ continue;
44
+ }
45
+ lines.push(`## ${role}`);
46
+ if (msg.timestamp) {
47
+ lines.push(`*${msg.timestamp}*`);
48
+ }
49
+ lines.push('');
50
+ // Handle different content types
51
+ if (typeof msg.content === 'string') {
52
+ lines.push(msg.content);
53
+ }
54
+ else if (Array.isArray(msg.content)) {
55
+ // Handle content parts (text, tool calls, tool results)
56
+ for (const part of msg.content) {
57
+ if (typeof part === 'string') {
58
+ lines.push(part);
59
+ }
60
+ else if (part && typeof part === 'object') {
61
+ if ('text' in part) {
62
+ lines.push(String(part.text));
63
+ }
64
+ else if ('type' in part && part.type === 'tool-call' && includeToolCalls) {
65
+ const toolCall = part;
66
+ lines.push('');
67
+ lines.push(`### Tool: ${toolCall.toolName || 'unknown'}`);
68
+ lines.push('```json');
69
+ lines.push(JSON.stringify(toolCall.args || {}, null, 2));
70
+ lines.push('```');
71
+ }
72
+ else if ('type' in part && part.type === 'tool-result' && includeToolCalls) {
73
+ const toolResult = part;
74
+ lines.push('');
75
+ lines.push('<details>');
76
+ lines.push(`<summary>Result: ${toolResult.toolName || 'unknown'}</summary>`);
77
+ lines.push('');
78
+ lines.push('```');
79
+ const resultStr = typeof toolResult.result === 'string'
80
+ ? toolResult.result
81
+ : JSON.stringify(toolResult.result, null, 2);
82
+ // Truncate very long results
83
+ if (resultStr.length > 2000) {
84
+ lines.push(resultStr.slice(0, 2000) + '\n... (truncated)');
85
+ }
86
+ else {
87
+ lines.push(resultStr);
88
+ }
89
+ lines.push('```');
90
+ lines.push('</details>');
91
+ }
92
+ }
93
+ }
94
+ }
95
+ else if (msg.content && typeof msg.content === 'object') {
96
+ lines.push('```json');
97
+ lines.push(JSON.stringify(msg.content, null, 2));
98
+ lines.push('```');
99
+ }
100
+ lines.push('');
101
+ lines.push('---');
102
+ lines.push('');
103
+ }
104
+ return lines.join('\n');
105
+ }
106
+ /**
107
+ * Format conversation history as JSON
108
+ */
109
+ function formatAsJson(messages, metadata, includeToolCalls) {
110
+ const filteredMessages = includeToolCalls
111
+ ? messages
112
+ : messages
113
+ .filter((m) => m.role !== 'tool')
114
+ .map((m) => ({
115
+ ...m,
116
+ content: Array.isArray(m.content)
117
+ ? m.content.filter((part) => {
118
+ if (!part || typeof part !== 'object')
119
+ return true;
120
+ return !('type' in part &&
121
+ (part.type === 'tool-call' || part.type === 'tool-result'));
122
+ })
123
+ : m.content,
124
+ }));
125
+ return JSON.stringify({
126
+ exportedAt: new Date().toISOString(),
127
+ session: metadata,
128
+ messages: filteredMessages,
129
+ }, null, 2);
130
+ }
131
+ /**
132
+ * Interactive wizard for exporting conversation
133
+ */
134
+ const ExportWizard = forwardRef(function ExportWizard({ isVisible, agent, sessionId, onClose }, ref) {
135
+ const [step, setStep] = useState('format');
136
+ const [options, setOptions] = useState({
137
+ format: 'markdown',
138
+ includeToolCalls: true,
139
+ filename: '',
140
+ });
141
+ const [selectedIndex, setSelectedIndex] = useState(0);
142
+ const [filenameInput, setFilenameInput] = useState('');
143
+ const [exportResult, setExportResult] = useState(null);
144
+ // Reset when becoming visible
145
+ useEffect(() => {
146
+ if (isVisible) {
147
+ setStep('format');
148
+ setOptions({
149
+ format: 'markdown',
150
+ includeToolCalls: true,
151
+ filename: '',
152
+ });
153
+ setSelectedIndex(0);
154
+ setFilenameInput('');
155
+ setExportResult(null);
156
+ }
157
+ }, [isVisible]);
158
+ // Update default filename when format changes
159
+ useEffect(() => {
160
+ if (step === 'filename' && !filenameInput) {
161
+ setFilenameInput(generateDefaultFilename(sessionId, options.format));
162
+ }
163
+ }, [step, sessionId, options.format, filenameInput]);
164
+ const doExport = useCallback(async () => {
165
+ if (!sessionId) {
166
+ setExportResult({ success: false, error: 'No active session' });
167
+ setStep('error');
168
+ return;
169
+ }
170
+ setStep('exporting');
171
+ try {
172
+ // Get session history and metadata
173
+ const history = await agent.getSessionHistory(sessionId);
174
+ const metadata = await agent.getSessionMetadata(sessionId);
175
+ const exportMetadata = {
176
+ sessionId,
177
+ title: metadata?.title,
178
+ createdAt: metadata?.createdAt
179
+ ? new Date(metadata.createdAt).toISOString()
180
+ : undefined,
181
+ };
182
+ // Format content - cast history to expected type
183
+ const formattedHistory = history.map((msg) => ({
184
+ role: msg.role,
185
+ content: msg.content,
186
+ timestamp: 'timestamp' in msg && typeof msg.timestamp === 'number'
187
+ ? new Date(msg.timestamp).toISOString()
188
+ : undefined,
189
+ }));
190
+ const content = options.format === 'markdown'
191
+ ? formatAsMarkdown(formattedHistory, exportMetadata, options.includeToolCalls)
192
+ : formatAsJson(formattedHistory, exportMetadata, options.includeToolCalls);
193
+ // Write file - use path.basename to prevent path traversal attacks
194
+ const rawFilename = options.filename || filenameInput;
195
+ const safeFilename = path.basename(rawFilename);
196
+ const outputPath = path.resolve(process.cwd(), safeFilename);
197
+ await fs.writeFile(outputPath, content, 'utf-8');
198
+ setExportResult({ success: true, path: outputPath });
199
+ setStep('done');
200
+ }
201
+ catch (error) {
202
+ setExportResult({
203
+ success: false,
204
+ error: error instanceof Error ? error.message : String(error),
205
+ });
206
+ setStep('error');
207
+ }
208
+ }, [agent, sessionId, options, filenameInput]);
209
+ // Handle keyboard input
210
+ useImperativeHandle(ref, () => ({
211
+ handleInput: (input, key) => {
212
+ if (!isVisible)
213
+ return false;
214
+ // Escape to close
215
+ if (key.escape) {
216
+ onClose();
217
+ return true;
218
+ }
219
+ // Done/error state - Enter/Esc closes, consume all other input
220
+ if (step === 'done' || step === 'error') {
221
+ if (key.return || key.escape) {
222
+ onClose();
223
+ }
224
+ return true; // Consume all input in terminal states
225
+ }
226
+ // Format selection step
227
+ if (step === 'format') {
228
+ if (key.upArrow || key.downArrow) {
229
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
230
+ return true;
231
+ }
232
+ if (key.return) {
233
+ setOptions((prev) => ({
234
+ ...prev,
235
+ format: selectedIndex === 0 ? 'markdown' : 'json',
236
+ }));
237
+ setSelectedIndex(0);
238
+ setStep('toolCalls');
239
+ return true;
240
+ }
241
+ return false;
242
+ }
243
+ // Tool calls selection step
244
+ if (step === 'toolCalls') {
245
+ if (key.upArrow || key.downArrow) {
246
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
247
+ return true;
248
+ }
249
+ if (key.return) {
250
+ setOptions((prev) => ({
251
+ ...prev,
252
+ includeToolCalls: selectedIndex === 0,
253
+ }));
254
+ setFilenameInput(generateDefaultFilename(sessionId, options.format));
255
+ setStep('filename');
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+ // Filename input step
261
+ if (step === 'filename') {
262
+ if (key.return) {
263
+ const finalFilename = filenameInput.trim();
264
+ if (!finalFilename) {
265
+ // Don't proceed with empty filename
266
+ return true;
267
+ }
268
+ setOptions((prev) => ({ ...prev, filename: finalFilename }));
269
+ setSelectedIndex(0); // Reset to "Export" option
270
+ setStep('confirm');
271
+ return true;
272
+ }
273
+ if (key.backspace || key.delete) {
274
+ setFilenameInput((prev) => prev.slice(0, -1));
275
+ return true;
276
+ }
277
+ if (input && !key.ctrl && !key.meta) {
278
+ setFilenameInput((prev) => prev + input);
279
+ return true;
280
+ }
281
+ return false;
282
+ }
283
+ // Confirm step
284
+ if (step === 'confirm') {
285
+ if (key.upArrow || key.downArrow) {
286
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
287
+ return true;
288
+ }
289
+ if (key.return) {
290
+ if (selectedIndex === 0) {
291
+ doExport();
292
+ }
293
+ else {
294
+ onClose();
295
+ }
296
+ return true;
297
+ }
298
+ return false;
299
+ }
300
+ return false;
301
+ },
302
+ }), [isVisible, step, selectedIndex, options, filenameInput, onClose, doExport, sessionId]);
303
+ if (!isVisible)
304
+ return null;
305
+ // Render based on current step
306
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginTop: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "\uD83D\uDCE4 Export Conversation" }) }), step === 'format' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Select export format:" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { ...(selectedIndex === 0 ? { color: 'cyan' } : {}), children: [selectedIndex === 0 ? '❯ ' : ' ', "Markdown (.md) - Human readable"] }), _jsxs(Text, { ...(selectedIndex === 1 ? { color: 'cyan' } : {}), children: [selectedIndex === 1 ? '❯ ' : ' ', "JSON (.json) - Structured data"] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 to select \u2022 Enter to continue \u2022 Esc to cancel" }) })] })), step === 'toolCalls' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Include tool calls and results?" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { ...(selectedIndex === 0 ? { color: 'cyan' } : {}), children: [selectedIndex === 0 ? '❯ ' : ' ', "Yes - Include all tool interactions"] }), _jsxs(Text, { ...(selectedIndex === 1 ? { color: 'cyan' } : {}), children: [selectedIndex === 1 ? '❯ ' : ' ', "No - Only user and assistant messages"] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 to select \u2022 Enter to continue \u2022 Esc to cancel" }) })] })), step === 'filename' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Filename:" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "> " }), _jsx(Text, { children: filenameInput }), _jsx(Text, { color: "cyan", children: "_" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Enter to continue \u2022 Esc to cancel" }) })] })), step === 'confirm' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Export with these settings?" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { color: "gray", children: ["Format:", ' ', _jsx(Text, { color: "white", children: options.format === 'markdown' ? 'Markdown' : 'JSON' })] }), _jsxs(Text, { color: "gray", children: ["Tool calls:", ' ', _jsx(Text, { color: "white", children: options.includeToolCalls ? 'Yes' : 'No' })] }), _jsxs(Text, { color: "gray", children: ["File: ", _jsx(Text, { color: "white", children: filenameInput })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { ...(selectedIndex === 0 ? { color: 'green' } : {}), children: [selectedIndex === 0 ? '❯ ' : ' ', "Export"] }), _jsxs(Text, { ...(selectedIndex === 1 ? { color: 'red' } : {}), children: [selectedIndex === 1 ? '❯ ' : ' ', "Cancel"] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 to select \u2022 Enter to confirm" }) })] })), step === 'exporting' && (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { color: "yellow", children: "Exporting..." }) })), step === 'done' && exportResult?.success && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "green", children: "\u2713 Exported successfully!" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "gray", children: "Saved to: " }), _jsx(Text, { children: exportResult.path })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press Enter or Esc to close" }) })] })), step === 'error' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u2717 Export failed" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: exportResult?.error }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press Enter or Esc to close" }) })] }))] }));
307
+ });
308
+ export default ExportWizard;
@@ -10,6 +10,7 @@ interface LogLevelSelectorProps {
10
10
  onSelect: (level: string) => void;
11
11
  onClose: () => void;
12
12
  agent: DextoAgent;
13
+ sessionId: string | null;
13
14
  }
14
15
  export interface LogLevelSelectorHandle {
15
16
  handleInput: (input: string, key: Key) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"LogLevelSelector.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/LogLevelSelector.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAuE,MAAM,OAAO,CAAC;AAE5F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,UAAU,qBAAqB;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAkBD;;GAEG;AACH,QAAA,MAAM,gBAAgB,sGAyFrB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"LogLevelSelector.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/LogLevelSelector.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAuE,MAAM,OAAO,CAAC;AAE5F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,UAAU,qBAAqB;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAkBD;;GAEG;AACH,QAAA,MAAM,gBAAgB,sGAwGrB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -17,7 +17,7 @@ const LOG_LEVELS = [
17
17
  /**
18
18
  * Log level selector - thin wrapper around BaseSelector
19
19
  */
20
- const LogLevelSelector = forwardRef(function LogLevelSelector({ isVisible, onSelect, onClose, agent }, ref) {
20
+ const LogLevelSelector = forwardRef(function LogLevelSelector({ isVisible, onSelect, onClose, agent, sessionId }, ref) {
21
21
  const baseSelectorRef = useRef(null);
22
22
  // Forward handleInput to BaseSelector
23
23
  useImperativeHandle(ref, () => ({
@@ -30,30 +30,41 @@ const LogLevelSelector = forwardRef(function LogLevelSelector({ isVisible, onSel
30
30
  const [logFilePath, setLogFilePath] = useState(null);
31
31
  // Build levels list with current indicator
32
32
  useEffect(() => {
33
- if (!isVisible) {
34
- setLogFilePath(null);
35
- return;
36
- }
37
- // Get current level from agent's logger (shared across all child loggers)
38
- const currentLevel = agent.logger.getLevel();
39
- const levelList = LOG_LEVELS.map((l) => ({
40
- ...l,
41
- isCurrent: l.level === currentLevel,
42
- }));
43
- setLevels(levelList);
44
- setLogFilePath(agent.logger.getLogFilePath());
45
- // Set initial selection to current level
46
- const currentIndex = levelList.findIndex((l) => l.isCurrent);
47
- if (currentIndex >= 0) {
48
- setSelectedIndex(currentIndex);
49
- }
50
- }, [isVisible, agent]);
33
+ let isCancelled = false;
34
+ const run = async () => {
35
+ if (!isVisible) {
36
+ setLogFilePath(null);
37
+ return;
38
+ }
39
+ // Get current level from agent's logger (shared across all child loggers)
40
+ const currentLevel = agent.logger.getLevel();
41
+ const levelList = LOG_LEVELS.map((l) => ({
42
+ ...l,
43
+ isCurrent: l.level === currentLevel,
44
+ }));
45
+ setLevels(levelList);
46
+ // File logging is session-scoped; prefer the active session logger if available.
47
+ const session = sessionId ? await agent.getSession(sessionId) : undefined;
48
+ if (!isCancelled) {
49
+ setLogFilePath(session?.logger.getLogFilePath() ?? null);
50
+ }
51
+ // Set initial selection to current level
52
+ const currentIndex = levelList.findIndex((l) => l.isCurrent);
53
+ if (currentIndex >= 0) {
54
+ setSelectedIndex(currentIndex);
55
+ }
56
+ };
57
+ void run();
58
+ return () => {
59
+ isCancelled = true;
60
+ };
61
+ }, [isVisible, agent, sessionId]);
51
62
  // Format level item for display
52
63
  const formatItem = (option, isSelected) => (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [option.icon, " "] }), _jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: option.level }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [" - ", option.description] }), option.isCurrent && (_jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: [' ', "\u2190 Current"] }))] }));
53
64
  // Handle selection
54
65
  const handleSelect = (option) => {
55
66
  onSelect(option.level);
56
67
  };
57
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BaseSelector, { ref: baseSelectorRef, items: levels, isVisible: isVisible, isLoading: false, selectedIndex: selectedIndex, onSelectIndex: setSelectedIndex, onSelect: handleSelect, onClose: onClose, formatItem: formatItem, title: "Select Log Level", borderColor: "yellowBright", emptyMessage: "No log levels available" }), logFilePath && process.env.DEXTO_PRIVACY_MODE !== 'true' && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC1 Log file: ", logFilePath] }) }))] }));
68
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BaseSelector, { ref: baseSelectorRef, items: levels, isVisible: isVisible, isLoading: false, selectedIndex: selectedIndex, onSelectIndex: setSelectedIndex, onSelect: handleSelect, onClose: onClose, formatItem: formatItem, title: "Select Log Level", borderColor: "yellowBright", emptyMessage: "No log levels available" }), logFilePath && process.env.DEXTO_DEV_MODE === 'true' && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC1 Log file: ", logFilePath] }) }))] }));
58
69
  });
59
70
  export default LogLevelSelector;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MarketplaceAddPrompt Component
3
+ * Prompts user to enter a marketplace source to add
4
+ */
5
+ import React from 'react';
6
+ import type { Key } from '../../hooks/useInputOrchestrator.js';
7
+ interface MarketplaceAddPromptProps {
8
+ isVisible: boolean;
9
+ onComplete: (name: string, pluginCount: number) => void;
10
+ onClose: () => void;
11
+ }
12
+ export interface MarketplaceAddPromptHandle {
13
+ handleInput: (input: string, key: Key) => boolean;
14
+ }
15
+ /**
16
+ * Marketplace add prompt - single input for source
17
+ */
18
+ declare const MarketplaceAddPrompt: React.ForwardRefExoticComponent<MarketplaceAddPromptProps & React.RefAttributes<MarketplaceAddPromptHandle>>;
19
+ export default MarketplaceAddPrompt;
20
+ //# sourceMappingURL=MarketplaceAddPrompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarketplaceAddPrompt.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/MarketplaceAddPrompt.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA4E,MAAM,OAAO,CAAC;AAIjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAE/D,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,QAAA,MAAM,oBAAoB,8GAsIzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * MarketplaceAddPrompt Component
4
+ * Prompts user to enter a marketplace source to add
5
+ */
6
+ import { useState, useEffect, forwardRef, useImperativeHandle, useCallback } from 'react';
7
+ import { Box, Text } from 'ink';
8
+ import { addMarketplace } from '@dexto/agent-management';
9
+ import { logger } from '@dexto/core';
10
+ /**
11
+ * Marketplace add prompt - single input for source
12
+ */
13
+ const MarketplaceAddPrompt = forwardRef(function MarketplaceAddPrompt({ isVisible, onComplete, onClose }, ref) {
14
+ const [input, setInput] = useState('');
15
+ const [error, setError] = useState(null);
16
+ const [isAdding, setIsAdding] = useState(false);
17
+ // Reset when becoming visible
18
+ useEffect(() => {
19
+ if (isVisible) {
20
+ setInput('');
21
+ setError(null);
22
+ setIsAdding(false);
23
+ }
24
+ }, [isVisible]);
25
+ // Handle adding marketplace
26
+ const handleAdd = useCallback(async () => {
27
+ const source = input.trim();
28
+ if (!source) {
29
+ setError('Please enter a marketplace source');
30
+ return;
31
+ }
32
+ setError(null);
33
+ setIsAdding(true);
34
+ try {
35
+ const result = await addMarketplace(source);
36
+ onComplete(result.name, result.pluginCount);
37
+ }
38
+ catch (err) {
39
+ const errorMessage = err instanceof Error ? err.message : String(err);
40
+ setError(errorMessage);
41
+ logger.error(`MarketplaceAddPrompt.handleAdd failed: ${errorMessage}`);
42
+ }
43
+ finally {
44
+ setIsAdding(false);
45
+ }
46
+ }, [input, onComplete]);
47
+ // Handle input
48
+ useImperativeHandle(ref, () => ({
49
+ handleInput: (inputStr, key) => {
50
+ // Escape to close
51
+ if (key.escape) {
52
+ onClose();
53
+ return true;
54
+ }
55
+ // Enter to submit
56
+ if (key.return) {
57
+ if (!isAdding) {
58
+ handleAdd();
59
+ }
60
+ return true;
61
+ }
62
+ // Backspace
63
+ if (key.backspace || key.delete) {
64
+ setInput((prev) => prev.slice(0, -1));
65
+ setError(null);
66
+ return true;
67
+ }
68
+ // Regular character input
69
+ if (inputStr && !key.ctrl && !key.meta) {
70
+ setInput((prev) => prev + inputStr);
71
+ setError(null);
72
+ return true;
73
+ }
74
+ return false;
75
+ },
76
+ }), [handleAdd, isAdding, onClose]);
77
+ if (!isVisible)
78
+ return null;
79
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "green", children: "Add Marketplace" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "gray", children: "Enter marketplace source:" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", dimColor: true, children: "- GitHub: owner/repo (e.g., anthropics/claude-plugins-official)" }), _jsx(Text, { color: "gray", dimColor: true, children: "- Git URL: https://github.com/user/repo.git" }), _jsx(Text, { color: "gray", dimColor: true, children: "- Local: /path/to/marketplace or ~/marketplace" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '> ' }), _jsx(Text, { children: input }), _jsx(Text, { color: "cyan", children: "_" })] }), error && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: error }) })), isAdding && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "Adding marketplace..." }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press Enter to add, Escape to cancel" }) })] }));
80
+ });
81
+ export default MarketplaceAddPrompt;