@vibeframe/cli 0.27.0 → 0.29.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/adapters/index.d.ts +1 -0
  3. package/dist/agent/adapters/index.d.ts.map +1 -1
  4. package/dist/agent/adapters/index.js +5 -0
  5. package/dist/agent/adapters/index.js.map +1 -1
  6. package/dist/agent/adapters/openrouter.d.ts +16 -0
  7. package/dist/agent/adapters/openrouter.d.ts.map +1 -0
  8. package/dist/agent/adapters/openrouter.js +100 -0
  9. package/dist/agent/adapters/openrouter.js.map +1 -0
  10. package/dist/agent/types.d.ts +1 -1
  11. package/dist/agent/types.d.ts.map +1 -1
  12. package/dist/commands/agent.d.ts.map +1 -1
  13. package/dist/commands/agent.js +3 -1
  14. package/dist/commands/agent.js.map +1 -1
  15. package/dist/commands/setup.js +5 -2
  16. package/dist/commands/setup.js.map +1 -1
  17. package/dist/config/schema.d.ts +2 -1
  18. package/dist/config/schema.d.ts.map +1 -1
  19. package/dist/config/schema.js +2 -0
  20. package/dist/config/schema.js.map +1 -1
  21. package/dist/index.js +0 -0
  22. package/package.json +16 -12
  23. package/.turbo/turbo-build.log +0 -4
  24. package/.turbo/turbo-lint.log +0 -21
  25. package/.turbo/turbo-test.log +0 -689
  26. package/src/agent/adapters/claude.ts +0 -143
  27. package/src/agent/adapters/gemini.ts +0 -159
  28. package/src/agent/adapters/index.ts +0 -61
  29. package/src/agent/adapters/ollama.ts +0 -231
  30. package/src/agent/adapters/openai.ts +0 -116
  31. package/src/agent/adapters/xai.ts +0 -119
  32. package/src/agent/index.ts +0 -251
  33. package/src/agent/memory/index.ts +0 -151
  34. package/src/agent/prompts/system.ts +0 -106
  35. package/src/agent/tools/ai-editing.ts +0 -845
  36. package/src/agent/tools/ai-generation.ts +0 -1073
  37. package/src/agent/tools/ai-pipeline.ts +0 -1055
  38. package/src/agent/tools/ai.ts +0 -21
  39. package/src/agent/tools/batch.ts +0 -429
  40. package/src/agent/tools/e2e.test.ts +0 -545
  41. package/src/agent/tools/export.ts +0 -184
  42. package/src/agent/tools/filesystem.ts +0 -237
  43. package/src/agent/tools/index.ts +0 -150
  44. package/src/agent/tools/integration.test.ts +0 -775
  45. package/src/agent/tools/media.ts +0 -697
  46. package/src/agent/tools/project.ts +0 -313
  47. package/src/agent/tools/timeline.ts +0 -951
  48. package/src/agent/types.ts +0 -68
  49. package/src/commands/agent.ts +0 -340
  50. package/src/commands/ai-analyze.ts +0 -429
  51. package/src/commands/ai-animated-caption.ts +0 -390
  52. package/src/commands/ai-audio.ts +0 -941
  53. package/src/commands/ai-broll.ts +0 -490
  54. package/src/commands/ai-edit-cli.ts +0 -658
  55. package/src/commands/ai-edit.ts +0 -1542
  56. package/src/commands/ai-fill-gaps.ts +0 -566
  57. package/src/commands/ai-helpers.ts +0 -65
  58. package/src/commands/ai-highlights.ts +0 -1303
  59. package/src/commands/ai-image.ts +0 -761
  60. package/src/commands/ai-motion.ts +0 -347
  61. package/src/commands/ai-narrate.ts +0 -451
  62. package/src/commands/ai-review.ts +0 -309
  63. package/src/commands/ai-script-pipeline-cli.ts +0 -1710
  64. package/src/commands/ai-script-pipeline.ts +0 -1365
  65. package/src/commands/ai-suggest-edit.ts +0 -264
  66. package/src/commands/ai-video-fx.ts +0 -445
  67. package/src/commands/ai-video.ts +0 -915
  68. package/src/commands/ai-viral.ts +0 -595
  69. package/src/commands/ai-visual-fx.ts +0 -601
  70. package/src/commands/ai.test.ts +0 -627
  71. package/src/commands/ai.ts +0 -307
  72. package/src/commands/analyze.ts +0 -282
  73. package/src/commands/audio.ts +0 -644
  74. package/src/commands/batch.test.ts +0 -279
  75. package/src/commands/batch.ts +0 -440
  76. package/src/commands/detect.ts +0 -329
  77. package/src/commands/doctor.ts +0 -237
  78. package/src/commands/edit-cmd.ts +0 -1014
  79. package/src/commands/export.ts +0 -918
  80. package/src/commands/generate.ts +0 -2146
  81. package/src/commands/media.ts +0 -177
  82. package/src/commands/output.ts +0 -142
  83. package/src/commands/pipeline.ts +0 -398
  84. package/src/commands/project.test.ts +0 -127
  85. package/src/commands/project.ts +0 -149
  86. package/src/commands/sanitize.ts +0 -60
  87. package/src/commands/schema.ts +0 -130
  88. package/src/commands/setup.ts +0 -509
  89. package/src/commands/timeline.test.ts +0 -499
  90. package/src/commands/timeline.ts +0 -529
  91. package/src/commands/validate.ts +0 -77
  92. package/src/config/config.test.ts +0 -197
  93. package/src/config/index.ts +0 -125
  94. package/src/config/schema.ts +0 -82
  95. package/src/engine/index.ts +0 -2
  96. package/src/engine/project.test.ts +0 -702
  97. package/src/engine/project.ts +0 -439
  98. package/src/index.ts +0 -146
  99. package/src/utils/api-key.test.ts +0 -41
  100. package/src/utils/api-key.ts +0 -247
  101. package/src/utils/audio.ts +0 -83
  102. package/src/utils/exec-safe.ts +0 -75
  103. package/src/utils/first-run.ts +0 -52
  104. package/src/utils/provider-resolver.ts +0 -56
  105. package/src/utils/remotion.ts +0 -951
  106. package/src/utils/subtitle.test.ts +0 -227
  107. package/src/utils/subtitle.ts +0 -169
  108. package/src/utils/tty.ts +0 -196
  109. package/tsconfig.json +0 -20
@@ -1,227 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import {
3
- detectFormat,
4
- formatTranscript,
5
- formatSRT,
6
- formatVTT,
7
- formatSRTTime,
8
- formatVTTTime,
9
- parseSRT,
10
- } from "./subtitle.js";
11
-
12
- describe("subtitle utilities", () => {
13
- describe("detectFormat", () => {
14
- it("detects SRT from file extension", () => {
15
- expect(detectFormat("output.srt")).toBe("srt");
16
- expect(detectFormat("video.SRT")).toBe("srt");
17
- });
18
-
19
- it("detects VTT from file extension", () => {
20
- expect(detectFormat("output.vtt")).toBe("vtt");
21
- expect(detectFormat("video.VTT")).toBe("vtt");
22
- });
23
-
24
- it("defaults to JSON for unknown extensions", () => {
25
- expect(detectFormat("output.json")).toBe("json");
26
- expect(detectFormat("output.txt")).toBe("json");
27
- expect(detectFormat("output")).toBe("json");
28
- });
29
-
30
- it("uses explicit format over extension", () => {
31
- expect(detectFormat("output.json", "srt")).toBe("srt");
32
- expect(detectFormat("output.srt", "vtt")).toBe("vtt");
33
- expect(detectFormat("output.vtt", "json")).toBe("json");
34
- });
35
- });
36
-
37
- describe("formatSRTTime", () => {
38
- it("formats zero", () => {
39
- expect(formatSRTTime(0)).toBe("00:00:00,000");
40
- });
41
-
42
- it("formats seconds", () => {
43
- expect(formatSRTTime(5.5)).toBe("00:00:05,500");
44
- });
45
-
46
- it("formats minutes", () => {
47
- expect(formatSRTTime(65.25)).toBe("00:01:05,250");
48
- });
49
-
50
- it("formats hours", () => {
51
- expect(formatSRTTime(3661.123)).toBe("01:01:01,123");
52
- });
53
- });
54
-
55
- describe("formatVTTTime", () => {
56
- it("formats zero", () => {
57
- expect(formatVTTTime(0)).toBe("00:00:00.000");
58
- });
59
-
60
- it("formats seconds with period separator", () => {
61
- expect(formatVTTTime(5.5)).toBe("00:00:05.500");
62
- });
63
-
64
- it("formats hours", () => {
65
- expect(formatVTTTime(3661.123)).toBe("01:01:01.123");
66
- });
67
- });
68
-
69
- describe("formatSRT", () => {
70
- it("formats empty segments", () => {
71
- expect(formatSRT([])).toBe("");
72
- });
73
-
74
- it("formats single segment", () => {
75
- const segments = [
76
- { startTime: 0, endTime: 2.5, text: "Hello world" },
77
- ];
78
-
79
- const result = formatSRT(segments);
80
-
81
- expect(result).toBe(
82
- "1\n00:00:00,000 --> 00:00:02,500\nHello world\n"
83
- );
84
- });
85
-
86
- it("formats multiple segments", () => {
87
- const segments = [
88
- { startTime: 0, endTime: 2.5, text: "First line" },
89
- { startTime: 2.5, endTime: 5.0, text: "Second line" },
90
- ];
91
-
92
- const result = formatSRT(segments);
93
-
94
- expect(result).toContain("1\n00:00:00,000 --> 00:00:02,500\nFirst line\n");
95
- expect(result).toContain("2\n00:00:02,500 --> 00:00:05,000\nSecond line\n");
96
- });
97
- });
98
-
99
- describe("formatVTT", () => {
100
- it("includes WEBVTT header", () => {
101
- const result = formatVTT([]);
102
- expect(result).toBe("WEBVTT\n\n");
103
- });
104
-
105
- it("formats segments with period separator", () => {
106
- const segments = [
107
- { startTime: 0, endTime: 2.5, text: "Hello world" },
108
- ];
109
-
110
- const result = formatVTT(segments);
111
-
112
- expect(result).toBe(
113
- "WEBVTT\n\n1\n00:00:00.000 --> 00:00:02.500\nHello world\n"
114
- );
115
- });
116
- });
117
-
118
- describe("formatTranscript", () => {
119
- const mockResult = {
120
- id: "test-id",
121
- status: "completed",
122
- fullText: "Hello world. This is a test.",
123
- segments: [
124
- { startTime: 0, endTime: 2.5, text: "Hello world." },
125
- { startTime: 2.5, endTime: 5.0, text: "This is a test." },
126
- ],
127
- };
128
-
129
- it("formats as JSON", () => {
130
- const result = formatTranscript(mockResult, "json");
131
- const parsed = JSON.parse(result);
132
-
133
- expect(parsed.id).toBe("test-id");
134
- expect(parsed.segments).toHaveLength(2);
135
- });
136
-
137
- it("formats as SRT", () => {
138
- const result = formatTranscript(mockResult, "srt");
139
-
140
- expect(result).toContain("00:00:00,000 --> 00:00:02,500");
141
- expect(result).toContain("Hello world.");
142
- expect(result).not.toContain("WEBVTT");
143
- });
144
-
145
- it("formats as VTT", () => {
146
- const result = formatTranscript(mockResult, "vtt");
147
-
148
- expect(result).toContain("WEBVTT");
149
- expect(result).toContain("00:00:00.000 --> 00:00:02.500");
150
- expect(result).toContain("Hello world.");
151
- });
152
-
153
- it("handles empty segments", () => {
154
- const emptyResult = { id: "test", status: "completed" };
155
-
156
- expect(formatTranscript(emptyResult, "srt")).toBe("");
157
- expect(formatTranscript(emptyResult, "vtt")).toBe("WEBVTT\n\n");
158
- });
159
- });
160
-
161
- describe("parseSRT", () => {
162
- it("parses valid SRT content", () => {
163
- const srt = `1
164
- 00:00:00,000 --> 00:00:02,500
165
- Hello world
166
-
167
- 2
168
- 00:00:02,500 --> 00:00:05,000
169
- This is a test
170
- `;
171
- const segments = parseSRT(srt);
172
-
173
- expect(segments).toHaveLength(2);
174
- expect(segments[0].startTime).toBe(0);
175
- expect(segments[0].endTime).toBe(2.5);
176
- expect(segments[0].text).toBe("Hello world");
177
- expect(segments[1].startTime).toBe(2.5);
178
- expect(segments[1].endTime).toBe(5);
179
- expect(segments[1].text).toBe("This is a test");
180
- });
181
-
182
- it("parses multi-line subtitle text", () => {
183
- const srt = `1
184
- 00:00:00,000 --> 00:00:03,000
185
- First line
186
- Second line
187
- `;
188
- const segments = parseSRT(srt);
189
-
190
- expect(segments).toHaveLength(1);
191
- expect(segments[0].text).toBe("First line\nSecond line");
192
- });
193
-
194
- it("returns empty array for empty content", () => {
195
- expect(parseSRT("")).toHaveLength(0);
196
- expect(parseSRT(" \n \n ")).toHaveLength(0);
197
- });
198
-
199
- it("handles timestamps with hours", () => {
200
- const srt = `1
201
- 01:30:15,500 --> 02:00:00,000
202
- Long video subtitle
203
- `;
204
- const segments = parseSRT(srt);
205
-
206
- expect(segments).toHaveLength(1);
207
- expect(segments[0].startTime).toBe(5415.5); // 1*3600 + 30*60 + 15 + 0.5
208
- expect(segments[0].endTime).toBe(7200); // 2*3600
209
- });
210
-
211
- it("roundtrips with formatSRT", () => {
212
- const original = [
213
- { startTime: 0, endTime: 2.5, text: "Hello world" },
214
- { startTime: 2.5, endTime: 5, text: "Second line" },
215
- ];
216
-
217
- const srtString = formatSRT(original);
218
- const parsed = parseSRT(srtString);
219
-
220
- expect(parsed).toHaveLength(2);
221
- expect(parsed[0].text).toBe("Hello world");
222
- expect(parsed[1].text).toBe("Second line");
223
- expect(parsed[0].startTime).toBe(0);
224
- expect(parsed[0].endTime).toBe(2.5);
225
- });
226
- });
227
- });
@@ -1,169 +0,0 @@
1
- /**
2
- * Subtitle format utilities for SRT/VTT generation
3
- */
4
-
5
- export type SubtitleFormat = "json" | "srt" | "vtt";
6
-
7
- export interface SubtitleSegment {
8
- startTime: number;
9
- endTime: number;
10
- text: string;
11
- }
12
-
13
- export interface TranscriptResult {
14
- id: string;
15
- status: string;
16
- fullText?: string;
17
- segments?: SubtitleSegment[];
18
- detectedLanguage?: string;
19
- error?: string;
20
- }
21
-
22
- /**
23
- * Detect subtitle format from file extension or explicit format option
24
- */
25
- export function detectFormat(outputPath: string, explicitFormat?: string): SubtitleFormat {
26
- if (explicitFormat) {
27
- const fmt = explicitFormat.toLowerCase();
28
- if (fmt === "srt" || fmt === "vtt" || fmt === "json") {
29
- return fmt;
30
- }
31
- }
32
-
33
- const ext = outputPath.toLowerCase().split(".").pop();
34
- if (ext === "srt") return "srt";
35
- if (ext === "vtt") return "vtt";
36
- return "json";
37
- }
38
-
39
- /**
40
- * Format transcript result to the specified format
41
- */
42
- export function formatTranscript(result: TranscriptResult, format: SubtitleFormat): string {
43
- if (format === "json") {
44
- return JSON.stringify(result, null, 2);
45
- }
46
-
47
- const segments = result.segments || [];
48
-
49
- if (format === "srt") {
50
- return formatSRT(segments);
51
- }
52
-
53
- return formatVTT(segments);
54
- }
55
-
56
- /**
57
- * Format segments as SRT (SubRip Subtitle)
58
- *
59
- * SRT Format:
60
- * 1
61
- * 00:00:00,000 --> 00:00:02,500
62
- * Hello world
63
- */
64
- export function formatSRT(segments: SubtitleSegment[]): string {
65
- return segments.map((seg, index) => {
66
- const start = formatSRTTime(seg.startTime);
67
- const end = formatSRTTime(seg.endTime);
68
- return `${index + 1}\n${start} --> ${end}\n${seg.text}\n`;
69
- }).join("\n");
70
- }
71
-
72
- /**
73
- * Format segments as WebVTT (Web Video Text Tracks)
74
- *
75
- * VTT Format:
76
- * WEBVTT
77
- *
78
- * 1
79
- * 00:00:00.000 --> 00:00:02.500
80
- * Hello world
81
- */
82
- export function formatVTT(segments: SubtitleSegment[]): string {
83
- const header = "WEBVTT\n\n";
84
- const cues = segments.map((seg, index) => {
85
- const start = formatVTTTime(seg.startTime);
86
- const end = formatVTTTime(seg.endTime);
87
- return `${index + 1}\n${start} --> ${end}\n${seg.text}\n`;
88
- }).join("\n");
89
- return header + cues;
90
- }
91
-
92
- /**
93
- * Format time for SRT (uses comma for milliseconds)
94
- * Format: HH:MM:SS,mmm
95
- */
96
- export function formatSRTTime(seconds: number): string {
97
- const hours = Math.floor(seconds / 3600);
98
- const mins = Math.floor((seconds % 3600) / 60);
99
- const secs = Math.floor(seconds % 60);
100
- const ms = Math.round((seconds % 1) * 1000);
101
-
102
- return `${pad(hours, 2)}:${pad(mins, 2)}:${pad(secs, 2)},${pad(ms, 3)}`;
103
- }
104
-
105
- /**
106
- * Format time for VTT (uses period for milliseconds)
107
- * Format: HH:MM:SS.mmm
108
- */
109
- export function formatVTTTime(seconds: number): string {
110
- const hours = Math.floor(seconds / 3600);
111
- const mins = Math.floor((seconds % 3600) / 60);
112
- const secs = Math.floor(seconds % 60);
113
- const ms = Math.round((seconds % 1) * 1000);
114
-
115
- return `${pad(hours, 2)}:${pad(mins, 2)}:${pad(secs, 2)}.${pad(ms, 3)}`;
116
- }
117
-
118
- /**
119
- * Parse SRT content into SubtitleSegment array
120
- *
121
- * Handles standard SRT format:
122
- * 1
123
- * 00:00:00,000 --> 00:00:02,500
124
- * Hello world
125
- */
126
- export function parseSRT(content: string): SubtitleSegment[] {
127
- const segments: SubtitleSegment[] = [];
128
- const blocks = content.trim().split(/\n\s*\n/);
129
-
130
- for (const block of blocks) {
131
- const lines = block.trim().split("\n");
132
- if (lines.length < 3) continue;
133
-
134
- // Line 0: sequence number (skip)
135
- // Line 1: timestamp line
136
- const timeLine = lines[1].trim();
137
- const timeMatch = timeLine.match(
138
- /(\d{2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}[,.]\d{3})/,
139
- );
140
- if (!timeMatch) continue;
141
-
142
- const startTime = parseSRTTimestamp(timeMatch[1]);
143
- const endTime = parseSRTTimestamp(timeMatch[2]);
144
- // Lines 2+: subtitle text (may be multi-line)
145
- const text = lines.slice(2).join("\n").trim();
146
-
147
- if (text) {
148
- segments.push({ startTime, endTime, text });
149
- }
150
- }
151
-
152
- return segments;
153
- }
154
-
155
- function parseSRTTimestamp(timestamp: string): number {
156
- // Accept both comma (SRT) and period (VTT) as ms separator
157
- const normalized = timestamp.replace(",", ".");
158
- const parts = normalized.split(":");
159
- const hours = parseInt(parts[0], 10);
160
- const mins = parseInt(parts[1], 10);
161
- const secParts = parts[2].split(".");
162
- const secs = parseInt(secParts[0], 10);
163
- const ms = parseInt(secParts[1], 10);
164
- return hours * 3600 + mins * 60 + secs + ms / 1000;
165
- }
166
-
167
- function pad(num: number, size: number): string {
168
- return num.toString().padStart(size, "0");
169
- }
package/src/utils/tty.ts DELETED
@@ -1,196 +0,0 @@
1
- /**
2
- * TTY Input Utilities
3
- * Handles stdin fallback to /dev/tty for piped environments
4
- */
5
-
6
- import { createInterface, Interface } from "node:readline";
7
- import { ReadStream } from "node:tty";
8
-
9
- let ttyStream: ReadStream | null = null;
10
-
11
- /**
12
- * Get a TTY-capable input stream
13
- * Falls back to /dev/tty when stdin is piped (e.g., from curl)
14
- */
15
- export function getTTYInputStream(): typeof process.stdin | ReadStream {
16
- if (process.stdin.isTTY) {
17
- return process.stdin;
18
- }
19
-
20
- // stdin is not a TTY (piped), open /dev/tty directly
21
- if (!ttyStream) {
22
- try {
23
- // Open /dev/tty as a TTY stream
24
- const fd = require("fs").openSync("/dev/tty", "r");
25
- ttyStream = new ReadStream(fd);
26
- } catch {
27
- // Fallback to stdin if /dev/tty is not available
28
- console.warn("Warning: Cannot open /dev/tty, interactive input may not work");
29
- return process.stdin;
30
- }
31
- }
32
- return ttyStream;
33
- }
34
-
35
- /**
36
- * Check if we have TTY input available
37
- */
38
- export function hasTTY(): boolean {
39
- if (process.stdin.isTTY) return true;
40
-
41
- // Try to actually open /dev/tty to verify it's accessible
42
- try {
43
- const fs = require("fs");
44
- const fd = fs.openSync("/dev/tty", "r");
45
- fs.closeSync(fd);
46
- return true;
47
- } catch {
48
- return false;
49
- }
50
- }
51
-
52
- /**
53
- * Close the TTY stream if we opened one
54
- */
55
- export function closeTTYStream(): void {
56
- if (ttyStream) {
57
- ttyStream.destroy();
58
- ttyStream = null;
59
- }
60
- }
61
-
62
- /**
63
- * Create a readline interface with TTY support
64
- */
65
- export function createTTYInterface(options?: {
66
- prompt?: string;
67
- historySize?: number;
68
- }): Interface {
69
- const input = getTTYInputStream();
70
- return createInterface({
71
- input,
72
- output: process.stdout,
73
- terminal: true,
74
- historySize: options?.historySize ?? 100,
75
- prompt: options?.prompt ?? "> ",
76
- });
77
- }
78
-
79
- /**
80
- * Prompt for input (single line)
81
- * Throws in non-TTY environments to prevent hanging.
82
- */
83
- export async function prompt(question: string): Promise<string> {
84
- if (!hasTTY()) {
85
- throw new Error(
86
- "Interactive input required but no TTY available. " +
87
- "Use command flags to provide values non-interactively."
88
- );
89
- }
90
- const input = getTTYInputStream();
91
- const rl = createInterface({
92
- input,
93
- output: process.stdout,
94
- });
95
-
96
- return new Promise((resolve) => {
97
- rl.question(question, (answer) => {
98
- rl.close();
99
- resolve(answer);
100
- });
101
- });
102
- }
103
-
104
- /**
105
- * Prompt for hidden input (password/API key)
106
- * Characters are not echoed to terminal
107
- */
108
- export async function promptHidden(question: string): Promise<string> {
109
- const input = getTTYInputStream() as ReadStream;
110
-
111
- return new Promise((resolve) => {
112
- process.stdout.write(question);
113
-
114
- let value = "";
115
-
116
- // Check if we can use raw mode
117
- if (typeof input.setRawMode === "function") {
118
- input.setRawMode(true);
119
- input.resume();
120
- input.setEncoding("utf8");
121
-
122
- const onData = (char: string) => {
123
- if (char === "\n" || char === "\r" || char === "\u0004") {
124
- // Enter or EOF
125
- input.setRawMode(false);
126
- input.removeListener("data", onData);
127
- process.stdout.write("\n");
128
- resolve(value);
129
- } else if (char === "\u0003") {
130
- // Ctrl+C
131
- input.setRawMode(false);
132
- process.stdout.write("\n");
133
- process.exit(1);
134
- } else if (char === "\u007F" || char === "\b") {
135
- // Backspace
136
- if (value.length > 0) {
137
- value = value.slice(0, -1);
138
- // Clear character from display
139
- process.stdout.write("\b \b");
140
- }
141
- } else if (char >= " ") {
142
- // Printable character (may be multiple chars when pasting)
143
- value += char;
144
- process.stdout.write("*".repeat(char.length)); // Show asterisk for each char
145
- }
146
- };
147
-
148
- input.on("data", onData);
149
- } else {
150
- // Fallback: no raw mode available, input will be visible
151
- const rl = createInterface({ input, output: process.stdout });
152
- rl.question("", (answer) => {
153
- rl.close();
154
- resolve(answer);
155
- });
156
- }
157
- });
158
- }
159
-
160
- /**
161
- * Prompt for selection from a list (1-based index)
162
- */
163
- export async function promptSelect(
164
- question: string,
165
- options: string[],
166
- defaultIndex = 0
167
- ): Promise<number> {
168
- // Display options
169
- for (let i = 0; i < options.length; i++) {
170
- const marker = i === defaultIndex ? "→" : " ";
171
- console.log(` ${marker} ${i + 1}. ${options[i]}`);
172
- }
173
- console.log();
174
-
175
- const answer = await prompt(question);
176
- const index = parseInt(answer, 10) - 1;
177
-
178
- if (isNaN(index) || index < 0 || index >= options.length) {
179
- return defaultIndex;
180
- }
181
- return index;
182
- }
183
-
184
- /**
185
- * Prompt for yes/no confirmation
186
- */
187
- export async function promptConfirm(
188
- question: string,
189
- defaultYes = true
190
- ): Promise<boolean> {
191
- const hint = defaultYes ? "(Y/n)" : "(y/N)";
192
- const answer = await prompt(`${question} ${hint}: `);
193
-
194
- if (answer === "") return defaultYes;
195
- return answer.toLowerCase().startsWith("y");
196
- }
package/tsconfig.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "lib": ["ES2022"],
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true,
16
- "resolveJsonModule": true
17
- },
18
- "include": ["src/**/*"],
19
- "exclude": ["node_modules", "dist"]
20
- }