Init Scripts Guide¶
Init scripts (init.sh and init.ps1) set up the development environment at the start of each agent session. They ensure a consistent, reproducible starting point.
Purpose¶
Init scripts solve several problems for long-running agent workflows:
- Reproducible startup - Same environment every session
- Stale process cleanup - Kill orphaned dev servers
- Health verification - Confirm the app is running
- Background execution - Don't block the agent
Key Design Principle
Init scripts must start servers in the background and exit cleanly. The agent needs to continue working, not wait for a server to terminate.
Default Behavior¶
The included templates perform these steps:
graph LR
A[Check Prerequisites] --> B[Kill Stale Processes]
B --> C[Install Dependencies]
C --> D[Start Dev Server]
D --> E[Wait for Ready]
E --> F[Health Check]
F --> G[Exit Successfully]
Configuration¶
Both scripts have a configuration section at the top:
Customization by Stack¶
Node.js (Default)¶
The templates come pre-configured for Node.js projects:
# Check for Node.js
if ! command -v node &> /dev/null; then
echo "❌ Node.js is required but not installed"
exit 1
fi
# Install dependencies
if [ ! -d "node_modules" ]; then
npm install
fi
# Start dev server in background
npm run dev > .dev-server.log 2>&1 &
DEV_PID=$!
echo $DEV_PID > .dev-server.pid
# Check for Node.js
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Write-Host "❌ Node.js is required" -ForegroundColor Red
exit 1
}
# Install dependencies
if (-not (Test-Path "node_modules")) {
npm install
}
# Start dev server in background
$devJob = Start-Job -ScriptBlock {
Set-Location $using:PWD
npm run dev 2>&1
}
Python with uv¶
# Configuration
DEV_PORT=8000
# Check for uv
if ! command -v uv &> /dev/null; then
echo "❌ uv is required but not installed"
exit 1
fi
echo " ✅ uv $(uv --version)"
# Sync dependencies
uv sync
# Start dev server (Django example)
uv run python manage.py runserver 0.0.0.0:$DEV_PORT > .dev-server.log 2>&1 &
DEV_PID=$!
echo $DEV_PID > .dev-server.pid
# Configuration
$DEV_PORT = 8000
# Check for uv
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host "❌ uv is required" -ForegroundColor Red
exit 1
}
# Sync dependencies
uv sync
# Start dev server (Django example)
$devJob = Start-Job -ScriptBlock {
Set-Location $using:PWD
uv run python manage.py runserver 0.0.0.0:$using:DEV_PORT 2>&1
}
Python with pip¶
# Configuration
DEV_PORT=5000
# Check for Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 is required"
exit 1
fi
# Create/activate virtual environment
if [ ! -d ".venv" ]; then
python3 -m venv .venv
fi
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Start Flask dev server
flask run --port $DEV_PORT > .dev-server.log 2>&1 &
DEV_PID=$!
Rust¶
Go¶
Static Sites (MkDocs, Hugo, etc.)¶
Health Checks¶
The templates include health check logic with configurable timeouts:
# Wait for server with timeout
MAX_ATTEMPTS=30
ATTEMPT=0
SERVER_READY=false
while [ $ATTEMPT -lt $MAX_ATTEMPTS ] && [ "$SERVER_READY" = false ]; do
sleep 1
ATTEMPT=$((ATTEMPT + 1))
if curl -s -o /dev/null "http://localhost:$DEV_PORT" 2>/dev/null; then
SERVER_READY=true
fi
done
if [ "$SERVER_READY" = false ]; then
echo "❌ Server failed to start!"
exit 1
fi
# Wait for server with timeout
$maxAttempts = 30
$attempt = 0
$serverReady = $false
while ($attempt -lt $maxAttempts -and -not $serverReady) {
Start-Sleep -Seconds 1
$attempt++
$connection = Test-NetConnection -ComputerName localhost -Port $DEV_PORT -WarningAction SilentlyContinue
if ($connection.TcpTestSucceeded) {
$serverReady = $true
}
}
if (-not $serverReady) {
Write-Host "❌ Server failed to start!" -ForegroundColor Red
exit 1
}
Custom Health Endpoints¶
For APIs, check a specific health endpoint:
# Instead of just checking the port, verify the health endpoint
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$DEV_PORT/health")
if [ "$HTTP_CODE" != "200" ]; then
echo "❌ Health check failed (HTTP $HTTP_CODE)"
exit 1
fi
Process Management¶
Tracking the PID¶
The scripts save the server PID for later cleanup:
Stopping the Server¶
To stop the dev server:
Viewing Logs¶
Server output is captured to .dev-server.log:
Best Practices¶
Do¶
- ✅ Start servers in background (use
&orStart-Job) - ✅ Exit the script after server is ready
- ✅ Include health checks with timeouts
- ✅ Save PID for cleanup
- ✅ Capture output to log files
- ✅ Kill stale processes before starting new ones
Don't¶
- ❌ Block waiting for server to terminate
- ❌ Skip health checks
- ❌ Hardcode paths (use relative paths)
- ❌ Ignore exit codes
- ❌ Leave orphaned processes running
Integration with Klondike¶
Init scripts are called during the session start workflow:
# Standard session start
klondike session start --focus "F001 - Feature description"
# Then run init script
./init.sh # or .\init.ps1 on Windows
The session-start prompt includes init script execution as a standard step.
Troubleshooting¶
Server Won't Start¶
- Check
.dev-server.logfor error messages - Verify the port isn't in use:
lsof -i :3000 - Ensure dependencies are installed
Health Check Fails¶
- Increase
MAX_ATTEMPTSfor slow-starting apps - Check if the server uses a different startup path
- Verify firewall isn't blocking localhost
Stale Processes¶
If the script can't kill stale processes:
# Manually find and kill
lsof -ti:3000 | xargs kill -9
# Or on Windows
Get-Process | Where-Object { $_.MainWindowTitle -match "node" } | Stop-Process
Example: Complete Custom Script¶
Here's a complete example for a Python/FastAPI project with uv:
#!/bin/bash
set -e
# Configuration
DEV_PORT=8000
HEALTH_ENDPOINT="/api/health"
echo "🚀 Starting FastAPI development environment..."
# Prerequisites
if ! command -v uv &> /dev/null; then
echo "❌ uv is required"
exit 1
fi
echo " ✅ uv $(uv --version)"
# Clean up stale processes
STALE_PID=$(lsof -ti:$DEV_PORT 2>/dev/null || true)
if [ -n "$STALE_PID" ]; then
kill -9 $STALE_PID 2>/dev/null || true
sleep 1
fi
# Install/sync dependencies
uv sync
# Start uvicorn in background
uv run uvicorn app.main:app --host 0.0.0.0 --port $DEV_PORT --reload > .dev-server.log 2>&1 &
echo $! > .dev-server.pid
# Wait for health endpoint
MAX_ATTEMPTS=30
for i in $(seq 1 $MAX_ATTEMPTS); do
if curl -sf "http://localhost:$DEV_PORT$HEALTH_ENDPOINT" > /dev/null 2>&1; then
echo "✅ Server ready at http://localhost:$DEV_PORT"
echo "📖 API docs: http://localhost:$DEV_PORT/docs"
exit 0
fi
sleep 1
done
echo "❌ Server failed to start"
cat .dev-server.log
exit 1
Next Steps¶
- GitHub Copilot Customization - Full customization overview
- Session Management - Session workflow details
- Agent Best Practices - Effective agent patterns