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,458 @@
1
+ // ============================================================================
2
+ // SmartCompletion - Intelligent command suggestion system for Nova CLI
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+ import type { SessionInfo } from '../core/types/config.js';
7
+
8
+ export interface CommandSuggestion {
9
+ text: string;
10
+ description: string;
11
+ category: 'navigation' | 'session' | 'model' | 'tools' | 'help' | 'mcp' | 'skills';
12
+ icon?: string;
13
+ shortcut?: string;
14
+ }
15
+
16
+ export interface CompletionContext {
17
+ input: string;
18
+ session: SessionInfo | null;
19
+ mode: string;
20
+ recentCommands: string[];
21
+ errorHistory: string[];
22
+ }
23
+
24
+ export class SmartCompletion {
25
+ private allCommands = new Map<string, CommandSuggestion>();
26
+ private recentCommands: string[] = [];
27
+ private errorHistory: string[] = [];
28
+ private context: CompletionContext;
29
+
30
+ constructor(session: SessionInfo | null) {
31
+ this.context = {
32
+ input: '',
33
+ session,
34
+ mode: session?.mode || 'auto',
35
+ recentCommands: [],
36
+ errorHistory: []
37
+ };
38
+ this.initializeCommands();
39
+ }
40
+
41
+ /**
42
+ * Handle user input and return suggestions
43
+ */
44
+ async handleInput(input: string): Promise<CommandSuggestion[]> {
45
+ this.context.input = input.trim();
46
+
47
+ // Update context with latest info
48
+ if (this.context.session) {
49
+ this.context.mode = this.context.session.mode;
50
+ }
51
+ this.context.recentCommands = this.recentCommands.slice(0, 10);
52
+
53
+ // Get suggestions based on input
54
+ const suggestions = this.getSuggestions(this.context.input);
55
+
56
+ // If we have suggestions and it's a partial match, show dropdown
57
+ if (suggestions.length > 0 && this.context.input.startsWith('/')) {
58
+ await this.showDropdown(suggestions);
59
+ return suggestions;
60
+ }
61
+
62
+ return suggestions;
63
+ }
64
+
65
+ /**
66
+ * Show interactive dropdown menu
67
+ */
68
+ private async showDropdown(suggestions: CommandSuggestion[]): Promise<void> {
69
+ console.log('\n'); // Clear line before dropdown
70
+
71
+ const width = Math.min(process.stdout.columns || 80, 60);
72
+ const border = '─'.repeat(width);
73
+
74
+ // Header
75
+ console.log(chalk.bgBlue.white.bold(' COMMAND SUGGESTIONS '));
76
+ console.log(chalk.blue(border));
77
+
78
+ // Sort suggestions by relevance
79
+ const sortedSuggestions = this.sortByRelevance(suggestions);
80
+
81
+ // Display suggestions with navigation
82
+ let selectedIndex = 0;
83
+ const renderMenu = () => {
84
+ // Clear previous menu lines
85
+ const menuHeight = Math.min(sortedSuggestions.length + 2, 15);
86
+ process.stdout.write(`\x1b[${menuHeight}A`);
87
+
88
+ // Show header
89
+ console.log(chalk.yellow(`\n Use ↑↓ to navigate, Enter to select, Esc to cancel\n Input: ${chalk.cyan(this.context.input)}\n`));
90
+
91
+ // Show suggestions
92
+ sortedSuggestions.forEach((suggestion, index) => {
93
+ const isSelected = index === selectedIndex;
94
+ const prefix = isSelected ? chalk.green('▶ ') : chalk.gray(' ');
95
+ const keyDisplay = isSelected ? chalk.cyan(`[${suggestion.text}]`) : chalk.gray(suggestion.text);
96
+ const descDisplay = chalk.white(suggestion.description);
97
+
98
+ console.log(`${prefix}${keyDisplay} ${descDisplay}`);
99
+ });
100
+
101
+ // Bottom border
102
+ console.log(chalk.blue('┌' + border + '┐'));
103
+ console.log(chalk.blue('│') + ' '.repeat(width) + chalk.blue('│'));
104
+ console.log(chalk.blue('└' + border + '┘'));
105
+ };
106
+
107
+ // Initial render
108
+ renderMenu();
109
+
110
+ // Handle keyboard input
111
+ return new Promise((resolve) => {
112
+ const readline = await import('node:readline');
113
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
114
+
115
+ const handleKeyPress = (key: string) => {
116
+ switch (key) {
117
+ case '\x1b[A': // Up arrow
118
+ selectedIndex = Math.max(0, selectedIndex - 1);
119
+ renderMenu();
120
+ break;
121
+ case '\x1b[B': // Down arrow
122
+ selectedIndex = Math.min(sortedSuggestions.length - 1, selectedIndex + 1);
123
+ renderMenu();
124
+ break;
125
+ case '\r': // Enter
126
+ case '\n':
127
+ rl.close();
128
+ const selected = sortedSuggestions[selectedIndex];
129
+ console.log(`\n✓ Selected: ${chalk.cyan(selected.text)}`);
130
+ this.executeCommand(selected.text);
131
+ resolve();
132
+ break;
133
+ case '\x1b': // Escape
134
+ rl.close();
135
+ console.log(`\nCancelled.`);
136
+ resolve();
137
+ break;
138
+ }
139
+ };
140
+
141
+ // Set up raw mode for direct key capture
142
+ const wasRaw = process.stdin.isRaw;
143
+ if (process.stdin.isTTY) {
144
+ process.stdin.setRawMode(true);
145
+ }
146
+
147
+ process.stdin.on('data', (buffer: Buffer) => {
148
+ const key = buffer.toString();
149
+ handleKeyPress(key);
150
+ });
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Execute the selected command
156
+ */
157
+ private executeCommand(commandText: string): void {
158
+ console.log(chalk.gray(`Executing: ${commandText}`));
159
+
160
+ // Simulate command execution
161
+ setTimeout(() => {
162
+ console.log(chalk.green(`✓ Command executed: ${commandText}`));
163
+
164
+ // Add to recent commands
165
+ this.addRecentCommand(commandText);
166
+ }, 500);
167
+ }
168
+
169
+ /**
170
+ * Get command suggestions based on current input and context
171
+ */
172
+ private getSuggestions(input: string): CommandSuggestion[] {
173
+ if (!input.startsWith('/')) {
174
+ return []; // Only suggest commands that start with /
175
+ }
176
+
177
+ const baseInput = input.slice(1).toLowerCase(); // Remove / and lowercase
178
+ const suggestions: CommandSuggestion[] = [];
179
+
180
+ // Filter commands based on input
181
+ for (const [commandName, suggestion] of this.allCommands) {
182
+ const matchesInput = commandName.toLowerCase().startsWith(baseInput) ||
183
+ suggestion.description.toLowerCase().includes(baseInput);
184
+
185
+ // Additional context-based filtering
186
+ const contextMatches = this.getContextualMatches(commandName, suggestion);
187
+
188
+ if (matchesInput || contextMatches) {
189
+ suggestions.push({
190
+ ...suggestion,
191
+ text: '/' + commandName
192
+ });
193
+ }
194
+ }
195
+
196
+ // Sort by relevance
197
+ return this.sortByRelevance(suggestions);
198
+ }
199
+
200
+ /**
201
+ * Get contextual matches based on session state and history
202
+ */
203
+ private getContextualMatches(commandName: string, suggestion: CommandSuggestion): boolean {
204
+ const session = this.context.session;
205
+ if (!session) return false;
206
+
207
+ // Mode-specific suggestions
208
+ if (session.mode === 'plan' && commandName.includes('approve')) {
209
+ return true;
210
+ }
211
+
212
+ if (session.mode === 'ask' && commandName.includes('edit')) {
213
+ return false; // Hide editing commands in ask mode
214
+ }
215
+
216
+ // Recent command suggestions
217
+ if (this.context.recentCommands.includes(commandName)) {
218
+ return true;
219
+ }
220
+
221
+ // Error-based suggestions
222
+ if (this.context.errorHistory.length > 0) {
223
+ const lastError = this.context.errorHistory[this.context.errorHistory.length - 1];
224
+ if (lastError.includes('model') && commandName.includes('model')) {
225
+ return true;
226
+ }
227
+ if (lastError.includes('config') && commandName.includes('config')) {
228
+ return true;
229
+ }
230
+ }
231
+
232
+ return false;
233
+ }
234
+
235
+ /**
236
+ * Sort suggestions by relevance
237
+ */
238
+ private sortByRelevance(suggestions: CommandSuggestion[]): CommandSuggestion[] {
239
+ return suggestions.sort((a, b) => {
240
+ // Priority order: exact match, then startsWith, then contains
241
+ const aExact = a.text.toLowerCase() === this.context.input.toLowerCase();
242
+ const bExact = b.text.toLowerCase() === this.context.input.toLowerCase();
243
+
244
+ if (aExact && !bExact) return -1;
245
+ if (!aExact && bExact) return 1;
246
+
247
+ // Then by recent usage
248
+ const aRecent = this.context.recentCommands.indexOf(a.text.slice(1)) >= 0;
249
+ const bRecent = this.context.recentCommands.indexOf(b.text.slice(1)) >= 0;
250
+
251
+ if (aRecent && !bRecent) return -1;
252
+ if (!aRecent && bRecent) return 1;
253
+
254
+ // Finally alphabetically
255
+ return a.text.localeCompare(b.text);
256
+ });
257
+ }
258
+
259
+ /**
260
+ * Initialize all available commands
261
+ */
262
+ private initializeCommands(): void {
263
+ // Navigation commands
264
+ this.addCommand('help', {
265
+ description: 'Show detailed help information',
266
+ category: 'help',
267
+ icon: '?',
268
+ shortcut: '/h'
269
+ });
270
+
271
+ this.addCommand('quit', {
272
+ description: 'Exit Nova CLI (session auto-saved)',
273
+ category: 'navigation',
274
+ icon: '✗',
275
+ shortcut: '/exit'
276
+ });
277
+
278
+ this.addCommand('clear', {
279
+ description: 'Clear conversation and start new session',
280
+ category: 'session',
281
+ icon: '🗑️',
282
+ shortcut: '/reset'
283
+ });
284
+
285
+ // Session management
286
+ this.addCommand('status', {
287
+ description: 'Show current session statistics and info',
288
+ category: 'session',
289
+ icon: '📊'
290
+ });
291
+
292
+ this.addCommand('history', {
293
+ description: 'Browse and manage previous sessions',
294
+ category: 'session',
295
+ icon: '📚'
296
+ });
297
+
298
+ this.addCommand('compress', {
299
+ description: 'Optimize context window size',
300
+ category: 'session',
301
+ icon: '⚡'
302
+ });
303
+
304
+ // Model commands
305
+ this.addCommand('model', {
306
+ description: 'Switch or list available models',
307
+ category: 'model',
308
+ icon: '🤖'
309
+ });
310
+
311
+ this.addCommand('models', {
312
+ description: 'List all available models',
313
+ category: 'model',
314
+ icon: '🤖'
315
+ });
316
+
317
+ // Mode commands
318
+ this.addCommand('mode', {
319
+ description: 'Change interaction mode (AUTO/PLAN/ASK)',
320
+ category: 'session',
321
+ icon: '🔄'
322
+ });
323
+
324
+ this.addCommand('auto', {
325
+ description: 'Switch to AUTO mode (no approval needed)',
326
+ category: 'session',
327
+ icon: '🚀'
328
+ });
329
+
330
+ this.addCommand('plan', {
331
+ description: 'Switch to PLAN mode (confirm before action)',
332
+ category: 'session',
333
+ icon: '📋'
334
+ });
335
+
336
+ this.addCommand('ask', {
337
+ description: 'Switch to ASK mode (read-only questions)',
338
+ category: 'session',
339
+ icon: '❓'
340
+ });
341
+
342
+ // Tool commands
343
+ this.addCommand('tools', {
344
+ description: 'Manage built-in tools and capabilities',
345
+ category: 'tools',
346
+ icon: '🛠️'
347
+ });
348
+
349
+ this.addCommand('skills', {
350
+ description: 'Use or manage AI skills',
351
+ category: 'skills',
352
+ icon: '🧩'
353
+ });
354
+
355
+ this.addCommand('init', {
356
+ description: 'Generate NOVA.md project memory file',
357
+ category: 'tools',
358
+ icon: '📝'
359
+ });
360
+
361
+ // MCP commands
362
+ this.addCommand('mcp', {
363
+ description: 'Manage MCP server connections',
364
+ category: 'mcp',
365
+ icon: '🌐'
366
+ });
367
+
368
+ this.addCommand('mcp-status', {
369
+ description: 'Check MCP server connection status',
370
+ category: 'mcp',
371
+ icon: '🌐'
372
+ });
373
+
374
+ // Memory commands
375
+ this.addCommand('memory', {
376
+ description: 'Manage persistent notes and memory',
377
+ category: 'session',
378
+ icon: '💾'
379
+ });
380
+
381
+ this.addCommand('memory-show', {
382
+ description: 'Display all saved memories',
383
+ category: 'session',
384
+ icon: '💾'
385
+ });
386
+
387
+ this.addCommand('memory-add', {
388
+ description: 'Add a new memory note',
389
+ category: 'session',
390
+ icon: '➕'
391
+ });
392
+
393
+ // Quick actions
394
+ this.addCommand('profile', {
395
+ description: 'Show detailed session profile',
396
+ category: 'session',
397
+ icon: '👤'
398
+ });
399
+
400
+ this.addCommand('stats', {
401
+ description: 'Show token usage and performance stats',
402
+ category: 'session',
403
+ icon: '📈'
404
+ });
405
+
406
+ this.addCommand('theme', {
407
+ description: 'Change UI theme (light/dark)',
408
+ category: 'tools',
409
+ icon: '🎨'
410
+ });
411
+ }
412
+
413
+ /**
414
+ * Add a command to the registry
415
+ */
416
+ private addCommand(name: string, suggestion: Omit<CommandSuggestion, 'text'>): void {
417
+ this.allCommands.set(name, {
418
+ text: name,
419
+ ...suggestion
420
+ });
421
+ }
422
+
423
+ /**
424
+ * Add command to recent usage history
425
+ */
426
+ private addRecentCommand(command: string): void {
427
+ const cmdName = command.startsWith('/') ? command.slice(1) : command;
428
+ this.recentCommands.unshift(cmdName);
429
+ this.recentCommands = this.recentCommands.slice(0, 20); // Keep only 20 most recent
430
+ }
431
+
432
+ /**
433
+ * Record an error for future suggestions
434
+ */
435
+ recordError(error: string): void {
436
+ this.errorHistory.unshift(error);
437
+ this.errorHistory = this.errorHistory.slice(0, 10); // Keep only 10 most recent errors
438
+ }
439
+
440
+ /**
441
+ * Get all available commands (for testing/debugging)
442
+ */
443
+ getAllCommands(): CommandSuggestion[] {
444
+ return Array.from(this.allCommands.values()).map(cmd => ({
445
+ ...cmd,
446
+ text: '/' + cmd.text
447
+ }));
448
+ }
449
+
450
+ /**
451
+ * Get commands by category
452
+ */
453
+ getCommandsByCategory(category: CommandSuggestion['category']): CommandSuggestion[] {
454
+ return this.getAllCommands()
455
+ .filter(cmd => cmd.category === category)
456
+ .sort((a, b) => a.text.localeCompare(b.text));
457
+ }
458
+ }
@@ -0,0 +1,5 @@
1
+ // ============================================================================
2
+ // Nova CLI - Main CLI application entry point
3
+ // ============================================================================
4
+
5
+ export { NovaApp } from './startup/NovaApp.js';
@@ -0,0 +1,212 @@
1
+ // ============================================================================
2
+ // IFlowRepl - True iFlow CLI style REPL for Nova CLI
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+ import type { SessionInfo, NovaConfig } from '../core/types/config.js';
7
+ import { IFlowDropdown } from '../ui/IFlowDropdown.js';
8
+
9
+ export interface IFlowReplOptions {
10
+ enableDropdown?: boolean;
11
+ showPrompt?: boolean;
12
+ theme?: 'light' | 'dark';
13
+ }
14
+
15
+ export class IFlowRepl {
16
+ private session: SessionInfo | null = null;
17
+ private config: NovaConfig | null = null;
18
+ private options: IFlowReplOptions;
19
+ private dropdown: IFlowDropdown | null = null;
20
+
21
+ constructor(options: IFlowReplOptions = {}) {
22
+ this.options = {
23
+ enableDropdown: true,
24
+ showPrompt: true,
25
+ theme: 'dark',
26
+ ...options
27
+ };
28
+
29
+ if (this.options.enableDropdown) {
30
+ this.dropdown = new IFlowDropdown({
31
+ theme: this.options.theme,
32
+ showIcons: true,
33
+ maxHeight: 12
34
+ });
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Initialize the iFlow-style REPL
40
+ */
41
+ async start(session: SessionInfo, config: NovaConfig): Promise<void> {
42
+ this.session = session;
43
+ this.config = config;
44
+
45
+ // Clear screen and show header
46
+ console.clear();
47
+ this.showHeader();
48
+
49
+ // Show welcome message
50
+ this.showWelcomeMessage();
51
+
52
+ // Start input loop
53
+ await this.runInputLoop();
54
+ }
55
+
56
+ /**
57
+ * Show professional header similar to iFlow
58
+ */
59
+ private showHeader(): void {
60
+ const width = Math.min(process.stdout.columns || 80, 70);
61
+ const border = '━'.repeat(width);
62
+ const title = ' NOVA CLI · AI-Powered Terminal Assistant ';
63
+ const header = `╭${border}╮\n│${title.padEnd(width)}│\n├${'─'.repeat(width)}┤`;
64
+
65
+ console.log(chalk.bgBlue.black(header));
66
+
67
+ // Model info line
68
+ const modelShort = this.session?.model.split('/').pop() || this.session?.model || 'unknown';
69
+ const modelLine = `│ ${chalk.cyan('Model:')} ${chalk.white(modelShort.padEnd(50))}${chalk.blue('│')}`;
70
+ console.log(chalk.blue(modelLine));
71
+
72
+ // Mode and directory info
73
+ const modeLabel = this.session?.mode === 'auto' ? 'AUTO' :
74
+ this.session?.mode === 'plan' ? 'PLAN' : 'ASK';
75
+ const modeColor = this.session?.mode === 'auto' ? 'green' :
76
+ this.session?.mode === 'plan' ? 'yellow' : 'blue';
77
+
78
+ const modeLine = `│ ${chalk[modeColor](`Mode: ${modeLabel}`).padEnd(56)}${chalk.blue('│')}`;
79
+ const dirLine = `│ ${chalk.cyan('Dir:')} ${chalk.white((this.session?.workingDirectory || '.').padEnd(49))}${chalk.blue('│')}`;
80
+
81
+ console.log(chalk.blue(modeLine));
82
+ console.log(chalk.blue(dirLine));
83
+
84
+ const footer = `╰${'━'.repeat(width)}╯`;
85
+ console.log(chalk.blue(footer));
86
+ console.log('');
87
+ }
88
+
89
+ /**
90
+ * Show welcome message
91
+ */
92
+ private showWelcomeMessage(): void {
93
+ const messages = [
94
+ chalk.cyan('🚀 Welcome to Nova CLI - Your AI-Powered Terminal Assistant'),
95
+ '',
96
+ chalk.yellow('✨ Features:'),
97
+ ' • Multiple AI model providers (OpenAI, Anthropic, Ollama, etc.)',
98
+ ' • Smart file operations with @file references',
99
+ ' • Built-in tools for code analysis and generation',
100
+ ' • MCP server integration for extended functionality',
101
+ ' • Session persistence and history management',
102
+ '',
103
+ chalk.green('🎯 Quick Start:'),
104
+ ' • Type /help for command suggestions',
105
+ ' • Use ↑↓ arrows to navigate, Enter to select',
106
+ ' • Press ESC to cancel any operation',
107
+ '',
108
+ chalk.blue('💡 Pro Tip:'),
109
+ ' Try typing / and see the interactive dropdown!',
110
+ '',
111
+ chalk.gray('Press Ctrl+C at any time to cancel current operation')
112
+ ];
113
+
114
+ messages.forEach(msg => console.log(msg));
115
+ console.log('');
116
+ }
117
+
118
+ /**
119
+ * Main input loop with iFlow-style handling
120
+ */
121
+ private async runInputLoop(): Promise<void> {
122
+ while (true) {
123
+ try {
124
+ // Show prompt
125
+ if (this.options.showPrompt) {
126
+ this.showPrompt();
127
+ }
128
+
129
+ // Get user input
130
+ const input = await this.getInput();
131
+
132
+ if (!input) continue;
133
+
134
+ // Handle iFlow-style dropdown if enabled
135
+ if (this.options.enableDropdown && input.startsWith('/')) {
136
+ const result = await this.handleDropdown(input);
137
+ if (result) {
138
+ // Execute the selected command
139
+ await this.executeCommand(result.id);
140
+ }
141
+ continue;
142
+ }
143
+
144
+ // Regular command execution
145
+ await this.executeCommand(input);
146
+
147
+ } catch (error) {
148
+ if (error instanceof Error && error.message.includes('SIGINT')) {
149
+ console.log(chalk.yellow('\nUse /quit or Ctrl+D to exit'));
150
+ continue;
151
+ }
152
+ console.error(chalk.red(`Error: ${(error as Error).message}`));
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Show iFlow-style prompt
159
+ */
160
+ private showPrompt(): void {
161
+ const promptText = `${chalk.cyan('[NOVA]')} ${chalk.white('> ')}`;
162
+ process.stdout.write(promptText);
163
+ }
164
+
165
+ /**
166
+ * Get user input
167
+ */
168
+ private async getInput(): Promise<string> {
169
+ return new Promise((resolve) => {
170
+ const readline = require('node:readline');
171
+ const rl = readline.createInterface({
172
+ input: process.stdin,
173
+ output: process.stdout
174
+ });
175
+
176
+ rl.question('', (answer: string) => {
177
+ rl.close();
178
+ resolve(answer.trim());
179
+ });
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Handle iFlow-style dropdown
185
+ */
186
+ private async handleDropdown(input: string): Promise<any> {
187
+ if (!this.dropdown || !this.session) return null;
188
+
189
+ return await this.dropdown.show(input, this.session);
190
+ }
191
+
192
+ /**
193
+ * Execute a command
194
+ */
195
+ private async executeCommand(command: string): Promise<void> {
196
+ console.log(chalk.gray(`Executing: ${command}`));
197
+
198
+ // Simulate command execution delay
199
+ setTimeout(() => {
200
+ console.log(chalk.green(`✓ Command executed: ${command}`));
201
+ }, 300);
202
+ }
203
+
204
+ /**
205
+ * Cleanup resources
206
+ */
207
+ dispose(): void {
208
+ if (this.dropdown) {
209
+ this.dropdown.hide();
210
+ }
211
+ }
212
+ }