aiexecode 1.0.66 → 1.0.68

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.

Potentially problematic release.


This version of aiexecode might be problematic. Click here for more details.

Files changed (86) hide show
  1. package/config_template/settings.json +1 -3
  2. package/index.js +46 -71
  3. package/package.json +1 -12
  4. package/payload_viewer/out/404/index.html +1 -1
  5. package/payload_viewer/out/404.html +1 -1
  6. package/payload_viewer/out/index.html +1 -1
  7. package/payload_viewer/out/index.txt +1 -1
  8. package/payload_viewer/web_server.js +0 -163
  9. package/src/ai_based/completion_judge.js +96 -5
  10. package/src/ai_based/orchestrator.js +71 -3
  11. package/src/ai_based/pip_package_installer.js +14 -12
  12. package/src/ai_based/pip_package_lookup.js +13 -10
  13. package/src/commands/apikey.js +8 -34
  14. package/src/commands/help.js +3 -4
  15. package/src/commands/model.js +17 -74
  16. package/src/commands/reasoning_effort.js +1 -1
  17. package/src/config/feature_flags.js +0 -12
  18. package/src/{ui → frontend}/App.js +23 -25
  19. package/src/frontend/README.md +81 -0
  20. package/src/{ui/components/SuggestionsDisplay.js → frontend/components/AutocompleteMenu.js} +3 -3
  21. package/src/{ui/components/HistoryItemDisplay.js → frontend/components/ConversationItem.js} +37 -89
  22. package/src/{ui → frontend}/components/CurrentModelView.js +3 -5
  23. package/src/{ui → frontend}/components/Footer.js +4 -6
  24. package/src/{ui → frontend}/components/Header.js +2 -5
  25. package/src/{ui/components/InputPrompt.js → frontend/components/Input.js} +16 -54
  26. package/src/frontend/components/ModelListView.js +106 -0
  27. package/src/{ui → frontend}/components/ModelUpdatedView.js +3 -5
  28. package/src/{ui → frontend}/components/SessionSpinner.js +3 -3
  29. package/src/{ui → frontend}/components/SetupWizard.js +8 -101
  30. package/src/{ui → frontend}/components/ToolApprovalPrompt.js +16 -14
  31. package/src/frontend/design/themeColors.js +42 -0
  32. package/src/{ui → frontend}/index.js +7 -7
  33. package/src/frontend/utils/inputBuffer.js +441 -0
  34. package/src/{ui/utils/markdownRenderer.js → frontend/utils/markdownParser.js} +3 -3
  35. package/src/{ui/utils/ConsolePatcher.js → frontend/utils/outputRedirector.js} +9 -9
  36. package/src/{ui/utils/codeColorizer.js → frontend/utils/syntaxHighlighter.js} +2 -3
  37. package/src/system/ai_request.js +145 -595
  38. package/src/system/code_executer.js +111 -16
  39. package/src/system/file_integrity.js +5 -7
  40. package/src/system/log.js +3 -3
  41. package/src/system/mcp_integration.js +15 -13
  42. package/src/system/output_helper.js +0 -20
  43. package/src/system/session.js +97 -23
  44. package/src/system/session_memory.js +2 -82
  45. package/src/system/system_info.js +1 -1
  46. package/src/system/ui_events.js +0 -43
  47. package/src/tools/code_editor.js +17 -2
  48. package/src/tools/file_reader.js +17 -2
  49. package/src/tools/glob.js +9 -1
  50. package/src/tools/response_message.js +0 -2
  51. package/src/tools/ripgrep.js +9 -1
  52. package/src/tools/web_downloader.js +9 -1
  53. package/src/util/config.js +3 -8
  54. package/src/util/debug_log.js +4 -11
  55. package/src/util/mcp_config_manager.js +3 -5
  56. package/src/util/output_formatter.js +0 -47
  57. package/src/util/prompt_loader.js +3 -4
  58. package/src/util/safe_fs.js +60 -0
  59. package/src/util/setup_wizard.js +1 -3
  60. package/src/util/text_formatter.js +0 -86
  61. package/src/config/claude_models.js +0 -195
  62. package/src/ui/README.md +0 -208
  63. package/src/ui/api.js +0 -167
  64. package/src/ui/components/AgenticProgressDisplay.js +0 -126
  65. package/src/ui/components/Composer.js +0 -55
  66. package/src/ui/components/LoadingIndicator.js +0 -54
  67. package/src/ui/components/ModelListView.js +0 -214
  68. package/src/ui/components/Notifications.js +0 -55
  69. package/src/ui/components/StreamingIndicator.js +0 -36
  70. package/src/ui/contexts/AppContext.js +0 -25
  71. package/src/ui/contexts/StreamingContext.js +0 -20
  72. package/src/ui/contexts/UIStateContext.js +0 -117
  73. package/src/ui/example-usage.js +0 -180
  74. package/src/ui/hooks/useTerminalResize.js +0 -39
  75. package/src/ui/themes/semantic-tokens.js +0 -73
  76. package/src/ui/utils/text-buffer.js +0 -975
  77. /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_buildManifest.js +0 -0
  78. /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_clientMiddlewareManifest.json +0 -0
  79. /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_ssgManifest.js +0 -0
  80. /package/src/{ui → frontend}/components/BlankLine.js +0 -0
  81. /package/src/{ui → frontend}/components/FileDiffViewer.js +0 -0
  82. /package/src/{ui → frontend}/components/HelpView.js +0 -0
  83. /package/src/{ui → frontend}/hooks/useCompletion.js +0 -0
  84. /package/src/{ui → frontend}/hooks/useKeypress.js +0 -0
  85. /package/src/{ui → frontend}/utils/diffUtils.js +0 -0
  86. /package/src/{ui → frontend}/utils/renderInkComponent.js +0 -0
@@ -4,24 +4,17 @@
4
4
 
5
5
  import React, { useState, useRef } from 'react';
6
6
  import { Box, Text, useInput } from 'ink';
7
- import { theme } from '../themes/semantic-tokens.js';
8
- import { CLAUDE_MODELS, getClaude4Models, getClaude3Models, DEFAULT_CLAUDE_MODEL } from '../../config/claude_models.js';
7
+ import { theme } from '../design/themeColors.js';
9
8
  import { OPENAI_MODELS, getGPT5Models, DEFAULT_OPENAI_MODEL } from '../../config/openai_models.js';
10
- import { ENABLE_ANTHROPIC_PROVIDER } from '../../config/feature_flags.js';
11
9
 
12
10
  const STEPS = {
13
- PROVIDER: 'provider',
14
11
  OPENAI_KEY: 'openai_key',
15
12
  OPENAI_MODEL: 'openai_model',
16
- OPENAI_EFFORT: 'openai_effort',
17
- ANTHROPIC_KEY: 'anthropic_key',
18
- ANTHROPIC_MODEL: 'anthropic_model'
13
+ OPENAI_EFFORT: 'openai_effort'
19
14
  };
20
15
 
21
16
  export function SetupWizard({ onComplete, onCancel }) {
22
- // Feature flag에 따라 초기 단계 결정
23
- const initialStep = ENABLE_ANTHROPIC_PROVIDER ? STEPS.PROVIDER : STEPS.OPENAI_KEY;
24
- const [step, setStep] = useState(initialStep);
17
+ const [step, setStep] = useState(STEPS.OPENAI_KEY);
25
18
  const [selectedIndex, setSelectedIndex] = useState(0);
26
19
  const [textInput, setTextInput] = useState('');
27
20
 
@@ -30,13 +23,11 @@ export function SetupWizard({ onComplete, onCancel }) {
30
23
  AI_PROVIDER: 'openai',
31
24
  OPENAI_API_KEY: '',
32
25
  OPENAI_MODEL: DEFAULT_OPENAI_MODEL,
33
- OPENAI_REASONING_EFFORT: 'medium',
34
- ANTHROPIC_API_KEY: '',
35
- ANTHROPIC_MODEL: DEFAULT_CLAUDE_MODEL
26
+ OPENAI_REASONING_EFFORT: 'medium'
36
27
  });
37
28
 
38
29
  // 현재 스텝이 텍스트 입력인지 선택지인지 판단
39
- const isTextInputStep = step === STEPS.OPENAI_KEY || step === STEPS.ANTHROPIC_KEY;
30
+ const isTextInputStep = step === STEPS.OPENAI_KEY;
40
31
 
41
32
  const completeSetup = () => {
42
33
  if (onComplete) {
@@ -47,17 +38,6 @@ export function SetupWizard({ onComplete, onCancel }) {
47
38
 
48
39
  const handleStepComplete = () => {
49
40
  switch (step) {
50
- case STEPS.PROVIDER:
51
- if (selectedIndex === 0) {
52
- settingsRef.current.AI_PROVIDER = 'openai';
53
- setStep(STEPS.OPENAI_KEY);
54
- } else {
55
- settingsRef.current.AI_PROVIDER = 'anthropic';
56
- setStep(STEPS.ANTHROPIC_KEY);
57
- }
58
- setSelectedIndex(0);
59
- break;
60
-
61
41
  case STEPS.OPENAI_KEY:
62
42
  if (!textInput.trim()) {
63
43
  return;
@@ -84,24 +64,6 @@ export function SetupWizard({ onComplete, onCancel }) {
84
64
  // 완료
85
65
  completeSetup();
86
66
  break;
87
-
88
- case STEPS.ANTHROPIC_KEY:
89
- if (!textInput.trim()) {
90
- return;
91
- }
92
- settingsRef.current.ANTHROPIC_API_KEY = textInput.trim();
93
- setStep(STEPS.ANTHROPIC_MODEL);
94
- setTextInput('');
95
- setSelectedIndex(0);
96
- break;
97
-
98
- case STEPS.ANTHROPIC_MODEL:
99
- // 중앙 설정에서 모든 Claude 모델 목록 가져오기
100
- const anthropicModels = [...getClaude4Models(), ...getClaude3Models()];
101
- settingsRef.current.ANTHROPIC_MODEL = anthropicModels[selectedIndex];
102
- // 완료
103
- completeSetup();
104
- break;
105
67
  }
106
68
  };
107
69
 
@@ -150,14 +112,10 @@ export function SetupWizard({ onComplete, onCancel }) {
150
112
 
151
113
  const getMaxIndexForStep = (currentStep) => {
152
114
  switch (currentStep) {
153
- case STEPS.PROVIDER:
154
- return ENABLE_ANTHROPIC_PROVIDER ? 1 : 0; // 2 options if enabled, 1 if disabled
155
115
  case STEPS.OPENAI_MODEL:
156
116
  return getGPT5Models().length - 1;
157
117
  case STEPS.OPENAI_EFFORT:
158
118
  return 3; // 4 options
159
- case STEPS.ANTHROPIC_MODEL:
160
- return [...getClaude4Models(), ...getClaude3Models()].length - 1;
161
119
  default:
162
120
  return 0;
163
121
  }
@@ -179,28 +137,9 @@ export function SetupWizard({ onComplete, onCancel }) {
179
137
 
180
138
  const renderStep = () => {
181
139
  switch (step) {
182
- case STEPS.PROVIDER:
183
- const providerOptions = ENABLE_ANTHROPIC_PROVIDER
184
- ? [
185
- 'OpenAI (GPT-5, GPT-4, o1 series)',
186
- 'Anthropic (Claude series)'
187
- ]
188
- : [
189
- 'OpenAI (GPT-5, GPT-4, o1 series)'
190
- ];
191
-
192
- return React.createElement(Box, { flexDirection: 'column' },
193
- React.createElement(Text, { bold: true, color: theme.text.accent }, '1. Choose AI Provider:'),
194
- React.createElement(Text, null),
195
- renderOptions(providerOptions),
196
- React.createElement(Text, null),
197
- React.createElement(Text, { dimColor: true }, '↑↓: Navigate Enter: Confirm')
198
- );
199
-
200
140
  case STEPS.OPENAI_KEY:
201
- const openaiKeyStep = ENABLE_ANTHROPIC_PROVIDER ? '2' : '1';
202
141
  return React.createElement(Box, { flexDirection: 'column' },
203
- React.createElement(Text, { bold: true, color: theme.text.accent }, `${openaiKeyStep}. OpenAI API Key:`),
142
+ React.createElement(Text, { bold: true, color: theme.text.accent }, '1. OpenAI API Key:'),
204
143
  React.createElement(Text, { color: theme.text.secondary }, ' Get your API key from: https://platform.openai.com/account/api-keys'),
205
144
  React.createElement(Text, null),
206
145
  React.createElement(Box, {
@@ -215,9 +154,8 @@ export function SetupWizard({ onComplete, onCancel }) {
215
154
  );
216
155
 
217
156
  case STEPS.OPENAI_MODEL:
218
- const openaiModelStep = ENABLE_ANTHROPIC_PROVIDER ? '3' : '2';
219
157
  return React.createElement(Box, { flexDirection: 'column' },
220
- React.createElement(Text, { bold: true, color: theme.text.accent }, `${openaiModelStep}. Choose Model:`),
158
+ React.createElement(Text, { bold: true, color: theme.text.accent }, '2. Choose Model:'),
221
159
  React.createElement(Text, null),
222
160
  renderOptions(
223
161
  getGPT5Models().map(modelId => {
@@ -230,9 +168,8 @@ export function SetupWizard({ onComplete, onCancel }) {
230
168
  );
231
169
 
232
170
  case STEPS.OPENAI_EFFORT:
233
- const openaiEffortStep = ENABLE_ANTHROPIC_PROVIDER ? '4' : '3';
234
171
  return React.createElement(Box, { flexDirection: 'column' },
235
- React.createElement(Text, { bold: true, color: theme.text.accent }, `${openaiEffortStep}. Reasoning Effort:`),
172
+ React.createElement(Text, { bold: true, color: theme.text.accent }, '3. Reasoning Effort:'),
236
173
  React.createElement(Text, null),
237
174
  renderOptions([
238
175
  'minimal (Fastest)',
@@ -244,36 +181,6 @@ export function SetupWizard({ onComplete, onCancel }) {
244
181
  React.createElement(Text, { dimColor: true }, '↑↓: Navigate Enter: Confirm')
245
182
  );
246
183
 
247
- case STEPS.ANTHROPIC_KEY:
248
- return React.createElement(Box, { flexDirection: 'column' },
249
- React.createElement(Text, { bold: true, color: theme.text.accent }, '2. Anthropic API Key:'),
250
- React.createElement(Text, { color: theme.text.secondary }, ' Get your API key from: https://console.anthropic.com/settings/keys'),
251
- React.createElement(Text, null),
252
- React.createElement(Box, {
253
- borderStyle: 'round',
254
- borderColor: theme.border.focused,
255
- paddingX: 1
256
- },
257
- React.createElement(Text, null, textInput ? '*'.repeat(textInput.length) : ' ')
258
- ),
259
- React.createElement(Text, null),
260
- React.createElement(Text, { dimColor: true }, 'Type your API key and press Enter')
261
- );
262
-
263
- case STEPS.ANTHROPIC_MODEL:
264
- return React.createElement(Box, { flexDirection: 'column' },
265
- React.createElement(Text, { bold: true, color: theme.text.accent }, '3. Choose Model:'),
266
- React.createElement(Text, null),
267
- renderOptions(
268
- [...getClaude4Models(), ...getClaude3Models()].map(modelId => {
269
- const model = CLAUDE_MODELS[modelId];
270
- return `${modelId} (${model.name})`;
271
- })
272
- ),
273
- React.createElement(Text, null),
274
- React.createElement(Text, { dimColor: true }, '↑↓: Navigate Enter: Confirm')
275
- );
276
-
277
184
  default:
278
185
  return null;
279
186
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React, { useState, useEffect } from 'react';
6
6
  import { Box, Text, useInput } from 'ink';
7
- import { theme } from '../themes/semantic-tokens.js';
7
+ import { theme } from '../design/themeColors.js';
8
8
  import { FileDiffViewer } from './FileDiffViewer.js';
9
9
  import { common, createLowlight } from 'lowlight';
10
10
  import { getToolDisplayName } from '../../system/tool_registry.js';
@@ -42,14 +42,14 @@ export function ToolApprovalPrompt({ toolName, args, onDecision, mcpToolInfo = n
42
42
  return React.createElement(Box, {
43
43
  flexDirection: 'column',
44
44
  borderStyle: 'round',
45
- borderColor: 'yellow',
45
+ borderColor: theme.brand.dark,
46
46
  paddingX: 1,
47
47
  flexGrow: 1
48
48
  },
49
49
  // Tool info
50
50
  React.createElement(Box, { flexDirection: 'column', marginBottom: 0, marginTop: 0 },
51
51
  React.createElement(Box, { marginBottom: 0 },
52
- React.createElement(Text, { bold: true, color: 'cyan' }, 'Tool '),
52
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'Tool '),
53
53
  React.createElement(Text, { color: 'white' }, displayName)
54
54
  ),
55
55
  renderToolArgs(toolName, args, mcpToolInfo)
@@ -61,11 +61,11 @@ export function ToolApprovalPrompt({ toolName, args, onDecision, mcpToolInfo = n
61
61
  ...choices.map((choice, index) =>
62
62
  React.createElement(Box, { key: index, marginLeft: 1 },
63
63
  React.createElement(Text, {
64
- color: index === selectedIndex ? 'cyan' : 'white',
64
+ color: index === selectedIndex ? theme.brand.light : 'white',
65
65
  bold: index === selectedIndex
66
66
  }, index === selectedIndex ? '› ' : ' '),
67
67
  React.createElement(Text, {
68
- color: index === selectedIndex ? 'cyan' : 'white'
68
+ color: index === selectedIndex ? theme.brand.light : 'white'
69
69
  }, choice.label)
70
70
  )
71
71
  )
@@ -196,12 +196,12 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
196
196
  return React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
197
197
  // Server info
198
198
  React.createElement(Box, { marginBottom: 0 },
199
- React.createElement(Text, { bold: true, color: 'cyan' }, 'Server '),
199
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'Server '),
200
200
  React.createElement(Text, { color: 'white' }, mcpToolInfo.server)
201
201
  ),
202
202
  // Description (박스 안에 담기)
203
203
  mcpToolInfo.description && React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
204
- React.createElement(Text, { bold: true, color: 'cyan' }, 'Description'),
204
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'Description'),
205
205
  React.createElement(Box, {
206
206
  borderStyle: 'single',
207
207
  borderColor: '#444444',
@@ -213,7 +213,7 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
213
213
  ),
214
214
  // Arguments (JSON 하이라이팅)
215
215
  React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
216
- React.createElement(Text, { bold: true, color: 'cyan' }, 'Arguments'),
216
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'Arguments'),
217
217
  React.createElement(Box, {
218
218
  borderStyle: 'single',
219
219
  borderColor: '#444444',
@@ -292,7 +292,7 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
292
292
 
293
293
  return React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
294
294
  React.createElement(Box, {},
295
- React.createElement(Text, { bold: true, color: 'cyan' }, 'File '),
295
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'File '),
296
296
  React.createElement(Text, { color: 'white' }, toDisplayPath(args.file_path))
297
297
  ),
298
298
  React.createElement(Box, {
@@ -372,7 +372,7 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
372
372
 
373
373
  return React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
374
374
  React.createElement(Box, { marginBottom: 0 },
375
- React.createElement(Text, { bold: true, color: 'cyan' }, 'File '),
375
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'File '),
376
376
  React.createElement(Text, { color: 'white' }, toDisplayPath(args.file_path))
377
377
  ),
378
378
  oldContent !== null ? React.createElement(FileDiffViewer, {
@@ -383,7 +383,8 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
383
383
  newContent: args.new_content || '',
384
384
  contextBefore: contextBefore,
385
385
  contextAfter: contextAfter,
386
- contextStartLine: contextStartLine
386
+ contextStartLine: contextStartLine,
387
+ showHeader: false
387
388
  }) : React.createElement(Text, { color: 'gray' }, 'Loading...')
388
389
  );
389
390
  }
@@ -414,7 +415,7 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
414
415
 
415
416
  return React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
416
417
  React.createElement(Box, { marginBottom: 0 },
417
- React.createElement(Text, { bold: true, color: 'cyan' }, 'File '),
418
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'File '),
418
419
  React.createElement(Text, { color: 'white' }, toDisplayPath(args.file_path))
419
420
  ),
420
421
  diffData ? (
@@ -429,7 +430,8 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
429
430
  contextBefore: diffData.contextBefore,
430
431
  contextAfter: diffData.contextAfter,
431
432
  contextStartLine: diffData.contextStartLine,
432
- isReplaceMode: true
433
+ isReplaceMode: true,
434
+ showHeader: false
433
435
  })
434
436
  ) : React.createElement(Text, { color: 'gray' }, 'Loading...')
435
437
  );
@@ -437,7 +439,7 @@ function renderToolArgs(toolName, args, mcpToolInfo = null) {
437
439
 
438
440
  // 기타 도구
439
441
  return React.createElement(Box, { flexDirection: 'column', marginTop: 0 },
440
- React.createElement(Text, { bold: true, color: 'cyan' }, 'Arguments'),
442
+ React.createElement(Text, { bold: true, color: theme.brand.light }, 'Arguments'),
441
443
  React.createElement(Box, {
442
444
  borderStyle: 'single',
443
445
  borderColor: 'gray',
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Theme Colors - Color definitions for theming
3
+ */
4
+
5
+ const defaultTheme = {
6
+ text: {
7
+ primary: '#E0E0E0',
8
+ secondary: '#888888',
9
+ accent: '#3fbe96',
10
+ link: '#3fbe96',
11
+ },
12
+ background: {
13
+ default: '#1E1E1E',
14
+ highlight: '#2D2D2D',
15
+ },
16
+ border: {
17
+ default: '#333333',
18
+ focused: '#333333',
19
+ },
20
+ ui: {
21
+ symbol: '#666666',
22
+ gradient: ['#00D9FF', '#7B68EE'],
23
+ },
24
+ status: {
25
+ error: '#FF0055',
26
+ warning: '#FFAA00',
27
+ success: '#00FF88',
28
+ info: '#00A8FF',
29
+ },
30
+ brand: {
31
+ dark: '#418972',
32
+ light: '#3fbe96',
33
+ },
34
+ };
35
+
36
+ const currentTheme = defaultTheme;
37
+
38
+ export const theme = new Proxy({}, {
39
+ get(target, prop) {
40
+ return currentTheme[prop];
41
+ }
42
+ });
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
  import { render } from 'ink';
7
7
  import { App } from './App.js';
8
- import { ConsolePatcher } from './utils/ConsolePatcher.js';
8
+ import { OutputRedirector } from './utils/outputRedirector.js';
9
9
  import { createDebugLogger } from '../util/debug_log.js';
10
10
 
11
11
  const debugLog = createDebugLogger('ui.log', 'index');
@@ -14,9 +14,9 @@ export function startUI({ onSubmit, onClearScreen, onExit, commands = [], model
14
14
  debugLog(`startUI called - model: ${model}, version: ${version}, commands: ${commands.length}, initialHistory: ${initialHistory.length}`);
15
15
 
16
16
  // Patch console methods to prevent interference with Ink rendering
17
- const consolePatcher = new ConsolePatcher();
18
- consolePatcher.patch();
19
- debugLog('ConsolePatcher patched');
17
+ const outputRedirector = new OutputRedirector();
18
+ outputRedirector.patch();
19
+ debugLog('OutputRedirector patched');
20
20
 
21
21
  // Disable terminal line wrapping to reduce rendering artifacts
22
22
  // This prevents the terminal from wrapping lines itself, letting Ink handle all wrapping
@@ -36,7 +36,7 @@ export function startUI({ onSubmit, onClearScreen, onExit, commands = [], model
36
36
  }),
37
37
  {
38
38
  // Use patchConsole option to prevent console output from interfering
39
- patchConsole: false, // We're handling this manually with ConsolePatcher
39
+ patchConsole: false, // We're handling this manually with OutputRedirector
40
40
  // Don't exit on Ctrl+C - let the app handle it
41
41
  exitOnCtrlC: false,
42
42
  // Debug mode ON - prevents clearing terminal and preserves previous content
@@ -52,8 +52,8 @@ export function startUI({ onSubmit, onClearScreen, onExit, commands = [], model
52
52
  debugLog('wrappedUnmount called - cleaning up UI');
53
53
  // Re-enable line wrapping
54
54
  process.stdout.write('\x1b[?7h');
55
- consolePatcher.cleanup();
56
- debugLog('ConsolePatcher cleanup completed');
55
+ outputRedirector.cleanup();
56
+ debugLog('OutputRedirector cleanup completed');
57
57
  originalUnmount();
58
58
  debugLog('UI unmounted');
59
59
  };