diff --git a/package.json b/package.json index ad7c7045..150cae1f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "postinstall": "npx @playwright/mcp install-browser chrome-for-testing || exit 0", "prebuild": "node scripts/bootstrap-pilotdeck-config.mjs && cd src/context/memory/edgeclaw-memory-core && npm run build", "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node -e \"require('fs').cpSync('src/extension/plugins/builtin','dist/src/extension/plugins/builtin',{recursive:true})\"", + "preserver": "node scripts/bootstrap-pilotdeck-config.mjs", "server": "tsx src/cli/pilotdeck.ts server", + "preserver:built": "node scripts/bootstrap-pilotdeck-config.mjs", "server:built": "node dist/src/cli/pilotdeck.js server", "skills:migrate": "tsx src/cli/pilotdeck.ts skills migrate", "predev": "node scripts/bootstrap-pilotdeck-config.mjs", diff --git a/scripts/dev-launcher.mjs b/scripts/dev-launcher.mjs index 58b260a3..1e121877 100644 --- a/scripts/dev-launcher.mjs +++ b/scripts/dev-launcher.mjs @@ -122,7 +122,7 @@ async function main() { const child = spawn( 'npm', ['--workspace', 'ui', 'run', 'dev:concurrent'], - { cwd: repoRoot, env, stdio: 'inherit' }, + { cwd: repoRoot, env, stdio: 'inherit', shell: true }, ); const forward = (signal) => { diff --git a/ui/package.json b/ui/package.json index cbbe60bf..80522522 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,7 @@ "dev:gateway": "npm --prefix .. run server", "dev:server": "node --import tsx server/index.js", "dev:client": "vite", + "preserver": "node ../scripts/bootstrap-pilotdeck-config.mjs", "server": "node --import tsx server/index.js", "gateway": "npm --prefix .. run server", "client": "vite", @@ -20,6 +21,7 @@ "typecheck": "tsc --noEmit -p tsconfig.json", "lint": "eslint src/", "lint:fix": "eslint src/ --fix", + "prestart": "node ../scripts/bootstrap-pilotdeck-config.mjs", "start": "npm run build && npm run start:built", "start:built": "concurrently --kill-others --names gateway,server \"npm:gateway\" \"npm:server\"", "postinstall": "node scripts/fix-node-pty.js" diff --git a/ui/server/load-env.js b/ui/server/load-env.js index 4063ec8c..2c51a872 100644 --- a/ui/server/load-env.js +++ b/ui/server/load-env.js @@ -2,6 +2,7 @@ import fs from 'fs'; import os from 'os'; import path from 'path'; import { fileURLToPath } from 'url'; +import { execSync } from 'child_process'; import { applyConfigToProcessEnv, getPilotDeckConfigPath, @@ -18,6 +19,31 @@ const REPO_ROOT = path.resolve(__dirname, '../..'); // chat execution goes through pilotdeck-bridge.js → src/gateway, which // reads ~/.pilotdeck/pilotdeck.yaml directly. The sanity check has been // retired; ui/server boots even when the config file is missing. +// ─── Auto-bootstrap ────────────────────────────────────────────────────────── +// On first run the ~/.pilotdeck/pilotdeck.yaml config file doesn't exist yet. +// Rather than relying solely on npm `pre` hooks (which are skipped when the +// server is started directly, e.g. `node server/index.js` or `pilotdeck start`), +// we run the bootstrap script inline whenever the config is missing. +// This ensures Windows users always get the folder + placeholder config +// regardless of how they launch the project. + +function ensurePilotDeckConfigExists() { + if (hasPilotDeckConfigFile()) return; + + const bootstrapScript = path.resolve(REPO_ROOT, 'scripts', 'bootstrap-pilotdeck-config.mjs'); + if (!fs.existsSync(bootstrapScript)) return; + + try { + execSync(`node "${bootstrapScript}"`, { + stdio: 'inherit', + cwd: REPO_ROOT, + timeout: 30000, + windowsHide: true, + }); + } catch (err) { + console.warn('[load-env] Config bootstrap warning:', err instanceof Error ? err.message : String(err)); + } +} function applyDerivedRuntimeEnv() { const { config } = readPilotDeckConfigFile(); @@ -45,6 +71,7 @@ export function assertRequiredPilotDeckEnv() { } export function loadRootPilotDeckEnv() { + ensurePilotDeckConfigExists(); applyDerivedRuntimeEnv(); if (!process.env.DATABASE_PATH) {