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,429 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Error Enhancement - 增强的错误提示系统
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { CliUI, Colors, BoxChars } from './CliUI.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 增强的错误类型
|
|
9
|
+
*/
|
|
10
|
+
export enum ErrorType {
|
|
11
|
+
CONFIG = 'CONFIG',
|
|
12
|
+
AUTH = 'AUTH',
|
|
13
|
+
NETWORK = 'NETWORK',
|
|
14
|
+
FILE = 'FILE',
|
|
15
|
+
MODEL = 'MODEL',
|
|
16
|
+
TOOL = 'TOOL',
|
|
17
|
+
VALIDATION = 'VALIDATION',
|
|
18
|
+
PERMISSION = 'PERMISSION',
|
|
19
|
+
UNKNOWN = 'UNKNOWN',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 错误建议
|
|
24
|
+
*/
|
|
25
|
+
interface ErrorSuggestion {
|
|
26
|
+
command?: string;
|
|
27
|
+
description: string;
|
|
28
|
+
type: 'fix' | 'info' | 'warning';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 错误详情
|
|
33
|
+
*/
|
|
34
|
+
interface ErrorDetail {
|
|
35
|
+
type: ErrorType;
|
|
36
|
+
message: string;
|
|
37
|
+
suggestions: ErrorSuggestion[];
|
|
38
|
+
showHelpCommand?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 错误增强器
|
|
43
|
+
*/
|
|
44
|
+
export class ErrorEnhancer {
|
|
45
|
+
private static errorMap: Map<RegExp, ErrorDetail> = new Map([
|
|
46
|
+
// 认证错误
|
|
47
|
+
[
|
|
48
|
+
/api.?key|auth|credential/i,
|
|
49
|
+
{
|
|
50
|
+
type: ErrorType.AUTH,
|
|
51
|
+
message: 'Authentication failed',
|
|
52
|
+
suggestions: [
|
|
53
|
+
{
|
|
54
|
+
type: 'fix',
|
|
55
|
+
description: 'Check your API key configuration',
|
|
56
|
+
command: 'nova auth set <provider>',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'info',
|
|
60
|
+
description: 'Ensure your API key is valid and has sufficient permissions',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
// 配置错误
|
|
66
|
+
[
|
|
67
|
+
/config|yaml|json/i,
|
|
68
|
+
{
|
|
69
|
+
type: ErrorType.CONFIG,
|
|
70
|
+
message: 'Configuration error',
|
|
71
|
+
suggestions: [
|
|
72
|
+
{
|
|
73
|
+
type: 'fix',
|
|
74
|
+
description: 'Validate your configuration file',
|
|
75
|
+
command: 'nova config edit',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'info',
|
|
79
|
+
description: 'Check syntax and required fields',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
// 网络错误
|
|
85
|
+
[
|
|
86
|
+
/network|connection|timeout|econnrefused/i,
|
|
87
|
+
{
|
|
88
|
+
type: ErrorType.NETWORK,
|
|
89
|
+
message: 'Network error',
|
|
90
|
+
suggestions: [
|
|
91
|
+
{
|
|
92
|
+
type: 'fix',
|
|
93
|
+
description: 'Check your internet connection',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: 'info',
|
|
97
|
+
description: 'Try again later or use a different provider',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
// 文件错误
|
|
103
|
+
[
|
|
104
|
+
/file|directory|path|not found|enoent/i,
|
|
105
|
+
{
|
|
106
|
+
type: ErrorType.FILE,
|
|
107
|
+
message: 'File system error',
|
|
108
|
+
suggestions: [
|
|
109
|
+
{
|
|
110
|
+
type: 'fix',
|
|
111
|
+
description: 'Check file path and permissions',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'info',
|
|
115
|
+
description: 'Ensure the file or directory exists',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
// 模型错误
|
|
121
|
+
[
|
|
122
|
+
/model|llm|anthropic|openai|gpt|claude/i,
|
|
123
|
+
{
|
|
124
|
+
type: ErrorType.MODEL,
|
|
125
|
+
message: 'Model error',
|
|
126
|
+
suggestions: [
|
|
127
|
+
{
|
|
128
|
+
type: 'fix',
|
|
129
|
+
description: 'Check available models',
|
|
130
|
+
command: 'nova model list',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'info',
|
|
134
|
+
description: 'Verify model name and provider support',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
// 权限错误
|
|
140
|
+
[
|
|
141
|
+
/permission|access|denied|eacces/i,
|
|
142
|
+
{
|
|
143
|
+
type: ErrorType.PERMISSION,
|
|
144
|
+
message: 'Permission denied',
|
|
145
|
+
suggestions: [
|
|
146
|
+
{
|
|
147
|
+
type: 'fix',
|
|
148
|
+
description: 'Check file and directory permissions',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'info',
|
|
152
|
+
description: 'Run with appropriate permissions',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
]);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 分析错误并生成增强的错误信息
|
|
161
|
+
*/
|
|
162
|
+
static enhance(error: Error | string): ErrorDetail {
|
|
163
|
+
const message = error instanceof Error ? error.message : error;
|
|
164
|
+
|
|
165
|
+
// 查找匹配的错误模式
|
|
166
|
+
for (const [pattern, detail] of this.errorMap.entries()) {
|
|
167
|
+
if (pattern.test(message)) {
|
|
168
|
+
return {
|
|
169
|
+
...detail,
|
|
170
|
+
message: message,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 默认错误
|
|
176
|
+
return {
|
|
177
|
+
type: ErrorType.UNKNOWN,
|
|
178
|
+
message: message,
|
|
179
|
+
suggestions: [
|
|
180
|
+
{
|
|
181
|
+
type: 'info',
|
|
182
|
+
description: 'An unexpected error occurred',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: 'fix',
|
|
186
|
+
description: 'Check the logs for more details',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
showHelpCommand: 'nova --help',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 显示增强的错误信息
|
|
195
|
+
*/
|
|
196
|
+
static showError(error: Error | string, context?: string): void {
|
|
197
|
+
const detail = this.enhance(error);
|
|
198
|
+
const width = CliUI.getWidth(60, 100);
|
|
199
|
+
|
|
200
|
+
// 打印错误框
|
|
201
|
+
console.error('');
|
|
202
|
+
console.error(
|
|
203
|
+
`${Colors.error}${BoxChars.tl}${BoxChars.hThick.repeat(width - 2)}${BoxChars.tr}${Colors.reset}`
|
|
204
|
+
);
|
|
205
|
+
console.error(
|
|
206
|
+
`${Colors.error}${BoxChars.v}${Colors.reset} ${Colors.primary}Error: ${detail.message}${' '.repeat(Math.max(0, width - detail.message.length - 12))}${Colors.error}${BoxChars.v}${Colors.reset}`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (context) {
|
|
210
|
+
console.error(
|
|
211
|
+
`${Colors.error}${BoxChars.v}${Colors.reset} ${Colors.dim}Context: ${context}${' '.repeat(Math.max(0, width - context.length - 13))}${Colors.error}${BoxChars.v}${Colors.reset}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.error(
|
|
216
|
+
`${Colors.error}${BoxChars.v}${Colors.reset} ${Colors.dim}Type: ${detail.type}${' '.repeat(Math.max(0, width - detail.type.length - 11))}${Colors.error}${BoxChars.v}${Colors.reset}`
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
console.error(
|
|
220
|
+
`${Colors.error}${BoxChars.bl}${BoxChars.hThick.repeat(width - 2)}${BoxChars.br}${Colors.reset}`
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// 打印建议
|
|
224
|
+
if (detail.suggestions.length > 0) {
|
|
225
|
+
console.error('');
|
|
226
|
+
console.error(`${Colors.primary} Suggestions:${Colors.reset}`);
|
|
227
|
+
console.error('');
|
|
228
|
+
|
|
229
|
+
detail.suggestions.forEach((suggestion, index) => {
|
|
230
|
+
const icon = suggestion.type === 'fix' ? Colors.success + BoxChars.check : Colors.info + BoxChars.circle;
|
|
231
|
+
const prefix = `${icon} ${Colors.reset}`;
|
|
232
|
+
|
|
233
|
+
if (suggestion.command) {
|
|
234
|
+
console.error(` ${prefix}${Colors.info}${suggestion.command}${Colors.reset}`);
|
|
235
|
+
console.error(` ${Colors.dim}${suggestion.description}${Colors.reset}`);
|
|
236
|
+
} else {
|
|
237
|
+
console.error(` ${prefix}${suggestion.description}${Colors.reset}`);
|
|
238
|
+
}
|
|
239
|
+
console.error('');
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 打印帮助命令
|
|
244
|
+
if (detail.showHelpCommand) {
|
|
245
|
+
console.error(`${Colors.muted} Run ${Colors.info}${detail.showHelpCommand}${Colors.muted} for more information${Colors.reset}`);
|
|
246
|
+
console.error('');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 显示使用错误
|
|
252
|
+
*/
|
|
253
|
+
static showUsageError(command: string, error: string, correctUsage: string, examples?: string[]): void {
|
|
254
|
+
const width = CliUI.getWidth(60, 100);
|
|
255
|
+
|
|
256
|
+
console.error('');
|
|
257
|
+
console.error(
|
|
258
|
+
`${Colors.warning}${BoxChars.tl}${BoxChars.hThick.repeat(width - 2)}${BoxChars.tr}${Colors.reset}`
|
|
259
|
+
);
|
|
260
|
+
console.error(
|
|
261
|
+
`${Colors.warning}${BoxChars.v}${Colors.reset} ${Colors.primary}Usage Error${' '.repeat(width - 16)}${Colors.warning}${BoxChars.v}${Colors.reset}`
|
|
262
|
+
);
|
|
263
|
+
console.error(
|
|
264
|
+
`${Colors.warning}${BoxChars.v}${Colors.reset} ${Colors.dim}Command: ${command}${' '.repeat(width - command.length - 14)}${Colors.warning}${BoxChars.v}${Colors.reset}`
|
|
265
|
+
);
|
|
266
|
+
console.error(
|
|
267
|
+
`${Colors.warning}${BoxChars.bl}${BoxChars.hThick.repeat(width - 2)}${BoxChars.br}${Colors.reset}`
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
console.error('');
|
|
271
|
+
console.error(`${Colors.error} ${BoxChars.cross} ${error}${Colors.reset}`);
|
|
272
|
+
console.error('');
|
|
273
|
+
console.error(`${Colors.primary} Correct Usage:${Colors.reset}`);
|
|
274
|
+
console.error(`${Colors.muted} ${correctUsage}${Colors.reset}`);
|
|
275
|
+
|
|
276
|
+
if (examples && examples.length > 0) {
|
|
277
|
+
console.error('');
|
|
278
|
+
console.error(`${Colors.info} Examples:${Colors.reset}`);
|
|
279
|
+
examples.forEach((ex) => {
|
|
280
|
+
console.error(`${Colors.muted} ${ex}${Colors.reset}`);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.error('');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 添加自定义错误模式
|
|
289
|
+
*/
|
|
290
|
+
static addErrorPattern(pattern: RegExp, detail: ErrorDetail): void {
|
|
291
|
+
this.errorMap.set(pattern, detail);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 显示警告
|
|
296
|
+
*/
|
|
297
|
+
static showWarning(message: string, suggestion?: string): void {
|
|
298
|
+
console.warn('');
|
|
299
|
+
console.warn(`${Colors.warning} ${BoxChars.diamond} ${message}${Colors.reset}`);
|
|
300
|
+
if (suggestion) {
|
|
301
|
+
console.warn(` ${Colors.dim}${suggestion}${Colors.reset}`);
|
|
302
|
+
}
|
|
303
|
+
console.warn('');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 显示成功消息
|
|
308
|
+
*/
|
|
309
|
+
static showSuccess(message: string): void {
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(`${Colors.success} ${BoxChars.check} ${message}${Colors.reset}`);
|
|
312
|
+
console.log('');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 显示信息消息
|
|
317
|
+
*/
|
|
318
|
+
static showInfo(message: string): void {
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log(`${Colors.info} ${BoxChars.circle} ${message}${Colors.reset}`);
|
|
321
|
+
console.log('');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 进度提示
|
|
327
|
+
*/
|
|
328
|
+
export class TaskProgressIndicator {
|
|
329
|
+
private static startTime: number = 0;
|
|
330
|
+
private static message: string = '';
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 开始任务
|
|
334
|
+
*/
|
|
335
|
+
static start(message: string): void {
|
|
336
|
+
this.startTime = Date.now();
|
|
337
|
+
this.message = message;
|
|
338
|
+
console.log('');
|
|
339
|
+
console.log(`${Colors.info} ${BoxChars.spinner[0]} ${message}${Colors.reset}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* 更新进度
|
|
344
|
+
*/
|
|
345
|
+
static update(message?: string): void {
|
|
346
|
+
if (message) {
|
|
347
|
+
this.message = message;
|
|
348
|
+
}
|
|
349
|
+
// 在实际使用中,这里会显示旋转的spinner
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 完成任务
|
|
354
|
+
*/
|
|
355
|
+
static complete(message?: string): void {
|
|
356
|
+
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(2);
|
|
357
|
+
const msg = message || this.message;
|
|
358
|
+
console.log(`${Colors.success} ${BoxChars.check} ${msg} ${Colors.dim}(${elapsed}s)${Colors.reset}`);
|
|
359
|
+
console.log('');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 任务失败
|
|
364
|
+
*/
|
|
365
|
+
static fail(error: Error | string): void {
|
|
366
|
+
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(2);
|
|
367
|
+
const errorMsg = error instanceof Error ? error.message : error;
|
|
368
|
+
console.log(`${Colors.error} ${BoxChars.cross} ${this.message} ${Colors.dim}(${elapsed}s)${Colors.reset}`);
|
|
369
|
+
console.log(` ${Colors.dim}${errorMsg}${Colors.reset}`);
|
|
370
|
+
console.log('');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 表格输出
|
|
376
|
+
*/
|
|
377
|
+
export class TableRenderer {
|
|
378
|
+
/**
|
|
379
|
+
* 渲染表格
|
|
380
|
+
*/
|
|
381
|
+
static render(headers: string[], rows: (string | number)[][]): void {
|
|
382
|
+
const colWidths = headers.map((h, i) => {
|
|
383
|
+
const maxWidth = Math.max(
|
|
384
|
+
h.length,
|
|
385
|
+
...rows.map(r => String(r[i] || '').length)
|
|
386
|
+
);
|
|
387
|
+
return maxWidth + 2;
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// 渲染表头
|
|
391
|
+
console.log('');
|
|
392
|
+
const headerRow = headers.map((h, i) =>
|
|
393
|
+
`${Colors.primary}${h.padEnd(colWidths[i])}${Colors.reset}`
|
|
394
|
+
).join('');
|
|
395
|
+
console.log(headerRow);
|
|
396
|
+
|
|
397
|
+
// 渲染分隔线
|
|
398
|
+
const separator = colWidths.map(w =>
|
|
399
|
+
Colors.dim + BoxChars.h.repeat(w - 1) + BoxChars.ht
|
|
400
|
+
).join('') + BoxChars.h;
|
|
401
|
+
console.log(separator);
|
|
402
|
+
|
|
403
|
+
// 渲染数据行
|
|
404
|
+
rows.forEach(row => {
|
|
405
|
+
const cells = row.map((cell, i) =>
|
|
406
|
+
`${Colors.reset}${String(cell).padEnd(colWidths[i])}`
|
|
407
|
+
).join('');
|
|
408
|
+
console.log(cells);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
console.log('');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* 渲染键值对列表
|
|
416
|
+
*/
|
|
417
|
+
static renderKeyValue(pairs: Record<string, string | number>): void {
|
|
418
|
+
const maxLength = Math.max(...Object.keys(pairs).map(k => k.length));
|
|
419
|
+
const width = maxLength + 4;
|
|
420
|
+
|
|
421
|
+
console.log('');
|
|
422
|
+
Object.entries(pairs).forEach(([key, value]) => {
|
|
423
|
+
const paddedKey = Colors.primary + key + ':'.padEnd(width) + Colors.reset;
|
|
424
|
+
const paddedValue = Colors.muted + String(value) + Colors.reset;
|
|
425
|
+
console.log(` ${paddedKey}${paddedValue}`);
|
|
426
|
+
});
|
|
427
|
+
console.log('');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Output Formatter - Structured output for Agent consumption
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Output format mode
|
|
7
|
+
*/
|
|
8
|
+
export type OutputFormat = 'text' | 'json';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Global output configuration
|
|
12
|
+
*/
|
|
13
|
+
export interface OutputConfig {
|
|
14
|
+
format: OutputFormat;
|
|
15
|
+
noColor: boolean;
|
|
16
|
+
compact: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Global output formatter instance
|
|
21
|
+
*/
|
|
22
|
+
let globalConfig: OutputConfig = {
|
|
23
|
+
format: 'text',
|
|
24
|
+
noColor: false,
|
|
25
|
+
compact: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize output formatter with CLI args
|
|
30
|
+
*/
|
|
31
|
+
export function initOutputFormatter(config: Partial<OutputConfig>): void {
|
|
32
|
+
globalConfig = { ...globalConfig, ...config };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get current output config
|
|
37
|
+
*/
|
|
38
|
+
export function getOutputConfig(): OutputConfig {
|
|
39
|
+
return { ...globalConfig };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if JSON mode is enabled
|
|
44
|
+
*/
|
|
45
|
+
export function isJsonMode(): boolean {
|
|
46
|
+
return globalConfig.format === 'json';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if colors should be disabled
|
|
51
|
+
*/
|
|
52
|
+
export function shouldDisableColors(): boolean {
|
|
53
|
+
return globalConfig.noColor || isJsonMode() || !process.stdout.isTTY;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Format a single item for output
|
|
58
|
+
*/
|
|
59
|
+
export function formatOutput<T>(data: T): string {
|
|
60
|
+
if (isJsonMode()) {
|
|
61
|
+
return JSON.stringify(data, null, globalConfig.compact ? 0 : 2);
|
|
62
|
+
}
|
|
63
|
+
return String(data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Format a list of items for output
|
|
68
|
+
*/
|
|
69
|
+
export function formatList<T>(
|
|
70
|
+
items: T[],
|
|
71
|
+
formatter: (item: T) => Record<string, unknown>
|
|
72
|
+
): string {
|
|
73
|
+
if (isJsonMode()) {
|
|
74
|
+
return JSON.stringify(items.map(formatter), null, globalConfig.compact ? 0 : 2);
|
|
75
|
+
}
|
|
76
|
+
return items.map(item => formatOutput(formatter(item))).join('\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format an error for output
|
|
81
|
+
*/
|
|
82
|
+
export function formatError(error: {
|
|
83
|
+
type: string;
|
|
84
|
+
message: string;
|
|
85
|
+
code?: string;
|
|
86
|
+
suggestions?: Array<{ description: string; command?: string }>;
|
|
87
|
+
context?: string;
|
|
88
|
+
}): string {
|
|
89
|
+
if (isJsonMode()) {
|
|
90
|
+
return JSON.stringify({
|
|
91
|
+
error: true,
|
|
92
|
+
type: error.type,
|
|
93
|
+
code: error.code,
|
|
94
|
+
message: error.message,
|
|
95
|
+
context: error.context,
|
|
96
|
+
suggestions: error.suggestions,
|
|
97
|
+
}, null, globalConfig.compact ? 0 : 2);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Text format
|
|
101
|
+
const lines: string[] = [];
|
|
102
|
+
lines.push(`\x1b[31m╭──────────────────────────────────────────────────────────────────╮\x1b[0m`);
|
|
103
|
+
lines.push(`\x1b[31m│\x1b[0m Error: ${error.message.slice(0, 55).padEnd(55)}\x1b[31m│\x1b[0m`);
|
|
104
|
+
if (error.context) {
|
|
105
|
+
lines.push(`\x1b[31m│\x1b[0m Context: ${error.context.slice(0, 52).padEnd(52)}\x1b[31m│\x1b[0m`);
|
|
106
|
+
}
|
|
107
|
+
lines.push(`\x1b[31m╰──────────────────────────────────────────────────────────────────╯\x1b[0m`);
|
|
108
|
+
|
|
109
|
+
if (error.suggestions && error.suggestions.length > 0) {
|
|
110
|
+
lines.push('');
|
|
111
|
+
lines.push(' Suggestions:');
|
|
112
|
+
error.suggestions.forEach(s => {
|
|
113
|
+
if (s.command) {
|
|
114
|
+
lines.push(` \x1b[32m✓\x1b[0m ${s.command}`);
|
|
115
|
+
lines.push(` ${s.description}`);
|
|
116
|
+
} else {
|
|
117
|
+
lines.push(` • ${s.description}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Format a success result for output
|
|
127
|
+
*/
|
|
128
|
+
export function formatSuccess(data: {
|
|
129
|
+
message: string;
|
|
130
|
+
details?: Record<string, unknown>;
|
|
131
|
+
}): string {
|
|
132
|
+
if (isJsonMode()) {
|
|
133
|
+
return JSON.stringify({
|
|
134
|
+
success: true,
|
|
135
|
+
message: data.message,
|
|
136
|
+
details: data.details,
|
|
137
|
+
}, null, globalConfig.compact ? 0 : 2);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lines: string[] = [];
|
|
141
|
+
lines.push(`\x1b[32m ✓ ${data.message}\x1b[0m`);
|
|
142
|
+
if (data.details) {
|
|
143
|
+
Object.entries(data.details).forEach(([key, value]) => {
|
|
144
|
+
lines.push(` ${key}: ${value}`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return lines.join('\n');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Format a list result with truncation hint
|
|
152
|
+
*/
|
|
153
|
+
export function formatListWithTruncation<T>(
|
|
154
|
+
items: T[],
|
|
155
|
+
total: number,
|
|
156
|
+
formatter: (item: T) => Record<string, unknown>,
|
|
157
|
+
hint?: string
|
|
158
|
+
): string {
|
|
159
|
+
const showing = items.length;
|
|
160
|
+
const hasMore = showing < total;
|
|
161
|
+
|
|
162
|
+
if (isJsonMode()) {
|
|
163
|
+
return JSON.stringify({
|
|
164
|
+
items: items.map(formatter),
|
|
165
|
+
pagination: {
|
|
166
|
+
showing,
|
|
167
|
+
total,
|
|
168
|
+
hasMore,
|
|
169
|
+
hint: hasMore ? hint : undefined,
|
|
170
|
+
},
|
|
171
|
+
}, null, globalConfig.compact ? 0 : 2);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const lines: string[] = items.map(item => formatOutput(formatter(item)));
|
|
175
|
+
|
|
176
|
+
if (hasMore) {
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push(` Showing ${showing} of ${total} items.`);
|
|
179
|
+
if (hint) {
|
|
180
|
+
lines.push(` Hint: ${hint}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return lines.join('\n');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Strip ANSI codes from string (for JSON mode)
|
|
189
|
+
*/
|
|
190
|
+
export function stripAnsi(str: string): string {
|
|
191
|
+
// eslint-disable-next-line no-control-regex
|
|
192
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
193
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Utils Index - 统一导出所有工具函数
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export * from './CliUI.js';
|
|
6
|
+
export * from './ErrorEnhancer.js';
|
|
7
|
+
export * from './CompletionHelper.js';
|
|
8
|
+
export * from './OutputFormatter.js';
|
|
9
|
+
|