dexto 1.5.0 → 1.5.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 (60) hide show
  1. package/dist/cli/commands/interactive-commands/command-parser.d.ts +1 -1
  2. package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
  3. package/dist/cli/commands/interactive-commands/command-parser.js +11 -1
  4. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  5. package/dist/cli/commands/interactive-commands/general-commands.js +63 -0
  6. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
  7. package/dist/cli/commands/interactive-commands/system/system-commands.js +3 -2
  8. package/dist/cli/commands/setup.d.ts.map +1 -1
  9. package/dist/cli/commands/setup.js +163 -28
  10. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  11. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +8 -2
  12. package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
  13. package/dist/cli/ink-cli/components/TextBufferInput.js +24 -6
  14. package/dist/cli/ink-cli/components/chat/Header.js +1 -1
  15. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts +3 -1
  16. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  17. package/dist/cli/ink-cli/components/chat/MessageItem.js +37 -10
  18. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.d.ts.map +1 -1
  19. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
  20. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +3 -3
  21. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  22. package/dist/cli/ink-cli/components/modes/StaticCLI.js +10 -3
  23. package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts.map +1 -1
  24. package/dist/cli/ink-cli/components/overlays/CustomModelWizard.js +19 -8
  25. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +1 -1
  26. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts +2 -1
  27. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  28. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +64 -2
  29. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
  30. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +44 -1
  31. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts +6 -0
  32. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts.map +1 -1
  33. package/dist/cli/ink-cli/components/renderers/GenericRenderer.js +1 -1
  34. package/dist/cli/ink-cli/components/renderers/SearchRenderer.js +1 -1
  35. package/dist/cli/ink-cli/components/shared/MarkdownText.d.ts.map +1 -1
  36. package/dist/cli/ink-cli/components/shared/MarkdownText.js +4 -4
  37. package/dist/cli/ink-cli/constants/processingPhrases.d.ts.map +1 -1
  38. package/dist/cli/ink-cli/constants/processingPhrases.js +58 -48
  39. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  40. package/dist/cli/ink-cli/constants/tips.js +33 -32
  41. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  42. package/dist/cli/ink-cli/containers/InputContainer.js +18 -11
  43. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  44. package/dist/cli/ink-cli/containers/OverlayContainer.js +26 -3
  45. package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts +8 -1
  46. package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
  47. package/dist/cli/ink-cli/hooks/useAgentEvents.js +144 -6
  48. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  49. package/dist/cli/ink-cli/hooks/useCLIState.js +17 -1
  50. package/dist/cli/ink-cli/hooks/useTokenCounter.d.ts +11 -7
  51. package/dist/cli/ink-cli/hooks/useTokenCounter.d.ts.map +1 -1
  52. package/dist/cli/ink-cli/hooks/useTokenCounter.js +41 -18
  53. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  54. package/dist/cli/ink-cli/services/processStream.js +20 -8
  55. package/dist/cli/ink-cli/state/types.d.ts +2 -2
  56. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  57. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  58. package/dist/cli/ink-cli/utils/messageFormatting.js +92 -5
  59. package/dist/index.js +24 -3
  60. package/package.json +7 -7
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
3
  * MessageItem Component
4
4
  * Displays a single message with visual hierarchy
@@ -6,6 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { memo } from 'react';
8
8
  import { Box, Text } from 'ink';
9
+ import wrapAnsi from 'wrap-ansi';
9
10
  import { ConfigBox, StatsBox, HelpBox, SessionListBox, SessionHistoryBox, LogConfigBox, ShortcutsBox, SyspromptBox, } from './styled-boxes/index.js';
10
11
  import { ToolResultRenderer } from '../renderers/index.js';
11
12
  import { MarkdownText } from '../shared/MarkdownText.js';
@@ -36,7 +37,7 @@ function formatDuration(ms) {
36
37
  * Memoization with custom comparator prevents re-renders when message array changes
37
38
  * but individual message content hasn't changed.
38
39
  */
39
- export const MessageItem = memo(({ message }) => {
40
+ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
40
41
  // Check for styled message first
41
42
  if (message.styledType && message.styledData) {
42
43
  switch (message.styledType) {
@@ -55,8 +56,11 @@ export const MessageItem = memo(({ message }) => {
55
56
  case 'run-summary': {
56
57
  const data = message.styledData;
57
58
  const durationStr = formatDuration(data.durationMs);
58
- const tokensStr = data.outputTokens > 0 ? `, Used ${data.outputTokens} tokens` : '';
59
- return (_jsx(Box, { marginTop: 1, marginBottom: 1, width: "100%", children: _jsxs(Text, { color: "gray", children: ["\u2500 Worked for ", durationStr, tokensStr, " \u2500"] }) }));
59
+ // Only show tokens when >= 1000, using K notation
60
+ const tokensStr = data.totalTokens >= 1000
61
+ ? `, Used ${(data.totalTokens / 1000).toFixed(1)}K tokens`
62
+ : '';
63
+ return (_jsx(Box, { marginTop: 1, marginBottom: 1, width: terminalWidth, children: _jsxs(Text, { color: "gray", children: ["\u2500 Worked for ", durationStr, tokensStr, " \u2500"] }) }));
60
64
  }
61
65
  case 'shortcuts':
62
66
  return _jsx(ShortcutsBox, { data: message.styledData });
@@ -65,8 +69,18 @@ export const MessageItem = memo(({ message }) => {
65
69
  }
66
70
  }
67
71
  // User message: '>' prefix with gray background
72
+ // Properly wrap text accounting for prefix "> " (2 chars) and paddingX={1} (2 chars total)
68
73
  if (message.role === 'user') {
69
- return (_jsx(Box, { flexDirection: "column", marginTop: 2, marginBottom: 1, width: "100%", children: _jsxs(Box, { flexDirection: "row", paddingX: 1, backgroundColor: "gray", children: [_jsx(Text, { color: "green", children: '> ' }), _jsx(Text, { color: "white", wrap: "wrap", children: message.content })] }) }));
74
+ const prefix = '> ';
75
+ const paddingChars = 2; // paddingX={1} = 1 char on each side
76
+ const availableWidth = Math.max(20, terminalWidth - prefix.length - paddingChars);
77
+ const wrappedContent = wrapAnsi(message.content, availableWidth, {
78
+ hard: true,
79
+ wordWrap: true,
80
+ trim: false,
81
+ });
82
+ const lines = wrappedContent.split('\n');
83
+ return (_jsx(Box, { flexDirection: "column", marginTop: 2, marginBottom: 1, width: terminalWidth, children: _jsx(Box, { flexDirection: "column", paddingX: 1, backgroundColor: "gray", children: lines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: i === 0 ? prefix : ' ' }), _jsx(Text, { color: "white", children: line })] }, i))) }) }));
70
84
  }
71
85
  // Assistant message: Gray circle indicator (unless continuation)
72
86
  // IMPORTANT: width="100%" is required to prevent Ink layout failures on large content.
@@ -75,12 +89,12 @@ export const MessageItem = memo(({ message }) => {
75
89
  if (message.role === 'assistant') {
76
90
  // Continuation messages: no indicator, just content
77
91
  if (message.isContinuation) {
78
- return (_jsx(Box, { flexDirection: "column", width: "100%", children: _jsx(MarkdownText, { children: message.content || '' }) }));
92
+ return (_jsx(Box, { flexDirection: "column", width: terminalWidth, children: _jsx(MarkdownText, { children: message.content || '' }) }));
79
93
  }
80
94
  // Regular assistant message: bullet prefix inline with first line
81
95
  // Text wraps at terminal width - wrapped lines may start at column 0
82
96
  // This is simpler and avoids mid-word splitting issues with Ink's wrap
83
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, width: "100%", children: _jsx(MarkdownText, { bulletPrefix: "\u23FA ", children: message.content || '' }) }));
97
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: _jsx(MarkdownText, { bulletPrefix: "\u23FA ", children: message.content || '' }) }));
84
98
  }
85
99
  // Tool message: Animated icon based on status
86
100
  // - Running: green spinner + "Running..."
@@ -95,10 +109,22 @@ export const MessageItem = memo(({ message }) => {
95
109
  const parenIndex = message.content.indexOf('(');
96
110
  const toolName = parenIndex > 0 ? message.content.slice(0, parenIndex) : message.content;
97
111
  const toolArgs = parenIndex > 0 ? message.content.slice(parenIndex) : '';
98
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: "100%", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false }), _jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { bold: true, children: toolName }), _jsx(Text, { children: toolArgs }), isRunning && _jsx(Text, { color: "green", children: " Running..." }), isPending && _jsx(Text, { color: "yellowBright", children: " Waiting..." })] }) })] }), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
112
+ // Build the full tool header text for wrapping
113
+ const statusSuffix = isRunning ? ' Running...' : isPending ? ' Waiting...' : '';
114
+ const fullToolText = `${toolName}${toolArgs}${statusSuffix}`;
115
+ // ToolIcon takes 2 chars ("● "), so available width is terminalWidth - 2
116
+ const iconWidth = 2;
117
+ const availableWidth = Math.max(20, terminalWidth - iconWidth);
118
+ const wrappedToolText = wrapAnsi(fullToolText, availableWidth, {
119
+ hard: true,
120
+ wordWrap: true,
121
+ trim: false,
122
+ });
123
+ const toolLines = wrappedToolText.split('\n');
124
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
99
125
  }
100
126
  // System message: Compact gray text
101
- return (_jsx(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: _jsx(Text, { color: "gray", children: message.content }) }));
127
+ return (_jsx(Box, { flexDirection: "column", marginBottom: 1, width: terminalWidth, children: _jsx(Text, { color: "gray", children: message.content }) }));
102
128
  },
103
129
  // Custom comparator: only re-render if message content actually changed
104
130
  (prev, next) => {
@@ -113,6 +139,7 @@ export const MessageItem = memo(({ message }) => {
113
139
  prev.message.isContinuation === next.message.isContinuation &&
114
140
  prev.message.isError === next.message.isError &&
115
141
  prev.message.toolDisplayData === next.message.toolDisplayData &&
116
- prev.message.toolContent === next.message.toolContent);
142
+ prev.message.toolContent === next.message.toolContent &&
143
+ prev.terminalWidth === next.terminalWidth);
117
144
  });
118
145
  MessageItem.displayName = 'MessageItem';
@@ -1 +1 @@
1
- {"version":3,"file":"LogConfigBox.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,UAAU,iBAAiB;IACvB,IAAI,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA4BvD"}
1
+ {"version":3,"file":"LogConfigBox.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,UAAU,iBAAiB;IACvB,IAAI,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA8BvD"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { StyledBox, StyledRow, StyledListItem } from './StyledBox.js';
4
4
  export function LogConfigBox({ data }) {
5
- return (_jsxs(StyledBox, { title: "Logging Configuration", children: [_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StyledRow, { label: "Current level", value: data.currentLevel, valueColor: "green" }), data.logFile && _jsx(StyledRow, { label: "Log file", value: data.logFile })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Available levels (least to most verbose):" }), data.availableLevels.map((level) => {
5
+ return (_jsxs(StyledBox, { title: "Logging Configuration", children: [_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StyledRow, { label: "Current level", value: data.currentLevel, valueColor: "green" }), data.logFile && process.env.DEXTO_PRIVACY_MODE !== 'true' && (_jsx(StyledRow, { label: "Log file", value: data.logFile }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Available levels (least to most verbose):" }), data.availableLevels.map((level) => {
6
6
  const isCurrent = level === data.currentLevel;
7
7
  return (_jsx(StyledListItem, { icon: isCurrent ? '>' : ' ', text: level, isActive: isCurrent }, level));
8
8
  })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Use /log <level> to change level" }) })] }));
@@ -94,7 +94,7 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
94
94
  return () => clearTimeout(timer);
95
95
  }, [selectionHintVisible]);
96
96
  // Get terminal dimensions - updates on resize
97
- const { rows: terminalHeight } = useTerminalSize();
97
+ const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize();
98
98
  // Build list data: header as first item, then finalized + pending + dequeued buffer
99
99
  // In alternate buffer mode, everything is re-rendered anyway, so we combine all
100
100
  // Order: finalized messages → pending/streaming → dequeued user messages (guarantees order)
@@ -119,8 +119,8 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
119
119
  if (item.type === 'header') {
120
120
  return (_jsx(Header, { modelName: session.modelName, sessionId: session.id || undefined, hasActiveSession: session.hasActiveSession, startupInfo: startupInfo }));
121
121
  }
122
- return _jsx(MessageItem, { message: item.message });
123
- }, [session.modelName, session.id, session.hasActiveSession, startupInfo]);
122
+ return _jsx(MessageItem, { message: item.message, terminalWidth: terminalWidth });
123
+ }, [session.modelName, session.id, session.hasActiveSession, startupInfo, terminalWidth]);
124
124
  // Smart height estimation based on item type and content
125
125
  const estimateItemHeight = useCallback((index) => {
126
126
  const item = listData[index];
@@ -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;AAkBxD,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,2CA2MhB"}
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;AAkBxD,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,2CAoNhB"}
@@ -87,9 +87,16 @@ export function StaticCLI({ agent, initialSessionId, startupInfo, useStreaming =
87
87
  const staticItems = useMemo(() => {
88
88
  const items = [
89
89
  _jsx(Header, { modelName: session.modelName, sessionId: session.id || undefined, hasActiveSession: session.hasActiveSession, startupInfo: startupInfo }, "header"),
90
- ...visibleMessages.map((msg) => _jsx(MessageItem, { message: msg }, msg.id)),
90
+ ...visibleMessages.map((msg) => (_jsx(MessageItem, { message: msg, terminalWidth: terminalWidth }, msg.id))),
91
91
  ];
92
92
  return items;
93
- }, [visibleMessages, session.modelName, session.id, session.hasActiveSession, startupInfo]);
94
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item) => item }, staticRemountKey), pendingMessages.map((message) => (_jsx(MessageItem, { message: message }, message.id))), dequeuedBuffer.map((message) => (_jsx(MessageItem, { message: message }, message.id))), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, 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, 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, { modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
93
+ }, [
94
+ visibleMessages,
95
+ session.modelName,
96
+ session.id,
97
+ session.hasActiveSession,
98
+ startupInfo,
99
+ terminalWidth,
100
+ ]);
101
+ 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, 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, 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, { modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
95
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CustomModelWizard.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/CustomModelWizard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,EAIH,KAAK,WAAW,EAMnB,MAAM,yBAAyB,CAAC;AAmBjC,UAAU,sBAAsB;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,uEAAuE;IACvE,YAAY,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,uBAAuB;IACpC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;;GAGG;AACH,QAAA,MAAM,iBAAiB,wGAqXtB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"CustomModelWizard.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/CustomModelWizard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,EAIH,KAAK,WAAW,EAMnB,MAAM,yBAAyB,CAAC;AAmBjC,UAAU,sBAAsB;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,uEAAuE;IACvE,YAAY,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,uBAAuB;IACpC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;;GAGG;AACH,QAAA,MAAM,iBAAiB,wGAwYtB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -37,8 +37,17 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
37
37
  const localModelWizardRef = useRef(null);
38
38
  // Get provider config (data-driven, no conditionals)
39
39
  const providerConfig = selectedProvider ? getProviderConfig(selectedProvider) : null;
40
- const wizardSteps = providerConfig?.steps ?? [];
41
- const currentStepConfig = wizardSteps[currentStep];
40
+ const allWizardSteps = providerConfig?.steps ?? [];
41
+ /**
42
+ * Get visible steps based on current values.
43
+ * Steps with a condition function are only shown if the condition returns true.
44
+ */
45
+ const getVisibleSteps = useCallback((currentValues) => {
46
+ return allWizardSteps.filter((step) => !step.condition || step.condition(currentValues));
47
+ }, [allWizardSteps]);
48
+ // Current visible steps based on accumulated values
49
+ const visibleSteps = getVisibleSteps(values);
50
+ const currentStepConfig = visibleSteps[currentStep];
42
51
  // Reset when becoming visible
43
52
  useEffect(() => {
44
53
  if (isVisible) {
@@ -117,15 +126,17 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
117
126
  setValues(newValues);
118
127
  setError(null);
119
128
  setCurrentInput('');
129
+ // Get updated visible steps with new values
130
+ const updatedVisibleSteps = getVisibleSteps(newValues);
120
131
  // Check if we're done
121
- if (currentStep >= wizardSteps.length - 1) {
132
+ if (currentStep >= updatedVisibleSteps.length - 1) {
122
133
  await saveModel(newValues);
123
134
  }
124
135
  else {
125
136
  const nextStep = currentStep + 1;
126
137
  setCurrentStep(nextStep);
127
138
  // Pre-populate next step from stored values (for edit mode)
128
- const nextStepConfig = wizardSteps[nextStep];
139
+ const nextStepConfig = updatedVisibleSteps[nextStep];
129
140
  const nextValue = nextStepConfig ? newValues[nextStepConfig.field] : undefined;
130
141
  setCurrentInput(nextValue ?? '');
131
142
  }
@@ -133,11 +144,11 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
133
144
  currentInput,
134
145
  currentStep,
135
146
  currentStepConfig,
147
+ getVisibleSteps,
136
148
  isSaving,
137
149
  isValidating,
138
150
  selectedProvider,
139
151
  values,
140
- wizardSteps,
141
152
  ]);
142
153
  /**
143
154
  * Build and save the model using provider config's buildModel function.
@@ -191,7 +202,7 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
191
202
  if (currentStep > 0) {
192
203
  setCurrentStep(currentStep - 1);
193
204
  // Restore previous value
194
- const prevStep = wizardSteps[currentStep - 1];
205
+ const prevStep = visibleSteps[currentStep - 1];
195
206
  if (prevStep) {
196
207
  setCurrentInput(values[prevStep.field] || '');
197
208
  }
@@ -205,7 +216,7 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
205
216
  else {
206
217
  onClose();
207
218
  }
208
- }, [currentStep, onClose, selectedProvider, values, wizardSteps]);
219
+ }, [currentStep, onClose, selectedProvider, values, visibleSteps]);
209
220
  // Handle keyboard input
210
221
  useImperativeHandle(ref, () => ({
211
222
  handleInput: (input, key) => {
@@ -281,6 +292,6 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
281
292
  // Wizard steps screen for other providers
282
293
  if (!currentStepConfig || !providerConfig)
283
294
  return null;
284
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "green", children: isEditing ? 'Edit Custom Model' : 'Add Custom Model' }), _jsxs(Text, { color: "gray", children: [' ', "(", providerConfig.displayName, ") Step ", currentStep + 1, "/", wizardSteps.length] })] }), providerConfig.setupInfo && currentStep === 0 && (_jsx(SetupInfoBanner, { title: providerConfig.setupInfo.title, description: providerConfig.setupInfo.description, docsUrl: providerConfig.setupInfo.docsUrl })), _jsx(WizardStepInput, { step: currentStepConfig, currentInput: currentInput, error: error, isValidating: isValidating, isSaving: isSaving, additionalContent: currentStepConfig.field === 'apiKey' ? (_jsx(ApiKeyStep, { provider: selectedProvider })) : undefined }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["Enter to continue \u2022 Esc to", ' ', currentStep > 0 ? 'go back' : 'back to provider'] }) })] }));
295
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "green", children: isEditing ? 'Edit Custom Model' : 'Add Custom Model' }), _jsxs(Text, { color: "gray", children: [' ', "(", providerConfig.displayName, ") Step ", currentStep + 1, "/", visibleSteps.length] })] }), providerConfig.setupInfo && currentStep === 0 && (_jsx(SetupInfoBanner, { title: providerConfig.setupInfo.title, description: providerConfig.setupInfo.description, docsUrl: providerConfig.setupInfo.docsUrl })), _jsx(WizardStepInput, { step: currentStepConfig, currentInput: currentInput, error: error, isValidating: isValidating, isSaving: isSaving, additionalContent: currentStepConfig.field === 'apiKey' ? (_jsx(ApiKeyStep, { provider: selectedProvider })) : undefined }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["Enter to continue \u2022 Esc to", ' ', currentStep > 0 ? 'go back' : 'back to provider'] }) })] }));
285
296
  });
286
297
  export default CustomModelWizard;
@@ -54,6 +54,6 @@ const LogLevelSelector = forwardRef(function LogLevelSelector({ isVisible, onSel
54
54
  const handleSelect = (option) => {
55
55
  onSelect(option.level);
56
56
  };
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 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC1 Log file: ", logFilePath] }) }))] }));
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] }) }))] }));
58
58
  });
59
59
  export default LogLevelSelector;
@@ -7,9 +7,10 @@
7
7
  import type { Key } from '../../hooks/useInputOrchestrator.js';
8
8
  import type { DextoAgent, LLMProvider } from '@dexto/core';
9
9
  import { type CustomModel } from '@dexto/agent-management';
10
+ type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
10
11
  interface ModelSelectorProps {
11
12
  isVisible: boolean;
12
- onSelectModel: (provider: LLMProvider, model: string, displayName?: string, baseURL?: string) => void;
13
+ onSelectModel: (provider: LLMProvider, model: string, displayName?: string, baseURL?: string, reasoningEffort?: ReasoningEffort) => void;
13
14
  onClose: () => void;
14
15
  onAddCustomModel: () => void;
15
16
  onEditCustomModel: (model: CustomModel) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAIH,KAAK,WAAW,EACnB,MAAM,yBAAyB,CAAC;AAEjC,UAAU,kBAAkB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,KACf,IAAI,CAAC;IACV,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AA0BD;;GAEG;AACH,QAAA,MAAM,aAAa,oHAmmBjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO3D,OAAO,EAIH,KAAK,WAAW,EACnB,MAAM,yBAAyB,CAAC;AAEjC,KAAK,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhF,UAAU,kBAAkB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,eAAe,KAChC,IAAI,CAAC;IACV,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAiDD;;GAEG;AACH,QAAA,MAAM,aAAa,oHA4rBjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -7,12 +7,30 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  */
8
8
  import { useState, useEffect, forwardRef, useRef, useImperativeHandle, useMemo, useCallback, } from 'react';
9
9
  import { Box, Text } from 'ink';
10
- import { listOllamaModels, DEFAULT_OLLAMA_URL, getLocalModelById } from '@dexto/core';
10
+ import { listOllamaModels, DEFAULT_OLLAMA_URL, getLocalModelById, isReasoningCapableModel, } from '@dexto/core';
11
11
  import { loadCustomModels, deleteCustomModel, getAllInstalledModels, } from '@dexto/agent-management';
12
12
  function isAddCustomOption(item) {
13
13
  return 'type' in item && item.type === 'add-custom';
14
14
  }
15
15
  const MAX_VISIBLE_ITEMS = 10;
16
+ // Reasoning effort options - defined at module scope to avoid recreation on each render
17
+ const REASONING_EFFORT_OPTIONS = [
18
+ {
19
+ value: 'auto',
20
+ label: 'Auto',
21
+ description: 'Let the model decide (recommended for most tasks)',
22
+ },
23
+ { value: 'none', label: 'None', description: 'No reasoning, fastest responses' },
24
+ { value: 'minimal', label: 'Minimal', description: 'Barely any reasoning, very fast' },
25
+ { value: 'low', label: 'Low', description: 'Light reasoning, fast responses' },
26
+ {
27
+ value: 'medium',
28
+ label: 'Medium',
29
+ description: 'Balanced reasoning (OpenAI recommended)',
30
+ },
31
+ { value: 'high', label: 'High', description: 'Thorough reasoning for complex tasks' },
32
+ { value: 'xhigh', label: 'Extra High', description: 'Maximum quality, slower/costlier' },
33
+ ];
16
34
  /**
17
35
  * Model selector with search and custom model support
18
36
  */
@@ -27,6 +45,9 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
27
45
  const [pendingDeleteConfirm, setPendingDeleteConfirm] = useState(false);
28
46
  const selectedIndexRef = useRef(selectedIndex);
29
47
  const deleteTimeoutRef = useRef(null);
48
+ // Reasoning effort sub-step state
49
+ const [pendingReasoningModel, setPendingReasoningModel] = useState(null);
50
+ const [reasoningEffortIndex, setReasoningEffortIndex] = useState(0); // Default to 'Auto' (index 0)
30
51
  // Keep ref in sync
31
52
  selectedIndexRef.current = selectedIndex;
32
53
  // Clear delete confirmation timeout on unmount
@@ -48,6 +69,8 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
48
69
  setScrollOffset(0);
49
70
  setCustomModelAction(null);
50
71
  setPendingDeleteConfirm(false);
72
+ setPendingReasoningModel(null);
73
+ setReasoningEffortIndex(0); // Default to 'Auto'
51
74
  if (deleteTimeoutRef.current) {
52
75
  clearTimeout(deleteTimeoutRef.current);
53
76
  deleteTimeoutRef.current = null;
@@ -256,6 +279,30 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
256
279
  handleInput: (input, key) => {
257
280
  if (!isVisible)
258
281
  return false;
282
+ // Handle reasoning effort sub-step
283
+ if (pendingReasoningModel) {
284
+ if (key.escape) {
285
+ // Go back to model selection
286
+ setPendingReasoningModel(null);
287
+ return true;
288
+ }
289
+ if (key.upArrow) {
290
+ setReasoningEffortIndex((prev) => prev > 0 ? prev - 1 : REASONING_EFFORT_OPTIONS.length - 1);
291
+ return true;
292
+ }
293
+ if (key.downArrow) {
294
+ setReasoningEffortIndex((prev) => prev < REASONING_EFFORT_OPTIONS.length - 1 ? prev + 1 : 0);
295
+ return true;
296
+ }
297
+ if (key.return) {
298
+ const selectedOption = REASONING_EFFORT_OPTIONS[reasoningEffortIndex];
299
+ const reasoningEffort = selectedOption?.value === 'auto' ? undefined : selectedOption?.value;
300
+ onSelectModel(pendingReasoningModel.provider, pendingReasoningModel.name, pendingReasoningModel.displayName, pendingReasoningModel.baseURL, reasoningEffort);
301
+ setPendingReasoningModel(null);
302
+ return true;
303
+ }
304
+ return true; // Consume all input in reasoning effort mode
305
+ }
259
306
  // Escape always works
260
307
  if (key.escape) {
261
308
  // If in action mode, just clear it first
@@ -408,7 +455,13 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
408
455
  }
409
456
  return true;
410
457
  }
411
- // Normal selection
458
+ // Normal selection - check if reasoning-capable
459
+ if (isReasoningCapableModel(item.name)) {
460
+ // Show reasoning effort sub-step
461
+ setPendingReasoningModel(item);
462
+ setReasoningEffortIndex(0); // Default to 'Auto'
463
+ return true;
464
+ }
412
465
  onSelectModel(item.provider, item.name, item.displayName, item.baseURL);
413
466
  return true;
414
467
  }
@@ -426,12 +479,21 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
426
479
  pendingDeleteConfirm,
427
480
  customModels,
428
481
  handleDeleteCustomModel,
482
+ pendingReasoningModel,
483
+ reasoningEffortIndex,
429
484
  ]);
430
485
  if (!isVisible)
431
486
  return null;
432
487
  if (isLoading) {
433
488
  return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "Loading models..." }) }));
434
489
  }
490
+ // Reasoning effort sub-step UI
491
+ if (pendingReasoningModel) {
492
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "cyan", bold: true, children: "Configure Reasoning Effort" }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "gray", children: ["for ", pendingReasoningModel.displayName || pendingReasoningModel.name] }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc back" }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), REASONING_EFFORT_OPTIONS.map((option, index) => {
493
+ const isSelected = index === reasoningEffortIndex;
494
+ return (_jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: [isSelected ? '› ' : ' ', option.label] }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', "- ", option.description] })] }, option.value));
495
+ })] }));
496
+ }
435
497
  const visibleItems = filteredItems.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS);
436
498
  const hasCustomModels = customModels.length > 0;
437
499
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "cyan", bold: true, children: ["Select Model (", selectedIndex + 1, "/", filteredItems.length, ")"] }) }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc close" }), hasCustomModels && _jsx(Text, { color: "gray", children: ", \u2192\u2190 for custom actions" })] }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\uD83D\uDD0D " }), _jsx(Text, { color: searchQuery ? 'white' : 'gray', children: searchQuery || 'Type to search...' }), _jsx(Text, { color: "cyan", children: "\u258C" })] }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), visibleItems.map((item, visibleIndex) => {
@@ -1 +1 @@
1
- {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGhF,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,YAAY,CAAC;AAqC7D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,mBAAmB,EAAE,cAAc,CAwWxE,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,cAAc,CAE/E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAGtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,mBAAmB,EAAE,CAEtE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAEzE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAQhF,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,YAAY,CAAC;AA4D7D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,mBAAmB,EAAE,cAAc,CA4XxE,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,cAAc,CAE/E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAGtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,mBAAmB,EAAE,CAEtE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAEzE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
@@ -3,7 +3,7 @@
3
3
  * Each provider has its own config with display name, description, steps, and model builder.
4
4
  */
5
5
  import { CUSTOM_MODEL_PROVIDERS } from '@dexto/agent-management';
6
- import { lookupOpenRouterModel, refreshOpenRouterModelCache, getLocalModelById } from '@dexto/core';
6
+ import { lookupOpenRouterModel, refreshOpenRouterModelCache, getLocalModelById, isReasoningCapableModel, } from '@dexto/core';
7
7
  import { validators } from './types.js';
8
8
  import * as fs from 'fs';
9
9
  import * as os from 'os';
@@ -36,6 +36,29 @@ const DISPLAY_NAME_STEP = {
36
36
  placeholder: 'e.g., My Custom Model',
37
37
  required: false,
38
38
  };
39
+ /**
40
+ * Common reasoning effort step - for OpenAI reasoning models (o1, o3, codex, gpt-5.x).
41
+ * Only shown when the model name indicates reasoning capability.
42
+ */
43
+ const REASONING_EFFORT_STEP = {
44
+ field: 'reasoningEffort',
45
+ label: 'Reasoning Effort (optional)',
46
+ placeholder: 'none | minimal | low | medium | high | xhigh (blank for auto)',
47
+ required: false,
48
+ validate: (value) => {
49
+ if (!value?.trim())
50
+ return null;
51
+ const validValues = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'];
52
+ if (!validValues.includes(value.toLowerCase())) {
53
+ return `Invalid reasoning effort. Use: ${validValues.join(', ')}`;
54
+ }
55
+ return null;
56
+ },
57
+ condition: (values) => {
58
+ const modelName = values.name || '';
59
+ return isReasoningCapableModel(modelName);
60
+ },
61
+ };
39
62
  /**
40
63
  * Provider configuration registry.
41
64
  * Keys are CustomModelProvider values.
@@ -61,6 +84,7 @@ export const PROVIDER_CONFIGS = {
61
84
  },
62
85
  { ...DISPLAY_NAME_STEP, placeholder: 'e.g., My Local Llama 3' },
63
86
  MAX_INPUT_TOKENS_STEP,
87
+ REASONING_EFFORT_STEP,
64
88
  API_KEY_STEP,
65
89
  ],
66
90
  buildModel: (values, provider) => {
@@ -77,6 +101,10 @@ export const PROVIDER_CONFIGS = {
77
101
  if (values.maxInputTokens?.trim()) {
78
102
  model.maxInputTokens = parseInt(values.maxInputTokens, 10);
79
103
  }
104
+ if (values.reasoningEffort?.trim()) {
105
+ model.reasoningEffort =
106
+ values.reasoningEffort.toLowerCase();
107
+ }
80
108
  return model;
81
109
  },
82
110
  },
@@ -92,6 +120,7 @@ export const PROVIDER_CONFIGS = {
92
120
  validate: validators.slashFormat,
93
121
  },
94
122
  { ...DISPLAY_NAME_STEP, placeholder: 'e.g., Claude 3.5 Sonnet' },
123
+ REASONING_EFFORT_STEP,
95
124
  {
96
125
  ...API_KEY_STEP,
97
126
  placeholder: 'Saved as OPENROUTER_API_KEY if not set, otherwise per-model',
@@ -105,6 +134,10 @@ export const PROVIDER_CONFIGS = {
105
134
  if (values.displayName?.trim()) {
106
135
  model.displayName = values.displayName.trim();
107
136
  }
137
+ if (values.reasoningEffort?.trim()) {
138
+ model.reasoningEffort =
139
+ values.reasoningEffort.toLowerCase();
140
+ }
108
141
  return model;
109
142
  },
110
143
  asyncValidation: {
@@ -141,6 +174,7 @@ export const PROVIDER_CONFIGS = {
141
174
  validate: validators.slashFormat,
142
175
  },
143
176
  { ...DISPLAY_NAME_STEP, placeholder: 'e.g., GPT-4o via Glama' },
177
+ REASONING_EFFORT_STEP,
144
178
  {
145
179
  ...API_KEY_STEP,
146
180
  placeholder: 'Saved as GLAMA_API_KEY if not set, otherwise per-model',
@@ -154,6 +188,10 @@ export const PROVIDER_CONFIGS = {
154
188
  if (values.displayName?.trim()) {
155
189
  model.displayName = values.displayName.trim();
156
190
  }
191
+ if (values.reasoningEffort?.trim()) {
192
+ model.reasoningEffort =
193
+ values.reasoningEffort.toLowerCase();
194
+ }
157
195
  return model;
158
196
  },
159
197
  },
@@ -177,6 +215,7 @@ export const PROVIDER_CONFIGS = {
177
215
  },
178
216
  { ...DISPLAY_NAME_STEP, placeholder: 'e.g., My LiteLLM GPT-4' },
179
217
  MAX_INPUT_TOKENS_STEP,
218
+ REASONING_EFFORT_STEP,
180
219
  {
181
220
  ...API_KEY_STEP,
182
221
  placeholder: 'Saved as LITELLM_API_KEY if not set, otherwise per-model',
@@ -196,6 +235,10 @@ export const PROVIDER_CONFIGS = {
196
235
  if (values.maxInputTokens?.trim()) {
197
236
  model.maxInputTokens = parseInt(values.maxInputTokens, 10);
198
237
  }
238
+ if (values.reasoningEffort?.trim()) {
239
+ model.reasoningEffort =
240
+ values.reasoningEffort.toLowerCase();
241
+ }
199
242
  return model;
200
243
  },
201
244
  },
@@ -12,6 +12,12 @@ export interface WizardStep {
12
12
  placeholder: string;
13
13
  required: boolean;
14
14
  validate?: (value: string) => string | null;
15
+ /**
16
+ * Optional condition to determine if this step should be shown.
17
+ * Takes the current accumulated values and returns true if the step should be shown.
18
+ * If omitted, the step is always shown.
19
+ */
20
+ condition?: (values: Record<string, string>) => boolean;
15
21
  }
16
22
  /**
17
23
  * Props passed to each provider-specific wizard component.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wCAAwC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,sDAAsD;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IAClD,sCAAsC;IACtC,QAAQ,EAAE,MAAM,UAAU,EAAE,CAAC;IAC7B,8BAA8B;IAC9B,oBAAoB,EAAE,MAAM,UAAU,GAAG,SAAS,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,mBAAmB,KAAK,WAAW,CAAC;IAC3F,oDAAoD;IACpD,eAAe,CAAC,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;KACvD,CAAC;IACF,sDAAsD;IACtD,SAAS,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACL;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;sBACD,MAAM,MAAM,GAAG,MAAM;aAE9B,MAAM;wBAaK,MAAM;qBAOT,MAAM;CAK1B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wCAAwC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC5C;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,sDAAsD;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IAClD,sCAAsC;IACtC,QAAQ,EAAE,MAAM,UAAU,EAAE,CAAC;IAC7B,8BAA8B;IAC9B,oBAAoB,EAAE,MAAM,UAAU,GAAG,SAAS,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,mBAAmB,KAAK,WAAW,CAAC;IAC3F,oDAAoD;IACpD,eAAe,CAAC,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;KACvD,CAAC;IACF,sDAAsD;IACtD,SAAS,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACL;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;sBACD,MAAM,MAAM,GAAG,MAAM;aAE9B,MAAM;wBAaK,MAAM;qBAOT,MAAM;CAK1B,CAAC"}
@@ -22,5 +22,5 @@ export function GenericRenderer({ content, maxLines = 15 }) {
22
22
  const line = lines[0];
23
23
  return (_jsxs(Text, { color: "gray", children: [' ⎿ ', line.slice(0, 80), line.length > 80 ? '...' : ''] }));
24
24
  }
25
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', displayLines.length, " lines"] }), displayLines.map((line, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: [' ', line] }, i))), truncated && (_jsxs(Text, { color: "gray", children: [' ', "... ", lines.length - maxLines, " more lines"] }))] }));
25
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', displayLines.length, " lines"] }), displayLines.map((line, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: ['', line] }, i))), truncated && (_jsxs(Text, { color: "gray", children: ['', "... ", lines.length - maxLines, " more lines"] }))] }));
26
26
  }
@@ -8,5 +8,5 @@ export function SearchRenderer({ data, maxMatches = 5 }) {
8
8
  const { pattern, matches, totalMatches, truncated: dataTruncated } = data;
9
9
  const displayMatches = matches.slice(0, maxMatches);
10
10
  const truncated = dataTruncated || matches.length > maxMatches;
11
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', totalMatches, " match", totalMatches !== 1 ? 'es' : '', " for \"", pattern, "\"", truncated && ' (truncated)'] }), displayMatches.map((match, i) => (_jsxs(Text, { color: "gray", children: [' ', match.file, match.line > 0 && `:${match.line}`] }, i))), matches.length > maxMatches && (_jsxs(Text, { color: "gray", children: [' ', "... ", matches.length - maxMatches, " more"] }))] }));
11
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', totalMatches, " match", totalMatches !== 1 ? 'es' : '', " for \"", pattern, "\"", truncated && ' (truncated)'] }), displayMatches.map((match, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: ['', match.file, match.line > 0 && `:${match.line}`] }, i))), matches.length > maxMatches && (_jsxs(Text, { color: "gray", children: ['', "... ", matches.length - maxMatches, " more"] }))] }));
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownText.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/shared/MarkdownText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAwB,MAAM,OAAO,CAAC;AAU7C,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuE1D;AA0ID,UAAU,iBAAiB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAkED,QAAA,MAAM,YAAY,+CAA6B,CAAC;AAMhD,UAAU,iBAAiB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AA4RD,eAAO,MAAM,YAAY,+CAA6B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"MarkdownText.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/shared/MarkdownText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAwB,MAAM,OAAO,CAAC;AAU7C,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuE1D;AAyID,UAAU,iBAAiB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAkED,QAAA,MAAM,YAAY,+CAA6B,CAAC;AAMhD,UAAU,iBAAiB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AA4RD,eAAO,MAAM,YAAY,+CAA6B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
@@ -168,12 +168,12 @@ const WrappedParagraphInternal = ({ text, defaultColor, bulletPrefix, isFirstPar
168
168
  return wrapped.split('\n');
169
169
  }, [text, defaultColor, bulletPrefix, isFirstParagraph, terminalWidth]);
170
170
  // Calculate indent for continuation lines (spaces to align with first line content)
171
- const continuationIndent = bulletPrefix && isFirstParagraph ? ' '.repeat(stringWidth(bulletPrefix)) : '';
171
+ // All lines get indentation when bulletPrefix is provided, for consistent left margin
172
+ const indentSpaces = bulletPrefix ? ' '.repeat(stringWidth(bulletPrefix)) : '';
172
173
  return (_jsx(_Fragment, { children: wrappedLines.map((line, i) => {
173
174
  const isFirstLine = i === 0;
174
- const prefix = isFirstLine && bulletPrefix && isFirstParagraph
175
- ? bulletPrefix
176
- : continuationIndent;
175
+ // First line of first paragraph gets bullet, all other lines get space indent
176
+ const prefix = isFirstLine && isFirstParagraph && bulletPrefix ? bulletPrefix : indentSpaces;
177
177
  return (_jsx(Box, { children: _jsxs(Text, { children: [prefix, line] }) }, i));
178
178
  }) }));
179
179
  };
@@ -1 +1 @@
1
- {"version":3,"file":"processingPhrases.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/processingPhrases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAsDrC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
1
+ {"version":3,"file":"processingPhrases.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/processingPhrases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAiErC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}