A simple CLI tool for scaffolding interactive coding tutorials built with Astro/Starlight and designed to run in GitHub Codespaces.
npm install
npm run buildAfter building, you can use the CLI via the vonage-tutorial command. If you're developing locally, you can run it directly:
node dist/cli/index.js new my-tutorialTo install globally and use the vonage-tutorial command from anywhere:
npm install -g .Or if you're developing and want to link it:
npm linkAfter installation, the vonage-tutorial command will be available globally. The wrapper script automatically builds the CLI if needed on first run.
# Create a Node.js tutorial
vonage-tutorial new my-tutorial --runtime node
# Create a Python tutorial
vonage-tutorial new my-tutorial --runtime python --port 8000
# With custom title and description
vonage-tutorial new webhook-demo \
--runtime node \
--title "Build a Webhook Listener" \
--description "Learn how to receive incoming webhooks"cd my-tutorial
vonage-tutorial add step "Create the server"
vonage-tutorial add step "Start listening on port 3000"
vonage-tutorial add step "Handle incoming requests"vonage-tutorial generate devcontainervonage-tutorial validateCreate a new tutorial repository.
Options:
-r, --runtime <runtime>- Runtime (node, python). Default: node-t, --title <title>- Tutorial title-d, --description <description>- Tutorial description-p, --port <port>- Application port--author <author>- Author name--content <format>- Content format (markdown, markdoc). Default: markdown-y, --yes- Accept defaults without prompts--force- Overwrite existing directory
Example:
vonage-tutorial new webhook-demo --runtime node --port 3000Add a new tutorial step.
Options:
--slug <slug>- Custom filename slug--after <step>- Insert after existing step--before <step>- Insert before existing step--open- Open file after creation
Example:
vonage-tutorial add step "Handle incoming webhook"Generate or update .devcontainer/devcontainer.json.
Options:
--force- Overwrite existing file
Example:
vonage-tutorial generate devcontainerValidate tutorial configuration and structure.
Options:
--strict- Fail on warnings
Example:
vonage-tutorial validate --strictShow help information and list all available commands.
Show help information and list all available commands.
When you create a new tutorial, it generates the following structure:
my-tutorial/
├── package.json # Astro project dependencies
├── astro.config.mjs # Astro configuration
├── tutorial.config.yaml # Tutorial configuration
├── .gitignore # Git ignore rules
├── src/
│ ├── content.config.ts # Content collection config
│ ├── components/
│ │ ├── StepNavigation.astro # Navigation component
│ │ └── Footer.astro # Footer override
│ └── content/
│ └── docs/
│ ├── index.md # Landing page
│ └── step-*.md # Tutorial steps
├── workspace/
│ ├── README.md # Learner project README
│ └── [runtime files] # Starter code files
├── .devcontainer/
│ └── devcontainer.json # Codespaces configuration
└── scripts/
└── start.sh # Startup script for both servers
The tutorial.config.yaml file controls the tutorial setup:
version: 1
tutorial:
id: my-tutorial
title: My Tutorial
description: Learn something cool
runtime:
type: node
workingDirectory: workspace
installCommand: npm install
startCommand: npm run dev
application:
port: 3000
tutorialServer:
enabled: true
port: 1234
host: 0.0.0.0
installCommand: npm install
startCommand: npm run dev -- --host 0.0.0.0 --port 1234
autoInstall: true
autoStart: true
devcontainer:
enabled: true
forwardPorts:
- 3000
- 1234When you create a GitHub Codespace from a tutorial repository, both servers start automatically:
- Installs dependencies - Runs
npm installfor both tutorial site and learner project during container creation - Starts both servers - Launches Astro tutorial server (port 1234) and learner application (port 3000/8000) when the Codespace attaches
- Exposes ports - Both ports are automatically forwarded:
- Port 1234 (Tutorial) - Opens automatically in preview
- Port 3000/8000 (Learner App) - Available but doesn't auto-open
The generated .devcontainer/devcontainer.json includes:
- Port forwarding: Both tutorial port (1234) and learner app port are forwarded
- Port attributes: Tutorial port opens automatically in preview; learner app port is available but doesn't auto-open
postCreateCommand: Installs dependencies for both tutorial site and learner projectpostAttachCommand: Sets learner app port visibility to public and starts both servers usingscripts/start.sh- Startup script:
scripts/start.shruns both processes concurrently in the same terminal with labeled output - Auto-restart: Learner application uses nodemon (Node.js) or Flask debug mode (Python) for automatic restarts on file changes
The tutorialServer section in tutorial.config.yaml controls this behavior:
enabled- Enable/disable auto-start (default:true)port- Port for the tutorial site (default:1234)host- Bind address (default:0.0.0.0for Codespaces compatibility)autoInstall- Automatically install dependencies on Codespace creation (default:true)autoStart- Automatically start servers when Codespace attaches (default:true)
Note: Both servers run concurrently in the same terminal window. The tutorial site runs on port 1234, separate from your learner application port (e.g., 3000 or 8000).
# 1. Create a new tutorial
vonage-tutorial new webhook-demo --runtime node --port 3000
# 2. Navigate to the tutorial
cd webhook-demo
# 3. Add steps
vonage-tutorial add step "Create the server"
vonage-tutorial add step "Start listening on port 3000"
vonage-tutorial add step "Handle incoming POST requests"
# 4. Generate devcontainer config
vonage-tutorial generate devcontainer
# 5. Validate everything
vonage-tutorial validate
# 6. Install dependencies and start dev server
npm install
npm run devTo work on the CLI itself:
# Build
npm run build
# Watch mode (for development)
npm run dev
# Run a command (from project root)
node dist/cli/index.js new test-tutorialThe CLI includes built-in templates for different runtimes:
- Default port: 3000
- Install command:
npm install - Start command:
npm run dev(uses nodemon for auto-restart) - Files generated:
package.jsonwith Express and nodemon dependenciesserver.jswith basic HTTP server.gitignore
- Default port: 8000
- Install command:
pip install -r requirements.txt - Start command:
flask run --host 0.0.0.0 --port 8000 --debug(auto-reload enabled) - Files generated:
requirements.txtwith Flaskapp.pywith basic Flask app (debug mode enabled).gitignore
If vonage-tutorial command is not found after installation:
-
Check if npm global bin is in your PATH:
npm config get prefix
-
Add it to your PATH if needed (add to
~/.zshrcor~/.bashrc):export PATH="$(npm config get prefix)/bin:$PATH"
-
Reinstall:
npm install -g .
If you see build errors, try:
npm run buildThe wrapper script should auto-build on first run, but you can build manually if needed.
If you encounter path resolution errors when running globally, make sure you've rebuilt after the latest changes:
npm run build
npm link # or npm install -g .ISC