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.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- 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
|
+
}
|