@claude-flow/cli 3.0.0-alpha.13 → 3.0.0-alpha.15
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.
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +75 -2
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -4
- package/.agentic-flow/intelligence.json +0 -17
- package/.claude-flow/agents/store.json +0 -16
- package/.claude-flow/daemon-state.json +0 -123
- package/.claude-flow/daemon-test.log +0 -0
- package/.claude-flow/daemon.log +0 -0
- package/.claude-flow/daemon2.log +0 -0
- package/.claude-flow/daemon3.log +0 -0
- package/.claude-flow/hive-mind/state.json +0 -51
- package/.claude-flow/metrics/agent-metrics.json +0 -1
- package/.claude-flow/metrics/codebase-map.json +0 -11
- package/.claude-flow/metrics/consolidation.json +0 -6
- package/.claude-flow/metrics/performance.json +0 -87
- package/.claude-flow/metrics/security-audit.json +0 -10
- package/.claude-flow/metrics/task-metrics.json +0 -10
- package/.claude-flow/metrics/test-gaps.json +0 -6
- package/__tests__/README.md +0 -140
- package/__tests__/TEST_SUMMARY.md +0 -144
- package/__tests__/cli.test.ts +0 -558
- package/__tests__/commands.test.ts +0 -726
- package/__tests__/config-adapter.test.ts +0 -362
- package/__tests__/config-loading.test.ts +0 -106
- package/__tests__/coverage/.tmp/coverage-0.json +0 -1
- package/__tests__/coverage/.tmp/coverage-1.json +0 -1
- package/__tests__/coverage/.tmp/coverage-2.json +0 -1
- package/__tests__/coverage/.tmp/coverage-3.json +0 -1
- package/__tests__/coverage/.tmp/coverage-4.json +0 -1
- package/__tests__/coverage/.tmp/coverage-5.json +0 -1
- package/__tests__/mcp-client.test.ts +0 -480
- package/__tests__/p1-commands.test.ts +0 -1064
- package/agents/architect.yaml +0 -11
- package/agents/coder.yaml +0 -11
- package/agents/reviewer.yaml +0 -10
- package/agents/security-architect.yaml +0 -10
- package/agents/tester.yaml +0 -10
- package/docs/CONFIG_LOADING.md +0 -236
- package/docs/IMPLEMENTATION_COMPLETE.md +0 -421
- package/docs/MCP_CLIENT_GUIDE.md +0 -620
- package/docs/REFACTORING_SUMMARY.md +0 -247
- package/scripts/publish.sh +0 -46
- package/src/commands/agent.ts +0 -955
- package/src/commands/claims.ts +0 -317
- package/src/commands/completions.ts +0 -558
- package/src/commands/config.ts +0 -452
- package/src/commands/daemon.ts +0 -621
- package/src/commands/deployment.ts +0 -323
- package/src/commands/doctor.ts +0 -382
- package/src/commands/embeddings.ts +0 -686
- package/src/commands/hive-mind.ts +0 -928
- package/src/commands/hooks.ts +0 -2603
- package/src/commands/index.ts +0 -154
- package/src/commands/init.ts +0 -597
- package/src/commands/mcp.ts +0 -753
- package/src/commands/memory.ts +0 -1161
- package/src/commands/migrate.ts +0 -447
- package/src/commands/neural.ts +0 -253
- package/src/commands/performance.ts +0 -292
- package/src/commands/plugins.ts +0 -316
- package/src/commands/process.ts +0 -695
- package/src/commands/providers.ts +0 -259
- package/src/commands/security.ts +0 -288
- package/src/commands/session.ts +0 -891
- package/src/commands/start.ts +0 -457
- package/src/commands/status.ts +0 -736
- package/src/commands/swarm.ts +0 -648
- package/src/commands/task.ts +0 -792
- package/src/commands/workflow.ts +0 -742
- package/src/config-adapter.ts +0 -210
- package/src/index.ts +0 -443
- package/src/infrastructure/in-memory-repositories.ts +0 -310
- package/src/init/claudemd-generator.ts +0 -631
- package/src/init/executor.ts +0 -762
- package/src/init/helpers-generator.ts +0 -628
- package/src/init/index.ts +0 -60
- package/src/init/mcp-generator.ts +0 -83
- package/src/init/settings-generator.ts +0 -284
- package/src/init/statusline-generator.ts +0 -211
- package/src/init/types.ts +0 -447
- package/src/mcp-client.ts +0 -241
- package/src/mcp-server.ts +0 -577
- package/src/mcp-tools/agent-tools.ts +0 -466
- package/src/mcp-tools/config-tools.ts +0 -370
- package/src/mcp-tools/hive-mind-tools.ts +0 -521
- package/src/mcp-tools/hooks-tools.ts +0 -1888
- package/src/mcp-tools/index.ts +0 -16
- package/src/mcp-tools/memory-tools.ts +0 -270
- package/src/mcp-tools/session-tools.ts +0 -359
- package/src/mcp-tools/swarm-tools.ts +0 -105
- package/src/mcp-tools/task-tools.ts +0 -347
- package/src/mcp-tools/types.ts +0 -33
- package/src/mcp-tools/workflow-tools.ts +0 -573
- package/src/output.ts +0 -639
- package/src/parser.ts +0 -417
- package/src/prompt.ts +0 -619
- package/src/services/index.ts +0 -15
- package/src/services/worker-daemon.ts +0 -726
- package/src/suggest.ts +0 -245
- package/src/types.ts +0 -287
- package/tmp.json +0 -0
- package/tsconfig.json +0 -16
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -13
package/src/prompt.ts
DELETED
|
@@ -1,619 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* V3 CLI Interactive Prompt System
|
|
3
|
-
* Modern interactive prompts for user input
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as readline from 'readline';
|
|
7
|
-
import type {
|
|
8
|
-
SelectPromptOptions,
|
|
9
|
-
SelectOption,
|
|
10
|
-
ConfirmPromptOptions,
|
|
11
|
-
InputPromptOptions,
|
|
12
|
-
MultiSelectPromptOptions
|
|
13
|
-
} from './types.js';
|
|
14
|
-
import { output, OutputFormatter } from './output.js';
|
|
15
|
-
|
|
16
|
-
// ============================================
|
|
17
|
-
// Core Prompt Infrastructure
|
|
18
|
-
// ============================================
|
|
19
|
-
|
|
20
|
-
class PromptManager {
|
|
21
|
-
private rl: readline.Interface | null = null;
|
|
22
|
-
private formatter: OutputFormatter;
|
|
23
|
-
|
|
24
|
-
constructor(formatter: OutputFormatter = output) {
|
|
25
|
-
this.formatter = formatter;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private createInterface(): readline.Interface {
|
|
29
|
-
if (!this.rl) {
|
|
30
|
-
this.rl = readline.createInterface({
|
|
31
|
-
input: process.stdin,
|
|
32
|
-
output: process.stdout,
|
|
33
|
-
terminal: true
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Handle cleanup on exit
|
|
37
|
-
this.rl.on('close', () => {
|
|
38
|
-
this.rl = null;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return this.rl;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private close(): void {
|
|
45
|
-
if (this.rl) {
|
|
46
|
-
this.rl.close();
|
|
47
|
-
this.rl = null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private async question(prompt: string): Promise<string> {
|
|
52
|
-
return new Promise((resolve) => {
|
|
53
|
-
const rl = this.createInterface();
|
|
54
|
-
rl.question(prompt, (answer) => {
|
|
55
|
-
resolve(answer);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ============================================
|
|
61
|
-
// Select Prompt
|
|
62
|
-
// ============================================
|
|
63
|
-
|
|
64
|
-
async select<T = string>(options: SelectPromptOptions<T>): Promise<T> {
|
|
65
|
-
const { message, options: choices, default: defaultValue, pageSize = 10 } = options;
|
|
66
|
-
|
|
67
|
-
this.formatter.writeln();
|
|
68
|
-
this.formatter.writeln(this.formatter.bold(`? ${message}`));
|
|
69
|
-
this.formatter.writeln(this.formatter.dim(' (Use arrow keys to navigate, enter to select)'));
|
|
70
|
-
this.formatter.writeln();
|
|
71
|
-
|
|
72
|
-
// Find default index
|
|
73
|
-
let selectedIndex = 0;
|
|
74
|
-
if (defaultValue !== undefined) {
|
|
75
|
-
const idx = choices.findIndex(c => c.value === defaultValue);
|
|
76
|
-
if (idx !== -1) selectedIndex = idx;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Display options
|
|
80
|
-
const displayChoices = (currentIndex: number, startIndex: number = 0) => {
|
|
81
|
-
// Move cursor up to overwrite
|
|
82
|
-
if (startIndex > 0 || currentIndex > 0) {
|
|
83
|
-
process.stdout.write(`\x1b[${Math.min(choices.length, pageSize)}A`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const endIndex = Math.min(startIndex + pageSize, choices.length);
|
|
87
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
88
|
-
const choice = choices[i];
|
|
89
|
-
const isSelected = i === currentIndex;
|
|
90
|
-
const prefix = isSelected ? this.formatter.info('>') : ' ';
|
|
91
|
-
const label = isSelected
|
|
92
|
-
? this.formatter.highlight(choice.label)
|
|
93
|
-
: choice.label;
|
|
94
|
-
const hint = choice.hint ? this.formatter.dim(` - ${choice.hint}`) : '';
|
|
95
|
-
const disabled = choice.disabled ? this.formatter.dim(' (disabled)') : '';
|
|
96
|
-
|
|
97
|
-
this.formatter.writeln(` ${prefix} ${label}${hint}${disabled}`);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// Initial display
|
|
102
|
-
displayChoices(selectedIndex);
|
|
103
|
-
|
|
104
|
-
return new Promise<T>((resolve, reject) => {
|
|
105
|
-
const rl = this.createInterface();
|
|
106
|
-
|
|
107
|
-
// Enable raw mode for arrow key detection
|
|
108
|
-
if (process.stdin.isTTY) {
|
|
109
|
-
process.stdin.setRawMode(true);
|
|
110
|
-
}
|
|
111
|
-
process.stdin.resume();
|
|
112
|
-
|
|
113
|
-
const handleKeypress = (key: Buffer) => {
|
|
114
|
-
const keyStr = key.toString();
|
|
115
|
-
|
|
116
|
-
// Arrow keys
|
|
117
|
-
if (keyStr === '\x1b[A') { // Up
|
|
118
|
-
do {
|
|
119
|
-
selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;
|
|
120
|
-
} while (choices[selectedIndex].disabled && selectedIndex !== 0);
|
|
121
|
-
displayChoices(selectedIndex);
|
|
122
|
-
} else if (keyStr === '\x1b[B') { // Down
|
|
123
|
-
do {
|
|
124
|
-
selectedIndex = (selectedIndex + 1) % choices.length;
|
|
125
|
-
} while (choices[selectedIndex].disabled && selectedIndex !== choices.length - 1);
|
|
126
|
-
displayChoices(selectedIndex);
|
|
127
|
-
} else if (keyStr === '\r' || keyStr === '\n') { // Enter
|
|
128
|
-
cleanup();
|
|
129
|
-
const selected = choices[selectedIndex];
|
|
130
|
-
if (!selected.disabled) {
|
|
131
|
-
this.formatter.writeln();
|
|
132
|
-
this.formatter.writeln(this.formatter.success(`Selected: ${selected.label}`));
|
|
133
|
-
resolve(selected.value);
|
|
134
|
-
}
|
|
135
|
-
} else if (keyStr === '\x03') { // Ctrl+C
|
|
136
|
-
cleanup();
|
|
137
|
-
reject(new Error('User cancelled'));
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const cleanup = () => {
|
|
142
|
-
process.stdin.removeListener('data', handleKeypress);
|
|
143
|
-
if (process.stdin.isTTY) {
|
|
144
|
-
process.stdin.setRawMode(false);
|
|
145
|
-
}
|
|
146
|
-
this.close();
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
process.stdin.on('data', handleKeypress);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ============================================
|
|
154
|
-
// Confirm Prompt
|
|
155
|
-
// ============================================
|
|
156
|
-
|
|
157
|
-
async confirm(options: ConfirmPromptOptions): Promise<boolean> {
|
|
158
|
-
const {
|
|
159
|
-
message,
|
|
160
|
-
default: defaultValue = false,
|
|
161
|
-
active = 'Yes',
|
|
162
|
-
inactive = 'No'
|
|
163
|
-
} = options;
|
|
164
|
-
|
|
165
|
-
const defaultText = defaultValue ? `${active}/${inactive}` : `${active}/${inactive}`;
|
|
166
|
-
const hint = defaultValue ? `[${active}]` : `[${inactive}]`;
|
|
167
|
-
|
|
168
|
-
const prompt = `${this.formatter.bold('?')} ${message} ${this.formatter.dim(hint)} `;
|
|
169
|
-
|
|
170
|
-
const answer = await this.question(prompt);
|
|
171
|
-
this.close();
|
|
172
|
-
|
|
173
|
-
if (answer === '') {
|
|
174
|
-
return defaultValue;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const normalized = answer.toLowerCase().trim();
|
|
178
|
-
|
|
179
|
-
if (['y', 'yes', 'true', '1'].includes(normalized)) {
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (['n', 'no', 'false', '0'].includes(normalized)) {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return defaultValue;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// ============================================
|
|
191
|
-
// Input Prompt
|
|
192
|
-
// ============================================
|
|
193
|
-
|
|
194
|
-
async input(options: InputPromptOptions): Promise<string> {
|
|
195
|
-
const {
|
|
196
|
-
message,
|
|
197
|
-
default: defaultValue,
|
|
198
|
-
placeholder,
|
|
199
|
-
validate,
|
|
200
|
-
mask
|
|
201
|
-
} = options;
|
|
202
|
-
|
|
203
|
-
let prompt = `${this.formatter.bold('?')} ${message}`;
|
|
204
|
-
|
|
205
|
-
if (defaultValue) {
|
|
206
|
-
prompt += ` ${this.formatter.dim(`(${defaultValue})`)}`;
|
|
207
|
-
} else if (placeholder) {
|
|
208
|
-
prompt += ` ${this.formatter.dim(placeholder)}`;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
prompt += ': ';
|
|
212
|
-
|
|
213
|
-
while (true) {
|
|
214
|
-
let answer: string;
|
|
215
|
-
|
|
216
|
-
if (mask) {
|
|
217
|
-
answer = await this.inputMasked(prompt);
|
|
218
|
-
} else {
|
|
219
|
-
answer = await this.question(prompt);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Use default if empty
|
|
223
|
-
if (answer === '' && defaultValue !== undefined) {
|
|
224
|
-
answer = defaultValue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Validate
|
|
228
|
-
if (validate) {
|
|
229
|
-
const result = validate(answer);
|
|
230
|
-
if (result !== true) {
|
|
231
|
-
const errorMsg = typeof result === 'string' ? result : 'Invalid input';
|
|
232
|
-
this.formatter.writeln(this.formatter.error(` ${errorMsg}`));
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
this.close();
|
|
238
|
-
return answer;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
private async inputMasked(prompt: string): Promise<string> {
|
|
243
|
-
return new Promise((resolve) => {
|
|
244
|
-
const rl = this.createInterface();
|
|
245
|
-
let password = '';
|
|
246
|
-
|
|
247
|
-
// Don't echo characters
|
|
248
|
-
process.stdout.write(prompt);
|
|
249
|
-
|
|
250
|
-
if (process.stdin.isTTY) {
|
|
251
|
-
process.stdin.setRawMode(true);
|
|
252
|
-
}
|
|
253
|
-
process.stdin.resume();
|
|
254
|
-
|
|
255
|
-
const handleData = (chunk: Buffer) => {
|
|
256
|
-
const char = chunk.toString();
|
|
257
|
-
|
|
258
|
-
if (char === '\n' || char === '\r') {
|
|
259
|
-
// Enter pressed
|
|
260
|
-
process.stdin.removeListener('data', handleData);
|
|
261
|
-
if (process.stdin.isTTY) {
|
|
262
|
-
process.stdin.setRawMode(false);
|
|
263
|
-
}
|
|
264
|
-
process.stdout.write('\n');
|
|
265
|
-
resolve(password);
|
|
266
|
-
} else if (char === '\x7f' || char === '\x08') {
|
|
267
|
-
// Backspace
|
|
268
|
-
if (password.length > 0) {
|
|
269
|
-
password = password.slice(0, -1);
|
|
270
|
-
process.stdout.write('\b \b');
|
|
271
|
-
}
|
|
272
|
-
} else if (char === '\x03') {
|
|
273
|
-
// Ctrl+C
|
|
274
|
-
process.stdin.removeListener('data', handleData);
|
|
275
|
-
if (process.stdin.isTTY) {
|
|
276
|
-
process.stdin.setRawMode(false);
|
|
277
|
-
}
|
|
278
|
-
resolve('');
|
|
279
|
-
} else if (char.charCodeAt(0) >= 32) {
|
|
280
|
-
// Printable character
|
|
281
|
-
password += char;
|
|
282
|
-
process.stdout.write('*');
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
process.stdin.on('data', handleData);
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// ============================================
|
|
291
|
-
// Multi-Select Prompt
|
|
292
|
-
// ============================================
|
|
293
|
-
|
|
294
|
-
async multiSelect<T = string>(options: MultiSelectPromptOptions<T>): Promise<T[]> {
|
|
295
|
-
const {
|
|
296
|
-
message,
|
|
297
|
-
options: choices,
|
|
298
|
-
default: defaultValues = [],
|
|
299
|
-
required = false,
|
|
300
|
-
min,
|
|
301
|
-
max
|
|
302
|
-
} = options;
|
|
303
|
-
|
|
304
|
-
this.formatter.writeln();
|
|
305
|
-
this.formatter.writeln(this.formatter.bold(`? ${message}`));
|
|
306
|
-
this.formatter.writeln(this.formatter.dim(' (Use arrow keys to navigate, space to select, enter to confirm)'));
|
|
307
|
-
this.formatter.writeln();
|
|
308
|
-
|
|
309
|
-
// Initialize selection state
|
|
310
|
-
const selected = new Set<number>();
|
|
311
|
-
for (let i = 0; i < choices.length; i++) {
|
|
312
|
-
// Check both default array and individual selected property
|
|
313
|
-
if (defaultValues.includes(choices[i].value) || choices[i].selected) {
|
|
314
|
-
selected.add(i);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
let currentIndex = 0;
|
|
319
|
-
|
|
320
|
-
// Display options
|
|
321
|
-
const displayChoices = () => {
|
|
322
|
-
// Move cursor up to overwrite
|
|
323
|
-
process.stdout.write(`\x1b[${choices.length}A`);
|
|
324
|
-
|
|
325
|
-
for (let i = 0; i < choices.length; i++) {
|
|
326
|
-
const choice = choices[i];
|
|
327
|
-
const isCurrentRow = i === currentIndex;
|
|
328
|
-
const isSelected = selected.has(i);
|
|
329
|
-
|
|
330
|
-
const cursor = isCurrentRow ? this.formatter.info('>') : ' ';
|
|
331
|
-
const checkbox = isSelected
|
|
332
|
-
? this.formatter.success('[x]')
|
|
333
|
-
: this.formatter.dim('[ ]');
|
|
334
|
-
const label = isCurrentRow
|
|
335
|
-
? this.formatter.highlight(choice.label)
|
|
336
|
-
: choice.label;
|
|
337
|
-
const hint = choice.hint ? this.formatter.dim(` - ${choice.hint}`) : '';
|
|
338
|
-
const disabled = choice.disabled ? this.formatter.dim(' (disabled)') : '';
|
|
339
|
-
|
|
340
|
-
this.formatter.writeln(` ${cursor} ${checkbox} ${label}${hint}${disabled}`);
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
// Initial display
|
|
345
|
-
for (let i = 0; i < choices.length; i++) {
|
|
346
|
-
this.formatter.writeln('');
|
|
347
|
-
}
|
|
348
|
-
displayChoices();
|
|
349
|
-
|
|
350
|
-
return new Promise<T[]>((resolve, reject) => {
|
|
351
|
-
const rl = this.createInterface();
|
|
352
|
-
|
|
353
|
-
if (process.stdin.isTTY) {
|
|
354
|
-
process.stdin.setRawMode(true);
|
|
355
|
-
}
|
|
356
|
-
process.stdin.resume();
|
|
357
|
-
|
|
358
|
-
const handleKeypress = (key: Buffer) => {
|
|
359
|
-
const keyStr = key.toString();
|
|
360
|
-
|
|
361
|
-
if (keyStr === '\x1b[A') { // Up
|
|
362
|
-
currentIndex = (currentIndex - 1 + choices.length) % choices.length;
|
|
363
|
-
displayChoices();
|
|
364
|
-
} else if (keyStr === '\x1b[B') { // Down
|
|
365
|
-
currentIndex = (currentIndex + 1) % choices.length;
|
|
366
|
-
displayChoices();
|
|
367
|
-
} else if (keyStr === ' ') { // Space
|
|
368
|
-
if (!choices[currentIndex].disabled) {
|
|
369
|
-
if (selected.has(currentIndex)) {
|
|
370
|
-
selected.delete(currentIndex);
|
|
371
|
-
} else {
|
|
372
|
-
// Check max limit
|
|
373
|
-
if (!max || selected.size < max) {
|
|
374
|
-
selected.add(currentIndex);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
displayChoices();
|
|
378
|
-
}
|
|
379
|
-
} else if (keyStr === '\r' || keyStr === '\n') { // Enter
|
|
380
|
-
// Validate selection
|
|
381
|
-
if (required && selected.size === 0) {
|
|
382
|
-
this.formatter.writeln(this.formatter.error(' At least one option must be selected'));
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
if (min && selected.size < min) {
|
|
386
|
-
this.formatter.writeln(this.formatter.error(` At least ${min} options must be selected`));
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
cleanup();
|
|
391
|
-
const selectedValues = Array.from(selected).map(i => choices[i].value);
|
|
392
|
-
const selectedLabels = Array.from(selected).map(i => choices[i].label);
|
|
393
|
-
this.formatter.writeln();
|
|
394
|
-
this.formatter.writeln(this.formatter.success(`Selected: ${selectedLabels.join(', ')}`));
|
|
395
|
-
resolve(selectedValues);
|
|
396
|
-
} else if (keyStr === '\x03') { // Ctrl+C
|
|
397
|
-
cleanup();
|
|
398
|
-
reject(new Error('User cancelled'));
|
|
399
|
-
} else if (keyStr === 'a') { // Select all
|
|
400
|
-
if (!max || choices.length <= max) {
|
|
401
|
-
for (let i = 0; i < choices.length; i++) {
|
|
402
|
-
if (!choices[i].disabled) {
|
|
403
|
-
selected.add(i);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
displayChoices();
|
|
407
|
-
}
|
|
408
|
-
} else if (keyStr === 'n') { // Select none
|
|
409
|
-
selected.clear();
|
|
410
|
-
displayChoices();
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
const cleanup = () => {
|
|
415
|
-
process.stdin.removeListener('data', handleKeypress);
|
|
416
|
-
if (process.stdin.isTTY) {
|
|
417
|
-
process.stdin.setRawMode(false);
|
|
418
|
-
}
|
|
419
|
-
this.close();
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
process.stdin.on('data', handleKeypress);
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ============================================
|
|
427
|
-
// Text Prompt (Multi-line)
|
|
428
|
-
// ============================================
|
|
429
|
-
|
|
430
|
-
async text(message: string, placeholder?: string): Promise<string> {
|
|
431
|
-
this.formatter.writeln();
|
|
432
|
-
this.formatter.writeln(this.formatter.bold(`? ${message}`));
|
|
433
|
-
if (placeholder) {
|
|
434
|
-
this.formatter.writeln(this.formatter.dim(` ${placeholder}`));
|
|
435
|
-
}
|
|
436
|
-
this.formatter.writeln(this.formatter.dim(' (Enter an empty line to finish)'));
|
|
437
|
-
this.formatter.writeln();
|
|
438
|
-
|
|
439
|
-
const lines: string[] = [];
|
|
440
|
-
|
|
441
|
-
while (true) {
|
|
442
|
-
const line = await this.question(' > ');
|
|
443
|
-
if (line === '') {
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
lines.push(line);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
this.close();
|
|
450
|
-
return lines.join('\n');
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// ============================================
|
|
454
|
-
// Number Prompt
|
|
455
|
-
// ============================================
|
|
456
|
-
|
|
457
|
-
async number(
|
|
458
|
-
message: string,
|
|
459
|
-
options: { default?: number; min?: number; max?: number; } = {}
|
|
460
|
-
): Promise<number> {
|
|
461
|
-
const { default: defaultValue, min, max } = options;
|
|
462
|
-
|
|
463
|
-
const validate = (value: string): boolean | string => {
|
|
464
|
-
const num = Number(value);
|
|
465
|
-
if (isNaN(num)) {
|
|
466
|
-
return 'Please enter a valid number';
|
|
467
|
-
}
|
|
468
|
-
if (min !== undefined && num < min) {
|
|
469
|
-
return `Value must be at least ${min}`;
|
|
470
|
-
}
|
|
471
|
-
if (max !== undefined && num > max) {
|
|
472
|
-
return `Value must be at most ${max}`;
|
|
473
|
-
}
|
|
474
|
-
return true;
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
const result = await this.input({
|
|
478
|
-
message,
|
|
479
|
-
default: defaultValue?.toString(),
|
|
480
|
-
validate
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
return Number(result);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// ============================================
|
|
487
|
-
// Autocomplete Prompt
|
|
488
|
-
// ============================================
|
|
489
|
-
|
|
490
|
-
async autocomplete<T = string>(
|
|
491
|
-
message: string,
|
|
492
|
-
choices: SelectOption<T>[],
|
|
493
|
-
options: { limit?: number } = {}
|
|
494
|
-
): Promise<T> {
|
|
495
|
-
const { limit = 10 } = options;
|
|
496
|
-
|
|
497
|
-
this.formatter.writeln();
|
|
498
|
-
this.formatter.writeln(this.formatter.bold(`? ${message}`));
|
|
499
|
-
this.formatter.writeln(this.formatter.dim(' (Type to filter, arrow keys to navigate)'));
|
|
500
|
-
|
|
501
|
-
let query = '';
|
|
502
|
-
let selectedIndex = 0;
|
|
503
|
-
let filteredChoices = choices.slice(0, limit);
|
|
504
|
-
|
|
505
|
-
const filterChoices = (q: string): SelectOption<T>[] => {
|
|
506
|
-
if (q === '') return choices.slice(0, limit);
|
|
507
|
-
|
|
508
|
-
const normalized = q.toLowerCase();
|
|
509
|
-
return choices
|
|
510
|
-
.filter(c => c.label.toLowerCase().includes(normalized))
|
|
511
|
-
.slice(0, limit);
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
const displayChoices = () => {
|
|
515
|
-
// Clear previous output
|
|
516
|
-
process.stdout.write(`\x1b[${filteredChoices.length + 1}A`);
|
|
517
|
-
process.stdout.write('\x1b[J');
|
|
518
|
-
|
|
519
|
-
// Show input
|
|
520
|
-
this.formatter.writeln(` ${this.formatter.dim('>')} ${query}`);
|
|
521
|
-
|
|
522
|
-
// Show filtered options
|
|
523
|
-
for (let i = 0; i < filteredChoices.length; i++) {
|
|
524
|
-
const choice = filteredChoices[i];
|
|
525
|
-
const isSelected = i === selectedIndex;
|
|
526
|
-
const prefix = isSelected ? this.formatter.info('>') : ' ';
|
|
527
|
-
const label = isSelected ? this.formatter.highlight(choice.label) : choice.label;
|
|
528
|
-
this.formatter.writeln(` ${prefix} ${label}`);
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
// Initial display
|
|
533
|
-
this.formatter.writeln('');
|
|
534
|
-
for (let i = 0; i < limit; i++) {
|
|
535
|
-
this.formatter.writeln('');
|
|
536
|
-
}
|
|
537
|
-
displayChoices();
|
|
538
|
-
|
|
539
|
-
return new Promise<T>((resolve, reject) => {
|
|
540
|
-
const rl = this.createInterface();
|
|
541
|
-
|
|
542
|
-
if (process.stdin.isTTY) {
|
|
543
|
-
process.stdin.setRawMode(true);
|
|
544
|
-
}
|
|
545
|
-
process.stdin.resume();
|
|
546
|
-
|
|
547
|
-
const handleKeypress = (key: Buffer) => {
|
|
548
|
-
const keyStr = key.toString();
|
|
549
|
-
|
|
550
|
-
if (keyStr === '\x1b[A') { // Up
|
|
551
|
-
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
552
|
-
displayChoices();
|
|
553
|
-
} else if (keyStr === '\x1b[B') { // Down
|
|
554
|
-
selectedIndex = Math.min(filteredChoices.length - 1, selectedIndex + 1);
|
|
555
|
-
displayChoices();
|
|
556
|
-
} else if (keyStr === '\r' || keyStr === '\n') { // Enter
|
|
557
|
-
if (filteredChoices.length > 0) {
|
|
558
|
-
cleanup();
|
|
559
|
-
const selected = filteredChoices[selectedIndex];
|
|
560
|
-
this.formatter.writeln();
|
|
561
|
-
this.formatter.writeln(this.formatter.success(`Selected: ${selected.label}`));
|
|
562
|
-
resolve(selected.value);
|
|
563
|
-
}
|
|
564
|
-
} else if (keyStr === '\x7f' || keyStr === '\x08') { // Backspace
|
|
565
|
-
query = query.slice(0, -1);
|
|
566
|
-
filteredChoices = filterChoices(query);
|
|
567
|
-
selectedIndex = 0;
|
|
568
|
-
displayChoices();
|
|
569
|
-
} else if (keyStr === '\x03') { // Ctrl+C
|
|
570
|
-
cleanup();
|
|
571
|
-
reject(new Error('User cancelled'));
|
|
572
|
-
} else if (keyStr.charCodeAt(0) >= 32 && keyStr.charCodeAt(0) < 127) {
|
|
573
|
-
// Printable character
|
|
574
|
-
query += keyStr;
|
|
575
|
-
filteredChoices = filterChoices(query);
|
|
576
|
-
selectedIndex = 0;
|
|
577
|
-
displayChoices();
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
const cleanup = () => {
|
|
582
|
-
process.stdin.removeListener('data', handleKeypress);
|
|
583
|
-
if (process.stdin.isTTY) {
|
|
584
|
-
process.stdin.setRawMode(false);
|
|
585
|
-
}
|
|
586
|
-
this.close();
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
process.stdin.on('data', handleKeypress);
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Export singleton and convenience functions
|
|
595
|
-
export const promptManager = new PromptManager();
|
|
596
|
-
|
|
597
|
-
export const select = <T = string>(options: SelectPromptOptions<T>) =>
|
|
598
|
-
promptManager.select(options);
|
|
599
|
-
|
|
600
|
-
export const confirm = (options: ConfirmPromptOptions) =>
|
|
601
|
-
promptManager.confirm(options);
|
|
602
|
-
|
|
603
|
-
export const input = (options: InputPromptOptions) =>
|
|
604
|
-
promptManager.input(options);
|
|
605
|
-
|
|
606
|
-
export const multiSelect = <T = string>(options: MultiSelectPromptOptions<T>) =>
|
|
607
|
-
promptManager.multiSelect(options);
|
|
608
|
-
|
|
609
|
-
export const text = (message: string, placeholder?: string) =>
|
|
610
|
-
promptManager.text(message, placeholder);
|
|
611
|
-
|
|
612
|
-
export const number = (message: string, options?: { default?: number; min?: number; max?: number }) =>
|
|
613
|
-
promptManager.number(message, options);
|
|
614
|
-
|
|
615
|
-
export const autocomplete = <T = string>(
|
|
616
|
-
message: string,
|
|
617
|
-
choices: SelectOption<T>[],
|
|
618
|
-
options?: { limit?: number }
|
|
619
|
-
) => promptManager.autocomplete(message, choices, options);
|
package/src/services/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* V3 CLI Services Index
|
|
3
|
-
* Central registry for all background services
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
WorkerDaemon,
|
|
8
|
-
getDaemon,
|
|
9
|
-
startDaemon,
|
|
10
|
-
stopDaemon,
|
|
11
|
-
type WorkerType,
|
|
12
|
-
} from './worker-daemon.js';
|
|
13
|
-
|
|
14
|
-
// Re-export types
|
|
15
|
-
export type { default as WorkerDaemonType } from './worker-daemon.js';
|