Skip to content

Instantly share code, notes, and snippets.

@Hona
Last active April 8, 2026 02:54
Show Gist options
  • Select an option

  • Save Hona/090bf930c95dea76af347b326a8f0e92 to your computer and use it in GitHub Desktop.

Select an option

Save Hona/090bf930c95dea76af347b326a8f0e92 to your computer and use it in GitHub Desktop.
dev-opencode.ps1
#!/usr/bin/env pwsh
# Development workflow: build opentui -> copy into opencode -> run opencode dev server
#
# This script does NOT use `bun link` (corrupts global state) or symlinks (breaks module resolution).
# It uses a dedicated Bun cache for this workflow and replaces package directories instead of
# overwriting hardlinked files in place.
#
# Usage:
# .\dev-opencode.ps1 # Full flow: build, copy, run dev server
# .\dev-opencode.ps1 -NoDev # Build and copy only, don't start dev server
# .\dev-opencode.ps1 -Revert # Reinstall npm packages (removes local changes)
param(
[switch]$NoDev,
[switch]$Revert
)
$ErrorActionPreference = "Continue"
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$opentuiDir = Join-Path $root "opentui"
$opencodeDir = Join-Path $root "opencode"
$devCacheDir = Join-Path $opencodeDir ".bun-dev-cache"
$env:BUN_INSTALL_CACHE_DIR = $devCacheDir
function Resolve-PackagePath {
param(
[string]$LinkPath
)
if (-not (Test-Path $LinkPath)) {
return $null
}
$item = Get-Item -LiteralPath $LinkPath -Force
$target = $item.Target
if ($target -is [array]) {
$target = $target[0]
}
if ($item.LinkType -eq "SymbolicLink" -and $target) {
return [System.IO.Path]::GetFullPath((Join-Path (Split-Path -Parent $item.FullName) $target))
}
return (Resolve-Path -LiteralPath $LinkPath).Path
}
function Get-PackageVersion {
param(
[string]$PackagePath
)
if (-not $PackagePath) {
return $null
}
$packageJson = Join-Path $PackagePath "package.json"
if (-not (Test-Path $packageJson)) {
return $null
}
return (Get-Content -Path $packageJson -Raw | ConvertFrom-Json).version
}
function Get-StorePackagePath {
param(
[string]$Store,
[string]$Pattern,
[string]$PackagePath
)
$dir = Get-ChildItem -Path $Store -Directory -Filter $Pattern -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $dir) {
return $null
}
return Join-Path $dir.FullName $PackagePath
}
function Replace-PackagePath {
param(
[string]$Source,
[string]$Target
)
if (-not (Test-Path $Source)) {
throw "Missing source: $Source"
}
if (Test-Path $Target) {
Remove-Item -Path $Target -Recurse -Force
}
New-Item -ItemType Directory -Path $Target -Force | Out-Null
Copy-Item -Path (Join-Path $Source "*") -Destination $Target -Recurse -Force
}
function Remove-PackagePath {
param([string]$Target)
if (-not $Target -or -not (Test-Path $Target)) {
return $false
}
Remove-Item -Path $Target -Recurse -Force
return $true
}
Write-Host "OpenCode Development Workflow" -ForegroundColor Cyan
Write-Host "=============================" -ForegroundColor Cyan
Write-Host "Using Bun cache: $devCacheDir" -ForegroundColor DarkGray
Write-Host ""
# Revert mode: remove the overlaid package dirs, then reinstall them from the dedicated cache
if ($Revert) {
Write-Host "Reverting local opentui overlays..." -ForegroundColor Yellow
$bunStore = Join-Path $opencodeDir "node_modules\.bun"
$packageNodeModules = Join-Path $opencodeDir "packages\opencode\node_modules"
$coreTarget = Resolve-PackagePath (Join-Path $packageNodeModules "@opentui\core")
$coreVersion = Get-PackageVersion $coreTarget
$targets = @(
$coreTarget,
$(Get-StorePackagePath $bunStore "@opentui+core-win32-x64@$coreVersion*" "node_modules\@opentui\core-win32-x64"),
$(Resolve-PackagePath (Join-Path $packageNodeModules "@opentui\solid"))
)
foreach ($target in $targets) {
if (Remove-PackagePath $target) {
Write-Host " Removed $target" -ForegroundColor Gray
}
}
Write-Host " Reinstalling restored packages..." -ForegroundColor Gray
Push-Location $opencodeDir
bun install
Pop-Location
Write-Host ""
Write-Host "Done! Reverted to npm packages." -ForegroundColor Green
exit 0
}
# Step 1: Install opencode dependencies first
Write-Host "Step 1: Installing opencode dependencies..." -ForegroundColor Yellow
Push-Location $opencodeDir
bun install
Pop-Location
# Also install .opencode dependencies if they exist (user config directory)
$dotOpencode = Join-Path $opencodeDir ".opencode"
if (Test-Path (Join-Path $dotOpencode "package.json")) {
Write-Host " Installing .opencode plugin dependencies..." -ForegroundColor Gray
Push-Location $dotOpencode
bun install
Pop-Location
}
Write-Host " Done." -ForegroundColor Green
Write-Host ""
# Step 2: Build opentui
Write-Host "Step 2: Building opentui packages..." -ForegroundColor Yellow
Write-Host " Building @opentui/core native..." -ForegroundColor Gray
Push-Location (Join-Path $opentuiDir "packages\core")
bun run build:native
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Native build failed!" -ForegroundColor Red
Pop-Location
exit 1
}
Write-Host " Building @opentui/core lib..." -ForegroundColor Gray
bun run build:lib
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Core lib build failed!" -ForegroundColor Red
Pop-Location
exit 1
}
Pop-Location
Write-Host " Building @opentui/solid..." -ForegroundColor Gray
Push-Location (Join-Path $opentuiDir "packages\solid")
bun run build
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Solid build failed!" -ForegroundColor Red
Pop-Location
exit 1
}
Pop-Location
Write-Host " Done." -ForegroundColor Green
Write-Host ""
# Step 3: Replace installed package dirs with the local opentui builds
Write-Host "Step 3: Copying opentui dist into bun cache..." -ForegroundColor Yellow
$bunStore = Join-Path $opencodeDir "node_modules\.bun"
$packageNodeModules = Join-Path $opencodeDir "packages\opencode\node_modules"
# Resolve the actual installed @opentui/core path from Bun's isolated symlink
$coreNpmPath = Resolve-PackagePath (Join-Path $packageNodeModules "@opentui\core")
if (-not $coreNpmPath) {
Write-Host "Error: Could not resolve @opentui/core from packages/opencode/node_modules" -ForegroundColor Red
exit 1
}
$coreVersion = Get-PackageVersion $coreNpmPath
$coreDistPath = Join-Path $opentuiDir "packages\core\dist"
Write-Host " Copying @opentui/core dist files..." -ForegroundColor Gray
Replace-PackagePath $coreDistPath $coreNpmPath
Write-Host " Copied @opentui/core" -ForegroundColor Green
# Also copy native DLL for Windows (the JS code references FFI symbols that must exist in the DLL)
$nativePkg = "@opentui+core-win32-x64@$coreVersion*"
$nativeNpmPath = Get-StorePackagePath $bunStore $nativePkg "node_modules\@opentui\core-win32-x64"
if ($nativeNpmPath) {
$nativeSrcPath = Join-Path $opentuiDir "packages\core\node_modules\@opentui\core-win32-x64"
if (Test-Path $nativeSrcPath) {
Write-Host " Copying @opentui/core-win32-x64 native binary..." -ForegroundColor Gray
Replace-PackagePath $nativeSrcPath $nativeNpmPath
Write-Host " Copied native binary" -ForegroundColor Green
}
}
# Resolve the actual installed @opentui/solid path from Bun's isolated symlink
$solidNpmPath = Resolve-PackagePath (Join-Path $packageNodeModules "@opentui\solid")
if (-not $solidNpmPath) {
Write-Host "Error: Could not resolve @opentui/solid from packages/opencode/node_modules" -ForegroundColor Red
exit 1
}
$solidDistPath = Join-Path $opentuiDir "packages\solid\dist"
Write-Host " Copying @opentui/solid dist files..." -ForegroundColor Gray
Replace-PackagePath $solidDistPath $solidNpmPath
Write-Host " Copied @opentui/solid" -ForegroundColor Green
Write-Host ""
# Step 4: Run dev server (unless -NoDev)
if (-not $NoDev) {
Write-Host "Step 4: Starting opencode dev server..." -ForegroundColor Yellow
Write-Host ""
Push-Location $opencodeDir
bun dev
Pop-Location
} else {
Write-Host "Step 4: Skipping dev server (-NoDev flag)" -ForegroundColor Yellow
Write-Host ""
Write-Host "Done! To start dev server manually:" -ForegroundColor Green
Write-Host " cd $opencodeDir && bun dev" -ForegroundColor Gray
Write-Host ""
Write-Host "To revert to npm packages:" -ForegroundColor Gray
Write-Host " .\dev-opencode.ps1 -Revert" -ForegroundColor Gray
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment