@vibeframe/cli 0.27.0 → 0.30.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 (118) 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/ai-edit-cli.d.ts.map +1 -1
  16. package/dist/commands/ai-edit-cli.js +18 -0
  17. package/dist/commands/ai-edit-cli.js.map +1 -1
  18. package/dist/commands/generate.js +14 -0
  19. package/dist/commands/generate.js.map +1 -1
  20. package/dist/commands/schema.d.ts +1 -0
  21. package/dist/commands/schema.d.ts.map +1 -1
  22. package/dist/commands/schema.js +122 -21
  23. package/dist/commands/schema.js.map +1 -1
  24. package/dist/commands/setup.js +5 -2
  25. package/dist/commands/setup.js.map +1 -1
  26. package/dist/config/schema.d.ts +2 -1
  27. package/dist/config/schema.d.ts.map +1 -1
  28. package/dist/config/schema.js +2 -0
  29. package/dist/config/schema.js.map +1 -1
  30. package/dist/index.js +0 -0
  31. package/package.json +16 -12
  32. package/.turbo/turbo-build.log +0 -4
  33. package/.turbo/turbo-lint.log +0 -21
  34. package/.turbo/turbo-test.log +0 -689
  35. package/src/agent/adapters/claude.ts +0 -143
  36. package/src/agent/adapters/gemini.ts +0 -159
  37. package/src/agent/adapters/index.ts +0 -61
  38. package/src/agent/adapters/ollama.ts +0 -231
  39. package/src/agent/adapters/openai.ts +0 -116
  40. package/src/agent/adapters/xai.ts +0 -119
  41. package/src/agent/index.ts +0 -251
  42. package/src/agent/memory/index.ts +0 -151
  43. package/src/agent/prompts/system.ts +0 -106
  44. package/src/agent/tools/ai-editing.ts +0 -845
  45. package/src/agent/tools/ai-generation.ts +0 -1073
  46. package/src/agent/tools/ai-pipeline.ts +0 -1055
  47. package/src/agent/tools/ai.ts +0 -21
  48. package/src/agent/tools/batch.ts +0 -429
  49. package/src/agent/tools/e2e.test.ts +0 -545
  50. package/src/agent/tools/export.ts +0 -184
  51. package/src/agent/tools/filesystem.ts +0 -237
  52. package/src/agent/tools/index.ts +0 -150
  53. package/src/agent/tools/integration.test.ts +0 -775
  54. package/src/agent/tools/media.ts +0 -697
  55. package/src/agent/tools/project.ts +0 -313
  56. package/src/agent/tools/timeline.ts +0 -951
  57. package/src/agent/types.ts +0 -68
  58. package/src/commands/agent.ts +0 -340
  59. package/src/commands/ai-analyze.ts +0 -429
  60. package/src/commands/ai-animated-caption.ts +0 -390
  61. package/src/commands/ai-audio.ts +0 -941
  62. package/src/commands/ai-broll.ts +0 -490
  63. package/src/commands/ai-edit-cli.ts +0 -658
  64. package/src/commands/ai-edit.ts +0 -1542
  65. package/src/commands/ai-fill-gaps.ts +0 -566
  66. package/src/commands/ai-helpers.ts +0 -65
  67. package/src/commands/ai-highlights.ts +0 -1303
  68. package/src/commands/ai-image.ts +0 -761
  69. package/src/commands/ai-motion.ts +0 -347
  70. package/src/commands/ai-narrate.ts +0 -451
  71. package/src/commands/ai-review.ts +0 -309
  72. package/src/commands/ai-script-pipeline-cli.ts +0 -1710
  73. package/src/commands/ai-script-pipeline.ts +0 -1365
  74. package/src/commands/ai-suggest-edit.ts +0 -264
  75. package/src/commands/ai-video-fx.ts +0 -445
  76. package/src/commands/ai-video.ts +0 -915
  77. package/src/commands/ai-viral.ts +0 -595
  78. package/src/commands/ai-visual-fx.ts +0 -601
  79. package/src/commands/ai.test.ts +0 -627
  80. package/src/commands/ai.ts +0 -307
  81. package/src/commands/analyze.ts +0 -282
  82. package/src/commands/audio.ts +0 -644
  83. package/src/commands/batch.test.ts +0 -279
  84. package/src/commands/batch.ts +0 -440
  85. package/src/commands/detect.ts +0 -329
  86. package/src/commands/doctor.ts +0 -237
  87. package/src/commands/edit-cmd.ts +0 -1014
  88. package/src/commands/export.ts +0 -918
  89. package/src/commands/generate.ts +0 -2146
  90. package/src/commands/media.ts +0 -177
  91. package/src/commands/output.ts +0 -142
  92. package/src/commands/pipeline.ts +0 -398
  93. package/src/commands/project.test.ts +0 -127
  94. package/src/commands/project.ts +0 -149
  95. package/src/commands/sanitize.ts +0 -60
  96. package/src/commands/schema.ts +0 -130
  97. package/src/commands/setup.ts +0 -509
  98. package/src/commands/timeline.test.ts +0 -499
  99. package/src/commands/timeline.ts +0 -529
  100. package/src/commands/validate.ts +0 -77
  101. package/src/config/config.test.ts +0 -197
  102. package/src/config/index.ts +0 -125
  103. package/src/config/schema.ts +0 -82
  104. package/src/engine/index.ts +0 -2
  105. package/src/engine/project.test.ts +0 -702
  106. package/src/engine/project.ts +0 -439
  107. package/src/index.ts +0 -146
  108. package/src/utils/api-key.test.ts +0 -41
  109. package/src/utils/api-key.ts +0 -247
  110. package/src/utils/audio.ts +0 -83
  111. package/src/utils/exec-safe.ts +0 -75
  112. package/src/utils/first-run.ts +0 -52
  113. package/src/utils/provider-resolver.ts +0 -56
  114. package/src/utils/remotion.ts +0 -951
  115. package/src/utils/subtitle.test.ts +0 -227
  116. package/src/utils/subtitle.ts +0 -169
  117. package/src/utils/tty.ts +0 -196
  118. 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
- }