nova-terminal-assistant 0.1.0

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 nova-terminal-assistant might be problematic. Click here for more details.

Files changed (192) hide show
  1. package/README.md +358 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +12 -0
  4. package/package.json +67 -0
  5. package/src/cli/commands/SmartCompletion.ts +458 -0
  6. package/src/cli/index.ts +5 -0
  7. package/src/cli/startup/IFlowRepl.ts +212 -0
  8. package/src/cli/startup/InkBasedRepl.ts +1056 -0
  9. package/src/cli/startup/InteractiveRepl.ts +2833 -0
  10. package/src/cli/startup/NovaApp.ts +1861 -0
  11. package/src/cli/startup/index.ts +4 -0
  12. package/src/cli/startup/parseArgs.ts +293 -0
  13. package/src/cli/test-modules.ts +27 -0
  14. package/src/cli/ui/IFlowDropdown.ts +425 -0
  15. package/src/cli/ui/ModernReplUI.ts +276 -0
  16. package/src/cli/ui/SimpleSelector2.ts +215 -0
  17. package/src/cli/ui/components/ConfirmDialog.ts +176 -0
  18. package/src/cli/ui/components/ErrorPanel.ts +364 -0
  19. package/src/cli/ui/components/InkAppRunner.tsx +67 -0
  20. package/src/cli/ui/components/InkComponents.tsx +613 -0
  21. package/src/cli/ui/components/NovaInkApp.tsx +312 -0
  22. package/src/cli/ui/components/ProgressBar.ts +177 -0
  23. package/src/cli/ui/components/ProgressIndicator.ts +298 -0
  24. package/src/cli/ui/components/QuickActions.ts +396 -0
  25. package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
  26. package/src/cli/ui/components/StatusBar.ts +194 -0
  27. package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
  28. package/src/cli/ui/components/index.ts +27 -0
  29. package/src/cli/ui/ink-prototype.tsx +347 -0
  30. package/src/cli/utils/CliUI.ts +336 -0
  31. package/src/cli/utils/CompletionHelper.ts +388 -0
  32. package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
  33. package/src/cli/utils/EnhancedCompleter.ts +513 -0
  34. package/src/cli/utils/ErrorEnhancer.ts +429 -0
  35. package/src/cli/utils/OutputFormatter.ts +193 -0
  36. package/src/cli/utils/index.ts +9 -0
  37. package/src/core/agents/AgentOrchestrator.ts +515 -0
  38. package/src/core/agents/index.ts +17 -0
  39. package/src/core/audit/AuditLogger.ts +509 -0
  40. package/src/core/audit/index.ts +11 -0
  41. package/src/core/auth/AuthManager.d.ts.map +1 -0
  42. package/src/core/auth/AuthManager.ts +138 -0
  43. package/src/core/auth/index.d.ts.map +1 -0
  44. package/src/core/auth/index.ts +2 -0
  45. package/src/core/config/ConfigManager.d.ts.map +1 -0
  46. package/src/core/config/ConfigManager.test.ts +183 -0
  47. package/src/core/config/ConfigManager.ts +1219 -0
  48. package/src/core/config/index.d.ts.map +1 -0
  49. package/src/core/config/index.ts +1 -0
  50. package/src/core/context/ContextBuilder.d.ts.map +1 -0
  51. package/src/core/context/ContextBuilder.ts +171 -0
  52. package/src/core/context/ContextCompressor.d.ts.map +1 -0
  53. package/src/core/context/ContextCompressor.ts +642 -0
  54. package/src/core/context/LayeredMemoryManager.ts +657 -0
  55. package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
  56. package/src/core/context/MemoryDiscovery.ts +175 -0
  57. package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
  58. package/src/core/context/defaultSystemPrompt.ts +35 -0
  59. package/src/core/context/index.d.ts.map +1 -0
  60. package/src/core/context/index.ts +22 -0
  61. package/src/core/extensions/SkillGenerator.ts +421 -0
  62. package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
  63. package/src/core/extensions/SkillInstaller.ts +257 -0
  64. package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
  65. package/src/core/extensions/SkillRegistry.ts +361 -0
  66. package/src/core/extensions/SkillValidator.ts +525 -0
  67. package/src/core/extensions/index.ts +15 -0
  68. package/src/core/index.d.ts.map +1 -0
  69. package/src/core/index.ts +42 -0
  70. package/src/core/mcp/McpManager.d.ts.map +1 -0
  71. package/src/core/mcp/McpManager.ts +632 -0
  72. package/src/core/mcp/index.d.ts.map +1 -0
  73. package/src/core/mcp/index.ts +2 -0
  74. package/src/core/model/ModelClient.d.ts.map +1 -0
  75. package/src/core/model/ModelClient.ts +217 -0
  76. package/src/core/model/ModelConnectionTester.ts +363 -0
  77. package/src/core/model/ModelValidator.ts +348 -0
  78. package/src/core/model/index.d.ts.map +1 -0
  79. package/src/core/model/index.ts +6 -0
  80. package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
  81. package/src/core/model/providers/AnthropicProvider.ts +279 -0
  82. package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
  83. package/src/core/model/providers/CodingPlanProvider.ts +210 -0
  84. package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  85. package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
  86. package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
  87. package/src/core/model/providers/OllamaManager.ts +201 -0
  88. package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
  89. package/src/core/model/providers/OllamaProvider.ts +73 -0
  90. package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  91. package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
  92. package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
  93. package/src/core/model/providers/OpenAIProvider.ts +29 -0
  94. package/src/core/model/providers/index.d.ts.map +1 -0
  95. package/src/core/model/providers/index.ts +12 -0
  96. package/src/core/model/types.d.ts.map +1 -0
  97. package/src/core/model/types.ts +77 -0
  98. package/src/core/security/ApprovalManager.d.ts.map +1 -0
  99. package/src/core/security/ApprovalManager.ts +174 -0
  100. package/src/core/security/FileFilter.d.ts.map +1 -0
  101. package/src/core/security/FileFilter.ts +141 -0
  102. package/src/core/security/HookExecutor.d.ts.map +1 -0
  103. package/src/core/security/HookExecutor.ts +178 -0
  104. package/src/core/security/SandboxExecutor.ts +447 -0
  105. package/src/core/security/index.d.ts.map +1 -0
  106. package/src/core/security/index.ts +8 -0
  107. package/src/core/session/AgentLoop.d.ts.map +1 -0
  108. package/src/core/session/AgentLoop.ts +501 -0
  109. package/src/core/session/SessionManager.d.ts.map +1 -0
  110. package/src/core/session/SessionManager.test.ts +183 -0
  111. package/src/core/session/SessionManager.ts +460 -0
  112. package/src/core/session/index.d.ts.map +1 -0
  113. package/src/core/session/index.ts +3 -0
  114. package/src/core/telemetry/Telemetry.d.ts.map +1 -0
  115. package/src/core/telemetry/Telemetry.ts +90 -0
  116. package/src/core/telemetry/TelemetryService.ts +531 -0
  117. package/src/core/telemetry/index.d.ts.map +1 -0
  118. package/src/core/telemetry/index.ts +12 -0
  119. package/src/core/testing/AutoFixer.ts +385 -0
  120. package/src/core/testing/ErrorAnalyzer.ts +499 -0
  121. package/src/core/testing/TestRunner.ts +265 -0
  122. package/src/core/testing/agent-cli-tests.ts +538 -0
  123. package/src/core/testing/index.ts +11 -0
  124. package/src/core/tools/ToolRegistry.d.ts.map +1 -0
  125. package/src/core/tools/ToolRegistry.test.ts +206 -0
  126. package/src/core/tools/ToolRegistry.ts +260 -0
  127. package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
  128. package/src/core/tools/impl/EditFileTool.ts +97 -0
  129. package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  130. package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
  131. package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
  132. package/src/core/tools/impl/MemoryTool.ts +102 -0
  133. package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
  134. package/src/core/tools/impl/ReadFileTool.ts +58 -0
  135. package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
  136. package/src/core/tools/impl/SearchContentTool.ts +94 -0
  137. package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
  138. package/src/core/tools/impl/SearchFileTool.ts +61 -0
  139. package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
  140. package/src/core/tools/impl/ShellTool.ts +118 -0
  141. package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
  142. package/src/core/tools/impl/TaskTool.ts +207 -0
  143. package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
  144. package/src/core/tools/impl/TodoTool.ts +122 -0
  145. package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
  146. package/src/core/tools/impl/WebFetchTool.ts +103 -0
  147. package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
  148. package/src/core/tools/impl/WebSearchTool.ts +89 -0
  149. package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
  150. package/src/core/tools/impl/WriteFileTool.ts +49 -0
  151. package/src/core/tools/impl/index.d.ts.map +1 -0
  152. package/src/core/tools/impl/index.ts +16 -0
  153. package/src/core/tools/index.d.ts.map +1 -0
  154. package/src/core/tools/index.ts +7 -0
  155. package/src/core/tools/schemas/execution.d.ts.map +1 -0
  156. package/src/core/tools/schemas/execution.ts +42 -0
  157. package/src/core/tools/schemas/file.d.ts.map +1 -0
  158. package/src/core/tools/schemas/file.ts +119 -0
  159. package/src/core/tools/schemas/index.d.ts.map +1 -0
  160. package/src/core/tools/schemas/index.ts +11 -0
  161. package/src/core/tools/schemas/memory.d.ts.map +1 -0
  162. package/src/core/tools/schemas/memory.ts +52 -0
  163. package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
  164. package/src/core/tools/schemas/orchestration.ts +44 -0
  165. package/src/core/tools/schemas/search.d.ts.map +1 -0
  166. package/src/core/tools/schemas/search.ts +112 -0
  167. package/src/core/tools/schemas/todo.d.ts.map +1 -0
  168. package/src/core/tools/schemas/todo.ts +32 -0
  169. package/src/core/tools/schemas/web.d.ts.map +1 -0
  170. package/src/core/tools/schemas/web.ts +86 -0
  171. package/src/core/types/config.d.ts.map +1 -0
  172. package/src/core/types/config.ts +200 -0
  173. package/src/core/types/errors.d.ts.map +1 -0
  174. package/src/core/types/errors.ts +204 -0
  175. package/src/core/types/index.d.ts.map +1 -0
  176. package/src/core/types/index.ts +8 -0
  177. package/src/core/types/session.d.ts.map +1 -0
  178. package/src/core/types/session.ts +216 -0
  179. package/src/core/types/tools.d.ts.map +1 -0
  180. package/src/core/types/tools.ts +157 -0
  181. package/src/core/utils/CheckpointManager.d.ts.map +1 -0
  182. package/src/core/utils/CheckpointManager.ts +327 -0
  183. package/src/core/utils/Logger.d.ts.map +1 -0
  184. package/src/core/utils/Logger.ts +98 -0
  185. package/src/core/utils/RetryManager.ts +471 -0
  186. package/src/core/utils/TokenCounter.d.ts.map +1 -0
  187. package/src/core/utils/TokenCounter.ts +414 -0
  188. package/src/core/utils/VectorMemoryStore.ts +440 -0
  189. package/src/core/utils/helpers.d.ts.map +1 -0
  190. package/src/core/utils/helpers.ts +89 -0
  191. package/src/core/utils/index.d.ts.map +1 -0
  192. package/src/core/utils/index.ts +19 -0
@@ -0,0 +1,425 @@
1
+ // ============================================================================
2
+ // IFlowDropdown - True iFlow CLI style dropdown interface for Nova CLI
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+ import type { SessionInfo } from '../core/types/config.js';
7
+
8
+ export interface DropdownItem {
9
+ id: string;
10
+ label: string;
11
+ description: string;
12
+ category: 'navigation' | 'session' | 'model' | 'tools' | 'help' | 'mcp' | 'skills';
13
+ icon?: string;
14
+ shortcut?: string;
15
+ action?: () => void | Promise<void>;
16
+ }
17
+
18
+ export interface DropdownConfig {
19
+ maxHeight?: number; // Maximum dropdown height
20
+ animationDuration?: number; // Animation speed in ms
21
+ showIcons?: boolean; // Display icons
22
+ enableSearch?: boolean; // Enable real-time search
23
+ theme?: 'light' | 'dark'; // Theme selection
24
+ }
25
+
26
+ export class IFlowDropdown {
27
+ private items: DropdownItem[] = [];
28
+ private filteredItems: DropdownItem[] = [];
29
+ private selectedIndex: number = 0;
30
+ private isVisible: boolean = false;
31
+ private config: DropdownConfig;
32
+
33
+ constructor(config: DropdownConfig = {}) {
34
+ this.config = {
35
+ maxHeight: 15,
36
+ animationDuration: 200,
37
+ showIcons: true,
38
+ enableSearch: true,
39
+ theme: 'dark',
40
+ ...config
41
+ };
42
+
43
+ this.initializeItems();
44
+ }
45
+
46
+ /**
47
+ * Show dropdown with suggestions based on input
48
+ */
49
+ async show(input: string, session: SessionInfo | null): Promise<DropdownItem | null> {
50
+ if (!input.startsWith('/')) return null;
51
+
52
+ // Filter items based on input and context
53
+ this.filterItems(input, session);
54
+
55
+ if (this.filteredItems.length === 0) return null;
56
+
57
+ this.isVisible = true;
58
+ this.selectedIndex = 0;
59
+
60
+ await this.render(input);
61
+ return await this.handleInput(input);
62
+ }
63
+
64
+ /**
65
+ * Initialize all dropdown items
66
+ */
67
+ private initializeItems(): void {
68
+ this.items = [
69
+ // Navigation commands
70
+ {
71
+ id: 'help',
72
+ label: 'Help',
73
+ description: 'Show detailed help information',
74
+ category: 'navigation',
75
+ icon: '?',
76
+ shortcut: '/h'
77
+ },
78
+ {
79
+ id: 'quit',
80
+ label: 'Quit',
81
+ description: 'Exit Nova CLI (session auto-saved)',
82
+ category: 'navigation',
83
+ icon: '✗',
84
+ shortcut: '/exit'
85
+ },
86
+ {
87
+ id: 'clear',
88
+ label: 'Clear',
89
+ description: 'Clear conversation and start new session',
90
+ category: 'navigation',
91
+ icon: '🗑️',
92
+ shortcut: '/reset'
93
+ },
94
+
95
+ // Session management
96
+ {
97
+ id: 'status',
98
+ label: 'Status',
99
+ description: 'Show current session statistics and info',
100
+ category: 'session',
101
+ icon: '📊'
102
+ },
103
+ {
104
+ id: 'history',
105
+ label: 'History',
106
+ description: 'Browse and manage previous sessions',
107
+ category: 'session',
108
+ icon: '📚'
109
+ },
110
+ {
111
+ id: 'compress',
112
+ label: 'Compress',
113
+ description: 'Optimize context window size',
114
+ category: 'session',
115
+ icon: '⚡'
116
+ },
117
+
118
+ // Model commands
119
+ {
120
+ id: 'model',
121
+ label: 'Model',
122
+ description: 'Switch or list available models',
123
+ category: 'model',
124
+ icon: '🤖'
125
+ },
126
+ {
127
+ id: 'models',
128
+ label: 'Models',
129
+ description: 'List all available models',
130
+ category: 'model',
131
+ icon: '🤖'
132
+ },
133
+
134
+ // Mode commands
135
+ {
136
+ id: 'mode',
137
+ label: 'Mode',
138
+ description: 'Change interaction mode (AUTO/PLAN/ASK)',
139
+ category: 'session',
140
+ icon: '🔄'
141
+ },
142
+ {
143
+ id: 'auto',
144
+ label: 'Auto',
145
+ description: 'Switch to AUTO mode (no approval needed)',
146
+ category: 'session',
147
+ icon: '🚀'
148
+ },
149
+ {
150
+ id: 'plan',
151
+ label: 'Plan',
152
+ description: 'Switch to PLAN mode (confirm before action)',
153
+ category: 'session',
154
+ icon: '📋'
155
+ },
156
+ {
157
+ id: 'ask',
158
+ label: 'Ask',
159
+ description: 'Switch to ASK mode (read-only questions)',
160
+ category: 'session',
161
+ icon: '❓'
162
+ },
163
+
164
+ // Tool commands
165
+ {
166
+ id: 'tools',
167
+ label: 'Tools',
168
+ description: 'Manage built-in tools and capabilities',
169
+ category: 'tools',
170
+ icon: '🛠️'
171
+ },
172
+ {
173
+ id: 'skills',
174
+ label: 'Skills',
175
+ description: 'Use or manage AI skills',
176
+ category: 'skills',
177
+ icon: '🧩'
178
+ },
179
+ {
180
+ id: 'init',
181
+ label: 'Init',
182
+ description: 'Generate NOVA.md project memory file',
183
+ category: 'tools',
184
+ icon: '📝'
185
+ },
186
+
187
+ // MCP commands
188
+ {
189
+ id: 'mcp',
190
+ label: 'MCP',
191
+ description: 'Manage MCP server connections',
192
+ category: 'mcp',
193
+ icon: '🌐'
194
+ },
195
+ {
196
+ id: 'mcp-status',
197
+ label: 'MCP Status',
198
+ description: 'Check MCP server connection status',
199
+ category: 'mcp',
200
+ icon: '🌐'
201
+ },
202
+
203
+ // Memory commands
204
+ {
205
+ id: 'memory',
206
+ label: 'Memory',
207
+ description: 'Manage persistent notes and memory',
208
+ category: 'session',
209
+ icon: '💾'
210
+ },
211
+ {
212
+ id: 'profile',
213
+ label: 'Profile',
214
+ description: 'Show detailed session profile',
215
+ category: 'session',
216
+ icon: '👤'
217
+ }
218
+ ];
219
+ }
220
+
221
+ /**
222
+ * Filter items based on input and session context
223
+ */
224
+ private filterItems(input: string, session: SessionInfo | null): void {
225
+ const baseInput = input.slice(1).toLowerCase(); // Remove / and lowercase
226
+
227
+ this.filteredItems = this.items.filter(item => {
228
+ // Basic text matching
229
+ const matchesText = item.label.toLowerCase().includes(baseInput) ||
230
+ item.description.toLowerCase().includes(baseInput) ||
231
+ item.id.toLowerCase().startsWith(baseInput);
232
+
233
+ if (!matchesText) return false;
234
+
235
+ // Context-based filtering
236
+ if (session?.mode === 'ask') {
237
+ // In ask mode, hide editing commands
238
+ if (['clear', 'memory-add'].includes(item.id)) return false;
239
+ }
240
+
241
+ // Mode-specific suggestions based on recent errors would go here
242
+ // For now, return basic filtered results
243
+
244
+ return true;
245
+ });
246
+
247
+ // Sort by relevance
248
+ this.sortItems();
249
+ }
250
+
251
+ /**
252
+ * Sort items by relevance
253
+ */
254
+ private sortItems(): void {
255
+ this.filteredItems.sort((a, b) => {
256
+ // Exact match first
257
+ if (a.id === b.id) return 0;
258
+ if (a.id.toLowerCase() === this.currentInput.toLowerCase()) return -1;
259
+ if (b.id.toLowerCase() === this.currentInput.toLowerCase()) return 1;
260
+
261
+ // Then by startsWith
262
+ const aStarts = a.id.toLowerCase().startsWith(this.currentInput.toLowerCase());
263
+ const bStarts = b.id.toLowerCase().startsWith(this.currentInput.toLowerCase());
264
+
265
+ if (aStarts && !bStarts) return -1;
266
+ if (!aStarts && bStarts) return 1;
267
+
268
+ // Finally alphabetically
269
+ return a.label.localeCompare(b.label);
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Render the dropdown UI
275
+ */
276
+ private async render(input: string): Promise<void> {
277
+ console.clear();
278
+
279
+ // Header
280
+ const width = Math.min(process.stdout.columns || 80, 70);
281
+ const border = '━'.repeat(width);
282
+ const title = ' NOVA CLI COMMAND SUGGESTIONS ';
283
+ const header = `╭${border}╮\n│${title.padEnd(width)}│\n├${'─'.repeat(width)}┤`;
284
+
285
+ console.log(chalk.bgBlue.black(header));
286
+ console.log(chalk.blue(`│ Use ↑↓ to navigate, Enter to select, Esc to cancel${' '.repeat(width - 64)}│`));
287
+ console.log(chalk.blue(`│ Input: ${chalk.cyan(input)}${' '.repeat(width - 18 - input.length)}│`));
288
+ console.log(chalk.blue(`├${'─'.repeat(width)}┤`));
289
+
290
+ // Items
291
+ const displayCount = Math.min(
292
+ this.filteredItems.length,
293
+ this.config.maxHeight || 15
294
+ );
295
+
296
+ for (let i = 0; i < displayCount; i++) {
297
+ const item = this.filteredItems[i];
298
+ const isSelected = i === this.selectedIndex;
299
+
300
+ // Selection indicator
301
+ const prefix = isSelected ? chalk.green('▶ ') : chalk.gray(' ');
302
+ const indent = isSelected ? ' ' : '·';
303
+
304
+ // Icon
305
+ let iconDisplay = '';
306
+ if (this.config.showIcons && item.icon) {
307
+ iconDisplay = isSelected ? chalk.white(item.icon) : chalk.gray(item.icon);
308
+ }
309
+
310
+ // Label and description
311
+ const labelDisplay = isSelected
312
+ ? chalk.cyan.bold(item.label)
313
+ : chalk.white(item.label);
314
+
315
+ const descDisplay = isSelected
316
+ ? chalk.yellow(item.description)
317
+ : chalk.gray(item.description);
318
+
319
+ // Shortcut
320
+ let shortcutDisplay = '';
321
+ if (item.shortcut) {
322
+ shortcutDisplay = isSelected
323
+ ? chalk.magenta(` [${item.shortcut}]`)
324
+ : chalk.gray(` (${item.shortcut})`);
325
+ }
326
+
327
+ const line = `${prefix}${indent}${iconDisplay} ${labelDisplay} ${descDisplay}${shortcutDisplay}`;
328
+ console.log(chalk.blue(`│${line.padEnd(width)}│`));
329
+ }
330
+
331
+ // Footer
332
+ const footer = `╰${'━'.repeat(width)}╯`;
333
+ console.log(chalk.blue(footer));
334
+ console.log('');
335
+ }
336
+
337
+ /**
338
+ * Handle user input for dropdown navigation
339
+ */
340
+ private async handleInput(input: string): Promise<DropdownItem | null> {
341
+ return new Promise((resolve) => {
342
+ const readline = require('node:readline');
343
+ const rl = readline.createInterface({
344
+ input: process.stdin,
345
+ output: process.stdout
346
+ });
347
+
348
+ // Set raw mode for direct key capture
349
+ const wasRaw = process.stdin.isRaw;
350
+ if (process.stdin.isTTY) {
351
+ process.stdin.setRawMode(true);
352
+ }
353
+
354
+ const handleKeyPress = (key: string) => {
355
+ switch (key) {
356
+ case '\x1b[A': // Up arrow
357
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
358
+ this.render(input);
359
+ break;
360
+
361
+ case '\x1b[B': // Down arrow
362
+ this.selectedIndex = Math.min(
363
+ this.filteredItems.length - 1,
364
+ this.selectedIndex + 1
365
+ );
366
+ this.render(input);
367
+ break;
368
+
369
+ case '\r': // Enter
370
+ case '\n':
371
+ rl.close();
372
+ if (process.stdin.isTTY) {
373
+ process.stdin.setRawMode(wasRaw);
374
+ }
375
+
376
+ const selected = this.filteredItems[this.selectedIndex];
377
+ if (selected) {
378
+ console.log(chalk.green(`✓ Executing: ${selected.label}`));
379
+ setTimeout(() => {
380
+ if (selected.action) {
381
+ selected.action();
382
+ }
383
+ resolve(selected);
384
+ }, 500);
385
+ } else {
386
+ resolve(null);
387
+ }
388
+ break;
389
+
390
+ case '\x1b': // Escape
391
+ rl.close();
392
+ if (process.stdin.isTTY) {
393
+ process.stdin.setRawMode(wasRaw);
394
+ }
395
+ console.log(chalk.gray('\nCancelled.'));
396
+ resolve(null);
397
+ break;
398
+ }
399
+ };
400
+
401
+ process.stdin.on('data', handleKeyPress);
402
+ });
403
+ }
404
+
405
+ /**
406
+ * Hide dropdown
407
+ */
408
+ hide(): void {
409
+ this.isVisible = false;
410
+ }
411
+
412
+ /**
413
+ * Get all available items (for testing)
414
+ */
415
+ getAllItems(): DropdownItem[] {
416
+ return [...this.items];
417
+ }
418
+
419
+ /**
420
+ * Get items by category
421
+ */
422
+ getItemsByCategory(category: DropdownItem['category']): DropdownItem[] {
423
+ return this.items.filter(item => item.category === category);
424
+ }
425
+ }
@@ -0,0 +1,276 @@
1
+ // ============================================================================
2
+ // ModernReplUI - Integrated modern UI for Nova CLI REPL
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+ import type { SessionInfo, NovaConfig } from '../core/types/config.js';
7
+
8
+ import { StatusBar } from './components/StatusBar.js';
9
+ import { ProgressIndicator } from './components/ProgressIndicator.js';
10
+ import { ErrorPanel } from './components/ErrorPanel.js';
11
+ import { QuickActions } from './components/QuickActions.js';
12
+
13
+ export interface ModernReplOptions {
14
+ showStatusBar?: boolean;
15
+ showInputBox?: boolean;
16
+ enableQuickActions?: boolean;
17
+ compactMode?: boolean;
18
+ }
19
+
20
+ export class ModernReplUI {
21
+ private session: SessionInfo | null = null;
22
+ private config: NovaConfig | null = null;
23
+ private options: ModernReplOptions = {};
24
+
25
+ // UI Components
26
+ private statusBar: StatusBar;
27
+ private progressIndicator: ProgressIndicator;
28
+ private errorPanel: ErrorPanel;
29
+ private quickActions: QuickActions;
30
+
31
+ constructor(options: ModernReplOptions = {}) {
32
+ this.options = {
33
+ showStatusBar: true,
34
+ showInputBox: true,
35
+ enableQuickActions: true,
36
+ compactMode: false,
37
+ ...options
38
+ };
39
+
40
+ // Initialize components
41
+ this.statusBar = new StatusBar({ compact: this.options.compactMode });
42
+ this.progressIndicator = new ProgressIndicator();
43
+ this.errorPanel = new ErrorPanel({ compact: this.options.compactMode });
44
+ this.quickActions = new QuickActions(this.session);
45
+
46
+ console.log(chalk.bgGreen.black.bold(' NOVA CLI v0.1.0 '));
47
+ console.log(chalk.green('-'.repeat(50)));
48
+ console.log('');
49
+ }
50
+
51
+ async start(): Promise<void> {
52
+ // Clear screen and setup
53
+ console.clear();
54
+
55
+ if (this.options.showStatusBar) {
56
+ this.renderStatusBar();
57
+ }
58
+
59
+ // Show welcome message
60
+ this.showWelcomeMessage();
61
+
62
+ // Main input loop would be handled by parent component
63
+ console.log(chalk.dim('Type /help for commands, or press Ctrl+C to exit...'));
64
+ }
65
+
66
+ updateSession(session: SessionInfo): void {
67
+ this.session = session;
68
+ this.quickActions = new QuickActions(session);
69
+
70
+ if (this.options.showStatusBar) {
71
+ this.statusBar.update(session, this.config!);
72
+ }
73
+ }
74
+
75
+ updateConfig(config: NovaConfig): void {
76
+ this.config = config;
77
+
78
+ if (this.options.showStatusBar) {
79
+ this.statusBar.update(this.session!, config);
80
+ }
81
+ }
82
+
83
+ // Progress management
84
+ showProgress(message: string = 'Processing...', type: 'spinner' | 'bar' | 'dots' = 'spinner'): void {
85
+ this.progressIndicator.start(message);
86
+ this.progressIndicator.options.type = type;
87
+ }
88
+
89
+ updateProgress(progress: number, message?: string): void {
90
+ this.progressIndicator.update(progress, message);
91
+ }
92
+
93
+ completeProgress(message: string = 'Done!'): void {
94
+ this.progressIndicator.complete(message);
95
+ }
96
+
97
+ failProgress(error: Error | string, message: string = 'Failed'): void {
98
+ this.progressIndicator.fail(error, message);
99
+ }
100
+
101
+ // Error handling
102
+ handleError(error: Error | string, context?: any): void {
103
+ this.errorPanel.display(error, context);
104
+ }
105
+
106
+ // Quick actions
107
+ showQuickMenu(): Promise<string> {
108
+ if (!this.options.enableQuickActions) {
109
+ return Promise.resolve('');
110
+ }
111
+
112
+ this.quickActions.showMenu();
113
+ return this.quickActions.handleInput('');
114
+ }
115
+
116
+ // Input box rendering
117
+ renderInputBox(prompt: string = 'NOVA > '): void {
118
+ if (!this.options.showInputBox) return;
119
+
120
+ const width = Math.min(process.stdout.columns || 80, 100);
121
+ const border = '─'.repeat(width);
122
+
123
+ console.log('');
124
+ console.log(chalk.blue('┌' + border + '┐'));
125
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
126
+ console.log(chalk.blue('│') + chalk.white(` ${prompt}`).padEnd(width - 2) + chalk.blue('│'));
127
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
128
+ console.log(chalk.blue('└' + border + '┘'));
129
+ }
130
+
131
+ clearInputBox(): void {
132
+ if (!this.options.showInputBox) return;
133
+
134
+ const width = Math.min(process.stdout.columns || 80, 100);
135
+ console.log(chalk.blue('┌' + ' '.repeat(width) + '┐'));
136
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
137
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
138
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
139
+ console.log(chalk.blue('└' + ' '.repeat(width) + '┘'));
140
+ }
141
+
142
+ // Utility methods
143
+ private renderStatusBar(): void {
144
+ if (this.session && this.config) {
145
+ console.log(this.statusBar.render());
146
+ }
147
+ }
148
+
149
+ private showWelcomeMessage(): void {
150
+ const messages = [
151
+ chalk.cyan('🚀 Welcome to Nova CLI - Your AI-Powered Terminal Assistant'),
152
+ '',
153
+ chalk.yellow('✨ Features:'),
154
+ ' • Multiple AI model providers (OpenAI, Anthropic, Ollama, etc.)',
155
+ ' • Smart file operations with @file references',
156
+ ' • Built-in tools for code analysis and generation',
157
+ ' • MCP server integration for extended functionality',
158
+ ' • Session persistence and history management',
159
+ '',
160
+ chalk.blue('📖 Quick Start:'),
161
+ ' • Type your request and press Enter',
162
+ ' • Use @filename to reference files',
163
+ ' • Use !command to execute shell commands',
164
+ ' • Press /help for command reference',
165
+ '',
166
+ chalk.gray('Press Ctrl+C at any time to cancel current operation')
167
+ ];
168
+
169
+ messages.forEach(msg => console.log(msg));
170
+ console.log('');
171
+ }
172
+
173
+ // Static utility methods
174
+ static async withModernUI<T>(
175
+ task: (ui: ModernReplUI) => Promise<T>,
176
+ options?: ModernReplOptions
177
+ ): Promise<T> {
178
+ const ui = new ModernReplUI(options);
179
+ await ui.start();
180
+
181
+ try {
182
+ const result = await task(ui);
183
+ return result;
184
+ } finally {
185
+ // Cleanup
186
+ console.clear();
187
+ }
188
+ }
189
+
190
+ static async createFromExisting(
191
+ originalRepl: any,
192
+ options?: ModernReplOptions
193
+ ): Promise<ModernReplUI> {
194
+ const ui = new ModernReplUI(options);
195
+
196
+ // Hook into existing REPL methods
197
+ if (originalRepl.printBanner) {
198
+ const originalPrintBanner = originalRepl.printBanner.bind(originalRepl);
199
+ originalRepl.printBanner = async function() {
200
+ await originalPrintBanner();
201
+ console.log('\n' + ui.statusBar.render());
202
+ };
203
+ }
204
+
205
+ return ui;
206
+ }
207
+
208
+ // Configuration helpers
209
+ setOption<K extends keyof ModernReplOptions>(key: K, value: ModernReplOptions[K]): void {
210
+ this.options[key] = value;
211
+
212
+ switch (key) {
213
+ case 'showStatusBar':
214
+ if (value && this.session && this.config) {
215
+ this.statusBar.update(this.session, this.config);
216
+ }
217
+ break;
218
+ case 'compactMode':
219
+ this.statusBar.options.compact = value as boolean;
220
+ if (this.session && this.config) {
221
+ this.statusBar.update(this.session, this.config);
222
+ }
223
+ break;
224
+ case 'enableQuickActions':
225
+ // Actions can be toggled dynamically
226
+ break;
227
+ }
228
+ }
229
+
230
+ getStatus(): ReplUIStatus {
231
+ return {
232
+ hasSession: !!this.session,
233
+ hasConfig: !!this.config,
234
+ sessionStats: this.session ? {
235
+ id: this.session.id,
236
+ model: this.session.model,
237
+ turnCount: this.session.turnCount || 0,
238
+ tokenUsage: (this.session.totalInputTokens || 0) + (this.session.totalOutputTokens || 0),
239
+ duration: Date.now() - (this.session.createdAt || Date.now())
240
+ } : null,
241
+ uiOptions: { ...this.options },
242
+ components: {
243
+ statusBar: !!this.statusBar,
244
+ progressIndicator: !!this.progressIndicator,
245
+ errorPanel: !!this.errorPanel,
246
+ quickActions: !!this.quickActions
247
+ }
248
+ };
249
+ }
250
+
251
+ // Cleanup
252
+ dispose(): void {
253
+ this.progressIndicator.stop();
254
+ console.clear();
255
+ }
256
+ }
257
+
258
+ // Type definitions
259
+ interface ReplUIStatus {
260
+ hasSession: boolean;
261
+ hasConfig: boolean;
262
+ sessionStats: {
263
+ id: string;
264
+ model: string;
265
+ turnCount: number;
266
+ tokenUsage: number;
267
+ duration: number;
268
+ } | null;
269
+ uiOptions: ModernReplOptions;
270
+ components: {
271
+ statusBar: boolean;
272
+ progressIndicator: boolean;
273
+ errorPanel: boolean;
274
+ quickActions: boolean;
275
+ };
276
+ }