#!/usr/bin/env node import fs from 'node:fs' import path from 'node:path' import { spawnSync } from 'node:child_process' import { fileURLToPath } from 'node:url' function runOrExit(cmd, args, options = {}) { const result = spawnSync(cmd, args, { stdio: 'inherit', ...options, }) if (result.error) { console.error(`Failed to run ${cmd}: ${result.error.message}`) process.exit(1) } if (result.status !== 0) { process.exit(result.status ?? 1) } } function readPackageManagerCommand(repoRoot) { const packageJsonPath = path.join(repoRoot, 'package.json') const rawPackageJson = fs.readFileSync(packageJsonPath, 'utf8') const packageJson = JSON.parse(rawPackageJson) const rawPackageManager = packageJson.packageManager if (typeof rawPackageManager !== 'string' || rawPackageManager.trim().length === 0) { return 'pnpm' } const [packageManagerName] = rawPackageManager.trim().split('@', 1) if (!packageManagerName) { return 'pnpm' } return packageManagerName } const scriptDir = path.dirname(fileURLToPath(import.meta.url)) const uiRepoRoot = path.resolve(scriptDir, '..') // Keep the dev runtime target explicit. This workspace can contain multiple // sibling repos with the same package name, so auto-discovery is ambiguous and // can silently point the UI at the wrong runtime after branch switches. const runtimeRepoRoot = process.env.CLAUDE_DEV_RUNTIME_ROOT?.trim() if (!runtimeRepoRoot) { console.error( 'CLAUDE_DEV_RUNTIME_ROOT is required for pnpm dev. ' + 'Point it at the runtime repo root you want the UI to use in dev.', ) process.exit(1) } const runtimePackageJsonPath = path.join(runtimeRepoRoot, 'package.json') if (!fs.existsSync(runtimePackageJsonPath)) { console.error( `CLAUDE_DEV_RUNTIME_ROOT does not look like a repo root: ${runtimeRepoRoot}`, ) process.exit(1) } const runtimePackageManager = readPackageManagerCommand(runtimeRepoRoot) if (process.argv.includes('--print-runtime-path')) { process.stdout.write(`${runtimeRepoRoot}\n`) process.exit(0) } // Respect the runtime repo's own package manager. The UI repo uses pnpm, but // the runtime may legitimately be a Bun workspace, and forcing pnpm there can // fail before the build even starts. runOrExit(runtimePackageManager, ['run', 'build:dev'], { cwd: runtimeRepoRoot }) const runtimeCliPath = path.join(runtimeRepoRoot, 'cli-dev') runOrExit('pnpm', ['run', 'dev:ui'], { cwd: uiRepoRoot, env: { ...process.env, CLAUDE_CLI_PATH: runtimeCliPath, }, })