Last active
March 6, 2026 20:37
-
-
Save tylergermain/223e7de43e605cc6607dd672700194df to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires -Version 5.1 | |
| $ErrorActionPreference = "Stop" | |
| # -- Config --------------------------------------------------------------- | |
| $REPO = "https://github.com/AI-Essentials/screen-recording-skill.git" | |
| # -- Helpers --------------------------------------------------------------- | |
| $ESC = [char]27 | |
| $BOLD = "$ESC[1m" | |
| $DIM = "$ESC[2m" | |
| $RED = "$ESC[0;31m" | |
| $GREEN = "$ESC[0;32m" | |
| $YELLOW = "$ESC[1;33m" | |
| $CYAN = "$ESC[0;36m" | |
| $NC = "$ESC[0m" | |
| function Info($msg) { Write-Host " ${GREEN}>${NC} $msg" } | |
| function Warn($msg) { Write-Host " ${YELLOW}>${NC} $msg" } | |
| function Fail($msg) { Write-Host " ${RED}x${NC} $msg"; exit 1 } | |
| function Step($msg) { Write-Host "`n ${BOLD}$msg${NC}" } | |
| function Done_() { Write-Host " ${GREEN}>${NC} ${DIM}done${NC}" } | |
| $TMPDIR_CREATED = $null | |
| $TARGET_DIR = Get-Location | |
| # Detect install vs update | |
| if (Test-Path "$TARGET_DIR\.claude\skills\screen-demo") { | |
| $MODE = "update" | |
| } else { | |
| $MODE = "install" | |
| } | |
| Write-Host "" | |
| Write-Host " ${CYAN} ____ ____ ____ _____ _____ _ _${NC}" | |
| Write-Host " ${CYAN}/ ___| / ___| _ \| ____| ____| \ | |${NC}" | |
| Write-Host " ${CYAN}\___ \| | | |_) | _| | _| | \| |${NC}" | |
| Write-Host " ${CYAN} ___) | |___| _ <| |___| |___| |\ |${NC}" | |
| Write-Host " ${CYAN}|____/ \____|_| \_\_____|_____|_| \_|${NC}" | |
| Write-Host "" | |
| Write-Host " ${CYAN} ____ _____ __ __ ___${NC}" | |
| Write-Host " ${CYAN}| _ \| ____| \/ |/ _ \${NC}" | |
| Write-Host " ${CYAN}| | | | _| | |\/| | | | |${NC}" | |
| Write-Host " ${CYAN}| |_| | |___| | | | |_| |${NC}" | |
| Write-Host " ${CYAN}|____/|_____|_| |_|\___/${NC}" | |
| Write-Host "" | |
| if ($MODE -eq "update") { | |
| Write-Host " ${DIM}Updating to latest version${NC}" | |
| } else { | |
| Write-Host " ${DIM}Installing skill + dependencies${NC}" | |
| } | |
| # -- Resolve source files -------------------------------------------------- | |
| $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path | |
| if (Test-Path "$SCRIPT_DIR\.claude\skills\screen-demo\SKILL.md") { | |
| $SOURCE_DIR = $SCRIPT_DIR | |
| } else { | |
| Step "Fetching" | |
| $TMPDIR_CREATED = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName()) | |
| New-Item -ItemType Directory -Path $TMPDIR_CREATED -Force | Out-Null | |
| try { | |
| git clone --depth 1 $REPO $TMPDIR_CREATED 2>$null | |
| if ($LASTEXITCODE -ne 0) { throw } | |
| } catch { | |
| Fail "Clone failed. Do you have access to the repo?" | |
| } | |
| $SOURCE_DIR = $TMPDIR_CREATED | |
| Done_ | |
| } | |
| # -- Check prerequisites --------------------------------------------------- | |
| Step "Prerequisites" | |
| # Python | |
| if (-not (Get-Command python -ErrorAction SilentlyContinue)) { | |
| if (-not (Get-Command python3 -ErrorAction SilentlyContinue)) { | |
| Fail "Python 3 is required - https://python.org" | |
| } | |
| $PYTHON_CMD = "python3" | |
| } else { | |
| $PYTHON_CMD = "python" | |
| } | |
| $PYTHON_VERSION = & $PYTHON_CMD -c "import sys; v=sys.version_info; print(str(v.major)+'.'+str(v.minor))" | |
| $vParts = $PYTHON_VERSION.Split(".") | |
| $PYTHON_MAJOR = [int]$vParts[0] | |
| $PYTHON_MINOR = [int]$vParts[1] | |
| if ($PYTHON_MAJOR -lt 3 -or ($PYTHON_MAJOR -eq 3 -and $PYTHON_MINOR -lt 10)) { | |
| Fail "Python 3.10+ required (found $PYTHON_VERSION)" | |
| } | |
| Info "Python ${DIM}$PYTHON_VERSION${NC}" | |
| # Node | |
| if (-not (Get-Command node -ErrorAction SilentlyContinue)) { | |
| Fail "Node.js is required - https://nodejs.org" | |
| } | |
| $NODE_VERSION = (node -v).TrimStart("v") | |
| $NODE_MAJOR = [int]($NODE_VERSION.Split("."))[0] | |
| if ($NODE_MAJOR -lt 18) { | |
| Fail "Node.js 18+ required (found v$NODE_VERSION)" | |
| } | |
| Info "Node ${DIM}v$NODE_VERSION${NC}" | |
| # ffmpeg | |
| if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { | |
| Warn "ffmpeg not found, installing via winget..." | |
| try { | |
| winget install --id Gyan.FFmpeg -e --accept-source-agreements --accept-package-agreements | |
| # Refresh PATH so ffmpeg is available in this session | |
| $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") | |
| if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { | |
| Warn "ffmpeg installed but not yet on PATH - you may need to restart your terminal" | |
| } | |
| } catch { | |
| Fail "ffmpeg is required - install manually: winget install Gyan.FFmpeg" | |
| } | |
| } | |
| if (Get-Command ffmpeg -ErrorAction SilentlyContinue) { | |
| $FFMPEG_LINE = (ffmpeg -version 2>&1 | Select-Object -First 1).ToString() | |
| $FFMPEG_VERSION = $FFMPEG_LINE.Split(" ")[2] | |
| Info "ffmpeg ${DIM}$FFMPEG_VERSION${NC}" | |
| } | |
| # -- Copy skill files ------------------------------------------------------ | |
| if ($SOURCE_DIR -ne "$TARGET_DIR") { | |
| Step "Skill files" | |
| # Skill definition | |
| $skillDest = Join-Path $TARGET_DIR ".claude\skills\screen-demo" | |
| New-Item -ItemType Directory -Path (Join-Path $TARGET_DIR ".claude\skills") -Force | Out-Null | |
| if (Test-Path $skillDest) { | |
| Remove-Item -Recurse -Force $skillDest | |
| } | |
| Copy-Item -Recurse (Join-Path $SOURCE_DIR ".claude\skills\screen-demo") $skillDest | |
| Info ".claude/skills/screen-demo/" | |
| # Remotion source | |
| $remotionDest = Join-Path $TARGET_DIR "remotion" | |
| New-Item -ItemType Directory -Path (Join-Path $remotionDest "src\components") -Force | Out-Null | |
| New-Item -ItemType Directory -Path (Join-Path $remotionDest "src\lib") -Force | Out-Null | |
| foreach ($f in @("package.json", "tsconfig.json", "render.mjs")) { | |
| Copy-Item (Join-Path $SOURCE_DIR "remotion\$f") (Join-Path $remotionDest $f) | |
| } | |
| Copy-Item (Join-Path $SOURCE_DIR "remotion\src\index.ts") (Join-Path $remotionDest "src\index.ts") | |
| Copy-Item (Join-Path $SOURCE_DIR "remotion\src\Root.tsx") (Join-Path $remotionDest "src\Root.tsx") | |
| Copy-Item (Join-Path $SOURCE_DIR "remotion\src\components\CameraComposition.tsx") (Join-Path $remotionDest "src\components\CameraComposition.tsx") | |
| Copy-Item (Join-Path $SOURCE_DIR "remotion\src\lib\springInterpolate.ts") (Join-Path $remotionDest "src\lib\springInterpolate.ts") | |
| Info "remotion/" | |
| # Requirements - merge if exists, copy if not | |
| $reqDest = Join-Path $TARGET_DIR "requirements.txt" | |
| $reqSrc = Join-Path $SOURCE_DIR "requirements.txt" | |
| if (Test-Path $reqDest) { | |
| $existing = Get-Content $reqDest | |
| foreach ($line in Get-Content $reqSrc) { | |
| if ([string]::IsNullOrWhiteSpace($line)) { continue } | |
| if ($existing -notcontains $line) { | |
| Add-Content -Path $reqDest -Value $line | |
| } | |
| } | |
| } else { | |
| Copy-Item $reqSrc $reqDest | |
| } | |
| Info "requirements.txt" | |
| # .env.example | |
| $envExDest = Join-Path $TARGET_DIR ".env.example" | |
| if (-not (Test-Path $envExDest)) { | |
| Copy-Item (Join-Path $SOURCE_DIR ".env.example") $envExDest | |
| } | |
| } | |
| # -- Environment file ------------------------------------------------------ | |
| Push-Location $TARGET_DIR | |
| Step "Environment" | |
| $envFile = Join-Path $TARGET_DIR ".env" | |
| if (-not (Test-Path $envFile)) { | |
| Copy-Item ".env.example" ".env" | |
| Warn "Created .env ${DIM}- add your STEEL_API_KEY${NC}" | |
| Info "${DIM}https://app.steel.dev/settings/api-keys${NC}" | |
| } else { | |
| $envContent = Get-Content $envFile -Raw -ErrorAction SilentlyContinue | |
| if ($envContent -notmatch "STEEL_API_KEY") { | |
| Add-Content -Path $envFile -Value "" | |
| Add-Content -Path $envFile -Value "# Cloud browser - for recording website demos" | |
| Add-Content -Path $envFile -Value "STEEL_API_KEY=your_steel_api_key" | |
| Warn "Added STEEL_API_KEY to .env ${DIM}- fill it in before running${NC}" | |
| } else { | |
| Info ".env ${DIM}ready${NC}" | |
| } | |
| } | |
| # -- Python dependencies --------------------------------------------------- | |
| Step "Python" | |
| $venvDir = Join-Path $TARGET_DIR ".venv" | |
| if (-not (Test-Path $venvDir)) { | |
| & $PYTHON_CMD -m venv $venvDir | |
| } | |
| # Activate venv | |
| $activateScript = Join-Path $venvDir "Scripts\Activate.ps1" | |
| & $activateScript | |
| pip install -q -r requirements.txt | |
| Info "Packages installed" | |
| $ErrorActionPreference = "Continue" | |
| playwright install chromium 2>$null | |
| $ErrorActionPreference = "Stop" | |
| Info "Playwright browser installed" | |
| # -- Remotion (video editor) ----------------------------------------------- | |
| Step "Remotion" | |
| Push-Location (Join-Path $TARGET_DIR "remotion") | |
| npm install --silent | |
| Pop-Location | |
| Info "Packages installed" | |
| # -- Cleanup --------------------------------------------------------------- | |
| if ($TMPDIR_CREATED -and (Test-Path $TMPDIR_CREATED)) { | |
| Remove-Item -Recurse -Force $TMPDIR_CREATED | |
| } | |
| Pop-Location | |
| # -- Done ------------------------------------------------------------------ | |
| Write-Host "" | |
| Write-Host "" | |
| if ($MODE -eq "update") { | |
| Write-Host " ${GREEN}${BOLD}Updated!${NC}" | |
| Write-Host "" | |
| Write-Host " ${DIM}Restart Claude Code to pick up changes.${NC}" | |
| } else { | |
| Write-Host " ${GREEN}${BOLD}Installed!${NC}" | |
| Write-Host "" | |
| Write-Host " ${DIM}1.${NC} Add your STEEL_API_KEY to .env" | |
| Write-Host " ${DIM}2.${NC} Open Claude Code in this directory" | |
| Write-Host " ${DIM}3.${NC} Run ${CYAN}/screen-demo${NC}" | |
| } | |
| Write-Host "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment