salesprompter-cli 0.1.14 → 0.1.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/cli.js +116 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { emitKeypressEvents } from "node:readline";
|
|
4
5
|
import { createInterface } from "node:readline/promises";
|
|
5
6
|
import { Command } from "commander";
|
|
6
7
|
import { z } from "zod";
|
|
@@ -198,11 +199,121 @@ function writeSessionSummary(session) {
|
|
|
198
199
|
function normalizeChoiceText(value) {
|
|
199
200
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
200
201
|
}
|
|
202
|
+
function supportsInteractiveChoice() {
|
|
203
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function");
|
|
204
|
+
}
|
|
205
|
+
function renderInteractiveChoiceLines(prompt, options, activeIndex) {
|
|
206
|
+
const lines = [prompt];
|
|
207
|
+
for (const [index, option] of options.entries()) {
|
|
208
|
+
const description = option.description ? ` - ${option.description}` : "";
|
|
209
|
+
const text = `${index + 1}. ${option.label}${description}`;
|
|
210
|
+
if (index === activeIndex) {
|
|
211
|
+
lines.push(`\x1b[7m> ${text}\x1b[0m`);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
lines.push(` ${text}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
lines.push("\x1b[2mUse Up/Down or number keys. Press Enter to continue.\x1b[0m");
|
|
218
|
+
return lines;
|
|
219
|
+
}
|
|
220
|
+
function redrawInteractiveChoice(lines, previousLineCount) {
|
|
221
|
+
if (previousLineCount > 0) {
|
|
222
|
+
for (let index = 0; index < previousLineCount; index += 1) {
|
|
223
|
+
process.stdout.write("\x1b[1A\x1b[2K\r");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
process.stdout.write(`${line}\n`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function promptChoiceInteractive(prompt, options, defaultIndex) {
|
|
231
|
+
const stdin = process.stdin;
|
|
232
|
+
let activeIndex = defaultIndex;
|
|
233
|
+
let renderedLineCount = 0;
|
|
234
|
+
const render = () => {
|
|
235
|
+
const lines = renderInteractiveChoiceLines(prompt, options, activeIndex);
|
|
236
|
+
redrawInteractiveChoice(lines, renderedLineCount);
|
|
237
|
+
renderedLineCount = lines.length;
|
|
238
|
+
};
|
|
239
|
+
const cleanup = () => {
|
|
240
|
+
stdin.removeListener("keypress", onKeypress);
|
|
241
|
+
process.removeListener("SIGINT", onSigint);
|
|
242
|
+
if (stdin.isTTY) {
|
|
243
|
+
stdin.setRawMode(false);
|
|
244
|
+
}
|
|
245
|
+
stdin.pause();
|
|
246
|
+
};
|
|
247
|
+
const finalize = (index, resolve) => {
|
|
248
|
+
const selected = options[index];
|
|
249
|
+
if (!selected) {
|
|
250
|
+
throw new Error("wizard selection invariant violated");
|
|
251
|
+
}
|
|
252
|
+
const summary = `${prompt} ${selected.label}`;
|
|
253
|
+
redrawInteractiveChoice([summary], renderedLineCount);
|
|
254
|
+
renderedLineCount = 1;
|
|
255
|
+
process.stdout.write("\n");
|
|
256
|
+
cleanup();
|
|
257
|
+
resolve(selected.value);
|
|
258
|
+
};
|
|
259
|
+
const cancel = (reject) => {
|
|
260
|
+
cleanup();
|
|
261
|
+
process.stdout.write("\n");
|
|
262
|
+
reject(new Error("prompt cancelled"));
|
|
263
|
+
};
|
|
264
|
+
const onSigint = () => {
|
|
265
|
+
cancel(rejectPromise);
|
|
266
|
+
};
|
|
267
|
+
const onKeypress = (_character, key) => {
|
|
268
|
+
if (key.ctrl && key.name === "c") {
|
|
269
|
+
return cancel(rejectPromise);
|
|
270
|
+
}
|
|
271
|
+
if (key.name === "up") {
|
|
272
|
+
activeIndex = activeIndex === 0 ? options.length - 1 : activeIndex - 1;
|
|
273
|
+
render();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (key.name === "down") {
|
|
277
|
+
activeIndex = activeIndex === options.length - 1 ? 0 : activeIndex + 1;
|
|
278
|
+
render();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (key.name === "return" || key.name === "enter") {
|
|
282
|
+
finalize(activeIndex, resolvePromise);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const digit = Number(key.sequence ?? "");
|
|
286
|
+
if (Number.isInteger(digit) && digit >= 1 && digit <= options.length) {
|
|
287
|
+
finalize(digit - 1, resolvePromise);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
let resolvePromise;
|
|
291
|
+
let rejectPromise;
|
|
292
|
+
return await new Promise((resolve, reject) => {
|
|
293
|
+
resolvePromise = resolve;
|
|
294
|
+
rejectPromise = reject;
|
|
295
|
+
emitKeypressEvents(stdin);
|
|
296
|
+
stdin.setRawMode(true);
|
|
297
|
+
stdin.resume();
|
|
298
|
+
stdin.on("keypress", onKeypress);
|
|
299
|
+
process.on("SIGINT", onSigint);
|
|
300
|
+
render();
|
|
301
|
+
});
|
|
302
|
+
}
|
|
201
303
|
async function promptChoice(rl, prompt, options, defaultValue) {
|
|
202
304
|
const defaultIndex = options.findIndex((option) => option.value === defaultValue);
|
|
203
305
|
if (defaultIndex === -1) {
|
|
204
306
|
throw new Error(`wizard default option is invalid for ${prompt}`);
|
|
205
307
|
}
|
|
308
|
+
if (supportsInteractiveChoice()) {
|
|
309
|
+
rl.pause?.();
|
|
310
|
+
try {
|
|
311
|
+
return await promptChoiceInteractive(prompt, options, defaultIndex);
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
rl.resume?.();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
206
317
|
while (true) {
|
|
207
318
|
writeWizardLine(prompt);
|
|
208
319
|
for (const [index, option] of options.entries()) {
|
|
@@ -1551,6 +1662,11 @@ async function main() {
|
|
|
1551
1662
|
await program.parseAsync(process.argv);
|
|
1552
1663
|
}
|
|
1553
1664
|
main().catch((error) => {
|
|
1665
|
+
if (error instanceof Error &&
|
|
1666
|
+
(error.message === "prompt cancelled" || error.message === "readline was closed")) {
|
|
1667
|
+
process.exitCode = 130;
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1554
1670
|
const cliError = buildCliError(error);
|
|
1555
1671
|
const space = runtimeOutputOptions.json ? undefined : 2;
|
|
1556
1672
|
if (runtimeOutputOptions.json) {
|
package/package.json
CHANGED