varg.ai-sdk 0.1.0 → 0.4.0-alpha.1

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 (236) hide show
  1. package/.claude/settings.local.json +1 -1
  2. package/.env.example +3 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.husky/README.md +102 -0
  5. package/.husky/commit-msg +6 -0
  6. package/.husky/pre-commit +9 -0
  7. package/.husky/pre-push +6 -0
  8. package/.size-limit.json +8 -0
  9. package/.test-hooks.ts +5 -0
  10. package/CLAUDE.md +10 -3
  11. package/CONTRIBUTING.md +150 -0
  12. package/LICENSE.md +53 -0
  13. package/README.md +56 -209
  14. package/SKILLS.md +26 -10
  15. package/biome.json +7 -1
  16. package/bun.lock +1286 -0
  17. package/commitlint.config.js +22 -0
  18. package/docs/index.html +1130 -0
  19. package/docs/prompting.md +326 -0
  20. package/docs/react.md +834 -0
  21. package/docs/sdk.md +812 -0
  22. package/ffmpeg/CLAUDE.md +68 -0
  23. package/package.json +48 -8
  24. package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
  25. package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
  26. package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
  27. package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
  28. package/pipeline/cookbooks/text-to-tiktok.md +669 -0
  29. package/pipeline/cookbooks/trendwatching.md +156 -0
  30. package/plan.md +281 -0
  31. package/scripts/.gitkeep +0 -0
  32. package/src/ai-sdk/cache.ts +142 -0
  33. package/src/ai-sdk/examples/cached-generation.ts +53 -0
  34. package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
  35. package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
  36. package/src/ai-sdk/examples/duet-video.ts +56 -0
  37. package/src/ai-sdk/examples/editly-composition.ts +63 -0
  38. package/src/ai-sdk/examples/editly-test.ts +57 -0
  39. package/src/ai-sdk/examples/editly-video-test.ts +52 -0
  40. package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
  41. package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
  42. package/src/ai-sdk/examples/music-generation.ts +19 -0
  43. package/src/ai-sdk/examples/openai-sora.ts +34 -0
  44. package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
  45. package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
  46. package/src/ai-sdk/examples/talking-lion.ts +55 -0
  47. package/src/ai-sdk/examples/video-generation.ts +39 -0
  48. package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
  49. package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
  50. package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
  51. package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
  52. package/src/ai-sdk/file-cache.ts +112 -0
  53. package/src/ai-sdk/file.ts +238 -0
  54. package/src/ai-sdk/generate-element.ts +92 -0
  55. package/src/ai-sdk/generate-music.ts +46 -0
  56. package/src/ai-sdk/generate-video.ts +165 -0
  57. package/src/ai-sdk/index.ts +72 -0
  58. package/src/ai-sdk/music-model.ts +110 -0
  59. package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
  60. package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
  61. package/src/ai-sdk/providers/editly/index.ts +817 -0
  62. package/src/ai-sdk/providers/editly/layers.ts +776 -0
  63. package/src/ai-sdk/providers/editly/plan.md +144 -0
  64. package/src/ai-sdk/providers/editly/types.ts +328 -0
  65. package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
  66. package/src/ai-sdk/providers/fal-provider.ts +512 -0
  67. package/src/ai-sdk/providers/higgsfield.ts +379 -0
  68. package/src/ai-sdk/providers/openai.ts +251 -0
  69. package/src/ai-sdk/providers/replicate.ts +16 -0
  70. package/src/ai-sdk/video-model.ts +185 -0
  71. package/src/cli/commands/find.tsx +137 -0
  72. package/src/cli/commands/help.tsx +85 -0
  73. package/src/cli/commands/index.ts +6 -0
  74. package/src/cli/commands/list.tsx +238 -0
  75. package/src/cli/commands/render.tsx +71 -0
  76. package/src/cli/commands/run.tsx +511 -0
  77. package/src/cli/commands/which.tsx +253 -0
  78. package/src/cli/index.ts +114 -0
  79. package/src/cli/quiet.ts +44 -0
  80. package/src/cli/types.ts +32 -0
  81. package/src/cli/ui/components/Badge.tsx +29 -0
  82. package/src/cli/ui/components/DataTable.tsx +51 -0
  83. package/src/cli/ui/components/Header.tsx +23 -0
  84. package/src/cli/ui/components/HelpBlock.tsx +44 -0
  85. package/src/cli/ui/components/KeyValue.tsx +33 -0
  86. package/src/cli/ui/components/OptionRow.tsx +81 -0
  87. package/src/cli/ui/components/Separator.tsx +23 -0
  88. package/src/cli/ui/components/StatusBox.tsx +108 -0
  89. package/src/cli/ui/components/VargBox.tsx +51 -0
  90. package/src/cli/ui/components/VargProgress.tsx +36 -0
  91. package/src/cli/ui/components/VargSpinner.tsx +34 -0
  92. package/src/cli/ui/components/VargText.tsx +56 -0
  93. package/src/cli/ui/components/index.ts +19 -0
  94. package/src/cli/ui/index.ts +12 -0
  95. package/src/cli/ui/render.ts +35 -0
  96. package/src/cli/ui/theme.ts +63 -0
  97. package/src/cli/utils.ts +78 -0
  98. package/src/core/executor/executor.ts +201 -0
  99. package/src/core/executor/index.ts +13 -0
  100. package/src/core/executor/job.ts +214 -0
  101. package/src/core/executor/pipeline.ts +222 -0
  102. package/src/core/index.ts +11 -0
  103. package/src/core/registry/index.ts +9 -0
  104. package/src/core/registry/loader.ts +149 -0
  105. package/src/core/registry/registry.ts +221 -0
  106. package/src/core/registry/resolver.ts +206 -0
  107. package/src/core/schema/helpers.ts +134 -0
  108. package/src/core/schema/index.ts +8 -0
  109. package/src/core/schema/shared.ts +102 -0
  110. package/src/core/schema/types.ts +279 -0
  111. package/src/core/schema/validator.ts +92 -0
  112. package/src/definitions/actions/captions.ts +261 -0
  113. package/src/definitions/actions/edit.ts +298 -0
  114. package/src/definitions/actions/image.ts +125 -0
  115. package/src/definitions/actions/index.ts +114 -0
  116. package/src/definitions/actions/music.ts +205 -0
  117. package/src/definitions/actions/sync.ts +128 -0
  118. package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +63 -90
  119. package/src/definitions/actions/upload.ts +111 -0
  120. package/src/definitions/actions/video.ts +163 -0
  121. package/src/definitions/actions/voice.ts +119 -0
  122. package/src/definitions/index.ts +23 -0
  123. package/src/definitions/models/elevenlabs.ts +50 -0
  124. package/src/definitions/models/flux.ts +56 -0
  125. package/src/definitions/models/index.ts +36 -0
  126. package/src/definitions/models/kling.ts +56 -0
  127. package/src/definitions/models/llama.ts +54 -0
  128. package/src/definitions/models/nano-banana-pro.ts +102 -0
  129. package/src/definitions/models/sonauto.ts +68 -0
  130. package/src/definitions/models/soul.ts +65 -0
  131. package/src/definitions/models/wan.ts +54 -0
  132. package/src/definitions/models/whisper.ts +44 -0
  133. package/src/definitions/skills/index.ts +12 -0
  134. package/src/definitions/skills/talking-character.ts +87 -0
  135. package/src/definitions/skills/text-to-tiktok.ts +97 -0
  136. package/src/index.ts +118 -0
  137. package/src/providers/apify.ts +269 -0
  138. package/src/providers/base.ts +264 -0
  139. package/src/providers/elevenlabs.ts +217 -0
  140. package/src/providers/fal.ts +392 -0
  141. package/src/providers/ffmpeg.ts +544 -0
  142. package/src/providers/fireworks.ts +193 -0
  143. package/src/providers/groq.ts +149 -0
  144. package/src/providers/higgsfield.ts +145 -0
  145. package/src/providers/index.ts +143 -0
  146. package/src/providers/replicate.ts +147 -0
  147. package/src/providers/storage.ts +206 -0
  148. package/src/react/cli.ts +52 -0
  149. package/src/react/elements.ts +146 -0
  150. package/src/react/examples/branching.tsx +66 -0
  151. package/src/react/examples/captions-demo.tsx +37 -0
  152. package/src/react/examples/character-video.tsx +84 -0
  153. package/src/react/examples/grid.tsx +53 -0
  154. package/src/react/examples/layouts-demo.tsx +57 -0
  155. package/src/react/examples/madi.tsx +60 -0
  156. package/src/react/examples/music-test.tsx +35 -0
  157. package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
  158. package/src/react/examples/orange-portrait.tsx +41 -0
  159. package/src/react/examples/split-element-demo.tsx +60 -0
  160. package/src/react/examples/split-layout-demo.tsx +60 -0
  161. package/src/react/examples/split.tsx +41 -0
  162. package/src/react/examples/video-grid.tsx +46 -0
  163. package/src/react/index.ts +43 -0
  164. package/src/react/layouts/grid.tsx +28 -0
  165. package/src/react/layouts/index.ts +2 -0
  166. package/src/react/layouts/split.tsx +20 -0
  167. package/src/react/react.test.ts +309 -0
  168. package/src/react/render.ts +21 -0
  169. package/src/react/renderers/animate.ts +59 -0
  170. package/src/react/renderers/captions.ts +297 -0
  171. package/src/react/renderers/clip.ts +248 -0
  172. package/src/react/renderers/context.ts +17 -0
  173. package/src/react/renderers/image.ts +109 -0
  174. package/src/react/renderers/index.ts +22 -0
  175. package/src/react/renderers/music.ts +60 -0
  176. package/src/react/renderers/packshot.ts +84 -0
  177. package/src/react/renderers/progress.ts +173 -0
  178. package/src/react/renderers/render.ts +243 -0
  179. package/src/react/renderers/slider.ts +69 -0
  180. package/src/react/renderers/speech.ts +53 -0
  181. package/src/react/renderers/split.ts +91 -0
  182. package/src/react/renderers/subtitle.ts +16 -0
  183. package/src/react/renderers/swipe.ts +75 -0
  184. package/src/react/renderers/title.ts +17 -0
  185. package/src/react/renderers/utils.ts +124 -0
  186. package/src/react/renderers/video.ts +127 -0
  187. package/src/react/runtime/jsx-dev-runtime.ts +43 -0
  188. package/src/react/runtime/jsx-runtime.ts +35 -0
  189. package/src/react/types.ts +232 -0
  190. package/src/studio/index.ts +26 -0
  191. package/src/studio/scanner.ts +102 -0
  192. package/src/studio/server.ts +554 -0
  193. package/src/studio/stages.ts +251 -0
  194. package/src/studio/step-renderer.ts +279 -0
  195. package/src/studio/types.ts +60 -0
  196. package/src/studio/ui/cache.html +303 -0
  197. package/src/studio/ui/index.html +1820 -0
  198. package/src/tests/all.test.ts +509 -0
  199. package/src/tests/index.ts +33 -0
  200. package/src/tests/unit.test.ts +403 -0
  201. package/tsconfig.cli.json +8 -0
  202. package/tsconfig.json +21 -3
  203. package/TEST_RESULTS.md +0 -122
  204. package/action/captions/SKILL.md +0 -170
  205. package/action/captions/index.ts +0 -227
  206. package/action/edit/SKILL.md +0 -235
  207. package/action/edit/index.ts +0 -493
  208. package/action/image/SKILL.md +0 -140
  209. package/action/image/index.ts +0 -112
  210. package/action/sync/SKILL.md +0 -136
  211. package/action/sync/index.ts +0 -187
  212. package/action/transcribe/SKILL.md +0 -179
  213. package/action/video/SKILL.md +0 -116
  214. package/action/video/index.ts +0 -135
  215. package/action/voice/SKILL.md +0 -125
  216. package/action/voice/index.ts +0 -201
  217. package/index.ts +0 -38
  218. package/lib/README.md +0 -144
  219. package/lib/ai-sdk/fal.ts +0 -106
  220. package/lib/ai-sdk/replicate.ts +0 -107
  221. package/lib/elevenlabs.ts +0 -382
  222. package/lib/fal.ts +0 -478
  223. package/lib/ffmpeg.ts +0 -467
  224. package/lib/fireworks.ts +0 -235
  225. package/lib/groq.ts +0 -246
  226. package/lib/higgsfield.ts +0 -176
  227. package/lib/remotion/SKILL.md +0 -823
  228. package/lib/remotion/cli.ts +0 -115
  229. package/lib/remotion/functions.ts +0 -283
  230. package/lib/remotion/index.ts +0 -19
  231. package/lib/remotion/templates.ts +0 -73
  232. package/lib/replicate.ts +0 -304
  233. package/output.txt +0 -1
  234. package/test-import.ts +0 -7
  235. package/test-services.ts +0 -97
  236. package/utilities/s3.ts +0 -147
@@ -0,0 +1,81 @@
1
+ /**
2
+ * OptionRow - CLI option display with better spacing
3
+ * Shows option name, description, type hints, defaults, and enums
4
+ */
5
+
6
+ import { Box, Text } from "ink";
7
+ import { theme } from "../theme.ts";
8
+
9
+ interface OptionRowProps {
10
+ name: string;
11
+ description?: string;
12
+ required?: boolean;
13
+ defaultValue?: unknown;
14
+ enumValues?: (string | number)[];
15
+ type?: string;
16
+ nameWidth?: number;
17
+ }
18
+
19
+ export function OptionRow({
20
+ name,
21
+ description,
22
+ required = false,
23
+ defaultValue,
24
+ enumValues,
25
+ type,
26
+ nameWidth = theme.layout.optionNameWidth,
27
+ }: OptionRowProps) {
28
+ const paddedName = `--${name}`.padEnd(nameWidth);
29
+ const hasDefault = defaultValue !== undefined;
30
+ const hasEnum = enumValues && enumValues.length > 0;
31
+
32
+ // Format enum values - if many values, show abbreviated
33
+ const formatEnums = (values: (string | number)[]) => {
34
+ const stringValues = values.map(String);
35
+ const joined = stringValues.join(", ");
36
+ if (joined.length > 50) {
37
+ // Show first few and count
38
+ const shown = stringValues.slice(0, 4).join(", ");
39
+ return `${shown}, ... (${values.length} options)`;
40
+ }
41
+ return joined;
42
+ };
43
+
44
+ return (
45
+ <Box flexDirection="column" marginBottom={1}>
46
+ {/* Main line: name + description + required */}
47
+ <Box>
48
+ <Text color={theme.colors.accent}>{paddedName}</Text>
49
+ <Text>{description || ""}</Text>
50
+ {required && <Text color={theme.colors.warning}> (required)</Text>}
51
+ </Box>
52
+
53
+ {/* Type and default on same line if compact */}
54
+ {(type || hasDefault) && (
55
+ <Box paddingLeft={nameWidth}>
56
+ {type && (
57
+ <Text dimColor>
58
+ {"<"}
59
+ {type}
60
+ {">"}
61
+ </Text>
62
+ )}
63
+ {hasDefault && (
64
+ <Text dimColor>
65
+ {type ? " " : ""}default: {String(defaultValue)}
66
+ </Text>
67
+ )}
68
+ </Box>
69
+ )}
70
+
71
+ {/* Enum values on separate line for readability */}
72
+ {hasEnum && (
73
+ <Box paddingLeft={nameWidth}>
74
+ <Text dimColor>[{formatEnums(enumValues)}]</Text>
75
+ </Box>
76
+ )}
77
+ </Box>
78
+ );
79
+ }
80
+
81
+ export default OptionRow;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Separator - Horizontal divider line
3
+ * Minimal visual separator between sections
4
+ */
5
+
6
+ import { Box, Text } from "ink";
7
+ import { theme } from "../theme.ts";
8
+
9
+ interface SeparatorProps {
10
+ width?: number;
11
+ }
12
+
13
+ export function Separator({
14
+ width = theme.layout.maxWidth - 4,
15
+ }: SeparatorProps) {
16
+ return (
17
+ <Box>
18
+ <Text dimColor>{"─".repeat(width)}</Text>
19
+ </Box>
20
+ );
21
+ }
22
+
23
+ export default Separator;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * StatusBox - Execution status display
3
+ * Shows running/done/error state with params and results
4
+ */
5
+
6
+ import { Box, Text } from "ink";
7
+ import { icons, theme } from "../theme.ts";
8
+ import KeyValue from "./KeyValue.tsx";
9
+ import VargBox from "./VargBox.tsx";
10
+ import { VargProgress } from "./VargProgress.tsx";
11
+ import VargSpinner from "./VargSpinner.tsx";
12
+ import { VargText } from "./VargText.tsx";
13
+
14
+ type Status = "running" | "done" | "error";
15
+
16
+ interface StatusBoxProps {
17
+ title: string;
18
+ status: Status;
19
+ params?: Record<string, string>;
20
+ output?: string;
21
+ error?: string;
22
+ duration?: number;
23
+ progress?: number; // 0-100, shows progress bar when provided
24
+ }
25
+
26
+ const statusConfig: Record<
27
+ Status,
28
+ { icon: string; color: string; label: string }
29
+ > = {
30
+ running: {
31
+ icon: icons.running,
32
+ color: theme.colors.accent,
33
+ label: "running",
34
+ },
35
+ done: { icon: icons.success, color: theme.colors.success, label: "done" },
36
+ error: { icon: icons.error, color: theme.colors.error, label: "error" },
37
+ };
38
+
39
+ function formatDuration(ms: number): string {
40
+ if (ms >= 1000) {
41
+ return `${(ms / 1000).toFixed(1)}s`;
42
+ }
43
+ return `${ms}ms`;
44
+ }
45
+
46
+ export function StatusBox({
47
+ title,
48
+ status,
49
+ params,
50
+ output,
51
+ error,
52
+ duration,
53
+ progress,
54
+ }: StatusBoxProps) {
55
+ const config = statusConfig[status];
56
+
57
+ return (
58
+ <VargBox title={title} variant="bordered">
59
+ {/* Status indicator */}
60
+ <Box marginY={1}>
61
+ {status === "running" ? (
62
+ <VargSpinner label={config.label} />
63
+ ) : (
64
+ <Text color={config.color}>
65
+ {config.icon} {config.label}
66
+ </Text>
67
+ )}
68
+ </Box>
69
+
70
+ {/* Progress bar (when provided) */}
71
+ {progress !== undefined && status === "running" && (
72
+ <Box marginBottom={1}>
73
+ <VargProgress value={progress} label="processing" />
74
+ </Box>
75
+ )}
76
+
77
+ {/* Parameters */}
78
+ {params && Object.keys(params).length > 0 && (
79
+ <Box flexDirection="column" marginBottom={1}>
80
+ {Object.entries(params).map(([key, value]) => (
81
+ <KeyValue key={key} label={key} value={value} />
82
+ ))}
83
+ </Box>
84
+ )}
85
+
86
+ {/* Output */}
87
+ {output && (
88
+ <Box marginTop={1}>
89
+ <KeyValue label="output" value={output} />
90
+ </Box>
91
+ )}
92
+
93
+ {/* Error */}
94
+ {error && (
95
+ <Box marginTop={1}>
96
+ <VargText variant="error">error: {error}</VargText>
97
+ </Box>
98
+ )}
99
+
100
+ {/* Duration */}
101
+ {duration !== undefined && (
102
+ <KeyValue label="time" value={formatDuration(duration)} />
103
+ )}
104
+ </VargBox>
105
+ );
106
+ }
107
+
108
+ export default StatusBox;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * VargBox - Container component
3
+ * Luxury minimal styled box with optional title and borders
4
+ */
5
+
6
+ import { Box, Text } from "ink";
7
+ import type { ReactNode } from "react";
8
+ import { theme } from "../theme.ts";
9
+
10
+ type BorderStyle = "single" | "double" | "round" | "bold" | "classic";
11
+
12
+ interface VargBoxProps {
13
+ title?: string;
14
+ subtitle?: string;
15
+ variant?: "default" | "minimal" | "bordered";
16
+ width?: number;
17
+ children: ReactNode;
18
+ }
19
+
20
+ export function VargBox({
21
+ title,
22
+ subtitle,
23
+ variant = "default",
24
+ width = theme.layout.maxWidth,
25
+ children,
26
+ }: VargBoxProps) {
27
+ const showBorder = variant === "bordered" || (variant === "default" && title);
28
+
29
+ return (
30
+ <Box
31
+ flexDirection="column"
32
+ width={width}
33
+ borderStyle={
34
+ showBorder ? (theme.borders.style as BorderStyle) : undefined
35
+ }
36
+ borderColor={theme.colors.border}
37
+ paddingX={theme.layout.boxPadding}
38
+ paddingY={variant === "minimal" ? 0 : 1}
39
+ >
40
+ {title && (
41
+ <Box marginBottom={1}>
42
+ <Text bold>{title}</Text>
43
+ {subtitle && <Text dimColor> {subtitle}</Text>}
44
+ </Box>
45
+ )}
46
+ {children}
47
+ </Box>
48
+ );
49
+ }
50
+
51
+ export default VargBox;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * VargProgress - Progress bar component
3
+ * Elegant thin progress visualization
4
+ */
5
+
6
+ import { Text } from "ink";
7
+ import { theme } from "../theme.ts";
8
+
9
+ interface VargProgressProps {
10
+ value: number; // 0-100
11
+ width?: number;
12
+ showPercentage?: boolean;
13
+ label?: string;
14
+ }
15
+
16
+ export function VargProgress({
17
+ value,
18
+ width = 24,
19
+ showPercentage = true,
20
+ label,
21
+ }: VargProgressProps) {
22
+ const clampedValue = Math.max(0, Math.min(100, value));
23
+ const filled = Math.round((clampedValue / 100) * width);
24
+ const empty = width - filled;
25
+
26
+ return (
27
+ <Text>
28
+ <Text color={theme.colors.accent}>{"█".repeat(filled)}</Text>
29
+ <Text dimColor>{"░".repeat(empty)}</Text>
30
+ {showPercentage && <Text dimColor> {clampedValue}%</Text>}
31
+ {label && <Text dimColor> {label}</Text>}
32
+ </Text>
33
+ );
34
+ }
35
+
36
+ export default VargProgress;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * VargSpinner - Animated loading indicator
3
+ * Elegant braille spinner with label
4
+ */
5
+
6
+ import { Text } from "ink";
7
+ import { useEffect, useState } from "react";
8
+ import { theme } from "../theme.ts";
9
+
10
+ interface VargSpinnerProps {
11
+ label?: string;
12
+ }
13
+
14
+ export function VargSpinner({ label }: VargSpinnerProps) {
15
+ const [frame, setFrame] = useState(0);
16
+ const frames = theme.animation.spinnerFrames;
17
+
18
+ useEffect(() => {
19
+ const timer = setInterval(() => {
20
+ setFrame((f) => (f + 1) % frames.length);
21
+ }, theme.animation.spinnerInterval);
22
+
23
+ return () => clearInterval(timer);
24
+ }, [frames.length]);
25
+
26
+ return (
27
+ <Text>
28
+ <Text color={theme.colors.accent}>{frames[frame]}</Text>
29
+ {label && <Text> {label}</Text>}
30
+ </Text>
31
+ );
32
+ }
33
+
34
+ export default VargSpinner;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * VargText - Typography component
3
+ * Provides semantic text variants for consistent styling
4
+ */
5
+
6
+ import { Text } from "ink";
7
+ import type { ReactNode } from "react";
8
+ import { theme } from "../theme.ts";
9
+
10
+ export type TextVariant =
11
+ | "body"
12
+ | "label"
13
+ | "code"
14
+ | "muted"
15
+ | "accent"
16
+ | "success"
17
+ | "error"
18
+ | "warning";
19
+
20
+ interface VargTextProps {
21
+ variant?: TextVariant;
22
+ bold?: boolean;
23
+ dimColor?: boolean;
24
+ children: ReactNode;
25
+ }
26
+
27
+ const variantStyles: Record<
28
+ TextVariant,
29
+ { color?: string; dimColor?: boolean }
30
+ > = {
31
+ body: {},
32
+ label: { dimColor: true },
33
+ code: { color: theme.colors.accent },
34
+ muted: { dimColor: true },
35
+ accent: { color: theme.colors.accent },
36
+ success: { color: theme.colors.success },
37
+ error: { color: theme.colors.error },
38
+ warning: { color: theme.colors.warning },
39
+ };
40
+
41
+ export function VargText({
42
+ variant = "body",
43
+ bold,
44
+ dimColor,
45
+ children,
46
+ }: VargTextProps) {
47
+ const style = variantStyles[variant];
48
+
49
+ return (
50
+ <Text color={style.color} dimColor={dimColor ?? style.dimColor} bold={bold}>
51
+ {children}
52
+ </Text>
53
+ );
54
+ }
55
+
56
+ export default VargText;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Varg UI Components
3
+ * Re-exports all UI components for the CLI
4
+ */
5
+
6
+ export { Badge } from "./Badge.tsx";
7
+ export { DataTable } from "./DataTable.tsx";
8
+ export { Header } from "./Header.tsx";
9
+ export { HelpBlock } from "./HelpBlock.tsx";
10
+ export { KeyValue } from "./KeyValue.tsx";
11
+ export { OptionRow } from "./OptionRow.tsx";
12
+ export { Separator } from "./Separator.tsx";
13
+ export { StatusBox } from "./StatusBox.tsx";
14
+ export { VargBox } from "./VargBox.tsx";
15
+ export { VargProgress } from "./VargProgress.tsx";
16
+ export { VargSpinner } from "./VargSpinner.tsx";
17
+ // Re-export types
18
+ export type { TextVariant } from "./VargText.tsx";
19
+ export { VargText } from "./VargText.tsx";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Varg CLI UI Module
3
+ * Ink-based React components for the CLI
4
+ */
5
+
6
+ // Components
7
+ export * from "./components/index.ts";
8
+ // Render helpers
9
+ export { renderAndWait, renderLive, renderStatic } from "./render.ts";
10
+ export type { ThemeColors } from "./theme.ts";
11
+ // Theme and tokens
12
+ export { icons, theme } from "./theme.ts";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Citty ↔ Ink Bridge
3
+ * Helpers to render Ink components from citty commands
4
+ */
5
+
6
+ import { render } from "ink";
7
+ import type { ReactElement } from "react";
8
+
9
+ /**
10
+ * Render a static component that auto-exits after mounting
11
+ * Use for: list, help, which, find commands
12
+ */
13
+ export function renderStatic(element: ReactElement): void {
14
+ const { unmount } = render(element);
15
+ // Give React time to render, then unmount
16
+ setTimeout(() => unmount(), 50);
17
+ }
18
+
19
+ /**
20
+ * Render a component with live updates
21
+ * Use for: run command with spinner/progress
22
+ * Returns control handle for manual rerender and unmount
23
+ */
24
+ export function renderLive(element: ReactElement) {
25
+ return render(element);
26
+ }
27
+
28
+ /**
29
+ * Render and wait for component to signal exit
30
+ * Use for: interactive components (future)
31
+ */
32
+ export async function renderAndWait(element: ReactElement): Promise<void> {
33
+ const { waitUntilExit } = render(element);
34
+ await waitUntilExit();
35
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Design System for Varg CLI
3
+ * Luxury Minimal aesthetic - Vercel/Linear inspired
4
+ */
5
+
6
+ export const theme = {
7
+ colors: {
8
+ // Primary accent - single color for emphasis
9
+ accent: "cyan",
10
+
11
+ // Semantic colors
12
+ success: "green",
13
+ error: "red",
14
+ warning: "yellow",
15
+
16
+ // Neutrals (Ink uses color names, not hex)
17
+ text: {
18
+ primary: "white",
19
+ secondary: "gray",
20
+ muted: "gray",
21
+ },
22
+
23
+ // Borders
24
+ border: "gray",
25
+ },
26
+
27
+ spacing: {
28
+ xs: 1,
29
+ sm: 2,
30
+ md: 3,
31
+ lg: 4,
32
+ },
33
+
34
+ borders: {
35
+ style: "round" as const, // Rounded corners: ╭╮╰╯
36
+ },
37
+
38
+ animation: {
39
+ // Elegant braille spinner
40
+ spinnerFrames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
41
+ spinnerInterval: 80,
42
+ },
43
+
44
+ layout: {
45
+ maxWidth: 100,
46
+ boxPadding: 2,
47
+ optionNameWidth: 22,
48
+ },
49
+ } as const;
50
+
51
+ // Status icons - refined geometric shapes
52
+ export const icons = {
53
+ running: "●",
54
+ success: "✓",
55
+ error: "✗",
56
+ warning: "!",
57
+ info: "○",
58
+ arrow: "→",
59
+ bullet: "·",
60
+ required: "*",
61
+ } as const;
62
+
63
+ export type ThemeColors = keyof typeof theme.colors;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * CLI utilities
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import type { Definition } from "../core/schema/types";
7
+ import type { JsonSchema, NotFoundOptions } from "./types";
8
+
9
+ /**
10
+ * Handle "not found" errors consistently
11
+ */
12
+ export function handleNotFound(
13
+ name: string,
14
+ options: NotFoundOptions = {},
15
+ ): never | undefined {
16
+ const {
17
+ suggestions = [],
18
+ maxSuggestions = 3,
19
+ hint = "run `varg list` to see available items",
20
+ errorColorFn = (s) => s,
21
+ hintColorFn = (s) => s,
22
+ exit = true,
23
+ } = options;
24
+
25
+ console.error(`\n ${errorColorFn("not found:")} '${name}'\n`);
26
+
27
+ if (suggestions.length > 0) {
28
+ const shown = suggestions.slice(0, maxSuggestions).join(", ");
29
+ console.log(` did you mean: ${shown}?\n`);
30
+ }
31
+
32
+ if (hint) {
33
+ console.log(` ${hintColorFn(hint)}\n`);
34
+ }
35
+
36
+ if (exit) {
37
+ process.exit(1);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Convert a definition to a table row object
43
+ */
44
+ export const definitionToRow = (d: Definition) => ({
45
+ name: d.name,
46
+ description: d.description,
47
+ type: d.type,
48
+ });
49
+
50
+ /**
51
+ * Convert an array of definitions to table rows
52
+ */
53
+ export const definitionsToRows = (defs: Definition[]) =>
54
+ defs.map(definitionToRow);
55
+
56
+ /**
57
+ * Get JSON Schema for display - converts Zod schema to JSON Schema
58
+ */
59
+ export function getDisplaySchema(item: Definition): {
60
+ input: JsonSchema;
61
+ output: JsonSchema;
62
+ } {
63
+ if (!item.schema) {
64
+ return {
65
+ input: { type: "object", properties: {}, required: [] },
66
+ output: { type: "object" },
67
+ };
68
+ }
69
+
70
+ const input = z.toJSONSchema(item.schema.input, {
71
+ io: "input",
72
+ }) as JsonSchema;
73
+ const output = item.schema.output
74
+ ? (z.toJSONSchema(item.schema.output, { io: "output" }) as JsonSchema)
75
+ : { type: "object" };
76
+
77
+ return { input, output };
78
+ }