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,231 @@
1
+ // ============================================================================
2
+ // SimpleErrorPanel - Basic error display component for Nova CLI
3
+ // ============================================================================
4
+
5
+ export interface ErrorPanelOptions {
6
+ showStack?: boolean;
7
+ compact?: boolean;
8
+ }
9
+
10
+ export class SimpleErrorPanel {
11
+ private options: ErrorPanelOptions = {};
12
+
13
+ constructor(options: ErrorPanelOptions = {}) {
14
+ this.options = {
15
+ showStack: false,
16
+ compact: false,
17
+ ...options
18
+ };
19
+ }
20
+
21
+ display(error: Error | string, context?: any): void {
22
+ const errorInfo = this.parseError(error);
23
+
24
+ if (this.options.compact) {
25
+ this.displayCompact(errorInfo);
26
+ } else {
27
+ this.displayFull(errorInfo, context);
28
+ }
29
+ }
30
+
31
+ private parseError(error: Error | string): ErrorInfo {
32
+ const errorObj = typeof error === 'string' ? new Error(error) : error;
33
+
34
+ // Simple error categorization based on message content
35
+ let category: ErrorCategory = 'unknown';
36
+ let code: string | undefined;
37
+ let suggestion: string[] = [];
38
+
39
+ if (errorObj.message.includes('API key') ||
40
+ errorObj.message.includes('authentication')) {
41
+ category = 'auth';
42
+ code = 'AUTH_ERROR';
43
+ suggestion = ['Check your API key configuration', 'Verify provider settings'];
44
+ } else if (errorObj.message.includes('network') ||
45
+ errorObj.message.includes('connection')) {
46
+ category = 'network';
47
+ code = 'NETWORK_ERROR';
48
+ suggestion = ['Check internet connection', 'Try again later'];
49
+ } else if (errorObj.message.includes('config') ||
50
+ errorObj.message.includes('yaml')) {
51
+ category = 'config';
52
+ code = 'CONFIG_ERROR';
53
+ suggestion = ['Check config file syntax', 'Run nova config edit to fix'];
54
+ } else if (errorObj.message.includes('timeout')) {
55
+ category = 'timeout';
56
+ code = 'TIMEOUT_ERROR';
57
+ suggestion = ['Request timed out', 'Try with a simpler request'];
58
+ } else if (errorObj.message.includes('rate limit') ||
59
+ errorObj.message.includes('too many requests')) {
60
+ category = 'validation';
61
+ code = 'RATE_LIMIT_EXCEEDED';
62
+ suggestion = ['Wait a few minutes', 'Consider upgrading your plan'];
63
+ } else {
64
+ category = 'unknown';
65
+ code = 'UNKNOWN_ERROR';
66
+ suggestion = ['Check the Nova CLI documentation', 'Contact support if issue persists'];
67
+ }
68
+
69
+ return {
70
+ name: errorObj.name,
71
+ message: errorObj.message,
72
+ code,
73
+ category,
74
+ suggestion,
75
+ stack: this.options.showStack ? errorObj.stack : undefined
76
+ };
77
+ }
78
+
79
+ private displayCompact(errorInfo: ErrorInfo): void {
80
+ const icon = this.getIcon(errorInfo.category);
81
+ const color = this.getColor(errorInfo.category);
82
+
83
+ console.log(
84
+ `${color}${icon} ${errorInfo.message}`
85
+ );
86
+ }
87
+
88
+ private displayFull(errorInfo: ErrorInfo, context?: any): void {
89
+ console.log('\n');
90
+ console.log('\x1b[41m\x1b[37m ERROR \x1b[0m');
91
+ console.log('\x1b[31m' + '-'.repeat(50) + '\x1b[0m');
92
+
93
+ // Error header with category
94
+ const categoryLabel = this.formatCategory(errorInfo.category);
95
+ const codeLabel = errorInfo.code ? ` [${errorInfo.code}]` : '';
96
+
97
+ console.log(`\x1b[31m ${categoryLabel}${codeLabel}\x1b[0m`);
98
+
99
+ // Error message
100
+ console.log('');
101
+ console.log('\x1b[32mMessage:\x1b[0m');
102
+ console.log(`\x1b[33m ${errorInfo.message}\x1b[0m`);
103
+
104
+ // Context info
105
+ if (context) {
106
+ console.log('');
107
+ console.log('\x1b[32mContext:\x1b[0m');
108
+ if (typeof context === 'object') {
109
+ Object.entries(context).forEach(([key, value]) => {
110
+ console.log(`\x1b[90m ${key}: ${String(value)}\x1b[0m`);
111
+ });
112
+ } else {
113
+ console.log(`\x1b[90m ${context}\x1b[0m`);
114
+ }
115
+ }
116
+
117
+ // Suggestions
118
+ if (errorInfo.suggestion.length > 0) {
119
+ console.log('');
120
+ console.log('\x1b[32mSuggested actions:\x1b[0m');
121
+ errorInfo.suggestion.forEach((s, i) => {
122
+ console.log(`\x1b[32m ${i + 1}. ${s}\x1b[0m`);
123
+ });
124
+
125
+ // Quick commands
126
+ console.log('');
127
+ console.log('\x1b[36mQuick commands:\x1b[0m');
128
+ switch (errorInfo.category) {
129
+ case 'auth':
130
+ console.log('\x1b[36m • nova auth set <provider>\x1b[0m');
131
+ console.log('\x1b[36m • Check environment variables\x1b[0m');
132
+ break;
133
+ case 'config':
134
+ console.log('\x1b[36m • nova config edit\x1b[0m');
135
+ console.log('\x1b[36m • Check ~/.nova/config.yaml\x1b[0m');
136
+ break;
137
+ case 'network':
138
+ console.log('\x1b[36m • Check internet connection\x1b[0m');
139
+ console.log('\x1b[36m • Try nova ollama status\x1b[0m');
140
+ break;
141
+ case 'model':
142
+ console.log('\x1b[36m • nova model list\x1b[0m');
143
+ console.log('\x1b[36m • Check provider configuration\x1b[0m');
144
+ break;
145
+ }
146
+ }
147
+
148
+ // Stack trace (if enabled and not too long)
149
+ if (errorInfo.stack && this.options.showStack && errorInfo.stack.split('\n').length <= 20) {
150
+ console.log('');
151
+ console.log('\x1b[32mStack trace:\x1b[0m');
152
+ console.log(`\x1b[90m${errorInfo.stack}\x1b[0m`);
153
+ } else if (errorInfo.stack) {
154
+ console.log('');
155
+ console.log('\x1b[90m(Stack trace truncated. Use --verbose for full trace.)\x1b[0m');
156
+ }
157
+
158
+ console.log('');
159
+ console.log('\x1b[31m' + '-'.repeat(50) + '\x1b[0m');
160
+ console.log('');
161
+ }
162
+
163
+ private getIcon(category: ErrorCategory): string {
164
+ const icons = {
165
+ auth: '🔑',
166
+ config: '⚙️',
167
+ network: '🌐',
168
+ model: '🤖',
169
+ file: '📁',
170
+ execution: '⚡',
171
+ validation: '⚠️',
172
+ permission: '🚫',
173
+ timeout: '⏰',
174
+ quota: '💰',
175
+ unknown: '❌'
176
+ };
177
+
178
+ return icons[category] || icons.unknown;
179
+ }
180
+
181
+ private getColor(category: ErrorCategory): string {
182
+ const colors = {
183
+ auth: '\x1b[31m',
184
+ config: '\x1b[33m',
185
+ network: '\x1b[34m',
186
+ model: '\x1b[36m',
187
+ file: '\x1b[32m',
188
+ execution: '\x1b[35m',
189
+ validation: '\x1b[33m',
190
+ permission: '\x1b[31m',
191
+ timeout: '\x1b[33m',
192
+ quota: '\x1b[31m',
193
+ unknown: '\x1b[31m'
194
+ };
195
+
196
+ return colors[category] || colors.unknown;
197
+ }
198
+
199
+ private formatCategory(category: ErrorCategory): string {
200
+ const labels = {
201
+ auth: 'Authentication Error',
202
+ config: 'Configuration Error',
203
+ network: 'Network Error',
204
+ model: 'Model Error',
205
+ file: 'File System Error',
206
+ execution: 'Execution Error',
207
+ validation: 'Validation Error',
208
+ permission: 'Permission Error',
209
+ timeout: 'Timeout Error',
210
+ quota: 'Quota Exceeded',
211
+ unknown: 'Unknown Error'
212
+ };
213
+
214
+ return labels[category] || labels.unknown;
215
+ }
216
+ }
217
+
218
+ // Type definitions
219
+ interface ErrorInfo {
220
+ name: string;
221
+ message: string;
222
+ code?: string;
223
+ category: ErrorCategory;
224
+ suggestion: string[];
225
+ stack?: string;
226
+ }
227
+
228
+ type ErrorCategory =
229
+ | 'auth' | 'config' | 'network' | 'model' | 'file'
230
+ | 'execution' | 'validation' | 'permission'
231
+ | 'timeout' | 'quota' | 'unknown';
@@ -0,0 +1,194 @@
1
+ // ============================================================================
2
+ // StatusBar - Modern status bar component for Nova CLI REPL
3
+ // ============================================================================
4
+
5
+ import chalk from 'chalk';
6
+ import type { SessionInfo, NovaConfig } from '../../../../core/types/config.js';
7
+
8
+ export interface StatusBarOptions {
9
+ showTokens?: boolean;
10
+ showMode?: boolean;
11
+ showProvider?: boolean;
12
+ compact?: boolean;
13
+ }
14
+
15
+ export class StatusBar {
16
+ private session: SessionInfo | null = null;
17
+ private config: NovaConfig | null = null;
18
+ private options: StatusBarOptions = {};
19
+
20
+ constructor(options: StatusBarOptions = {}) {
21
+ this.options = {
22
+ showTokens: true,
23
+ showMode: true,
24
+ showProvider: true,
25
+ compact: false,
26
+ ...options
27
+ };
28
+ }
29
+
30
+ update(session: SessionInfo, config: NovaConfig): void {
31
+ this.session = session;
32
+ this.config = config;
33
+ }
34
+
35
+ render(): string {
36
+ if (!this.session || !this.config) {
37
+ return '';
38
+ }
39
+
40
+ const parts: string[] = [];
41
+
42
+ // Compact mode
43
+ if (this.options.compact) {
44
+ return this.renderCompact();
45
+ }
46
+
47
+ // Full mode
48
+ return this.renderFull();
49
+ }
50
+
51
+ private renderCompact(): string {
52
+ const session = this.session!;
53
+ const config = this.config!;
54
+
55
+ // [NOVA] model • turns • tokens • mode
56
+ const modelShort = session.model.split('/').pop() || session.model;
57
+ const turnCount = session.turnCount || 0;
58
+ const tokenCount = session.totalInputTokens + session.totalOutputTokens;
59
+
60
+ return chalk.dim(
61
+ `[${chalk.bold('NOVA')}] ${chalk.cyan(modelShort)} • ` +
62
+ `${chalk.yellow(turnCount)} turns • ` +
63
+ `${chalk.green(tokenCount.toLocaleString())} tok • ` +
64
+ `${chalk[session.mode === 'auto' ? 'green' : session.mode === 'plan' ? 'yellow' : 'blue'](session.mode.toUpperCase())}`
65
+ );
66
+ }
67
+
68
+ private renderFull(): string {
69
+ const session = this.session!;
70
+ const config = this.config!;
71
+
72
+ // Top border
73
+ const width = Math.min(process.stdout.columns || 80, 120);
74
+ const border = '─'.repeat(width);
75
+
76
+ // Main content
77
+ const lines: string[] = [];
78
+
79
+ // Header
80
+ lines.push(chalk.bgBlue.black(` NOVA CLI `));
81
+ lines.push(chalk.dim(border));
82
+
83
+ // Model info
84
+ const modelShort = session.model.split('/').pop() || session.model;
85
+ const provider = session.model.includes('/') ? session.model.split('/')[0] : 'local';
86
+ const modelDisplay = `${provider}/${modelShort}`;
87
+
88
+ lines.push(`${chalk.white('Model')}`.padEnd(15) + chalk.cyan(modelDisplay));
89
+ lines.push(`${chalk.white('Session')}`.padEnd(15) + chalk.gray(session.id.slice(0, 8)));
90
+
91
+ // Stats
92
+ if (this.options.showTokens) {
93
+ const totalTokens = session.totalInputTokens + session.totalOutputTokens;
94
+ const inputTokens = session.totalInputTokens || 0;
95
+ const outputTokens = session.totalOutputTokens || 0;
96
+
97
+ lines.push(`${chalk.white('Tokens')}`.padEnd(15) +
98
+ `${chalk.green(inputTokens.toLocaleString())} in / ${chalk.blue(outputTokens.toLocaleString())} out`);
99
+ }
100
+
101
+ lines.push(`${chalk.white('Turns')}`.padEnd(15) + chalk.yellow((session.turnCount || 0).toString()));
102
+
103
+ // Mode
104
+ if (this.options.showMode) {
105
+ const modeColors = {
106
+ auto: 'green',
107
+ plan: 'yellow',
108
+ ask: 'blue'
109
+ };
110
+ const modeColor = modeColors[session.mode as keyof typeof modeColors] || 'gray';
111
+ lines.push(`${chalk.white('Mode')}`.padEnd(15) + chalk[modeColor](session.mode.toUpperCase()));
112
+ }
113
+
114
+ // Working directory
115
+ const cwd = session.workingDirectory || process.cwd();
116
+ const shortCwd = cwd.length > 40 ? '...' + cwd.slice(-37) : cwd;
117
+ lines.push(`${chalk.white('Dir')}`.padEnd(15) + chalk.gray(shortCwd));
118
+
119
+ // Bottom border
120
+ lines.push(chalk.dim(border));
121
+
122
+ return lines.join('\n');
123
+ }
124
+
125
+ // Quick access methods
126
+ getTokenUsage(): { input: number; output: number; total: number } {
127
+ const session = this.session!;
128
+ const input = session.totalInputTokens || 0;
129
+ const output = session.totalOutputTokens || 0;
130
+ return { input, output, total: input + output };
131
+ }
132
+
133
+ getSessionStats(): { turnCount: number; messageCount: number; duration: string } {
134
+ const session = this.session!;
135
+ const duration = this.formatDuration(Date.now() - (session.createdAt || Date.now()));
136
+
137
+ return {
138
+ turnCount: session.turnCount || 0,
139
+ messageCount: session.messageCount || 0,
140
+ duration
141
+ };
142
+ }
143
+
144
+ private formatDuration(ms: number): string {
145
+ const seconds = Math.floor(ms / 1000);
146
+ const minutes = Math.floor(seconds / 60);
147
+ const hours = Math.floor(minutes / 60);
148
+
149
+ if (hours > 0) {
150
+ return `${hours}h ${minutes % 60}m`;
151
+ } else if (minutes > 0) {
152
+ return `${minutes}m ${seconds % 60}s`;
153
+ } else {
154
+ return `${seconds}s`;
155
+ }
156
+ }
157
+
158
+ // Interactive methods
159
+ async showQuickMenu(): Promise<string> {
160
+ console.log('\n' + this.render());
161
+ console.log(chalk.dim('Press:'));
162
+ console.log(` ${chalk.green('t')} Toggle tokens display`);
163
+ console.log(` ${chalk.yellow('m')} Switch mode (AUTO↔PLAN↔ASK)`);
164
+ console.log(` ${chalk.blue('h')} Show/hide this help`);
165
+ console.log(` ${chalk.gray('q')} Continue to input\n`);
166
+
167
+ // Simulate menu for testing - in real usage this would use actual input
168
+ console.log('Simulating menu interaction...');
169
+ return 't'; // Return simulated input
170
+ }
171
+
172
+ private showHelp(): void {
173
+ console.log('\n' + this.render());
174
+ console.log(chalk.bgWhite.black(' STATUS BAR HELP '));
175
+ console.log(chalk.dim('-'.repeat(50)));
176
+ console.log(chalk.white('Left side:') + ' Current model and session ID');
177
+ console.log(chalk.white('Center:') + ' Conversation statistics (turns, tokens)');
178
+ console.log(chalk.white('Right:') + ' Current mode and working directory');
179
+ console.log('');
180
+ console.log(chalk.yellow('Quick actions:'));
181
+ console.log(' /status - Detailed session information');
182
+ console.log(' /mode - Change interaction mode');
183
+ console.log(' /compress - Optimize context window');
184
+ console.log(' /history - Manage previous sessions');
185
+ console.log('');
186
+ console.log(chalk.dim('Press any key to continue...'));
187
+ process.stdin.setRawMode(true);
188
+ process.stdin.resume();
189
+ process.stdin.once('data', () => {
190
+ process.stdin.setRawMode(false);
191
+ process.stdin.pause();
192
+ });
193
+ }
194
+ }