mobius-loop 1.0.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 (72) hide show
  1. package/.claude/commands/linear/define.md +22 -0
  2. package/.claude/commands/linear/execute.md +22 -0
  3. package/.claude/commands/linear/refine.md +22 -0
  4. package/.claude/commands/linear/verify.md +22 -0
  5. package/.claude/skills/define-linear-issue/SKILL.md +386 -0
  6. package/.claude/skills/execute-linear-issue/SKILL.md +629 -0
  7. package/.claude/skills/refine-linear-issue/SKILL.md +379 -0
  8. package/.claude/skills/verify-linear-issue/SKILL.md +663 -0
  9. package/AGENTS.md +70 -0
  10. package/LICENSE +21 -0
  11. package/README.md +457 -0
  12. package/dist/bin/mobius.d.ts +3 -0
  13. package/dist/bin/mobius.d.ts.map +1 -0
  14. package/dist/bin/mobius.js +75 -0
  15. package/dist/bin/mobius.js.map +1 -0
  16. package/dist/commands/config.d.ts +6 -0
  17. package/dist/commands/config.d.ts.map +1 -0
  18. package/dist/commands/config.js +88 -0
  19. package/dist/commands/config.js.map +1 -0
  20. package/dist/commands/doctor.d.ts +2 -0
  21. package/dist/commands/doctor.d.ts.map +1 -0
  22. package/dist/commands/doctor.js +86 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/run.d.ts +10 -0
  25. package/dist/commands/run.d.ts.map +1 -0
  26. package/dist/commands/run.js +62 -0
  27. package/dist/commands/run.js.map +1 -0
  28. package/dist/commands/setup.d.ts +2 -0
  29. package/dist/commands/setup.d.ts.map +1 -0
  30. package/dist/commands/setup.js +131 -0
  31. package/dist/commands/setup.js.map +1 -0
  32. package/dist/lib/checks/cclean.d.ts +3 -0
  33. package/dist/lib/checks/cclean.d.ts.map +1 -0
  34. package/dist/lib/checks/cclean.js +23 -0
  35. package/dist/lib/checks/cclean.js.map +1 -0
  36. package/dist/lib/checks/claude.d.ts +3 -0
  37. package/dist/lib/checks/claude.d.ts.map +1 -0
  38. package/dist/lib/checks/claude.js +38 -0
  39. package/dist/lib/checks/claude.js.map +1 -0
  40. package/dist/lib/checks/config.d.ts +4 -0
  41. package/dist/lib/checks/config.d.ts.map +1 -0
  42. package/dist/lib/checks/config.js +45 -0
  43. package/dist/lib/checks/config.js.map +1 -0
  44. package/dist/lib/checks/docker.d.ts +3 -0
  45. package/dist/lib/checks/docker.d.ts.map +1 -0
  46. package/dist/lib/checks/docker.js +46 -0
  47. package/dist/lib/checks/docker.js.map +1 -0
  48. package/dist/lib/checks/linear-mcp.d.ts +3 -0
  49. package/dist/lib/checks/linear-mcp.d.ts.map +1 -0
  50. package/dist/lib/checks/linear-mcp.js +46 -0
  51. package/dist/lib/checks/linear-mcp.js.map +1 -0
  52. package/dist/lib/checks/path.d.ts +5 -0
  53. package/dist/lib/checks/path.d.ts.map +1 -0
  54. package/dist/lib/checks/path.js +49 -0
  55. package/dist/lib/checks/path.js.map +1 -0
  56. package/dist/lib/config.d.ts +37 -0
  57. package/dist/lib/config.d.ts.map +1 -0
  58. package/dist/lib/config.js +145 -0
  59. package/dist/lib/config.js.map +1 -0
  60. package/dist/lib/paths.d.ts +39 -0
  61. package/dist/lib/paths.d.ts.map +1 -0
  62. package/dist/lib/paths.js +117 -0
  63. package/dist/lib/paths.js.map +1 -0
  64. package/dist/types.d.ts +39 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +19 -0
  67. package/dist/types.js.map +1 -0
  68. package/mobius.config.yaml +38 -0
  69. package/package.json +56 -0
  70. package/scripts/mobius.sh +394 -0
  71. package/scripts/render-diagrams.sh +38 -0
  72. package/scripts/render-terminal.sh +49 -0
@@ -0,0 +1,394 @@
1
+ #!/bin/bash
2
+ # Mobius - AI-Powered Development Workflow Tool
3
+ #
4
+ # Executes sub-tasks of an issue in a loop using Claude skills.
5
+ # Supports multiple planning backends (Linear, Jira, etc.)
6
+ #
7
+ # Usage:
8
+ # mobius VER-159 # Execute sub-tasks (uses default backend)
9
+ # mobius VER-159 10 # Max 10 iterations
10
+ # mobius VER-159 --local # Run locally (bypass sandbox)
11
+ # mobius VER-159 --backend=jira # Use Jira backend
12
+ #
13
+ # Configuration:
14
+ # ~/.config/mobius/config.yaml # User config (takes precedence)
15
+ # Environment variables # Override config file settings
16
+
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
21
+ CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mobius"
22
+ # Support config path from npm wrapper (MOBIUS_CONFIG_FILE env var)
23
+ CONFIG_FILE="${MOBIUS_CONFIG_FILE:-$CONFIG_DIR/config.yaml}"
24
+ DEFAULT_CONFIG="$PROJECT_ROOT/mobius.config.yaml"
25
+
26
+ # Default configuration (can be overridden by config file or environment)
27
+ DELAY_SECONDS="${MOBIUS_DELAY_SECONDS:-3}"
28
+ DEFAULT_BACKEND="${MOBIUS_BACKEND:-linear}"
29
+ MAX_ITERATIONS_DEFAULT="${MOBIUS_MAX_ITERATIONS:-50}"
30
+ CONTAINER_NAME="${MOBIUS_CONTAINER:-mobius-sandbox}"
31
+ MODEL="${MOBIUS_MODEL:-opus}"
32
+ USE_SANDBOX="${MOBIUS_SANDBOX_ENABLED:-true}"
33
+
34
+ # Backend skill mappings
35
+ declare -A BACKEND_SKILLS=(
36
+ [linear]="/execute-linear-issue"
37
+ [jira]="/execute-jira-issue" # Future: implement Jira skill
38
+ )
39
+
40
+ declare -A BACKEND_ID_PATTERNS=(
41
+ [linear]='^[A-Z]+-[0-9]+$'
42
+ [jira]='^[A-Z]+-[0-9]+$'
43
+ )
44
+
45
+ # Parse YAML config file (basic parser for simple key: value pairs)
46
+ # Supports nested keys like "execution.delay_seconds"
47
+ parse_yaml_config() {
48
+ local config_file="$1"
49
+
50
+ if [ ! -f "$config_file" ]; then
51
+ return 0
52
+ fi
53
+
54
+ local current_section=""
55
+
56
+ while IFS= read -r line || [ -n "$line" ]; do
57
+ # Skip comments and empty lines
58
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
59
+ [[ -z "${line// }" ]] && continue
60
+
61
+ # Detect section headers (no leading whitespace, ends with colon, no value)
62
+ if [[ "$line" =~ ^([a-zA-Z_][a-zA-Z0-9_]*):$ ]] || [[ "$line" =~ ^([a-zA-Z_][a-zA-Z0-9_]*):[[:space:]]*$ ]]; then
63
+ current_section="${BASH_REMATCH[1]}"
64
+ continue
65
+ fi
66
+
67
+ # Parse key: value pairs
68
+ if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*):[[:space:]]*(.+)$ ]]; then
69
+ local key="${BASH_REMATCH[1]}"
70
+ local value="${BASH_REMATCH[2]}"
71
+
72
+ # Remove comments from value
73
+ value="${value%%#*}"
74
+ # Trim whitespace
75
+ value="${value%"${value##*[![:space:]]}"}"
76
+
77
+ # Skip empty values
78
+ [ -z "$value" ] && continue
79
+
80
+ # Build full key name
81
+ local full_key="$key"
82
+ if [ -n "$current_section" ]; then
83
+ full_key="${current_section}.${key}"
84
+ fi
85
+
86
+ # Map config keys to variables (only if not already set by env)
87
+ case "$full_key" in
88
+ backend)
89
+ [ -z "${MOBIUS_BACKEND:-}" ] && DEFAULT_BACKEND="$value"
90
+ ;;
91
+ execution.delay_seconds)
92
+ [ -z "${MOBIUS_DELAY_SECONDS:-}" ] && DELAY_SECONDS="$value"
93
+ ;;
94
+ execution.max_iterations)
95
+ [ -z "${MOBIUS_MAX_ITERATIONS:-}" ] && MAX_ITERATIONS_DEFAULT="$value"
96
+ ;;
97
+ execution.model)
98
+ [ -z "${MOBIUS_MODEL:-}" ] && MODEL="$value"
99
+ ;;
100
+ execution.sandbox)
101
+ [ -z "${MOBIUS_SANDBOX_ENABLED:-}" ] && USE_SANDBOX="$value"
102
+ ;;
103
+ execution.container_name)
104
+ [ -z "${MOBIUS_CONTAINER:-}" ] && CONTAINER_NAME="$value"
105
+ ;;
106
+ esac
107
+ fi
108
+ done < "$config_file"
109
+ }
110
+
111
+ # Load configuration (user config takes precedence over defaults)
112
+ load_config() {
113
+ # Load default config if it exists
114
+ if [ -f "$DEFAULT_CONFIG" ]; then
115
+ parse_yaml_config "$DEFAULT_CONFIG"
116
+ fi
117
+
118
+ # Load user config (overrides defaults)
119
+ if [ -f "$CONFIG_FILE" ]; then
120
+ parse_yaml_config "$CONFIG_FILE"
121
+ fi
122
+ }
123
+
124
+ # Parse arguments
125
+ TASK_ID=""
126
+ MAX_ITERATIONS=0
127
+ RUN_LOCAL=false
128
+ BACKEND=""
129
+ SHOW_CONFIG=false
130
+
131
+ parse_args() {
132
+ local args=()
133
+ for arg in "$@"; do
134
+ case "$arg" in
135
+ --local|-l)
136
+ RUN_LOCAL=true
137
+ ;;
138
+ --help|-h)
139
+ show_help
140
+ exit 0
141
+ ;;
142
+ --version|-v)
143
+ echo "mobius v1.0.0"
144
+ exit 0
145
+ ;;
146
+ --config)
147
+ SHOW_CONFIG=true
148
+ ;;
149
+ --backend=*)
150
+ BACKEND="${arg#*=}"
151
+ ;;
152
+ --model=*)
153
+ MODEL="${arg#*=}"
154
+ ;;
155
+ --delay=*)
156
+ DELAY_SECONDS="${arg#*=}"
157
+ ;;
158
+ *)
159
+ args+=("$arg")
160
+ ;;
161
+ esac
162
+ done
163
+
164
+ # Show config and exit if requested
165
+ if [ "$SHOW_CONFIG" = "true" ]; then
166
+ show_config
167
+ exit 0
168
+ fi
169
+
170
+ if [ ${#args[@]} -eq 0 ]; then
171
+ echo "Error: Task ID required"
172
+ echo "Usage: mobius <TASK-ID> [max-iterations] [options]"
173
+ echo "Run 'mobius --help' for more information."
174
+ exit 1
175
+ fi
176
+
177
+ TASK_ID="${args[0]}"
178
+
179
+ # Use default backend if not specified via flag
180
+ [ -z "$BACKEND" ] && BACKEND="$DEFAULT_BACKEND"
181
+
182
+ # Validate backend
183
+ if [ -z "${BACKEND_SKILLS[$BACKEND]:-}" ]; then
184
+ echo "Error: Unknown backend: $BACKEND"
185
+ echo "Available backends: ${!BACKEND_SKILLS[*]}"
186
+ exit 1
187
+ fi
188
+
189
+ # Validate task ID format
190
+ local pattern="${BACKEND_ID_PATTERNS[$BACKEND]}"
191
+ if ! [[ "$TASK_ID" =~ $pattern ]]; then
192
+ echo "Error: Invalid task ID format for $BACKEND: $TASK_ID"
193
+ echo "Expected format: PREFIX-NUMBER (e.g., VER-159)"
194
+ exit 1
195
+ fi
196
+
197
+ # Optional max iterations from args (overrides config)
198
+ if [ ${#args[@]} -ge 2 ] && [[ "${args[1]}" =~ ^[0-9]+$ ]]; then
199
+ MAX_ITERATIONS="${args[1]}"
200
+ else
201
+ MAX_ITERATIONS="$MAX_ITERATIONS_DEFAULT"
202
+ fi
203
+ }
204
+
205
+ show_help() {
206
+ cat << EOF
207
+ mobius - AI-Powered Development Workflow Tool
208
+
209
+ Execute sub-tasks of an issue using Claude skills in an autonomous loop.
210
+
211
+ USAGE:
212
+ mobius <TASK-ID> [max-iterations] [options]
213
+
214
+ ARGUMENTS:
215
+ TASK-ID Issue ID (e.g., VER-159 for Linear, PROJ-123 for Jira)
216
+ max-iterations Maximum iterations (default: from config or 50)
217
+
218
+ OPTIONS:
219
+ --backend=NAME Planning backend to use (default: $DEFAULT_BACKEND)
220
+ --model=MODEL Claude model: opus, sonnet, haiku (default: $MODEL)
221
+ --delay=SECONDS Delay between iterations (default: $DELAY_SECONDS)
222
+ --local, -l Run locally (bypass container sandbox)
223
+ --config Show current configuration and exit
224
+ --version, -v Show version
225
+ --help, -h Show this help message
226
+
227
+ AVAILABLE BACKENDS:
228
+ ${!BACKEND_SKILLS[*]}
229
+
230
+ CONFIGURATION:
231
+ User config: $CONFIG_FILE
232
+ Default config: $DEFAULT_CONFIG
233
+
234
+ Environment variables override config file settings:
235
+ MOBIUS_BACKEND Default backend
236
+ MOBIUS_DELAY_SECONDS Delay between iterations
237
+ MOBIUS_MAX_ITERATIONS Maximum iterations
238
+ MOBIUS_MODEL Claude model
239
+ MOBIUS_CONTAINER Docker container name
240
+ MOBIUS_SANDBOX_ENABLED Enable sandbox mode (true/false)
241
+
242
+ EXAMPLES:
243
+ mobius VER-159 Execute sub-tasks of VER-159 (Linear)
244
+ mobius VER-159 10 Max 10 iterations
245
+ mobius VER-159 --local Run locally with browser access
246
+ mobius VER-159 --model=sonnet Use Sonnet model
247
+ mobius PROJ-123 --backend=jira Use Jira backend
248
+
249
+ WORKFLOW:
250
+ Each iteration:
251
+ 1. Claude runs the backend's execute skill
252
+ 2. The skill finds the next ready sub-task and executes it
253
+ 3. Sub-task is marked complete in the planning tool
254
+ 4. Loop continues until all sub-tasks are done
255
+
256
+ The loop reads AGENTS.md from your project root for context.
257
+ EOF
258
+ }
259
+
260
+ show_config() {
261
+ echo "Mobius Configuration"
262
+ echo "===================="
263
+ echo ""
264
+ echo "Config files:"
265
+ if [ -f "$CONFIG_FILE" ]; then
266
+ echo " User: $CONFIG_FILE (active)"
267
+ else
268
+ echo " User: $CONFIG_FILE (not found)"
269
+ fi
270
+ if [ -f "$DEFAULT_CONFIG" ]; then
271
+ echo " Default: $DEFAULT_CONFIG (active)"
272
+ else
273
+ echo " Default: $DEFAULT_CONFIG (not found)"
274
+ fi
275
+ echo ""
276
+ echo "Current settings:"
277
+ echo " backend: $DEFAULT_BACKEND"
278
+ echo " model: $MODEL"
279
+ echo " delay_seconds: $DELAY_SECONDS"
280
+ echo " max_iterations: $MAX_ITERATIONS_DEFAULT"
281
+ echo " sandbox: $USE_SANDBOX"
282
+ echo " container: $CONTAINER_NAME"
283
+ echo ""
284
+ echo "Available backends: ${!BACKEND_SKILLS[*]}"
285
+ }
286
+
287
+ log() { echo "[mobius] $1"; }
288
+
289
+ # Sandbox delegation (optional - requires container setup)
290
+ delegate_to_sandbox() {
291
+ local args="$TASK_ID"
292
+ if [ "$MAX_ITERATIONS" -gt 0 ]; then
293
+ args="$TASK_ID $MAX_ITERATIONS"
294
+ fi
295
+ args="$args --backend=$BACKEND"
296
+
297
+ if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"; then
298
+ log "Delegating to sandbox..."
299
+ exec docker exec -it "$CONTAINER_NAME" bash -c "cd /workspace && MOBIUS_SANDBOX=1 ./scripts/mobius.sh $args"
300
+ else
301
+ # Check if sandbox script exists
302
+ if [ -f "$PROJECT_ROOT/scripts/sandbox.sh" ]; then
303
+ log "Starting sandbox..."
304
+ "$PROJECT_ROOT/scripts/sandbox.sh"
305
+ exec docker exec -it "$CONTAINER_NAME" bash -c "cd /workspace && MOBIUS_SANDBOX=1 ./scripts/mobius.sh $args"
306
+ else
307
+ log "No sandbox configured, running locally"
308
+ RUN_LOCAL=true
309
+ fi
310
+ fi
311
+ }
312
+
313
+ # The main loop
314
+ run_loop() {
315
+ trap 'echo ""; log "Stopped"; exit 0' INT TERM
316
+
317
+ local skill="${BACKEND_SKILLS[$BACKEND]}"
318
+
319
+ echo ""
320
+ echo "================================"
321
+ echo " Mobius - AI Development Workflow"
322
+ echo "================================"
323
+ echo ""
324
+ log "Task: $TASK_ID"
325
+ log "Backend: $BACKEND"
326
+ log "Skill: $skill"
327
+ log "Model: $MODEL"
328
+ if [ "$MAX_ITERATIONS" -gt 0 ]; then
329
+ log "Max iterations: $MAX_ITERATIONS"
330
+ else
331
+ log "Max iterations: unlimited"
332
+ fi
333
+ log "Delay: ${DELAY_SECONDS}s between iterations"
334
+ log "Press Ctrl+C to stop"
335
+ echo ""
336
+
337
+ local iteration=0
338
+
339
+ while true; do
340
+ # Check max iterations
341
+ if [ "$MAX_ITERATIONS" -gt 0 ] && [ "$iteration" -ge "$MAX_ITERATIONS" ]; then
342
+ echo ""
343
+ log "Reached max iterations: $MAX_ITERATIONS"
344
+ break
345
+ fi
346
+
347
+ iteration=$((iteration + 1))
348
+ echo ""
349
+ echo "--- Iteration $iteration$([ "$MAX_ITERATIONS" -gt 0 ] && echo "/$MAX_ITERATIONS") ($(date +%H:%M:%S)) ---"
350
+ echo ""
351
+
352
+ # Run Claude with the backend-specific skill
353
+ local chrome_flag=""
354
+ if [ "$RUN_LOCAL" = "true" ]; then
355
+ chrome_flag="--chrome"
356
+ fi
357
+
358
+ # The skill will find the next ready sub-task and execute it
359
+ # If no sub-tasks remain, Claude will indicate completion
360
+ echo "$skill $TASK_ID" | claude -p \
361
+ --dangerously-skip-permissions \
362
+ --verbose \
363
+ --output-format=stream-json \
364
+ --model "$MODEL" \
365
+ $chrome_flag | cclean
366
+
367
+ echo ""
368
+ log "Waiting ${DELAY_SECONDS}s..."
369
+ sleep "$DELAY_SECONDS"
370
+ done
371
+ }
372
+
373
+ # Main
374
+ main() {
375
+ cd "$PROJECT_ROOT"
376
+
377
+ # Load configuration before parsing args
378
+ load_config
379
+
380
+ parse_args "$@"
381
+
382
+ # Delegate to sandbox unless already inside, --local flag, or sandbox disabled
383
+ if [ -z "${MOBIUS_SANDBOX:-}" ] && [ "$RUN_LOCAL" = "false" ] && [ "$USE_SANDBOX" = "true" ]; then
384
+ delegate_to_sandbox
385
+ fi
386
+
387
+ if [ "$RUN_LOCAL" = "true" ]; then
388
+ log "Running locally (sandbox bypassed)"
389
+ fi
390
+
391
+ run_loop
392
+ }
393
+
394
+ main "$@"
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ # Render Mermaid diagrams to SVG using mermaid-cli
3
+ # Usage: ./scripts/render-diagrams.sh
4
+
5
+ set -e
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
9
+ SRC_DIR="$PROJECT_DIR/assets/diagrams/src"
10
+ OUT_DIR="$PROJECT_DIR/assets/diagrams"
11
+
12
+ # Check if mermaid-cli is available
13
+ if ! command -v mmdc &> /dev/null && ! npx mmdc --version &> /dev/null 2>&1; then
14
+ echo "Installing @mermaid-js/mermaid-cli..."
15
+ npm install -g @mermaid-js/mermaid-cli
16
+ fi
17
+
18
+ echo "Rendering Mermaid diagrams..."
19
+
20
+ for file in "$SRC_DIR"/*.mmd; do
21
+ if [ -f "$file" ]; then
22
+ name=$(basename "$file" .mmd)
23
+ echo " Rendering $name.svg..."
24
+ npx -p @mermaid-js/mermaid-cli mmdc \
25
+ -i "$file" \
26
+ -o "$OUT_DIR/$name.svg" \
27
+ -t dark \
28
+ -b transparent \
29
+ --configFile "$PROJECT_DIR/assets/diagrams/mermaid.config.json" 2>/dev/null || \
30
+ npx -p @mermaid-js/mermaid-cli mmdc \
31
+ -i "$file" \
32
+ -o "$OUT_DIR/$name.svg" \
33
+ -t dark \
34
+ -b transparent
35
+ fi
36
+ done
37
+
38
+ echo "Done! Diagrams rendered to $OUT_DIR/"
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+ # Render VHS tape files to SVG terminal recordings
3
+ # Usage: ./scripts/render-terminal.sh
4
+ #
5
+ # Requirements:
6
+ # - VHS: brew install charmbracelet/tap/vhs (macOS) or go install github.com/charmbracelet/vhs@latest
7
+ # - svg-term-cli: npm install -g svg-term-cli
8
+
9
+ set -e
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
13
+ TAPES_DIR="$PROJECT_DIR/assets/terminal/tapes"
14
+ OUT_DIR="$PROJECT_DIR/assets/terminal"
15
+
16
+ # Check for VHS
17
+ if ! command -v vhs &> /dev/null; then
18
+ echo "Error: VHS is not installed."
19
+ echo "Install with: brew install charmbracelet/tap/vhs (macOS)"
20
+ echo " or: go install github.com/charmbracelet/vhs@latest"
21
+ exit 1
22
+ fi
23
+
24
+ # Check for svg-term-cli
25
+ if ! command -v svg-term &> /dev/null && ! npx svg-term --version &> /dev/null 2>&1; then
26
+ echo "Installing svg-term-cli..."
27
+ npm install -g svg-term-cli
28
+ fi
29
+
30
+ echo "Rendering terminal recordings..."
31
+
32
+ for tape in "$TAPES_DIR"/*.tape; do
33
+ if [ -f "$tape" ]; then
34
+ name=$(basename "$tape" .tape)
35
+ cast_file="$OUT_DIR/$name.cast"
36
+ svg_file="$OUT_DIR/$name.svg"
37
+
38
+ echo " Recording $name..."
39
+ vhs "$tape" -o "$cast_file"
40
+
41
+ echo " Converting to SVG..."
42
+ npx svg-term --in "$cast_file" --out "$svg_file" --window --no-cursor
43
+
44
+ # Clean up cast file (optional, comment out to keep)
45
+ rm -f "$cast_file"
46
+ fi
47
+ done
48
+
49
+ echo "Done! Terminal recordings rendered to $OUT_DIR/"