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,215 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// SimpleSelector2 - Simplified interactive selection for NOVA CLI
|
|
3
|
+
// Provides iFlow CLI-like interactive selection experience
|
|
4
|
+
// Windows-compatible: uses ANSI cursor movement instead of clearScreenDown
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
export interface SelectItem {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Simple interactive selection using arrow keys.
|
|
17
|
+
* Uses ANSI escape codes for cursor movement (Windows-compatible).
|
|
18
|
+
*/
|
|
19
|
+
export async function selectInteractive(
|
|
20
|
+
items: SelectItem[],
|
|
21
|
+
title: string = 'Select an option'
|
|
22
|
+
): Promise<string | null> {
|
|
23
|
+
|
|
24
|
+
if (items.length === 0) {
|
|
25
|
+
console.log(chalk.dim(' No items available'));
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let selectedIndex = 0;
|
|
30
|
+
const pageSize = 10;
|
|
31
|
+
let renderedLineCount = 0; // Track how many lines were rendered
|
|
32
|
+
|
|
33
|
+
// Function to render the selection menu
|
|
34
|
+
const render = (isFirst: boolean) => {
|
|
35
|
+
if (isFirst) {
|
|
36
|
+
// First render: just draw from current cursor position
|
|
37
|
+
doRender();
|
|
38
|
+
} else {
|
|
39
|
+
// Subsequent renders: move cursor up to redraw in place
|
|
40
|
+
// Move cursor up by renderedLineCount lines
|
|
41
|
+
if (renderedLineCount > 0) {
|
|
42
|
+
process.stdout.write(`\x1b[${renderedLineCount}A`);
|
|
43
|
+
}
|
|
44
|
+
// Now clear and redraw each line
|
|
45
|
+
doRender();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const doRender = () => {
|
|
50
|
+
// Calculate pagination
|
|
51
|
+
const startIndex = Math.floor(selectedIndex / pageSize) * pageSize;
|
|
52
|
+
const endIndex = Math.min(startIndex + pageSize, items.length);
|
|
53
|
+
|
|
54
|
+
// Count lines we're about to render
|
|
55
|
+
let lineCount = 0;
|
|
56
|
+
|
|
57
|
+
// Title (2 lines: title + hint)
|
|
58
|
+
process.stdout.write('\x1b[2K\r' + chalk.hex('#7C3AED').bold(` ${title}`) + '\n');
|
|
59
|
+
lineCount++;
|
|
60
|
+
process.stdout.write('\x1b[2K\r' + chalk.dim(' \u2191\u2193 to navigate, Enter to select, Esc to cancel') + '\n');
|
|
61
|
+
lineCount++;
|
|
62
|
+
process.stdout.write('\x1b[2K\r');
|
|
63
|
+
process.stdout.write('\n');
|
|
64
|
+
lineCount++;
|
|
65
|
+
|
|
66
|
+
// Display items
|
|
67
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
68
|
+
process.stdout.write('\x1b[2K\r');
|
|
69
|
+
const item = items[i];
|
|
70
|
+
const isSelected = i === selectedIndex;
|
|
71
|
+
const prefix = isSelected ? chalk.hex('#7C3AED')('> ') : ' ';
|
|
72
|
+
const label = isSelected ? chalk.hex('#7C3AED').bold(item.label) : chalk.white(item.label);
|
|
73
|
+
|
|
74
|
+
let line = `${prefix}${label}`;
|
|
75
|
+
|
|
76
|
+
if (item.description) {
|
|
77
|
+
line += chalk.dim(` - ${item.description}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.stdout.write(line + '\n');
|
|
81
|
+
lineCount++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Pagination info (1-2 lines)
|
|
85
|
+
if (items.length > pageSize) {
|
|
86
|
+
const currentPage = Math.floor(selectedIndex / pageSize) + 1;
|
|
87
|
+
const totalPages = Math.ceil(items.length / pageSize);
|
|
88
|
+
process.stdout.write('\x1b[2K\r');
|
|
89
|
+
process.stdout.write('\n');
|
|
90
|
+
process.stdout.write('\x1b[2K\r' + chalk.dim(` Page ${currentPage}/${totalPages} (${items.length} items)`));
|
|
91
|
+
process.stdout.write('\n');
|
|
92
|
+
lineCount += 2;
|
|
93
|
+
} else {
|
|
94
|
+
process.stdout.write('\x1b[2K\r');
|
|
95
|
+
process.stdout.write('\n');
|
|
96
|
+
process.stdout.write('\x1b[2K\r' + chalk.dim(` ${items.length} items`));
|
|
97
|
+
process.stdout.write('\n');
|
|
98
|
+
lineCount += 2;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
renderedLineCount = lineCount;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Initial render
|
|
105
|
+
render(true);
|
|
106
|
+
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
// Set raw mode for keypress handling
|
|
109
|
+
const wasRaw = process.stdin.isRaw;
|
|
110
|
+
process.stdin.setRawMode(true);
|
|
111
|
+
process.stdin.resume();
|
|
112
|
+
process.stdin.setEncoding('utf8');
|
|
113
|
+
|
|
114
|
+
const onData = (data: Buffer) => {
|
|
115
|
+
const str = data.toString();
|
|
116
|
+
|
|
117
|
+
// Handle arrow keys
|
|
118
|
+
if (str === '\u001b[A') { // Up arrow
|
|
119
|
+
if (selectedIndex > 0) {
|
|
120
|
+
selectedIndex--;
|
|
121
|
+
render(false);
|
|
122
|
+
}
|
|
123
|
+
} else if (str === '\u001b[B') { // Down arrow
|
|
124
|
+
if (selectedIndex < items.length - 1) {
|
|
125
|
+
selectedIndex++;
|
|
126
|
+
render(false);
|
|
127
|
+
}
|
|
128
|
+
} else if (str === '\r' || str === '\n') { // Enter
|
|
129
|
+
cleanup();
|
|
130
|
+
const selectedItem = items[selectedIndex];
|
|
131
|
+
resolve(selectedItem ? selectedItem.value : null);
|
|
132
|
+
} else if (str === '\u001b' || str === '\u0003') { // Escape or Ctrl+C
|
|
133
|
+
cleanup();
|
|
134
|
+
resolve(null);
|
|
135
|
+
}
|
|
136
|
+
// Ignore other keys
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const cleanup = () => {
|
|
140
|
+
process.stdin.removeListener('data', onData);
|
|
141
|
+
process.stdin.setRawMode(wasRaw);
|
|
142
|
+
process.stdin.pause();
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
process.stdin.on('data', onData);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Model selection helper with provider grouping.
|
|
151
|
+
* Only includes models from configured/available providers.
|
|
152
|
+
*/
|
|
153
|
+
export async function selectModelInteractive(
|
|
154
|
+
models: Array<{ provider: string; model: string; description?: string; configured?: boolean }>
|
|
155
|
+
): Promise<string | null> {
|
|
156
|
+
const items: SelectItem[] = [];
|
|
157
|
+
|
|
158
|
+
// Group by provider for better organization
|
|
159
|
+
const providers = new Map<string, Array<{ model: string; description?: string }>>();
|
|
160
|
+
|
|
161
|
+
for (const m of models) {
|
|
162
|
+
if (!providers.has(m.provider)) {
|
|
163
|
+
providers.set(m.provider, []);
|
|
164
|
+
}
|
|
165
|
+
providers.get(m.provider)!.push({ model: m.model, description: m.description });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Create items with provider headers
|
|
169
|
+
for (const [provider, providerModels] of providers) {
|
|
170
|
+
// Provider header
|
|
171
|
+
items.push({
|
|
172
|
+
label: `${provider.toUpperCase()}`,
|
|
173
|
+
value: `provider:${provider}`,
|
|
174
|
+
description: `${providerModels.length} models`,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Models under provider
|
|
178
|
+
for (const pm of providerModels) {
|
|
179
|
+
items.push({
|
|
180
|
+
label: ` ${pm.model}`,
|
|
181
|
+
value: `${provider}/${pm.model}`,
|
|
182
|
+
description: pm.description,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Separator
|
|
187
|
+
items.push({
|
|
188
|
+
label: '\u2500'.repeat(40),
|
|
189
|
+
value: 'separator',
|
|
190
|
+
description: '',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Remove last separator if it exists
|
|
195
|
+
if (items.length > 0 && items[items.length - 1].value === 'separator') {
|
|
196
|
+
items.pop();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return selectInteractive(items, 'Select Model');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Skill selection helper
|
|
204
|
+
*/
|
|
205
|
+
export async function selectSkillInteractive(
|
|
206
|
+
skills: Array<{ name: string; description: string }>
|
|
207
|
+
): Promise<string | null> {
|
|
208
|
+
const items: SelectItem[] = skills.map(skill => ({
|
|
209
|
+
label: skill.name,
|
|
210
|
+
value: skill.name,
|
|
211
|
+
description: skill.description.length > 50 ? skill.description.substring(0, 47) + '...' : skill.description,
|
|
212
|
+
}));
|
|
213
|
+
|
|
214
|
+
return selectInteractive(items, 'Select Skill');
|
|
215
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// ConfirmDialog - Terminal confirmation dialog
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import * as readline from 'node:readline';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
export interface ConfirmDialogOptions {
|
|
9
|
+
/** Dialog message/question */
|
|
10
|
+
message: string;
|
|
11
|
+
/** Default value (default: false) */
|
|
12
|
+
default?: boolean;
|
|
13
|
+
/** Custom yes label (default: 'yes') */
|
|
14
|
+
yesLabel?: string;
|
|
15
|
+
/** Custom no label (default: 'no') */
|
|
16
|
+
noLabel?: string;
|
|
17
|
+
/** Show as warning (default: false) */
|
|
18
|
+
warning?: boolean;
|
|
19
|
+
/** Show as danger (default: false) */
|
|
20
|
+
danger?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class ConfirmDialog {
|
|
24
|
+
private rl: readline.Interface;
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
this.rl = readline.createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Show confirmation dialog and return user response
|
|
35
|
+
*/
|
|
36
|
+
async show(options: ConfirmDialogOptions): Promise<boolean> {
|
|
37
|
+
const {
|
|
38
|
+
message,
|
|
39
|
+
default: defaultValue = false,
|
|
40
|
+
yesLabel = 'yes',
|
|
41
|
+
noLabel = 'no',
|
|
42
|
+
warning = false,
|
|
43
|
+
danger = false,
|
|
44
|
+
} = options;
|
|
45
|
+
|
|
46
|
+
// Style the message based on type
|
|
47
|
+
let styledMessage: string;
|
|
48
|
+
if (danger) {
|
|
49
|
+
styledMessage = chalk.redBright('⚠ ') + chalk.bold.red(message);
|
|
50
|
+
} else if (warning) {
|
|
51
|
+
styledMessage = chalk.yellowBright('⚠ ') + chalk.bold.yellow(message);
|
|
52
|
+
} else {
|
|
53
|
+
styledMessage = chalk.cyan('? ') + chalk.bold(message);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Build prompt with options
|
|
57
|
+
const prompt = defaultValue
|
|
58
|
+
? ` ${styledMessage} (${chalk.green(yesLabel)}/${noLabel}) `
|
|
59
|
+
: ` ${styledMessage} (${yesLabel}/${chalk.green(noLabel)}) `;
|
|
60
|
+
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
this.rl.question(prompt, (answer) => {
|
|
63
|
+
const normalized = answer.trim().toLowerCase();
|
|
64
|
+
|
|
65
|
+
// Handle different answer formats
|
|
66
|
+
if (normalized === '' && defaultValue !== undefined) {
|
|
67
|
+
resolve(defaultValue);
|
|
68
|
+
} else if (normalized === yesLabel.toLowerCase() || normalized === 'y') {
|
|
69
|
+
resolve(true);
|
|
70
|
+
} else if (normalized === noLabel.toLowerCase() || normalized === 'n') {
|
|
71
|
+
resolve(false);
|
|
72
|
+
} else {
|
|
73
|
+
// For invalid input, return default
|
|
74
|
+
resolve(defaultValue);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.rl.close();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Show danger dialog (destructive action)
|
|
84
|
+
*/
|
|
85
|
+
async danger(message: string, defaultValue = false): Promise<boolean> {
|
|
86
|
+
return this.show({
|
|
87
|
+
message,
|
|
88
|
+
default: defaultValue,
|
|
89
|
+
danger: true,
|
|
90
|
+
yesLabel: 'delete',
|
|
91
|
+
noLabel: 'cancel',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Show warning dialog
|
|
97
|
+
*/
|
|
98
|
+
async warning(message: string, defaultValue = false): Promise<boolean> {
|
|
99
|
+
return this.show({
|
|
100
|
+
message,
|
|
101
|
+
default: defaultValue,
|
|
102
|
+
warning: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Show info dialog
|
|
108
|
+
*/
|
|
109
|
+
async info(message: string, defaultValue = true): Promise<boolean> {
|
|
110
|
+
return this.show({
|
|
111
|
+
message,
|
|
112
|
+
default: defaultValue,
|
|
113
|
+
danger: false,
|
|
114
|
+
warning: false,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Cleanup and close
|
|
120
|
+
*/
|
|
121
|
+
close(): void {
|
|
122
|
+
try {
|
|
123
|
+
this.rl.close();
|
|
124
|
+
} catch {
|
|
125
|
+
// Already closed
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Quick confirmation helper
|
|
132
|
+
*/
|
|
133
|
+
export async function confirm(options: ConfirmDialogOptions): Promise<boolean> {
|
|
134
|
+
const dialog = new ConfirmDialog();
|
|
135
|
+
try {
|
|
136
|
+
return await dialog.show(options);
|
|
137
|
+
} finally {
|
|
138
|
+
dialog.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Quick danger confirmation
|
|
144
|
+
*/
|
|
145
|
+
export async function confirmDanger(message: string): Promise<boolean> {
|
|
146
|
+
const dialog = new ConfirmDialog();
|
|
147
|
+
try {
|
|
148
|
+
return await dialog.danger(message);
|
|
149
|
+
} finally {
|
|
150
|
+
dialog.close();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Quick warning confirmation
|
|
156
|
+
*/
|
|
157
|
+
export async function confirmWarning(message: string): Promise<boolean> {
|
|
158
|
+
const dialog = new ConfirmDialog();
|
|
159
|
+
try {
|
|
160
|
+
return await dialog.warning(message);
|
|
161
|
+
} finally {
|
|
162
|
+
dialog.close();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Example usage:
|
|
167
|
+
// const confirmed = await confirm({
|
|
168
|
+
// message: 'Do you want to continue?',
|
|
169
|
+
// default: true,
|
|
170
|
+
// });
|
|
171
|
+
//
|
|
172
|
+
// if (confirmed) {
|
|
173
|
+
// console.log('User confirmed');
|
|
174
|
+
// } else {
|
|
175
|
+
// console.log('User cancelled');
|
|
176
|
+
// }
|