@mukulaggarwal/pacman 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/onboarding-server.ts","../../template-engine/dist/index.js"],"sourcesContent":["import express from 'express';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\nimport { createLocalStorage } from '@personal-assistant/storage-local';\nimport { createGDriveStorage } from '@personal-assistant/storage-gdrive';\nimport { createConfigManager } from '@personal-assistant/config-manager';\nimport { createContextManager } from '@personal-assistant/context-manager';\nimport {\n getTemplate,\n renderTemplate,\n listProfiles,\n getResponsibilities,\n} from '@personal-assistant/template-engine';\nimport { validateIntegrationConfig } from '@personal-assistant/integration-runtime';\nimport { createSlackConnector, validateSlackAppToken } from '@personal-assistant/integrations-slack';\nimport { createNoopEventClient } from '@personal-assistant/event-client';\nimport type {\n AppConfig,\n LlmProvider,\n ProfileType,\n UserProfile,\n StorageConfig,\n GDriveStorageConfig,\n IntegrationConfig,\n SyncConfig,\n} from '@personal-assistant/core-types';\nimport { validateProviderConfig } from './provider-runtime.js';\n\ninterface GDriveAuthSession {\n clientId: string;\n clientSecret: string;\n status: 'pending' | 'authenticated' | 'complete' | 'error';\n // set after OAuth callback\n accessToken?: string;\n refreshToken?: string;\n // set after folder creation\n folderId?: string;\n folderName?: string;\n folderPath?: string;\n error?: string;\n}\n\nexport async function startOnboardingServer(\n port: number,\n workspacePath: string,\n): Promise<void> {\n const app = express();\n app.use(express.json());\n\n let gdriveAuthSession: GDriveAuthSession | null = null;\n\n const onboardingStaticPath = await resolveOnboardingStaticPath();\n\n // Try to serve built static files; fallback to inline HTML\n if (onboardingStaticPath) {\n app.use(express.static(onboardingStaticPath));\n } else {\n // Serve inline onboarding page if the web app isn't built\n app.get('/', (_req, res) => {\n res.send(getInlineOnboardingHtml());\n });\n }\n\n // --- API Routes ---\n\n app.get('/api/profiles', (_req, res) => {\n const profiles = listProfiles();\n res.json({ profiles });\n });\n\n app.get('/api/responsibilities/:profileType', (req, res) => {\n try {\n const responsibilities = getResponsibilities(req.params.profileType as ProfileType);\n res.json({ responsibilities });\n } catch {\n res.status(400).json({ error: 'Invalid profile type' });\n }\n });\n\n app.get('/api/template/:profileType', (req, res) => {\n try {\n const template = getTemplate(req.params.profileType as ProfileType);\n res.json({ template });\n } catch {\n res.status(400).json({ error: 'Invalid profile type' });\n }\n });\n\n app.post('/api/preview-template', (req, res) => {\n const { profileType, name, assistantName, responsibilities } = req.body;\n try {\n const template = getTemplate(profileType);\n const files = renderTemplate(template, { name, assistantName, responsibilities });\n res.json({ files });\n } catch (err) {\n res.status(400).json({ error: String(err) });\n }\n });\n\n app.post('/api/save', async (req, res) => {\n try {\n const config: AppConfig = req.body;\n\n // Use the user-chosen local path if provided, otherwise fall back to CLI --dir\n const effectivePath =\n config.storage.mode === 'local' && (config.storage as { workspacePath?: string }).workspacePath\n ? path.resolve((config.storage as { workspacePath: string }).workspacePath)\n : workspacePath;\n\n // Ensure the local directory exists (needed for bootstrap even in GDrive mode)\n await fs.mkdir(effectivePath, { recursive: true });\n\n // Save a global pointer so CLI commands can find the workspace without --dir\n const rcPath = path.join(process.env.HOME ?? process.env.USERPROFILE ?? '~', '.personal-assistant-rc.json');\n await fs.writeFile(rcPath, JSON.stringify({ workspacePath: effectivePath }, null, 2), 'utf-8');\n\n // Always save storage.json locally for bootstrap (so MCP server knows which backend to use)\n const localStorage = createLocalStorage(effectivePath);\n const localConfigManager = createConfigManager(localStorage);\n await localConfigManager.saveConfig(config);\n\n // Resolve the user's chosen storage backend\n let targetStorage: import('@personal-assistant/core-types').StorageBackend = localStorage;\n if (config.storage.mode === 'gdrive') {\n const gdriveConfig = config.storage as GDriveStorageConfig;\n const resolvedCachePath = path.isAbsolute(gdriveConfig.cachePath)\n ? gdriveConfig.cachePath\n : path.resolve(path.dirname(effectivePath), gdriveConfig.cachePath);\n\n const gdriveStorage = createGDriveStorage({\n ...gdriveConfig,\n cachePath: resolvedCachePath,\n });\n await gdriveStorage.initialize();\n targetStorage = gdriveStorage;\n\n // Write config to GDrive so all reads go through GDrive\n const gdriveConfigManager = createConfigManager(gdriveStorage);\n await gdriveConfigManager.saveConfig(config);\n }\n\n const contextManager = createContextManager(targetStorage);\n\n // Initialize workspace structure on the target backend\n await contextManager.initWorkspace();\n\n // Render and write template files to the target backend\n const template = getTemplate(config.user.profileType);\n const files = renderTemplate(template, {\n name: config.user.name,\n assistantName: config.user.assistantName,\n responsibilities: config.user.responsibilities,\n });\n await contextManager.writeCanonicalFiles(files);\n\n // Emit onboarding completed event\n const eventClient = createNoopEventClient();\n await eventClient.emit('onboarding_completed', {\n profile: config.user.profileType,\n storage_mode: config.storage.mode,\n });\n\n res.json({ success: true, workspacePath: effectivePath });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n // --- Integration credential validation ---\n\n app.post('/api/validate-integration', async (req, res) => {\n const { type, credentials } = req.body as {\n type: string;\n credentials: Record<string, string>;\n };\n\n try {\n const result = await validateIntegrationConfig({\n type: type as IntegrationConfig['type'],\n enabled: true,\n credentials,\n });\n\n if (result.ok) {\n res.json({ valid: true, info: result.summary ?? `Connected to ${type}` });\n return;\n }\n\n res.json({\n valid: false,\n error: [result.reason, ...(result.fix ?? [])].filter(Boolean).join(' '),\n });\n } catch (err) {\n res.json({ valid: false, error: String(err) });\n }\n });\n\n app.post('/api/validate-provider', async (req, res) => {\n const { provider, apiKey, model } = req.body as {\n provider: LlmProvider;\n apiKey: string;\n model?: string;\n };\n\n try {\n const result = await validateProviderConfig(provider, apiKey, model);\n res.json(result);\n } catch (err) {\n res.json({ valid: false, error: String(err) });\n }\n });\n\n app.post('/api/validate-slack-runtime', async (req, res) => {\n const {\n botToken,\n appToken,\n generationEnabled,\n providers,\n } = req.body as {\n botToken?: string;\n appToken?: string;\n generationEnabled?: boolean;\n providers?: {\n openai?: { apiKey?: string; model?: string };\n anthropic?: { apiKey?: string; model?: string };\n };\n };\n\n if (!botToken || !appToken) {\n res.json({ valid: false, error: 'Slack bot token and app token are required.' });\n return;\n }\n\n if (!botToken.startsWith('xoxb-')) {\n res.json({\n valid: false,\n error: 'Slack bot token must start with xoxb-. Paste the Bot User OAuth Token from OAuth & Permissions.',\n });\n return;\n }\n\n if (!appToken.startsWith('xapp-')) {\n res.json({\n valid: false,\n error: 'Slack Socket Mode app token must start with xapp-. Create an App-Level Token with connections:write under Socket Mode.',\n });\n return;\n }\n\n try {\n const connector = createSlackConnector();\n await connector.authenticate({\n type: 'slack',\n enabled: true,\n credentials: { botToken },\n });\n const health = await connector.healthCheck();\n await validateSlackAppToken(appToken);\n\n if (generationEnabled) {\n const providerEntries = [\n ['openai', providers?.openai],\n ['anthropic', providers?.anthropic],\n ] as const;\n\n const configuredProviders = providerEntries.filter(([, value]) => value?.apiKey);\n if (configuredProviders.length === 0) {\n res.json({ valid: false, error: 'Enable at least one provider when Slack generation is turned on.' });\n return;\n }\n\n for (const [provider, value] of configuredProviders) {\n const result = await validateProviderConfig(provider, value?.apiKey ?? '', value?.model);\n if (!result.valid) {\n res.json({ valid: false, error: result.error ?? `Failed to validate ${provider}` });\n return;\n }\n }\n }\n\n res.json({\n valid: true,\n info: `${health.summary ?? 'Slack connected'} via Socket Mode. Atlas will monitor channels where the bot is added.`,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('not_allowed_token_type')) {\n res.json({\n valid: false,\n error: 'Slack rejected the Socket Mode token. Use an App-Level Token that starts with xapp- and has the connections:write scope.',\n });\n return;\n }\n\n res.json({\n valid: false,\n error: message,\n });\n }\n });\n\n // --- Local folder picker ---\n\n app.get('/api/pick-folder', async (_req, res) => {\n try {\n let folderPath: string;\n const platform = process.platform;\n\n if (platform === 'darwin') {\n const { stdout } = await execAsync(\n `osascript -e 'POSIX path of (choose folder with prompt \"Select a folder for your personal assistant context\")'`,\n );\n folderPath = stdout.trim();\n } else if (platform === 'linux') {\n const { stdout } = await execAsync(\n 'zenity --file-selection --directory --title=\"Select storage folder\"',\n );\n folderPath = stdout.trim();\n } else if (platform === 'win32') {\n const ps =\n '[System.Reflection.Assembly]::LoadWithPartialName(\\'System.windows.forms\\') | Out-Null; ' +\n '$f = New-Object System.Windows.Forms.FolderBrowserDialog; ' +\n '$f.Description = \\'Select a folder for your personal assistant context\\'; ' +\n '$f.ShowDialog() | Out-Null; $f.SelectedPath';\n const { stdout } = await execAsync(`powershell -command \"${ps}\"`);\n folderPath = stdout.trim();\n } else {\n res.status(400).json({ error: 'Folder picker not supported on this platform' });\n return;\n }\n\n if (!folderPath) {\n res.json({ cancelled: true });\n return;\n }\n\n res.json({ path: folderPath });\n } catch (err) {\n // osascript throws when the user clicks Cancel — treat as cancellation\n if (String(err).includes('User canceled') || String(err).includes('cancel')) {\n res.json({ cancelled: true });\n return;\n }\n res.status(500).json({ error: String(err) });\n }\n });\n\n // --- Google Drive OAuth routes ---\n\n app.post('/api/gdrive/auth-start', async (req, res) => {\n const { clientId, clientSecret } = req.body as { clientId?: string; clientSecret?: string };\n if (!clientId || !clientSecret) {\n res.status(400).json({ error: 'clientId and clientSecret are required' });\n return;\n }\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);\n const authUrl = oauth2Client.generateAuthUrl({\n access_type: 'offline',\n prompt: 'consent',\n scope: ['https://www.googleapis.com/auth/drive.file'],\n });\n\n gdriveAuthSession = { clientId, clientSecret, status: 'pending' };\n res.json({ authUrl });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n app.get('/api/gdrive/callback', async (req, res) => {\n const { code } = req.query as { code?: string };\n\n if (!code || !gdriveAuthSession) {\n res.status(400).send('Invalid OAuth callback — no pending auth session.');\n return;\n }\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(\n gdriveAuthSession.clientId,\n gdriveAuthSession.clientSecret,\n redirectUri,\n );\n\n const { tokens } = await oauth2Client.getToken(code);\n\n // Store tokens only — folder is created in a separate step\n gdriveAuthSession = {\n ...gdriveAuthSession,\n status: 'authenticated',\n refreshToken: tokens.refresh_token ?? undefined,\n accessToken: tokens.access_token ?? undefined,\n };\n\n res.send(`<!DOCTYPE html><html><body>\n <script>window.close();</script>\n <p style=\"font-family:sans-serif;padding:2rem;color:#4ade80;\">\n ✓ Authentication successful — you can close this tab and return to the setup.\n </p>\n </body></html>`);\n } catch (err) {\n if (gdriveAuthSession) {\n gdriveAuthSession.status = 'error';\n gdriveAuthSession.error = String(err);\n }\n res.status(500).send('Authentication failed: ' + String(err));\n }\n });\n\n app.get('/api/gdrive/auth-status', (_req, res) => {\n if (!gdriveAuthSession) {\n res.json({ status: 'idle' });\n return;\n }\n const { clientId: _cid, clientSecret: _csec, accessToken: _at, refreshToken: _rt, ...publicSession } = gdriveAuthSession;\n res.json(publicSession);\n });\n\n app.post('/api/gdrive/create-folder', async (req, res) => {\n if (!gdriveAuthSession || gdriveAuthSession.status !== 'authenticated') {\n res.status(400).json({ error: 'Not authenticated. Please connect Google Drive first.' });\n return;\n }\n\n const { folderName = 'Personal Assistant', parentFolderName = '' } =\n req.body as { folderName?: string; parentFolderName?: string };\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(\n gdriveAuthSession.clientId,\n gdriveAuthSession.clientSecret,\n redirectUri,\n );\n oauth2Client.setCredentials({\n access_token: gdriveAuthSession.accessToken,\n refresh_token: gdriveAuthSession.refreshToken,\n });\n\n const drive = google.drive({ version: 'v3', auth: oauth2Client });\n\n // Resolve parent folder\n let parentId = 'root';\n let locationPath = 'My Drive';\n\n if (parentFolderName.trim()) {\n const searchRes = await drive.files.list({\n q: `name='${parentFolderName.trim()}' and mimeType='application/vnd.google-apps.folder' and trashed=false`,\n fields: 'files(id,name)',\n spaces: 'drive',\n });\n\n if (searchRes.data.files && searchRes.data.files.length > 0) {\n parentId = searchRes.data.files[0].id!;\n } else {\n // Create the parent folder at root\n const parentRes = await drive.files.create({\n requestBody: {\n name: parentFolderName.trim(),\n mimeType: 'application/vnd.google-apps.folder',\n },\n fields: 'id',\n });\n parentId = parentRes.data.id!;\n }\n locationPath = `My Drive / ${parentFolderName.trim()}`;\n }\n\n // Check if the main folder already exists under the parent before creating\n let folderId: string | undefined;\n let resolvedFolderName = folderName.trim();\n const existingSearch = await drive.files.list({\n q: `name='${resolvedFolderName}' and '${parentId}' in parents and mimeType='application/vnd.google-apps.folder' and trashed=false`,\n fields: 'files(id,name)',\n spaces: 'drive',\n });\n\n if (existingSearch.data.files && existingSearch.data.files.length > 0) {\n folderId = existingSearch.data.files[0].id!;\n resolvedFolderName = existingSearch.data.files[0].name!;\n } else {\n const folderRes = await drive.files.create({\n requestBody: {\n name: resolvedFolderName,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [parentId],\n },\n fields: 'id,name',\n });\n folderId = folderRes.data.id!;\n resolvedFolderName = folderRes.data.name!;\n }\n\n const folderPath = `${locationPath} / ${resolvedFolderName}`;\n\n // Refresh tokens in case they were rotated\n const freshCredentials = await oauth2Client.getAccessToken();\n const latestRefreshToken =\n (oauth2Client.credentials.refresh_token as string | undefined) ??\n gdriveAuthSession.refreshToken;\n\n gdriveAuthSession = {\n ...gdriveAuthSession,\n status: 'complete',\n folderId: folderId!,\n folderName: resolvedFolderName,\n folderPath,\n refreshToken: latestRefreshToken,\n accessToken: freshCredentials.token ?? undefined,\n };\n\n res.json({\n folderId,\n folderName: resolvedFolderName,\n folderPath,\n refreshToken: latestRefreshToken,\n });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n app.get('/api/status', async (_req, res) => {\n const storage = createLocalStorage(workspacePath);\n const configManager = createConfigManager(storage);\n try {\n const config = await configManager.loadConfig();\n res.json({ configured: true, config });\n } catch {\n res.json({ configured: false });\n }\n });\n\n // Start server\n const server = app.listen(port, () => {\n const url = `http://localhost:${port}`;\n console.log(`Onboarding server running at ${url}`);\n\n // Try to open browser\n import('open')\n .then((openModule) => openModule.default(url))\n .catch(() => {\n console.log(`Open ${url} in your browser to continue.`);\n });\n });\n\n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\nShutting down onboarding server...');\n server.close();\n process.exit(0);\n });\n}\n\nasync function resolveOnboardingStaticPath(): Promise<string | null> {\n const baseDir = import.meta.dirname ?? __dirname;\n const candidates = [\n path.resolve(baseDir, './onboarding-web'),\n path.resolve(baseDir, '../../../apps/onboarding-web/dist'),\n ];\n\n for (const candidate of candidates) {\n try {\n await fs.access(path.join(candidate, 'index.html'));\n return candidate;\n } catch {\n // keep looking\n }\n }\n\n return null;\n}\n\nfunction getInlineOnboardingHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Personal Assistant - Setup</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; }\n .container { max-width: 640px; margin: 0 auto; padding: 2rem; }\n h1 { font-size: 1.8rem; margin-bottom: 0.5rem; color: #f8fafc; }\n h2 { font-size: 1.2rem; margin-bottom: 1rem; color: #94a3b8; font-weight: 400; }\n .step { display: none; }\n .step.active { display: block; }\n .progress { display: flex; gap: 0.5rem; margin-bottom: 2rem; }\n .progress-dot { width: 2rem; height: 0.25rem; background: #334155; border-radius: 2px; transition: background 0.3s; }\n .progress-dot.done { background: #3b82f6; }\n .progress-dot.current { background: #60a5fa; }\n label { display: block; margin-bottom: 0.5rem; color: #cbd5e1; font-size: 0.9rem; }\n input, select, textarea { width: 100%; padding: 0.75rem; background: #1e293b; border: 1px solid #334155; border-radius: 0.5rem; color: #f8fafc; font-size: 1rem; margin-bottom: 1rem; }\n input:focus, select:focus, textarea:focus { outline: none; border-color: #3b82f6; }\n textarea { min-height: 200px; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.85rem; }\n button { padding: 0.75rem 1.5rem; border: none; border-radius: 0.5rem; font-size: 1rem; cursor: pointer; transition: all 0.2s; }\n .btn-primary { background: #3b82f6; color: white; }\n .btn-primary:hover { background: #2563eb; }\n .btn-secondary { background: #334155; color: #e2e8f0; }\n .btn-secondary:hover { background: #475569; }\n .btn-group { display: flex; gap: 0.75rem; margin-top: 1.5rem; }\n .checkbox-group { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem; }\n .checkbox-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: #1e293b; border: 1px solid #334155; border-radius: 0.375rem; cursor: pointer; }\n .checkbox-item:hover { border-color: #475569; }\n .checkbox-item input[type=\"checkbox\"] { width: auto; margin: 0; }\n .radio-group { display: flex; gap: 1rem; margin-bottom: 1rem; }\n .radio-item { flex: 1; padding: 1rem; background: #1e293b; border: 2px solid #334155; border-radius: 0.5rem; cursor: pointer; text-align: center; transition: all 0.2s; }\n .radio-item:hover { border-color: #475569; }\n .radio-item.selected { border-color: #3b82f6; background: #1e3a5f; }\n .success { text-align: center; padding: 3rem 0; }\n .success h2 { color: #4ade80; font-size: 1.5rem; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"progress\" id=\"progress\"></div>\n\n <!-- Step 1: Identity -->\n <div class=\"step active\" data-step=\"0\">\n <h1>Welcome</h1>\n <h2>Let's set up your personal assistant</h2>\n <label>Your Name</label>\n <input type=\"text\" id=\"userName\" placeholder=\"Jane Smith\">\n <label>Assistant Name</label>\n <input type=\"text\" id=\"assistantName\" placeholder=\"Atlas\" value=\"Atlas\">\n <label>Profile</label>\n <select id=\"profileType\">\n <option value=\"software-engineer\">Software Engineer</option>\n <option value=\"product-manager\">Product Manager</option>\n <option value=\"engineering-manager\">Engineering Manager</option>\n <option value=\"devops\">DevOps Engineer</option>\n </select>\n <div class=\"btn-group\"><button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button></div>\n </div>\n\n <!-- Step 2: Responsibilities -->\n <div class=\"step\" data-step=\"1\">\n <h1>Responsibilities</h1>\n <h2>Select your core responsibilities</h2>\n <div class=\"checkbox-group\" id=\"responsibilitiesGroup\"></div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 3: Template Preview -->\n <div class=\"step\" data-step=\"2\">\n <h1>Context Template</h1>\n <h2>Preview and customize your context template</h2>\n <textarea id=\"templatePreview\"></textarea>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 4: Storage -->\n <div class=\"step\" data-step=\"3\">\n <h1>Storage</h1>\n <h2>Where should your context be stored?</h2>\n <div class=\"radio-group\">\n <div class=\"radio-item selected\" onclick=\"selectStorage('local')\" id=\"storage-local\">\n <strong>Local Workspace</strong><br><small>Files stored on this machine</small>\n </div>\n <div class=\"radio-item\" onclick=\"selectStorage('gdrive')\" id=\"storage-gdrive\">\n <strong>Google Drive</strong><br><small>Files stored in your Drive folder</small>\n </div>\n </div>\n <div id=\"storage-local-fields\">\n <label>Storage path</label>\n <div style=\"display:flex;gap:0.5rem;align-items:center;\">\n <input type=\"text\" id=\"localPath\" value=\".personal-assistant\" placeholder=\".personal-assistant\" style=\"flex:1;margin-bottom:0;\">\n <button class=\"btn-secondary\" onclick=\"browseFolder()\" id=\"browseBtn\" style=\"white-space:nowrap;flex-shrink:0;\">Browse</button>\n </div>\n <small style=\"color:#64748b;font-size:0.8rem;display:block;margin-top:0.4rem;margin-bottom:1rem;\">Click Browse to open a folder picker, or type a path directly.</small>\n </div>\n <div id=\"storage-gdrive-fields\" style=\"display:none;\">\n <p style=\"color:#f59e0b;font-size:0.85rem;margin-bottom:1rem;\">\n Google Drive OAuth is only supported in the full onboarding UI.<br>\n Run <code>pnpm build</code> in <code>apps/onboarding-web</code> then restart <code>pacman init</code>.\n </p>\n </div>\n <div style=\"background:#1c1917;border:1px solid #92400e;border-radius:0.5rem;padding:0.75rem 1rem;margin-bottom:1.5rem;font-size:0.85rem;color:#fbbf24;\">\n ⚠️ Do not delete, move, or rename the folder structure created by this setup. Claude and the sync daemon rely on the exact file layout to load and update your context.\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 5: Integrations -->\n <div class=\"step\" data-step=\"4\">\n <h1>Integrations</h1>\n <h2>Connect your tools (optional)</h2>\n <div class=\"checkbox-group\" id=\"integrationsGroup\">\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"slack\"> Slack</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gmail\"> Gmail</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"github\"> GitHub</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gitlab\"> GitLab</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gdrive\"> Google Drive Docs</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gchat\"> Google Chat</label>\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 6: Sync Schedule -->\n <div class=\"step\" data-step=\"5\">\n <h1>Sync Schedule</h1>\n <h2>When should context be refreshed?</h2>\n <label>Daily Sync Time</label>\n <input type=\"time\" id=\"syncTime\" value=\"08:00\">\n <label>Timezone</label>\n <select id=\"timezone\">\n <option value=\"America/New_York\">Eastern Time (ET)</option>\n <option value=\"America/Chicago\">Central Time (CT)</option>\n <option value=\"America/Denver\">Mountain Time (MT)</option>\n <option value=\"America/Los_Angeles\">Pacific Time (PT)</option>\n <option value=\"UTC\">UTC</option>\n <option value=\"Europe/London\">London (GMT/BST)</option>\n <option value=\"Europe/Berlin\">Berlin (CET)</option>\n <option value=\"Asia/Tokyo\">Tokyo (JST)</option>\n <option value=\"Asia/Kolkata\">India (IST)</option>\n <option value=\"Australia/Sydney\">Sydney (AEST)</option>\n </select>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-item\"><input type=\"checkbox\" id=\"manualSync\" checked> Manual sync</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" id=\"asyncUpdate\" checked> Async updates</label>\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 7: Finish -->\n <div class=\"step\" data-step=\"6\">\n <h1>All Set!</h1>\n <h2>Review and save your configuration</h2>\n <div id=\"summary\" style=\"background:#1e293b;padding:1rem;border-radius:0.5rem;margin-bottom:1rem;font-family:monospace;font-size:0.85rem;white-space:pre-wrap;\"></div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"saveConfig()\">Save &amp; Finish</button>\n </div>\n </div>\n\n <!-- Success -->\n <div class=\"step\" data-step=\"7\">\n <div class=\"success\">\n <h2>Setup Complete!</h2>\n <p style=\"margin-top:1rem;color:#94a3b8;\">Your personal assistant context has been initialized.</p>\n <p style=\"margin-top:1rem;color:#94a3b8;\">Next steps:</p>\n <pre style=\"text-align:left;margin-top:1rem;background:#1e293b;padding:1rem;border-radius:0.5rem;font-size:0.85rem;\">\n# Install Claude Code integration\npacman claude install\npacman mcp claude install\n\n# Install Codex integration\npacman codex install\npacman mcp codex install\n\n# Start the sync daemon\npacman daemon\n\n# Start the real-time Slack listener\npacman slack listen\n\n# Use in Claude Code\n/personal-assistant start &lt;project&gt;\n\n# Use in Codex\nAsk Codex to load or refresh your Personal Assistant context for &lt;project&gt;</pre>\n <p style=\"margin-top:1rem;color:#64748b;font-size:0.85rem;\">Restart Claude Code or Codex after installing the MCP server.<br>If <code style=\"background:#1e293b;padding:0 4px;border-radius:3px;\">pacman</code> is not found, link it first. The legacy alias <code style=\"background:#1e293b;padding:0 4px;border-radius:3px;\">personal-assistant</code> also remains available:<br>\n <code style=\"background:#1e293b;padding:2px 6px;border-radius:3px;\">pnpm setup &amp;&amp; source ~/.zshrc &amp;&amp; cd packages/cli &amp;&amp; pnpm link --global</code></p>\n </div>\n </div>\n </div>\n\n <script>\n let currentStep = 0;\n const totalSteps = 7;\n let state = { storageMode: 'local', responsibilities: [] };\n\n function updateProgress() {\n const bar = document.getElementById('progress');\n bar.innerHTML = '';\n for (let i = 0; i < totalSteps; i++) {\n const dot = document.createElement('div');\n dot.className = 'progress-dot' + (i < currentStep ? ' done' : i === currentStep ? ' current' : '');\n bar.appendChild(dot);\n }\n }\n\n function showStep(n) {\n document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));\n document.querySelector('[data-step=\"' + n + '\"]').classList.add('active');\n currentStep = n;\n updateProgress();\n }\n\n async function nextStep() {\n if (currentStep === 0) await loadResponsibilities();\n if (currentStep === 1) await loadTemplatePreview();\n if (currentStep === 5) updateSummary();\n showStep(currentStep + 1);\n }\n\n function prevStep() { showStep(currentStep - 1); }\n\n async function loadResponsibilities() {\n const profileType = document.getElementById('profileType').value;\n const res = await fetch('/api/responsibilities/' + profileType);\n const data = await res.json();\n const group = document.getElementById('responsibilitiesGroup');\n group.innerHTML = data.responsibilities.map(r =>\n '<label class=\"checkbox-item\"><input type=\"checkbox\" value=\"' + r + '\" checked> ' + r + '</label>'\n ).join('');\n }\n\n async function loadTemplatePreview() {\n const profileType = document.getElementById('profileType').value;\n const name = document.getElementById('userName').value;\n const assistantName = document.getElementById('assistantName').value;\n const responsibilities = Array.from(document.querySelectorAll('#responsibilitiesGroup input:checked')).map(c => c.value);\n state.responsibilities = responsibilities;\n\n const res = await fetch('/api/preview-template', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ profileType, name, assistantName, responsibilities })\n });\n const data = await res.json();\n const preview = Object.entries(data.files).map(([f, c]) => '--- ' + f + ' ---\\\\n' + c).join('\\\\n\\\\n');\n document.getElementById('templatePreview').value = preview;\n }\n\n function selectStorage(mode) {\n state.storageMode = mode;\n document.querySelectorAll('.radio-item').forEach(r => r.classList.remove('selected'));\n document.getElementById('storage-' + mode).classList.add('selected');\n document.getElementById('storage-local-fields').style.display = mode === 'local' ? 'block' : 'none';\n document.getElementById('storage-gdrive-fields').style.display = mode === 'gdrive' ? 'block' : 'none';\n }\n\n async function browseFolder() {\n const btn = document.getElementById('browseBtn');\n btn.textContent = '…';\n btn.disabled = true;\n try {\n const res = await fetch('/api/pick-folder');\n const data = await res.json();\n if (!data.cancelled && data.path) {\n document.getElementById('localPath').value = data.path;\n }\n } catch (e) {\n // ignore — user can type path manually\n } finally {\n btn.textContent = 'Browse';\n btn.disabled = false;\n }\n }\n\n function updateSummary() {\n const config = buildConfig();\n document.getElementById('summary').textContent = JSON.stringify(config, null, 2);\n }\n\n function buildConfig() {\n const integrations = Array.from(document.querySelectorAll('#integrationsGroup input:checked')).map(c => ({\n type: c.value, enabled: true, credentials: {}, cursor: null, lastSyncAt: null\n }));\n\n return {\n user: {\n name: document.getElementById('userName').value,\n assistantName: document.getElementById('assistantName').value,\n profileType: document.getElementById('profileType').value,\n responsibilities: state.responsibilities\n },\n storage: state.storageMode === 'local'\n ? { mode: 'local', workspacePath: document.getElementById('localPath').value || '.personal-assistant' }\n : { mode: 'gdrive', folderId: '', folderName: 'Personal Assistant', cachePath: '.personal-assistant/cache' },\n integrations,\n sync: {\n dailySyncTime: document.getElementById('syncTime').value,\n timezone: document.getElementById('timezone').value,\n manualSyncEnabled: document.getElementById('manualSync').checked,\n asyncUpdateEnabled: document.getElementById('asyncUpdate').checked\n }\n };\n }\n\n async function saveConfig() {\n const config = buildConfig();\n const res = await fetch('/api/save', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config)\n });\n const data = await res.json();\n if (data.success) showStep(7);\n else alert('Error: ' + data.error);\n }\n\n updateProgress();\n </script>\n</body>\n</html>`;\n}\n","// src/index.ts\nvar PROFILE_TEMPLATES = {\n \"software-engineer\": {\n profileType: \"software-engineer\",\n name: \"Software Engineer\",\n description: \"Context template for software engineers\",\n responsibilities: [\n \"Code review and PR management\",\n \"Feature development\",\n \"Bug fixing and debugging\",\n \"Architecture decisions\",\n \"Technical documentation\",\n \"On-call and incident response\",\n \"Mentoring junior engineers\",\n \"Sprint planning and estimation\",\n \"CI/CD pipeline maintenance\",\n \"API design and development\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nSoftware Engineer\n\n## Team\n<!-- Your team name -->\n\n## Primary Focus\n<!-- Main area of work -->\n\n## Key Repositories\n<!-- List your main repos -->\n\n## Tech Stack\n<!-- Languages, frameworks, tools -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Core Duties\n<!-- Your primary responsibilities -->\n\n## On-Call\n<!-- On-call schedule and escalation paths -->\n\n## Code Review\n<!-- Review expectations and areas -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Direct Manager\n<!-- Name, role -->\n\n## Team Members\n<!-- Key teammates -->\n\n## Cross-Team Contacts\n<!-- People you work with in other teams -->\n\n## Escalation Path\n<!-- Who to escalate to and when -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Internal Docs\n<!-- Links to internal documentation -->\n\n## Runbooks\n<!-- Links to runbooks -->\n\n## Architecture Docs\n<!-- Links to architecture documents -->\n`\n }\n ]\n },\n \"product-manager\": {\n profileType: \"product-manager\",\n name: \"Product Manager\",\n description: \"Context template for product managers\",\n responsibilities: [\n \"Product roadmap management\",\n \"Feature prioritization\",\n \"Stakeholder communication\",\n \"User research and interviews\",\n \"Metrics and KPI tracking\",\n \"Sprint planning and grooming\",\n \"Go-to-market strategy\",\n \"Competitive analysis\",\n \"Cross-team coordination\",\n \"Product requirements documentation\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nProduct Manager\n\n## Product Area\n<!-- Your product area -->\n\n## Key Metrics\n<!-- North star and supporting metrics -->\n\n## Current Quarter Goals\n<!-- OKRs or key goals -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Product Areas\n<!-- Products/features you own -->\n\n## Decision Authority\n<!-- What you can decide vs. escalate -->\n\n## Regular Meetings\n<!-- Recurring meetings and cadences -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Engineering Leads\n<!-- Engineering partners -->\n\n## Design Partners\n<!-- Design team contacts -->\n\n## Business Stakeholders\n<!-- Sales, marketing, exec sponsors -->\n\n## Customers\n<!-- Key customer contacts or segments -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## PRDs\n<!-- Product requirement documents -->\n\n## Roadmap\n<!-- Roadmap links -->\n\n## Analytics Dashboards\n<!-- Links to dashboards -->\n`\n }\n ]\n },\n \"engineering-manager\": {\n profileType: \"engineering-manager\",\n name: \"Engineering Manager\",\n description: \"Context template for engineering managers\",\n responsibilities: [\n \"Team management and 1:1s\",\n \"Hiring and interviewing\",\n \"Performance reviews\",\n \"Technical strategy\",\n \"Cross-team coordination\",\n \"Sprint planning and velocity\",\n \"Incident management\",\n \"Budget and resource planning\",\n \"Career development coaching\",\n \"Process improvement\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nEngineering Manager\n\n## Team\n<!-- Team name and size -->\n\n## Charter\n<!-- Team charter / mission -->\n\n## Key Systems\n<!-- Systems your team owns -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## People Management\n<!-- Direct reports, 1:1 cadence -->\n\n## Technical Oversight\n<!-- Architecture, code quality, tech debt -->\n\n## Process\n<!-- Agile ceremonies, team rituals -->\n\n## Hiring\n<!-- Open roles, pipeline -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Direct Reports\n<!-- Team members -->\n\n## Skip Level\n<!-- Your manager -->\n\n## Product Partners\n<!-- PMs you work with -->\n\n## Peer Managers\n<!-- Other EMs -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Team Docs\n<!-- Team wiki, confluence pages -->\n\n## Process Docs\n<!-- Runbooks, playbooks -->\n\n## HR Resources\n<!-- Performance review templates, etc. -->\n`\n }\n ]\n },\n devops: {\n profileType: \"devops\",\n name: \"DevOps Engineer\",\n description: \"Context template for DevOps engineers\",\n responsibilities: [\n \"CI/CD pipeline management\",\n \"Infrastructure as code\",\n \"Monitoring and alerting\",\n \"Incident response\",\n \"Security compliance\",\n \"Cost optimization\",\n \"Deployment automation\",\n \"Container orchestration\",\n \"Database management\",\n \"Disaster recovery planning\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nDevOps Engineer\n\n## Infrastructure\n<!-- Cloud provider, key services -->\n\n## Key Environments\n<!-- Production, staging, dev -->\n\n## Monitoring Stack\n<!-- Tools and dashboards -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Infrastructure\n<!-- IaC, cloud resources -->\n\n## CI/CD\n<!-- Pipelines, deployment processes -->\n\n## On-Call\n<!-- PagerDuty, escalation paths -->\n\n## Security\n<!-- Compliance, access management -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Platform Team\n<!-- Team members -->\n\n## Engineering Teams\n<!-- Teams you support -->\n\n## Security Team\n<!-- Security contacts -->\n\n## Vendor Contacts\n<!-- Cloud, tooling vendors -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Runbooks\n<!-- Incident response runbooks -->\n\n## Architecture Diagrams\n<!-- Infrastructure diagrams -->\n\n## Compliance Docs\n<!-- SOC2, security policies -->\n`\n }\n ]\n }\n};\nfunction getTemplate(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return { ...template };\n}\nfunction listProfiles() {\n return Object.keys(PROFILE_TEMPLATES);\n}\nfunction getResponsibilities(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return [...template.responsibilities];\n}\nfunction renderTemplate(template, user) {\n const files = {};\n for (const section of template.sections) {\n let content = section.defaultContent;\n content = content.replace(\"<!-- Your team name -->\", `${user.name}'s team`);\n if (section.fileName === \"responsibilities.md\" && user.responsibilities.length > 0) {\n content += \"\\n## Selected Responsibilities\\n\";\n for (const r of user.responsibilities) {\n content += `- ${r}\n`;\n }\n }\n files[section.fileName] = content;\n }\n return files;\n}\nfunction getTemplateSections(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return template.sections.map((s) => ({ ...s }));\n}\nexport {\n getResponsibilities,\n getTemplate,\n getTemplateSections,\n listProfiles,\n renderTemplate\n};\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmVT;","names":[]}
1
+ {"version":3,"sources":["../src/onboarding-server.ts","../../template-engine/dist/index.js","../src/project-structure.ts"],"sourcesContent":["import express from 'express';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\nimport { createLocalStorage } from '@personal-assistant/storage-local';\nimport { createGDriveStorage } from '@personal-assistant/storage-gdrive';\nimport { createConfigManager } from '@personal-assistant/config-manager';\nimport { createContextManager } from '@personal-assistant/context-manager';\nimport {\n getTemplate,\n renderTemplate,\n listProfiles,\n getResponsibilities,\n} from '@personal-assistant/template-engine';\nimport { validateIntegrationConfig } from '@personal-assistant/integration-runtime';\nimport { createSlackConnector, validateSlackAppToken } from '@personal-assistant/integrations-slack';\nimport { createNoopEventClient } from '@personal-assistant/event-client';\nimport { WORKSPACE_PATHS } from '@personal-assistant/core-types';\nimport type {\n AppConfig,\n LlmProvider,\n ProfileType,\n UserProfile,\n StorageConfig,\n GDriveStorageConfig,\n IntegrationConfig,\n SyncConfig,\n} from '@personal-assistant/core-types';\nimport { validateProviderConfig } from './provider-runtime.js';\nimport { buildProjectStructurePreview } from './project-structure.js';\n\ninterface GDriveAuthSession {\n clientId: string;\n clientSecret: string;\n status: 'pending' | 'authenticated' | 'complete' | 'error';\n // set after OAuth callback\n accessToken?: string;\n refreshToken?: string;\n // set after folder creation\n folderId?: string;\n folderName?: string;\n folderPath?: string;\n error?: string;\n}\n\ntype FolderPickerPurpose = 'local-source' | 'storage';\n\ninterface FolderPickerRequest {\n defaultPath?: string;\n purpose?: FolderPickerPurpose;\n}\n\nconst PICKER_COPY: Record<FolderPickerPurpose, { prompt: string; title: string }> = {\n 'local-source': {\n prompt: 'Select a local project folder to tag in Pac-Man',\n title: 'Select project source folder',\n },\n storage: {\n prompt: 'Select the Pac-Man workspace folder',\n title: 'Select workspace folder',\n },\n};\n\nasync function resolvePickerStartPath(defaultPath?: string): Promise<string | undefined> {\n const trimmed = defaultPath?.trim();\n if (!trimmed) {\n return undefined;\n }\n\n const resolvedPath = path.resolve(trimmed);\n const candidates = [resolvedPath, path.dirname(resolvedPath)];\n\n for (const candidate of candidates) {\n try {\n const stats = await fs.stat(candidate);\n if (stats.isDirectory()) {\n return candidate;\n }\n } catch {\n // keep checking fallback candidates\n }\n }\n\n return undefined;\n}\n\nfunction escapeAppleScriptString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\nfunction escapePowerShellString(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction ensureTrailingSeparator(folderPath: string): string {\n return folderPath.endsWith(path.sep) ? folderPath : `${folderPath}${path.sep}`;\n}\n\nfunction isPickerCancelledError(err: unknown): boolean {\n const message = String(err).toLowerCase();\n return message.includes('user canceled') || message.includes('user cancelled') || message.includes('cancel');\n}\n\nasync function openNativeFolderPicker(\n platform: NodeJS.Platform,\n purpose: FolderPickerPurpose,\n startPath?: string,\n): Promise<string> {\n const copy = PICKER_COPY[purpose];\n\n if (platform === 'darwin') {\n const script = startPath\n ? `POSIX path of (choose folder with prompt \"${escapeAppleScriptString(copy.prompt)}\" default location POSIX file \"${escapeAppleScriptString(startPath)}\")`\n : `POSIX path of (choose folder with prompt \"${escapeAppleScriptString(copy.prompt)}\")`;\n const { stdout } = await execFileAsync('osascript', ['-e', script]);\n return stdout.trim();\n }\n\n if (platform === 'linux') {\n const args = ['--file-selection', '--directory', `--title=${copy.title}`];\n if (startPath) {\n args.push(`--filename=${ensureTrailingSeparator(startPath)}`);\n }\n const { stdout } = await execFileAsync('zenity', args);\n return stdout.trim();\n }\n\n if (platform === 'win32') {\n const script = [\n 'Add-Type -AssemblyName System.Windows.Forms',\n '$dialog = New-Object System.Windows.Forms.FolderBrowserDialog',\n `$dialog.Description = '${escapePowerShellString(copy.prompt)}'`,\n ...(startPath ? [`$dialog.SelectedPath = '${escapePowerShellString(startPath)}'`] : []),\n 'if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $dialog.SelectedPath }',\n ].join('; ');\n const { stdout } = await execFileAsync('powershell', ['-NoProfile', '-Command', script]);\n return stdout.trim();\n }\n\n throw new Error('Folder picker not supported on this platform');\n}\n\nexport async function startOnboardingServer(\n port: number,\n workspacePath: string,\n): Promise<void> {\n const app = express();\n app.use(express.json());\n\n let gdriveAuthSession: GDriveAuthSession | null = null;\n\n const onboardingStaticPath = await resolveOnboardingStaticPath();\n\n // Try to serve built static files; fallback to inline HTML\n if (onboardingStaticPath) {\n app.use(express.static(onboardingStaticPath));\n } else {\n // Serve inline onboarding page if the web app isn't built\n app.get('/', (_req, res) => {\n res.send(getInlineOnboardingHtml());\n });\n }\n\n // --- API Routes ---\n\n app.get('/api/profiles', (_req, res) => {\n const profiles = listProfiles();\n res.json({ profiles });\n });\n\n app.get('/api/responsibilities/:profileType', (req, res) => {\n try {\n const responsibilities = getResponsibilities(req.params.profileType as ProfileType);\n res.json({ responsibilities });\n } catch {\n res.status(400).json({ error: 'Invalid profile type' });\n }\n });\n\n app.get('/api/template/:profileType', (req, res) => {\n try {\n const template = getTemplate(req.params.profileType as ProfileType);\n res.json({ template });\n } catch {\n res.status(400).json({ error: 'Invalid profile type' });\n }\n });\n\n app.post('/api/preview-template', (req, res) => {\n const { profileType, name, assistantName, responsibilities } = req.body;\n try {\n const template = getTemplate(profileType);\n const files = renderTemplate(template, { name, assistantName, responsibilities });\n res.json({ files });\n } catch (err) {\n res.status(400).json({ error: String(err) });\n }\n });\n\n app.post('/api/project-structure-preview', async (req, res) => {\n const {\n profileType,\n name,\n assistantName,\n responsibilities,\n localFolders,\n integrations,\n } = req.body as {\n profileType: ProfileType;\n name: string;\n assistantName: string;\n responsibilities: string[];\n localFolders?: Array<{ path: string; project?: string }>;\n integrations?: string[];\n };\n\n try {\n const preview = await buildProjectStructurePreview({\n profileType,\n name,\n assistantName,\n responsibilities: responsibilities ?? [],\n localFolders: localFolders ?? [],\n integrations: integrations ?? [],\n });\n res.json(preview);\n } catch (err) {\n res.status(400).json({ error: String(err) });\n }\n });\n\n app.post('/api/save', async (req, res) => {\n try {\n const payload = req.body as AppConfig & {\n templateFiles?: Record<string, string>;\n inferredProjects?: Array<{ slug?: string; name?: string }>;\n };\n const config: AppConfig = payload;\n\n // Use the user-chosen local path if provided, otherwise fall back to CLI --dir\n const effectivePath =\n config.storage.mode === 'local' && (config.storage as { workspacePath?: string }).workspacePath\n ? path.resolve((config.storage as { workspacePath: string }).workspacePath)\n : workspacePath;\n\n // Ensure the local directory exists (needed for bootstrap even in GDrive mode)\n await fs.mkdir(effectivePath, { recursive: true });\n\n // Save a global pointer so CLI commands can find the workspace without --dir\n const rcPath = path.join(process.env.HOME ?? process.env.USERPROFILE ?? '~', '.personal-assistant-rc.json');\n await fs.writeFile(rcPath, JSON.stringify({ workspacePath: effectivePath }, null, 2), 'utf-8');\n\n // Always save storage.json locally for bootstrap (so MCP server knows which backend to use)\n const localStorage = createLocalStorage(effectivePath);\n const localConfigManager = createConfigManager(localStorage);\n await localConfigManager.saveConfig(config);\n\n // Resolve the user's chosen storage backend\n let targetStorage: import('@personal-assistant/core-types').StorageBackend = localStorage;\n if (config.storage.mode === 'gdrive') {\n const gdriveConfig = config.storage as GDriveStorageConfig;\n const resolvedCachePath = path.isAbsolute(gdriveConfig.cachePath)\n ? gdriveConfig.cachePath\n : path.resolve(path.dirname(effectivePath), gdriveConfig.cachePath);\n\n const gdriveStorage = createGDriveStorage({\n ...gdriveConfig,\n cachePath: resolvedCachePath,\n });\n await gdriveStorage.initialize();\n targetStorage = gdriveStorage;\n\n // Write config to GDrive so all reads go through GDrive\n const gdriveConfigManager = createConfigManager(gdriveStorage);\n await gdriveConfigManager.saveConfig(config);\n }\n\n const contextManager = createContextManager(targetStorage);\n\n // Initialize workspace structure on the target backend\n await contextManager.initWorkspace();\n\n // Render and write template files to the target backend\n const files =\n payload.templateFiles && Object.keys(payload.templateFiles).length > 0\n ? payload.templateFiles\n : renderTemplate(getTemplate(config.user.profileType), {\n name: config.user.name,\n assistantName: config.user.assistantName,\n responsibilities: config.user.responsibilities,\n });\n await contextManager.writeCanonicalFiles(files);\n\n if (payload.inferredProjects && payload.inferredProjects.length > 0) {\n await targetStorage.write(\n WORKSPACE_PATHS.context.derived.suggestions.inferredProjects,\n JSON.stringify(\n payload.inferredProjects.map((project, index) => ({\n name: project.slug ?? project.name ?? `project-${index + 1}`,\n score: Number((1 - index * 0.1).toFixed(2)),\n })),\n null,\n 2,\n ),\n );\n }\n\n // Emit onboarding completed event\n const eventClient = createNoopEventClient();\n await eventClient.emit('onboarding_completed', {\n profile: config.user.profileType,\n storage_mode: config.storage.mode,\n });\n\n res.json({ success: true, workspacePath: effectivePath });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n // --- Integration credential validation ---\n\n app.post('/api/validate-integration', async (req, res) => {\n const { type, credentials } = req.body as {\n type: string;\n credentials: Record<string, string>;\n };\n\n try {\n const result = await validateIntegrationConfig({\n type: type as IntegrationConfig['type'],\n enabled: true,\n credentials,\n });\n\n if (result.ok) {\n res.json({ valid: true, info: result.summary ?? `Connected to ${type}` });\n return;\n }\n\n res.json({\n valid: false,\n error: [result.reason, ...(result.fix ?? [])].filter(Boolean).join(' '),\n });\n } catch (err) {\n res.json({ valid: false, error: String(err) });\n }\n });\n\n app.post('/api/validate-provider', async (req, res) => {\n const { provider, apiKey, model } = req.body as {\n provider: LlmProvider;\n apiKey: string;\n model?: string;\n };\n\n try {\n const result = await validateProviderConfig(provider, apiKey, model);\n res.json(result);\n } catch (err) {\n res.json({ valid: false, error: String(err) });\n }\n });\n\n app.post('/api/validate-slack-runtime', async (req, res) => {\n const {\n botToken,\n appToken,\n generationEnabled,\n providers,\n } = req.body as {\n botToken?: string;\n appToken?: string;\n generationEnabled?: boolean;\n providers?: {\n openai?: { apiKey?: string; model?: string };\n anthropic?: { apiKey?: string; model?: string };\n };\n };\n\n if (!botToken || !appToken) {\n res.json({ valid: false, error: 'Slack bot token and app token are required.' });\n return;\n }\n\n if (!botToken.startsWith('xoxb-')) {\n res.json({\n valid: false,\n error: 'Slack bot token must start with xoxb-. Paste the Bot User OAuth Token from OAuth & Permissions.',\n });\n return;\n }\n\n if (!appToken.startsWith('xapp-')) {\n res.json({\n valid: false,\n error: 'Slack Socket Mode app token must start with xapp-. Create an App-Level Token with connections:write under Socket Mode.',\n });\n return;\n }\n\n try {\n const connector = createSlackConnector();\n await connector.authenticate({\n type: 'slack',\n enabled: true,\n credentials: { botToken },\n });\n const health = await connector.healthCheck();\n await validateSlackAppToken(appToken);\n\n if (generationEnabled) {\n const providerEntries = [\n ['openai', providers?.openai],\n ['anthropic', providers?.anthropic],\n ] as const;\n\n const configuredProviders = providerEntries.filter(([, value]) => value?.apiKey);\n if (configuredProviders.length === 0) {\n res.json({ valid: false, error: 'Enable at least one provider when Slack generation is turned on.' });\n return;\n }\n\n for (const [provider, value] of configuredProviders) {\n const result = await validateProviderConfig(provider, value?.apiKey ?? '', value?.model);\n if (!result.valid) {\n res.json({ valid: false, error: result.error ?? `Failed to validate ${provider}` });\n return;\n }\n }\n }\n\n res.json({\n valid: true,\n info: `${health.summary ?? 'Slack connected'} via Socket Mode. Atlas will monitor channels where the bot is added.`,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('not_allowed_token_type')) {\n res.json({\n valid: false,\n error: 'Slack rejected the Socket Mode token. Use an App-Level Token that starts with xapp- and has the connections:write scope.',\n });\n return;\n }\n\n res.json({\n valid: false,\n error: message,\n });\n }\n });\n\n // --- Local folder picker ---\n\n app.post('/api/pick-folder', async (req, res) => {\n const { defaultPath, purpose = 'storage' } = req.body as FolderPickerRequest;\n const pickerPurpose: FolderPickerPurpose = purpose === 'local-source' ? 'local-source' : 'storage';\n const requestStartedAt = Date.now();\n const startPath = await resolvePickerStartPath(defaultPath);\n\n console.info(\n `[onboarding] folder picker requested purpose=${pickerPurpose} start=${startPath ?? 'system-default'}`,\n );\n\n try {\n const folderPath = await openNativeFolderPicker(process.platform, pickerPurpose, startPath);\n\n if (!folderPath) {\n console.info(\n `[onboarding] folder picker returned no path after ${Date.now() - requestStartedAt}ms`,\n );\n res.json({ cancelled: true, durationMs: Date.now() - requestStartedAt });\n return;\n }\n\n console.info(\n `[onboarding] folder picker resolved in ${Date.now() - requestStartedAt}ms path=${folderPath}`,\n );\n res.json({ path: folderPath, durationMs: Date.now() - requestStartedAt });\n } catch (err) {\n if (isPickerCancelledError(err)) {\n console.info(\n `[onboarding] folder picker cancelled after ${Date.now() - requestStartedAt}ms`,\n );\n res.json({ cancelled: true, durationMs: Date.now() - requestStartedAt });\n return;\n }\n console.error('[onboarding] folder picker failed', err);\n res.status(500).json({\n error: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - requestStartedAt,\n });\n }\n });\n\n // --- Google Drive OAuth routes ---\n\n app.post('/api/gdrive/auth-start', async (req, res) => {\n const { clientId, clientSecret } = req.body as { clientId?: string; clientSecret?: string };\n if (!clientId || !clientSecret) {\n res.status(400).json({ error: 'clientId and clientSecret are required' });\n return;\n }\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);\n const authUrl = oauth2Client.generateAuthUrl({\n access_type: 'offline',\n prompt: 'consent',\n scope: ['https://www.googleapis.com/auth/drive.file'],\n });\n\n gdriveAuthSession = { clientId, clientSecret, status: 'pending' };\n res.json({ authUrl });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n app.get('/api/gdrive/callback', async (req, res) => {\n const { code } = req.query as { code?: string };\n\n if (!code || !gdriveAuthSession) {\n res.status(400).send('Invalid OAuth callback — no pending auth session.');\n return;\n }\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(\n gdriveAuthSession.clientId,\n gdriveAuthSession.clientSecret,\n redirectUri,\n );\n\n const { tokens } = await oauth2Client.getToken(code);\n\n // Store tokens only — folder is created in a separate step\n gdriveAuthSession = {\n ...gdriveAuthSession,\n status: 'authenticated',\n refreshToken: tokens.refresh_token ?? undefined,\n accessToken: tokens.access_token ?? undefined,\n };\n\n res.send(`<!DOCTYPE html><html><body>\n <script>window.close();</script>\n <p style=\"font-family:sans-serif;padding:2rem;color:#4ade80;\">\n ✓ Authentication successful — you can close this tab and return to the setup.\n </p>\n </body></html>`);\n } catch (err) {\n if (gdriveAuthSession) {\n gdriveAuthSession.status = 'error';\n gdriveAuthSession.error = String(err);\n }\n res.status(500).send('Authentication failed: ' + String(err));\n }\n });\n\n app.get('/api/gdrive/auth-status', (_req, res) => {\n if (!gdriveAuthSession) {\n res.json({ status: 'idle' });\n return;\n }\n const { clientId: _cid, clientSecret: _csec, accessToken: _at, refreshToken: _rt, ...publicSession } = gdriveAuthSession;\n res.json(publicSession);\n });\n\n app.post('/api/gdrive/create-folder', async (req, res) => {\n if (!gdriveAuthSession || gdriveAuthSession.status !== 'authenticated') {\n res.status(400).json({ error: 'Not authenticated. Please connect Google Drive first.' });\n return;\n }\n\n const { folderName = 'Personal Assistant', parentFolderName = '' } =\n req.body as { folderName?: string; parentFolderName?: string };\n\n try {\n const { google } = await import('googleapis');\n const redirectUri = `http://localhost:${port}/api/gdrive/callback`;\n const oauth2Client = new google.auth.OAuth2(\n gdriveAuthSession.clientId,\n gdriveAuthSession.clientSecret,\n redirectUri,\n );\n oauth2Client.setCredentials({\n access_token: gdriveAuthSession.accessToken,\n refresh_token: gdriveAuthSession.refreshToken,\n });\n\n const drive = google.drive({ version: 'v3', auth: oauth2Client });\n\n // Resolve parent folder\n let parentId = 'root';\n let locationPath = 'My Drive';\n\n if (parentFolderName.trim()) {\n const searchRes = await drive.files.list({\n q: `name='${parentFolderName.trim()}' and mimeType='application/vnd.google-apps.folder' and trashed=false`,\n fields: 'files(id,name)',\n spaces: 'drive',\n });\n\n if (searchRes.data.files && searchRes.data.files.length > 0) {\n parentId = searchRes.data.files[0].id!;\n } else {\n // Create the parent folder at root\n const parentRes = await drive.files.create({\n requestBody: {\n name: parentFolderName.trim(),\n mimeType: 'application/vnd.google-apps.folder',\n },\n fields: 'id',\n });\n parentId = parentRes.data.id!;\n }\n locationPath = `My Drive / ${parentFolderName.trim()}`;\n }\n\n // Check if the main folder already exists under the parent before creating\n let folderId: string | undefined;\n let resolvedFolderName = folderName.trim();\n const existingSearch = await drive.files.list({\n q: `name='${resolvedFolderName}' and '${parentId}' in parents and mimeType='application/vnd.google-apps.folder' and trashed=false`,\n fields: 'files(id,name)',\n spaces: 'drive',\n });\n\n if (existingSearch.data.files && existingSearch.data.files.length > 0) {\n folderId = existingSearch.data.files[0].id!;\n resolvedFolderName = existingSearch.data.files[0].name!;\n } else {\n const folderRes = await drive.files.create({\n requestBody: {\n name: resolvedFolderName,\n mimeType: 'application/vnd.google-apps.folder',\n parents: [parentId],\n },\n fields: 'id,name',\n });\n folderId = folderRes.data.id!;\n resolvedFolderName = folderRes.data.name!;\n }\n\n const folderPath = `${locationPath} / ${resolvedFolderName}`;\n\n // Refresh tokens in case they were rotated\n const freshCredentials = await oauth2Client.getAccessToken();\n const latestRefreshToken =\n (oauth2Client.credentials.refresh_token as string | undefined) ??\n gdriveAuthSession.refreshToken;\n\n gdriveAuthSession = {\n ...gdriveAuthSession,\n status: 'complete',\n folderId: folderId!,\n folderName: resolvedFolderName,\n folderPath,\n refreshToken: latestRefreshToken,\n accessToken: freshCredentials.token ?? undefined,\n };\n\n res.json({\n folderId,\n folderName: resolvedFolderName,\n folderPath,\n refreshToken: latestRefreshToken,\n });\n } catch (err) {\n res.status(500).json({ error: String(err) });\n }\n });\n\n app.get('/api/status', async (_req, res) => {\n const storage = createLocalStorage(workspacePath);\n const configManager = createConfigManager(storage);\n try {\n const config = await configManager.loadConfig();\n res.json({ configured: true, config });\n } catch {\n res.json({ configured: false });\n }\n });\n\n // Start server\n const server = app.listen(port, () => {\n const url = `http://localhost:${port}`;\n console.log(`Onboarding server running at ${url}`);\n\n // Try to open browser\n import('open')\n .then((openModule) => openModule.default(url))\n .catch(() => {\n console.log(`Open ${url} in your browser to continue.`);\n });\n });\n\n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\nShutting down onboarding server...');\n server.close();\n process.exit(0);\n });\n}\n\nasync function resolveOnboardingStaticPath(): Promise<string | null> {\n const baseDir = import.meta.dirname ?? __dirname;\n const candidates = [\n path.resolve(baseDir, './onboarding-web'),\n path.resolve(baseDir, '../../../apps/onboarding-web/dist'),\n ];\n\n for (const candidate of candidates) {\n try {\n await fs.access(path.join(candidate, 'index.html'));\n return candidate;\n } catch {\n // keep looking\n }\n }\n\n return null;\n}\n\nfunction getInlineOnboardingHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Personal Assistant - Setup</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; }\n .container { max-width: 640px; margin: 0 auto; padding: 2rem; }\n h1 { font-size: 1.8rem; margin-bottom: 0.5rem; color: #f8fafc; }\n h2 { font-size: 1.2rem; margin-bottom: 1rem; color: #94a3b8; font-weight: 400; }\n .step { display: none; }\n .step.active { display: block; }\n .progress { display: flex; gap: 0.5rem; margin-bottom: 2rem; }\n .progress-dot { width: 2rem; height: 0.25rem; background: #334155; border-radius: 2px; transition: background 0.3s; }\n .progress-dot.done { background: #3b82f6; }\n .progress-dot.current { background: #60a5fa; }\n label { display: block; margin-bottom: 0.5rem; color: #cbd5e1; font-size: 0.9rem; }\n input, select, textarea { width: 100%; padding: 0.75rem; background: #1e293b; border: 1px solid #334155; border-radius: 0.5rem; color: #f8fafc; font-size: 1rem; margin-bottom: 1rem; }\n input:focus, select:focus, textarea:focus { outline: none; border-color: #3b82f6; }\n textarea { min-height: 200px; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.85rem; }\n button { padding: 0.75rem 1.5rem; border: none; border-radius: 0.5rem; font-size: 1rem; cursor: pointer; transition: all 0.2s; }\n .btn-primary { background: #3b82f6; color: white; }\n .btn-primary:hover { background: #2563eb; }\n .btn-secondary { background: #334155; color: #e2e8f0; }\n .btn-secondary:hover { background: #475569; }\n .btn-group { display: flex; gap: 0.75rem; margin-top: 1.5rem; }\n .checkbox-group { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem; }\n .checkbox-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: #1e293b; border: 1px solid #334155; border-radius: 0.375rem; cursor: pointer; }\n .checkbox-item:hover { border-color: #475569; }\n .checkbox-item input[type=\"checkbox\"] { width: auto; margin: 0; }\n .radio-group { display: flex; gap: 1rem; margin-bottom: 1rem; }\n .radio-item { flex: 1; padding: 1rem; background: #1e293b; border: 2px solid #334155; border-radius: 0.5rem; cursor: pointer; text-align: center; transition: all 0.2s; }\n .radio-item:hover { border-color: #475569; }\n .radio-item.selected { border-color: #3b82f6; background: #1e3a5f; }\n .success { text-align: center; padding: 3rem 0; }\n .success h2 { color: #4ade80; font-size: 1.5rem; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"progress\" id=\"progress\"></div>\n\n <!-- Step 1: Identity -->\n <div class=\"step active\" data-step=\"0\">\n <h1>Welcome</h1>\n <h2>Let's set up your personal assistant</h2>\n <label>Your Name</label>\n <input type=\"text\" id=\"userName\" placeholder=\"Jane Smith\">\n <label>Assistant Name</label>\n <input type=\"text\" id=\"assistantName\" placeholder=\"Atlas\" value=\"Atlas\">\n <label>Profile</label>\n <select id=\"profileType\">\n <option value=\"software-engineer\">Software Engineer</option>\n <option value=\"product-manager\">Product Manager</option>\n <option value=\"engineering-manager\">Engineering Manager</option>\n <option value=\"devops\">DevOps Engineer</option>\n </select>\n <div class=\"btn-group\"><button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button></div>\n </div>\n\n <!-- Step 2: Responsibilities -->\n <div class=\"step\" data-step=\"1\">\n <h1>Responsibilities</h1>\n <h2>Select your core responsibilities</h2>\n <div class=\"checkbox-group\" id=\"responsibilitiesGroup\"></div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 3: Template Preview -->\n <div class=\"step\" data-step=\"2\">\n <h1>Context Template</h1>\n <h2>Preview and customize your context template</h2>\n <textarea id=\"templatePreview\"></textarea>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 4: Storage -->\n <div class=\"step\" data-step=\"3\">\n <h1>Storage</h1>\n <h2>Where should your context be stored?</h2>\n <div class=\"radio-group\">\n <div class=\"radio-item selected\" onclick=\"selectStorage('local')\" id=\"storage-local\">\n <strong>Local Workspace</strong><br><small>Files stored on this machine</small>\n </div>\n <div class=\"radio-item\" onclick=\"selectStorage('gdrive')\" id=\"storage-gdrive\">\n <strong>Google Drive</strong><br><small>Files stored in your Drive folder</small>\n </div>\n </div>\n <div id=\"storage-local-fields\">\n <label>Storage path</label>\n <div style=\"display:flex;gap:0.5rem;align-items:center;\">\n <input type=\"text\" id=\"localPath\" value=\".personal-assistant\" placeholder=\".personal-assistant\" style=\"flex:1;margin-bottom:0;\">\n <button class=\"btn-secondary\" onclick=\"browseFolder()\" id=\"browseBtn\" style=\"white-space:nowrap;flex-shrink:0;\">Browse</button>\n </div>\n <small style=\"color:#64748b;font-size:0.8rem;display:block;margin-top:0.4rem;margin-bottom:1rem;\">Click Browse to open a folder picker, or type a path directly.</small>\n </div>\n <div id=\"storage-gdrive-fields\" style=\"display:none;\">\n <p style=\"color:#f59e0b;font-size:0.85rem;margin-bottom:1rem;\">\n Google Drive OAuth is only supported in the full onboarding UI.<br>\n Run <code>pnpm build</code> in <code>apps/onboarding-web</code> then restart <code>pacman init</code>.\n </p>\n </div>\n <div style=\"background:#1c1917;border:1px solid #92400e;border-radius:0.5rem;padding:0.75rem 1rem;margin-bottom:1.5rem;font-size:0.85rem;color:#fbbf24;\">\n ⚠️ Do not delete, move, or rename the folder structure created by this setup. Claude and the sync daemon rely on the exact file layout to load and update your context.\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 5: Integrations -->\n <div class=\"step\" data-step=\"4\">\n <h1>Integrations</h1>\n <h2>Connect your tools (optional)</h2>\n <div class=\"checkbox-group\" id=\"integrationsGroup\">\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"slack\"> Slack</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gmail\"> Gmail</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"github\"> GitHub</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gitlab\"> GitLab</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gdrive\"> Google Drive Docs</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" value=\"gchat\"> Google Chat</label>\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 6: Sync Schedule -->\n <div class=\"step\" data-step=\"5\">\n <h1>Sync Schedule</h1>\n <h2>When should context be refreshed?</h2>\n <label>Daily Sync Time</label>\n <input type=\"time\" id=\"syncTime\" value=\"08:00\">\n <label>Timezone</label>\n <select id=\"timezone\">\n <option value=\"America/New_York\">Eastern Time (ET)</option>\n <option value=\"America/Chicago\">Central Time (CT)</option>\n <option value=\"America/Denver\">Mountain Time (MT)</option>\n <option value=\"America/Los_Angeles\">Pacific Time (PT)</option>\n <option value=\"UTC\">UTC</option>\n <option value=\"Europe/London\">London (GMT/BST)</option>\n <option value=\"Europe/Berlin\">Berlin (CET)</option>\n <option value=\"Asia/Tokyo\">Tokyo (JST)</option>\n <option value=\"Asia/Kolkata\">India (IST)</option>\n <option value=\"Australia/Sydney\">Sydney (AEST)</option>\n </select>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-item\"><input type=\"checkbox\" id=\"manualSync\" checked> Manual sync</label>\n <label class=\"checkbox-item\"><input type=\"checkbox\" id=\"asyncUpdate\" checked> Async updates</label>\n </div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"nextStep()\">Continue</button>\n </div>\n </div>\n\n <!-- Step 7: Finish -->\n <div class=\"step\" data-step=\"6\">\n <h1>All Set!</h1>\n <h2>Review and save your configuration</h2>\n <div id=\"summary\" style=\"background:#1e293b;padding:1rem;border-radius:0.5rem;margin-bottom:1rem;font-family:monospace;font-size:0.85rem;white-space:pre-wrap;\"></div>\n <div class=\"btn-group\">\n <button class=\"btn-secondary\" onclick=\"prevStep()\">Back</button>\n <button class=\"btn-primary\" onclick=\"saveConfig()\">Save &amp; Finish</button>\n </div>\n </div>\n\n <!-- Success -->\n <div class=\"step\" data-step=\"7\">\n <div class=\"success\">\n <h2>Setup Complete!</h2>\n <p style=\"margin-top:1rem;color:#94a3b8;\">Your personal assistant context has been initialized.</p>\n <p style=\"margin-top:1rem;color:#94a3b8;\">Next steps:</p>\n <pre style=\"text-align:left;margin-top:1rem;background:#1e293b;padding:1rem;border-radius:0.5rem;font-size:0.85rem;\">\n# Install Claude Code integration\npacman claude install\npacman mcp claude install\n\n# Install Codex integration\npacman codex install\npacman mcp codex install\n\n# Start the sync daemon\npacman daemon\n\n# Start the real-time Slack listener\npacman slack listen\n\n# Use in Claude Code\n/pacman start &lt;project&gt;\n\n# Use in Codex\nAsk Codex to load or refresh your Pac-Man context for &lt;project&gt;</pre>\n <p style=\"margin-top:1rem;color:#64748b;font-size:0.85rem;\">Restart Claude Code or Codex after installing the MCP server.<br>If <code style=\"background:#1e293b;padding:0 4px;border-radius:3px;\">pacman</code> is not found, link it first:<br>\n <code style=\"background:#1e293b;padding:2px 6px;border-radius:3px;\">pnpm setup &amp;&amp; source ~/.zshrc &amp;&amp; cd packages/cli &amp;&amp; pnpm link --global</code></p>\n </div>\n </div>\n </div>\n\n <script>\n let currentStep = 0;\n const totalSteps = 7;\n let state = { storageMode: 'local', responsibilities: [] };\n\n function updateProgress() {\n const bar = document.getElementById('progress');\n bar.innerHTML = '';\n for (let i = 0; i < totalSteps; i++) {\n const dot = document.createElement('div');\n dot.className = 'progress-dot' + (i < currentStep ? ' done' : i === currentStep ? ' current' : '');\n bar.appendChild(dot);\n }\n }\n\n function showStep(n) {\n document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));\n document.querySelector('[data-step=\"' + n + '\"]').classList.add('active');\n currentStep = n;\n updateProgress();\n }\n\n async function nextStep() {\n if (currentStep === 0) await loadResponsibilities();\n if (currentStep === 1) await loadTemplatePreview();\n if (currentStep === 5) updateSummary();\n showStep(currentStep + 1);\n }\n\n function prevStep() { showStep(currentStep - 1); }\n\n async function loadResponsibilities() {\n const profileType = document.getElementById('profileType').value;\n const res = await fetch('/api/responsibilities/' + profileType);\n const data = await res.json();\n const group = document.getElementById('responsibilitiesGroup');\n group.innerHTML = data.responsibilities.map(r =>\n '<label class=\"checkbox-item\"><input type=\"checkbox\" value=\"' + r + '\" checked> ' + r + '</label>'\n ).join('');\n }\n\n async function loadTemplatePreview() {\n const profileType = document.getElementById('profileType').value;\n const name = document.getElementById('userName').value;\n const assistantName = document.getElementById('assistantName').value;\n const responsibilities = Array.from(document.querySelectorAll('#responsibilitiesGroup input:checked')).map(c => c.value);\n state.responsibilities = responsibilities;\n\n const res = await fetch('/api/preview-template', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ profileType, name, assistantName, responsibilities })\n });\n const data = await res.json();\n const preview = Object.entries(data.files).map(([f, c]) => '--- ' + f + ' ---\\\\n' + c).join('\\\\n\\\\n');\n document.getElementById('templatePreview').value = preview;\n }\n\n function selectStorage(mode) {\n state.storageMode = mode;\n document.querySelectorAll('.radio-item').forEach(r => r.classList.remove('selected'));\n document.getElementById('storage-' + mode).classList.add('selected');\n document.getElementById('storage-local-fields').style.display = mode === 'local' ? 'block' : 'none';\n document.getElementById('storage-gdrive-fields').style.display = mode === 'gdrive' ? 'block' : 'none';\n }\n\n async function browseFolder() {\n const btn = document.getElementById('browseBtn');\n btn.textContent = 'Opening...';\n btn.disabled = true;\n try {\n const res = await fetch('/api/pick-folder', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n defaultPath: document.getElementById('localPath').value,\n purpose: 'storage'\n })\n });\n const data = await res.json();\n if (!data.cancelled && data.path) {\n document.getElementById('localPath').value = data.path;\n }\n } catch (e) {\n // ignore — user can type path manually\n } finally {\n btn.textContent = 'Browse';\n btn.disabled = false;\n }\n }\n\n function updateSummary() {\n const config = buildConfig();\n document.getElementById('summary').textContent = JSON.stringify(config, null, 2);\n }\n\n function buildConfig() {\n const integrations = Array.from(document.querySelectorAll('#integrationsGroup input:checked')).map(c => ({\n type: c.value, enabled: true, credentials: {}, cursor: null, lastSyncAt: null\n }));\n\n return {\n user: {\n name: document.getElementById('userName').value,\n assistantName: document.getElementById('assistantName').value,\n profileType: document.getElementById('profileType').value,\n responsibilities: state.responsibilities\n },\n storage: state.storageMode === 'local'\n ? { mode: 'local', workspacePath: document.getElementById('localPath').value || '.personal-assistant' }\n : { mode: 'gdrive', folderId: '', folderName: 'Personal Assistant', cachePath: '.personal-assistant/cache' },\n integrations,\n sync: {\n dailySyncTime: document.getElementById('syncTime').value,\n timezone: document.getElementById('timezone').value,\n manualSyncEnabled: document.getElementById('manualSync').checked,\n asyncUpdateEnabled: document.getElementById('asyncUpdate').checked\n }\n };\n }\n\n async function saveConfig() {\n const config = buildConfig();\n const res = await fetch('/api/save', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(config)\n });\n const data = await res.json();\n if (data.success) showStep(7);\n else alert('Error: ' + data.error);\n }\n\n updateProgress();\n </script>\n</body>\n</html>`;\n}\n","// src/index.ts\nvar PROFILE_TEMPLATES = {\n \"software-engineer\": {\n profileType: \"software-engineer\",\n name: \"Software Engineer\",\n description: \"Context template for software engineers\",\n responsibilities: [\n \"Code review and PR management\",\n \"Feature development\",\n \"Bug fixing and debugging\",\n \"Architecture decisions\",\n \"Technical documentation\",\n \"On-call and incident response\",\n \"Mentoring junior engineers\",\n \"Sprint planning and estimation\",\n \"CI/CD pipeline maintenance\",\n \"API design and development\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nSoftware Engineer\n\n## Team\n<!-- Your team name -->\n\n## Primary Focus\n<!-- Main area of work -->\n\n## Key Repositories\n<!-- List your main repos -->\n\n## Tech Stack\n<!-- Languages, frameworks, tools -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Core Duties\n<!-- Your primary responsibilities -->\n\n## On-Call\n<!-- On-call schedule and escalation paths -->\n\n## Code Review\n<!-- Review expectations and areas -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Direct Manager\n<!-- Name, role -->\n\n## Team Members\n<!-- Key teammates -->\n\n## Cross-Team Contacts\n<!-- People you work with in other teams -->\n\n## Escalation Path\n<!-- Who to escalate to and when -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Internal Docs\n<!-- Links to internal documentation -->\n\n## Runbooks\n<!-- Links to runbooks -->\n\n## Architecture Docs\n<!-- Links to architecture documents -->\n`\n }\n ]\n },\n \"product-manager\": {\n profileType: \"product-manager\",\n name: \"Product Manager\",\n description: \"Context template for product managers\",\n responsibilities: [\n \"Product roadmap management\",\n \"Feature prioritization\",\n \"Stakeholder communication\",\n \"User research and interviews\",\n \"Metrics and KPI tracking\",\n \"Sprint planning and grooming\",\n \"Go-to-market strategy\",\n \"Competitive analysis\",\n \"Cross-team coordination\",\n \"Product requirements documentation\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nProduct Manager\n\n## Product Area\n<!-- Your product area -->\n\n## Key Metrics\n<!-- North star and supporting metrics -->\n\n## Current Quarter Goals\n<!-- OKRs or key goals -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Product Areas\n<!-- Products/features you own -->\n\n## Decision Authority\n<!-- What you can decide vs. escalate -->\n\n## Regular Meetings\n<!-- Recurring meetings and cadences -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Engineering Leads\n<!-- Engineering partners -->\n\n## Design Partners\n<!-- Design team contacts -->\n\n## Business Stakeholders\n<!-- Sales, marketing, exec sponsors -->\n\n## Customers\n<!-- Key customer contacts or segments -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## PRDs\n<!-- Product requirement documents -->\n\n## Roadmap\n<!-- Roadmap links -->\n\n## Analytics Dashboards\n<!-- Links to dashboards -->\n`\n }\n ]\n },\n \"engineering-manager\": {\n profileType: \"engineering-manager\",\n name: \"Engineering Manager\",\n description: \"Context template for engineering managers\",\n responsibilities: [\n \"Team management and 1:1s\",\n \"Hiring and interviewing\",\n \"Performance reviews\",\n \"Technical strategy\",\n \"Cross-team coordination\",\n \"Sprint planning and velocity\",\n \"Incident management\",\n \"Budget and resource planning\",\n \"Career development coaching\",\n \"Process improvement\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nEngineering Manager\n\n## Team\n<!-- Team name and size -->\n\n## Charter\n<!-- Team charter / mission -->\n\n## Key Systems\n<!-- Systems your team owns -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## People Management\n<!-- Direct reports, 1:1 cadence -->\n\n## Technical Oversight\n<!-- Architecture, code quality, tech debt -->\n\n## Process\n<!-- Agile ceremonies, team rituals -->\n\n## Hiring\n<!-- Open roles, pipeline -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Direct Reports\n<!-- Team members -->\n\n## Skip Level\n<!-- Your manager -->\n\n## Product Partners\n<!-- PMs you work with -->\n\n## Peer Managers\n<!-- Other EMs -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Team Docs\n<!-- Team wiki, confluence pages -->\n\n## Process Docs\n<!-- Runbooks, playbooks -->\n\n## HR Resources\n<!-- Performance review templates, etc. -->\n`\n }\n ]\n },\n devops: {\n profileType: \"devops\",\n name: \"DevOps Engineer\",\n description: \"Context template for DevOps engineers\",\n responsibilities: [\n \"CI/CD pipeline management\",\n \"Infrastructure as code\",\n \"Monitoring and alerting\",\n \"Incident response\",\n \"Security compliance\",\n \"Cost optimization\",\n \"Deployment automation\",\n \"Container orchestration\",\n \"Database management\",\n \"Disaster recovery planning\"\n ],\n sections: [\n {\n name: \"Overview\",\n fileName: \"overview.md\",\n required: true,\n defaultContent: `# Overview\n\n## Role\nDevOps Engineer\n\n## Infrastructure\n<!-- Cloud provider, key services -->\n\n## Key Environments\n<!-- Production, staging, dev -->\n\n## Monitoring Stack\n<!-- Tools and dashboards -->\n`\n },\n {\n name: \"Responsibilities\",\n fileName: \"responsibilities.md\",\n required: true,\n defaultContent: `# Responsibilities\n\n## Infrastructure\n<!-- IaC, cloud resources -->\n\n## CI/CD\n<!-- Pipelines, deployment processes -->\n\n## On-Call\n<!-- PagerDuty, escalation paths -->\n\n## Security\n<!-- Compliance, access management -->\n`\n },\n {\n name: \"Stakeholders\",\n fileName: \"stakeholders.md\",\n required: false,\n defaultContent: `# Stakeholders\n\n## Platform Team\n<!-- Team members -->\n\n## Engineering Teams\n<!-- Teams you support -->\n\n## Security Team\n<!-- Security contacts -->\n\n## Vendor Contacts\n<!-- Cloud, tooling vendors -->\n`\n },\n {\n name: \"Documentation\",\n fileName: \"docs.md\",\n required: false,\n defaultContent: `# Documentation References\n\n## Runbooks\n<!-- Incident response runbooks -->\n\n## Architecture Diagrams\n<!-- Infrastructure diagrams -->\n\n## Compliance Docs\n<!-- SOC2, security policies -->\n`\n }\n ]\n }\n};\nfunction getTemplate(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return { ...template };\n}\nfunction listProfiles() {\n return Object.keys(PROFILE_TEMPLATES);\n}\nfunction getResponsibilities(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return [...template.responsibilities];\n}\nfunction renderTemplate(template, user) {\n const files = {};\n for (const section of template.sections) {\n let content = section.defaultContent;\n content = content.replace(\"<!-- Your team name -->\", `${user.name}'s team`);\n if (section.fileName === \"responsibilities.md\" && user.responsibilities.length > 0) {\n content += \"\\n## Selected Responsibilities\\n\";\n for (const r of user.responsibilities) {\n content += `- ${r}\n`;\n }\n }\n files[section.fileName] = content;\n }\n return files;\n}\nfunction getTemplateSections(profileType) {\n const template = PROFILE_TEMPLATES[profileType];\n if (!template) {\n throw new Error(`Unknown profile type: ${profileType}`);\n }\n return template.sections.map((s) => ({ ...s }));\n}\nexport {\n getResponsibilities,\n getTemplate,\n getTemplateSections,\n listProfiles,\n renderTemplate\n};\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { ProfileType } from '@personal-assistant/core-types';\nimport { getTemplate, renderTemplate } from '@personal-assistant/template-engine';\n\nconst MAX_DIR_ENTRIES = 10;\nconst README_CANDIDATES = ['README.md', 'readme.md', 'README.txt', 'readme.txt'];\nconst OLLAMA_BASE_URL = 'http://127.0.0.1:11434';\n\nexport interface LocalFolderInput {\n path: string;\n project?: string;\n}\n\nexport interface InferredProject {\n slug: string;\n name: string;\n summary: string;\n primaryFocus: string;\n sourceFolders: string[];\n connectedIntegrations: string[];\n keySignals: string[];\n}\n\nexport interface ProjectInferenceMeta {\n strategy: 'ollama' | 'heuristic';\n model?: string;\n note: string;\n}\n\nexport interface ProjectStructurePreview {\n files: Record<string, string>;\n projects: InferredProject[];\n inference: ProjectInferenceMeta;\n}\n\ninterface FolderSignal {\n path: string;\n explicitProject?: string;\n basename: string;\n packageName?: string;\n readmeHeading?: string;\n entries: string[];\n notes: string[];\n}\n\nexport async function buildProjectStructurePreview(input: {\n profileType: ProfileType;\n name: string;\n assistantName: string;\n responsibilities: string[];\n localFolders: LocalFolderInput[];\n integrations: string[];\n}): Promise<ProjectStructurePreview> {\n const baseFiles = renderTemplate(getTemplate(input.profileType), {\n name: input.name,\n assistantName: input.assistantName,\n responsibilities: input.responsibilities,\n });\n\n const folderSignals = await Promise.all(\n input.localFolders\n .filter((folder) => folder.path.trim())\n .map((folder) => collectFolderSignal(folder)),\n );\n\n const heuristicProjects = buildHeuristicProjects(folderSignals, input.integrations);\n const ollamaResult = await enrichProjectsWithOllama(\n heuristicProjects,\n folderSignals,\n input.integrations,\n );\n\n const projects = ollamaResult?.projects ?? heuristicProjects;\n const inference =\n ollamaResult?.inference ?? {\n strategy: 'heuristic' as const,\n note:\n 'Structured locally from folder tags, folder names, and visible directory signals. No external API calls were made.',\n };\n\n return {\n files: buildTemplateFiles(baseFiles, projects, folderSignals, input.integrations),\n projects,\n inference,\n };\n}\n\nfunction buildTemplateFiles(\n baseFiles: Record<string, string>,\n projects: InferredProject[],\n folderSignals: FolderSignal[],\n integrations: string[],\n): Record<string, string> {\n const files = { ...baseFiles };\n const integrationNames = integrations.map(formatIntegrationName);\n\n files['overview.md'] = [\n baseFiles['overview.md'] ?? '# Overview',\n '',\n '## Confirmed Project Structure',\n '',\n projects.length > 0\n ? projects\n .map((project) => `- **${project.name}** — ${project.primaryFocus || project.summary}`)\n .join('\\n')\n : '- No project groups were inferred yet. Add local folders or integrations and revisit this step.',\n ].join('\\n');\n\n const sourceLines: string[] = [];\n if (folderSignals.length > 0) {\n sourceLines.push('## Local Sources', '');\n sourceLines.push(...folderSignals.map((signal) => `- \\`${signal.path}\\``));\n sourceLines.push('');\n }\n if (integrationNames.length > 0) {\n sourceLines.push('## Connected Integrations', '');\n sourceLines.push(...integrationNames.map((name) => `- ${name}`));\n sourceLines.push('');\n }\n if (sourceLines.length > 0) {\n files['docs.md'] = [baseFiles['docs.md'] ?? '# Documentation References', '', ...sourceLines].join(\n '\\n',\n );\n }\n\n if (integrationNames.length > 0) {\n files['integrations.md'] = [\n '# Connected Integrations',\n '',\n ...integrationNames.map((name) => `- ${name}`),\n '',\n '## Notes',\n '',\n 'These integrations are workspace-level sources. Confirm project-specific mappings after the first sync.',\n '',\n ].join('\\n');\n }\n\n for (const project of projects) {\n files[`projects/${project.slug}.md`] = buildProjectFile(project);\n }\n\n return files;\n}\n\nfunction buildProjectFile(project: InferredProject): string {\n return [\n `# ${project.name}`,\n '',\n '## Summary',\n '',\n project.summary || '<!-- Add a short summary for this project -->',\n '',\n '## Primary Focus',\n '',\n project.primaryFocus || '<!-- Capture the main problem area, domain, or ownership -->',\n '',\n '## Local Sources',\n '',\n formatBulletList(\n project.sourceFolders.map((folder) => `\\`${folder}\\``),\n 'No local folders linked to this project yet.',\n ),\n '',\n '## Connected Integrations',\n '',\n formatBulletList(\n project.connectedIntegrations,\n 'No integrations connected during onboarding.',\n ),\n '',\n '## Grouping Signals',\n '',\n formatBulletList(project.keySignals, 'Add more details after the first sync.'),\n '',\n '## Notes To Confirm',\n '',\n '- Owners:',\n '- Key repos/docs:',\n '- Important decisions:',\n '',\n ].join('\\n');\n}\n\nfunction formatBulletList(items: string[], fallback: string): string {\n if (items.length === 0) {\n return `- ${fallback}`;\n }\n return items.map((item) => `- ${item}`).join('\\n');\n}\n\nasync function collectFolderSignal(folder: LocalFolderInput): Promise<FolderSignal> {\n const rawPath = folder.path.trim();\n const resolvedPath = path.resolve(rawPath);\n const basename = path.basename(resolvedPath);\n const notes: string[] = [];\n const entries: string[] = [];\n let packageName: string | undefined;\n let readmeHeading: string | undefined;\n\n if (folder.project?.trim()) {\n notes.push(`Tagged as \"${folder.project.trim()}\" during onboarding`);\n }\n\n try {\n const dirEntries = await fs.readdir(resolvedPath, { withFileTypes: true });\n const visibleEntries = dirEntries\n .filter((entry) => !entry.name.startsWith('.'))\n .slice(0, MAX_DIR_ENTRIES)\n .map((entry) => (entry.isDirectory() ? `${entry.name}/` : entry.name));\n\n entries.push(...visibleEntries);\n\n if (visibleEntries.length > 0) {\n notes.push(`Top entries: ${visibleEntries.slice(0, 5).join(', ')}`);\n }\n } catch {\n notes.push('Folder contents could not be read during onboarding');\n }\n\n try {\n const packageJson = await fs.readFile(path.join(resolvedPath, 'package.json'), 'utf-8');\n const parsed = JSON.parse(packageJson) as { name?: string };\n if (parsed.name?.trim()) {\n packageName = parsed.name.trim();\n notes.push(`package.json name: ${packageName}`);\n }\n } catch {\n // Ignore missing or invalid package.json\n }\n\n for (const candidate of README_CANDIDATES) {\n try {\n const readme = await fs.readFile(path.join(resolvedPath, candidate), 'utf-8');\n const heading = extractReadmeHeading(readme);\n if (heading) {\n readmeHeading = heading;\n notes.push(`README heading: ${heading}`);\n break;\n }\n } catch {\n // Ignore missing README files\n }\n }\n\n return {\n path: rawPath,\n explicitProject: folder.project?.trim() || undefined,\n basename,\n packageName,\n readmeHeading,\n entries,\n notes,\n };\n}\n\nfunction extractReadmeHeading(contents: string): string | undefined {\n for (const rawLine of contents.split('\\n').slice(0, 30)) {\n const line = rawLine.trim();\n if (!line) continue;\n if (line.startsWith('#')) {\n return line.replace(/^#+\\s*/, '').trim();\n }\n if (line.length <= 80) {\n return line;\n }\n }\n return undefined;\n}\n\nfunction buildHeuristicProjects(\n folderSignals: FolderSignal[],\n integrations: string[],\n): InferredProject[] {\n const integrationNames = integrations.map(formatIntegrationName);\n const groups = new Map<string, InferredProject>();\n\n if (folderSignals.length === 0 && integrationNames.length > 0) {\n return [\n {\n slug: 'workspace-context',\n name: 'Workspace Context',\n summary:\n 'Connected integrations are ready, but no local folders were added during onboarding. Confirm the main project boundaries after the first sync.',\n primaryFocus: 'Cross-project workspace context',\n sourceFolders: [],\n connectedIntegrations: integrationNames,\n keySignals: integrationNames.map((name) => `Connected integration: ${name}`),\n },\n ];\n }\n\n for (const signal of folderSignals) {\n const groupName = signal.explicitProject || inferProjectName(signal);\n const slug = slugify(groupName) || 'untitled-project';\n const existing = groups.get(slug);\n\n if (existing) {\n existing.sourceFolders.push(signal.path);\n existing.keySignals = unique([...existing.keySignals, ...signal.notes]);\n continue;\n }\n\n groups.set(slug, {\n slug,\n name: groupName,\n summary: buildHeuristicSummary(groupName, signal, integrationNames),\n primaryFocus: buildHeuristicFocus(signal),\n sourceFolders: [signal.path],\n connectedIntegrations: integrationNames,\n keySignals: unique(signal.notes),\n });\n }\n\n return [...groups.values()].sort((left, right) => left.name.localeCompare(right.name));\n}\n\nfunction buildHeuristicSummary(\n projectName: string,\n signal: FolderSignal,\n integrationNames: string[],\n): string {\n const integrationSuffix =\n integrationNames.length > 0\n ? ` It will also pull from ${integrationNames.join(', ')} once sync is enabled.`\n : '';\n\n if (signal.readmeHeading) {\n return `${projectName} appears to center on \"${signal.readmeHeading}\". Review and tighten this summary before finishing setup.${integrationSuffix}`;\n }\n\n if (signal.packageName) {\n return `${projectName} was inferred from the local package \"${signal.packageName}\". Review and tighten this summary before finishing setup.${integrationSuffix}`;\n }\n\n return `${projectName} was inferred from the selected local workspace folder. Review and tighten this summary before finishing setup.${integrationSuffix}`;\n}\n\nfunction buildHeuristicFocus(signal: FolderSignal): string {\n if (signal.packageName) {\n return `Owns or contributes to the ${humanizeLabel(signal.packageName)} codebase.`;\n }\n\n if (signal.entries.some((entry) => entry === 'docs/' || entry.toLowerCase().includes('docs'))) {\n return 'Mix of implementation and documentation workflows.';\n }\n\n if (signal.entries.some((entry) => entry === 'src/' || entry === 'app/' || entry.endsWith('.ts'))) {\n return 'Application code, implementation details, and related project context.';\n }\n\n return `Local context collected from the ${humanizeLabel(signal.basename)} workspace folder.`;\n}\n\nfunction inferProjectName(signal: FolderSignal): string {\n if (signal.packageName) {\n return humanizeLabel(signal.packageName);\n }\n\n const basename = humanizeLabel(signal.basename);\n return basename || 'Untitled Project';\n}\n\nasync function enrichProjectsWithOllama(\n projects: InferredProject[],\n folderSignals: FolderSignal[],\n integrations: string[],\n): Promise<{ projects: InferredProject[]; inference: ProjectInferenceMeta } | null> {\n if (projects.length === 0) {\n return null;\n }\n\n const model = await resolveOllamaModel();\n if (!model) {\n return null;\n }\n\n const prompt = [\n 'You are organizing local workspace context during onboarding.',\n 'Return strict JSON only in the format:',\n '{\"projects\":[{\"slug\":\"\",\"name\":\"\",\"summary\":\"\",\"primaryFocus\":\"\",\"keySignals\":[\"\"]}]}',\n 'Rules:',\n '- Keep the existing slugs exactly as provided.',\n '- Do not invent repositories, teams, or integrations that are not in the input.',\n '- Preserve the current project grouping; do not add or remove source folders.',\n '- Summaries should be concise and grounded in the visible folder signals.',\n '- Primary focus should be a short sentence.',\n '',\n JSON.stringify(\n {\n integrations: integrations.map(formatIntegrationName),\n folders: folderSignals,\n projects,\n },\n null,\n 2,\n ),\n ].join('\\n');\n\n try {\n const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model,\n prompt,\n stream: false,\n format: 'json',\n options: {\n temperature: 0.2,\n },\n }),\n signal: AbortSignal.timeout(15000),\n });\n\n if (!response.ok) {\n return null;\n }\n\n const payload = (await response.json()) as { response?: string };\n if (!payload.response) {\n return null;\n }\n\n const parsed = JSON.parse(payload.response) as {\n projects?: Array<{\n slug?: string;\n name?: string;\n summary?: string;\n primaryFocus?: string;\n keySignals?: string[];\n }>;\n };\n\n const enrichedBySlug = new Map(\n (parsed.projects ?? [])\n .filter((project) => project.slug?.trim())\n .map((project) => [project.slug!.trim(), project]),\n );\n\n const enrichedProjects = projects.map((project) => {\n const enriched = enrichedBySlug.get(project.slug);\n if (!enriched) {\n return project;\n }\n\n return {\n ...project,\n name: enriched.name?.trim() || project.name,\n summary: enriched.summary?.trim() || project.summary,\n primaryFocus: enriched.primaryFocus?.trim() || project.primaryFocus,\n keySignals:\n enriched.keySignals?.map((signal) => signal.trim()).filter(Boolean).slice(0, 5) ??\n project.keySignals,\n };\n });\n\n return {\n projects: enrichedProjects,\n inference: {\n strategy: 'ollama',\n model,\n note: `Structured locally and enriched with Ollama (${model}). No external API calls were made.`,\n },\n };\n } catch {\n return null;\n }\n}\n\nasync function resolveOllamaModel(): Promise<string | null> {\n try {\n const response = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n signal: AbortSignal.timeout(1500),\n });\n if (!response.ok) {\n return null;\n }\n\n const payload = (await response.json()) as {\n models?: Array<{ name?: string }>;\n };\n\n const modelNames = (payload.models ?? [])\n .map((model) => model.name?.trim() ?? '')\n .filter(Boolean);\n\n if (modelNames.length === 0) {\n return null;\n }\n\n return (\n modelNames.find((name) => /qwen|llama|mistral|gemma/i.test(name)) ??\n modelNames[0]\n );\n } catch {\n return null;\n }\n}\n\nfunction formatIntegrationName(id: string): string {\n const names: Record<string, string> = {\n slack: 'Slack',\n github: 'GitHub',\n gitlab: 'GitLab',\n gmail: 'Gmail',\n gdrive: 'Google Drive',\n gchat: 'Google Chat',\n };\n\n return names[id] ?? humanizeLabel(id);\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction humanizeLabel(value: string): string {\n return value\n .replace(/[-_]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values.filter(Boolean))];\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0VT;","names":["path","fs","basename"]}
@@ -0,0 +1 @@
1
+ :root{--pac-yellow: #FFD700;--pac-yellow-glow: rgba(255, 215, 0, .35);--pac-yellow-dim: rgba(255, 215, 0, .12);--ghost-red: #FF1744;--ghost-pink: #FF80AB;--ghost-cyan: #00E5FF;--ghost-orange: #FFAB40;--maze-blue: #1A1AFF;--maze-blue-glow: rgba(26, 26, 255, .25);--maze-wall: #2929cc;--bg-base: #060614;--bg-surface: #0c0c24;--bg-elevated: #14143a;--bg-input: #0a0a20;--bg-inset: #050510;--border: #1e1e5a;--border-hover: #2e2e8a;--border-focus: var(--pac-yellow);--accent: var(--pac-yellow);--accent-hover: #e6c200;--accent-subtle: rgba(255, 215, 0, .06);--accent-dim: rgba(255, 215, 0, .14);--text-primary: #e4e4ff;--text-secondary: #9898cc;--text-muted: #6464aa;--text-dim: #404088;--success: #00FF88;--success-subtle: rgba(0, 255, 136, .06);--success-dim: rgba(0, 255, 136, .14);--error: #FF4466;--error-subtle: rgba(255, 68, 102, .06);--warning: var(--pac-yellow);--warning-subtle: rgba(255, 215, 0, .06);--font-pixel: "Press Start 2P", monospace;--font-display: "Orbitron", sans-serif;--font-body: "Lexend", sans-serif;--font-mono: "Fira Code", "JetBrains Mono", monospace;--radius-lg: 16px;--radius: 12px;--radius-sm: 8px;--radius-xs: 6px}*{margin:0;padding:0;box-sizing:border-box}body{font-family:var(--font-body);background:var(--bg-base);color:var(--text-primary);min-height:100vh;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body:before{content:"";position:fixed;top:0;right:0;bottom:0;left:0;background:radial-gradient(ellipse 60% 40% at 50% 0%,rgba(26,26,255,.06) 0%,transparent 70%),repeating-linear-gradient(0deg,transparent,transparent 39px,rgba(26,26,255,.03) 39px,rgba(26,26,255,.03) 40px),repeating-linear-gradient(90deg,transparent,transparent 39px,rgba(26,26,255,.03) 39px,rgba(26,26,255,.03) 40px);pointer-events:none;z-index:0}#root{position:relative;z-index:1}.container{max-width:740px;margin:0 auto;padding:2rem 2rem 3rem}.container:has(.template-preview){max-width:960px}.container:has(.landing){max-width:900px;padding:0 2rem 3rem}.container.container-with-sidebar{max-width:1180px}.onboarding-shell.with-summary{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:1.5rem;align-items:start}.onboarding-main{min-width:0}.onboarding-summary-sidebar{position:sticky;top:1.5rem}.onboarding-summary-mobile-wrap{display:none;margin-bottom:1rem}.onboarding-summary-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.9rem 1rem;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-display);font-size:.78rem;letter-spacing:.03em}.onboarding-summary-panel{display:grid;gap:.9rem;padding:1rem;background:linear-gradient(180deg,#0c0c24fa,#050510fa);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:0 18px 40px #0003}.onboarding-summary-panel.mobile{margin-top:.75rem}.onboarding-summary-header{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem}.onboarding-summary-kicker{display:inline-block;margin-bottom:.35rem;font-family:var(--font-pixel);font-size:.42rem;color:var(--pac-yellow);letter-spacing:.04em}.onboarding-summary-header h3{font-family:var(--font-display);font-size:.9rem;color:var(--text-primary)}.onboarding-summary-count{font-size:.72rem;color:var(--text-dim);white-space:nowrap}.onboarding-summary-list{display:grid;gap:.75rem}.onboarding-summary-card{width:100%;display:grid;gap:.7rem;padding:.9rem;text-align:left;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-secondary);transition:border-color .2s ease,transform .2s ease,background .2s ease}.onboarding-summary-card:not(:disabled):hover{border-color:var(--border-hover);background:var(--bg-surface);transform:translateY(-1px)}.onboarding-summary-card.current{border-color:#ffd70047;background:var(--pac-yellow-dim)}.onboarding-summary-card:disabled{opacity:.55;cursor:not-allowed}.onboarding-summary-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:.75rem}.onboarding-summary-card-title{display:flex;align-items:flex-start;gap:.8rem}.onboarding-summary-step{width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--bg-elevated);border:1px solid var(--border);color:var(--pac-yellow);font-family:var(--font-pixel);font-size:.45rem;flex-shrink:0}.onboarding-summary-card-title strong{display:block;color:var(--text-primary);font-size:.85rem;margin-bottom:.2rem}.onboarding-summary-card-title span{display:block;font-size:.74rem;line-height:1.45;color:var(--text-dim)}.onboarding-summary-action{font-size:.72rem;color:var(--pac-yellow);white-space:nowrap}.onboarding-summary-lines{display:grid;gap:.32rem}.onboarding-summary-lines span{font-size:.76rem;line-height:1.45;color:var(--text-secondary)}.landing{animation:fadeIn .6s ease-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.landing-hero{text-align:center;padding:4rem 0 3rem;position:relative}.landing-hero:before{content:"";position:absolute;top:20%;left:50%;transform:translate(-50%);width:500px;height:500px;background:radial-gradient(circle,rgba(255,215,0,.06) 0%,transparent 70%);pointer-events:none}.pac-scene{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:2rem;position:relative}.pac-trail-dot{width:8px;height:8px;border-radius:50%;background:var(--pac-yellow);box-shadow:0 0 6px var(--pac-yellow-glow);opacity:.9}.pac-trail-dot.eaten{opacity:.1;box-shadow:none}.pac-trail-dot.power{width:14px;height:14px;animation:powerPulse 1s ease-in-out infinite}@keyframes powerPulse{0%,to{box-shadow:0 0 6px var(--pac-yellow-glow);transform:scale(1)}50%{box-shadow:0 0 18px var(--pac-yellow-glow);transform:scale(1.3)}}.pacman-char{width:36px;height:36px;position:relative}.pacman-char:before,.pacman-char:after{content:"";position:absolute;left:0;width:36px;height:18px;background:var(--pac-yellow)}.pacman-char:before{top:0;border-radius:18px 18px 0 0;transform-origin:bottom center;animation:chompTop .3s ease-in-out infinite alternate}.pacman-char:after{bottom:0;border-radius:0 0 18px 18px;transform-origin:top center;animation:chompBottom .3s ease-in-out infinite alternate}@keyframes chompTop{0%{transform:rotate(0)}to{transform:rotate(-18deg)}}@keyframes chompBottom{0%{transform:rotate(0)}to{transform:rotate(18deg)}}.pacman-char-small{width:22px;height:22px}.pacman-char-small:before,.pacman-char-small:after{width:22px;height:11px}.pacman-char-small:before{border-radius:11px 11px 0 0}.pacman-char-small:after{border-radius:0 0 11px 11px}.landing-brand{font-family:var(--font-pixel);font-size:2.5rem;color:var(--pac-yellow);text-shadow:0 0 20px var(--pac-yellow-glow),0 0 60px rgba(255,215,0,.15);margin-bottom:.75rem;letter-spacing:.1em}.landing-subtitle{font-family:var(--font-display);font-size:.85rem;font-weight:400;color:var(--text-secondary);letter-spacing:.15em;text-transform:uppercase;margin-bottom:1.25rem}.landing-tagline{font-family:var(--font-body);font-size:1.1rem;color:var(--text-muted);font-weight:300;max-width:480px;margin:0 auto;line-height:1.6}.ghost-section{padding:2rem 0}.section-heading{font-family:var(--font-display);font-size:1.1rem;font-weight:600;color:var(--text-primary);text-align:center;margin-bottom:2rem;letter-spacing:.04em;text-wrap:balance}.ghost-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1.5rem;align-items:stretch}.ghost-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.5rem;text-align:left;position:relative;overflow:hidden;display:flex;flex-direction:column;gap:1rem;min-height:100%;transition:border-color .3s,transform .3s,box-shadow .3s}.ghost-card:hover{transform:translateY(-4px);box-shadow:0 18px 40px #00000038}.ghost-card:before{content:"";position:absolute;right:-36px;bottom:-44px;width:140px;height:140px;border-radius:50%;opacity:.06;pointer-events:none}.ghost-card.ghost-red{border-color:#ff174433}.ghost-card.ghost-red:hover{border-color:#ff174466}.ghost-card.ghost-red:before{background:var(--ghost-red)}.ghost-card.ghost-pink{border-color:#ff80ab33}.ghost-card.ghost-pink:hover{border-color:#ff80ab66}.ghost-card.ghost-pink:before{background:var(--ghost-pink)}.ghost-card.ghost-cyan{border-color:#00e5ff33}.ghost-card.ghost-cyan:hover{border-color:#00e5ff66}.ghost-card.ghost-cyan:before{background:var(--ghost-cyan)}.ghost-card-top{display:flex;align-items:flex-start;gap:1rem}.ghost-card-copy{flex:1;min-width:0}.ghost-icon{width:56px;height:56px;margin:0;flex-shrink:0;animation:ghostFloat 3s ease-in-out infinite}.ghost-icon svg{width:100%;height:100%}@keyframes ghostFloat{0%,to{transform:translateY(0)}50%{transform:translateY(-8px)}}.ghost-card h3{font-family:var(--font-display);font-size:.9rem;font-weight:600;margin-bottom:.5rem;letter-spacing:.03em}.ghost-card.ghost-red h3{color:var(--ghost-red)}.ghost-card.ghost-pink h3{color:var(--ghost-pink)}.ghost-card.ghost-cyan h3{color:var(--ghost-cyan)}.ghost-card.ghost-orange{border-color:#ffab4033}.ghost-card.ghost-orange:hover{border-color:#ffab4066}.ghost-card.ghost-orange:before{background:var(--ghost-orange)}.ghost-card.ghost-orange h3{color:var(--ghost-orange)}.ghost-problem{font-size:.84rem;color:var(--text-muted);line-height:1.6;margin-bottom:0}.ghost-solution{display:grid;gap:.45rem;margin-top:auto;padding:.9rem 1rem;background:linear-gradient(180deg,#ffd7001a,#ffd7000a);border:1px solid rgba(255,215,0,.14);border-radius:var(--radius-sm)}.ghost-solution-kicker{display:inline-flex;align-items:center;gap:.45rem;font-family:var(--font-display);font-size:.68rem;font-weight:600;letter-spacing:.08em;color:var(--pac-yellow);text-transform:uppercase}.ghost-solution-title{font-size:.92rem;font-weight:600;color:var(--text-primary);line-height:1.4}.ghost-solution-detail{font-size:.78rem;color:var(--text-secondary);line-height:1.55}.comparison-section{padding:2.5rem 0}.comparison-grid{display:grid;grid-template-columns:1fr auto 1fr;gap:1rem;align-items:stretch}.comparison-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}.comparison-label{display:block;font-family:var(--font-pixel);font-size:.55rem;letter-spacing:.1em;padding:.625rem 1rem;border-bottom:1px solid var(--border)}.comparison-card.before .comparison-label{color:var(--ghost-red);background:#ff17440a}.comparison-card.after .comparison-label{color:var(--success);background:#00ff880a}.mock-chat{padding:1rem;display:flex;flex-direction:column;gap:.75rem}.mock-msg{display:flex;gap:.6rem;align-items:flex-start}.mock-avatar{width:28px;height:28px;border-radius:6px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:.6rem;font-weight:700;font-family:var(--font-mono)}.mock-avatar.user{background:var(--maze-wall);color:#fff}.mock-avatar.bot{background:var(--pac-yellow-dim);color:var(--pac-yellow)}.mock-msg-body{flex:1}.mock-msg-name{font-size:.72rem;font-weight:600;margin-bottom:.25rem}.mock-msg-name .badge{font-size:.55rem;background:var(--bg-elevated);color:var(--text-dim);padding:1px 5px;border-radius:3px;font-weight:500;margin-left:4px;vertical-align:middle}.mock-msg-text{font-size:.78rem;line-height:1.55;color:var(--text-secondary)}.mock-msg-text.dim{color:var(--text-muted);font-style:italic}.mock-msg-text.bright{color:var(--text-primary)}.comparison-arrow{display:flex;align-items:center;justify-content:center;padding:0 .25rem}.comparison-arrow-inner{display:flex;flex-direction:column;align-items:center;gap:.5rem}.comparison-arrow .pacman-char-small{animation:chompSlide 2s ease-in-out infinite}@keyframes chompSlide{0%,to{transform:translate(0)}50%{transform:translate(6px)}}.landing-cta{text-align:center;padding:2.5rem 0 1rem}.btn-start{display:inline-flex;align-items:center;gap:.75rem;padding:1rem 2.5rem;background:var(--pac-yellow);color:#060614;font-family:var(--font-display);font-size:.95rem;font-weight:700;border:none;border-radius:50px;cursor:pointer;letter-spacing:.04em;box-shadow:0 0 20px var(--pac-yellow-glow),0 4px 16px #0006;transition:all .25s}.btn-start:hover{transform:translateY(-2px) scale(1.03);box-shadow:0 0 40px var(--pac-yellow-glow),0 6px 24px #00000080}.btn-start:active{transform:translateY(0) scale(1)}.btn-start .pacman-char-small:before,.btn-start .pacman-char-small:after{background:#060614}@media(max-width:920px){.container:has(.landing){max-width:760px}.ghost-grid,.comparison-grid{grid-template-columns:1fr}.comparison-arrow{padding:.25rem 0}.comparison-arrow-inner{flex-direction:row}.comparison-arrow svg{transform:rotate(90deg)}}@media(max-width:720px){.container{padding:1.5rem 1rem 2.5rem}.container:has(.landing){padding:0 1rem 2.5rem}.landing-hero{padding:3rem 0 2.5rem}.landing-hero:before{top:10%;width:320px;height:320px}.landing-brand{font-size:1.85rem}.landing-subtitle{font-size:.72rem}.landing-tagline{font-size:1rem;max-width:none}.ghost-card{padding:1.25rem}.ghost-card-top{flex-direction:column;gap:.85rem}.ghost-icon{width:48px;height:48px}.btn-start{width:100%;justify-content:center;padding:1rem 1.5rem}}@media(max-width:520px){.pac-scene{gap:8px}.section-heading{font-size:1rem;margin-bottom:1.5rem}.ghost-solution{padding:.8rem .9rem}.mock-chat{padding:.875rem}.project-structure-grid{grid-template-columns:1fr}}.pac-progress{display:flex;align-items:center;justify-content:space-between;margin-bottom:2.5rem;position:relative;padding:0 1rem}.pac-progress:before{content:"";position:absolute;top:50%;left:2rem;right:2rem;height:2px;background:var(--border);transform:translateY(-50%);z-index:0}.pac-progress:after{content:"";position:absolute;top:50%;left:2rem;height:2px;width:var(--eaten-width, 0%);background:linear-gradient(90deg,var(--pac-yellow-dim),var(--pac-yellow-dim));transform:translateY(-50%);z-index:0;transition:width .5s ease}.pac-progress-step{display:flex;flex-direction:column;align-items:center;gap:.625rem;position:relative;z-index:1;background:none;border:none;padding:0;cursor:pointer;transition:opacity .2s ease}.pac-progress-step.locked{cursor:not-allowed}.pac-progress-step.clickable:hover .pac-step-label{color:var(--text-primary)}.pac-progress-step:disabled{opacity:.55}.pac-dot{width:10px;height:10px;border-radius:50%;background:var(--pac-yellow);box-shadow:0 0 8px var(--pac-yellow-glow);transition:all .4s ease}.pac-progress-step.eaten .pac-dot{width:6px;height:6px;background:var(--pac-yellow-dim);box-shadow:none;opacity:.4}.pac-progress-step.current .pac-dot{width:0;height:0;opacity:0}.pac-step-label{font-family:var(--font-display);font-size:.55rem;font-weight:500;color:var(--text-dim);letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;transition:color .3s}.pac-progress-step.eaten .pac-step-label{color:var(--pac-yellow);opacity:.5}.pac-progress-step.current .pac-step-label{color:var(--pac-yellow)}.pac-progress-pacman{position:absolute;top:50%;transform:translateY(-50%);z-index:2;transition:left .5s cubic-bezier(.4,0,.2,1)}.step-header{margin-bottom:2.5rem}.step-meta{display:flex;align-items:center;justify-content:space-between;margin-bottom:.875rem}.step-counter{font-family:var(--font-pixel);font-size:.55rem;color:var(--text-muted);letter-spacing:.06em}.step-name{font-family:var(--font-display);font-size:.7rem;font-weight:500;color:var(--pac-yellow);letter-spacing:.08em;text-transform:uppercase}.progress-track{height:3px;background:var(--border);border-radius:3px;overflow:hidden;position:relative}.progress-fill{height:100%;background:linear-gradient(90deg,var(--pac-yellow),var(--accent-hover));border-radius:3px;transition:width .6s cubic-bezier(.4,0,.2,1);box-shadow:0 0 8px var(--pac-yellow-glow)}h1{font-family:var(--font-display);font-size:1.75rem;font-weight:700;color:var(--text-primary);margin-bottom:.5rem;letter-spacing:.02em;line-height:1.2}h2{font-family:var(--font-body);font-size:1rem;margin-bottom:1.5rem;color:var(--text-secondary);font-weight:400;line-height:1.5}.step-content{animation:stepIn .45s ease-out}@keyframes stepIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}label{display:block;margin-bottom:.375rem;color:var(--text-secondary);font-size:.85rem;font-weight:500}input,select,textarea{width:100%;padding:.8rem 1rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text-primary);font-family:var(--font-body);font-size:.95rem;margin-bottom:1rem;transition:border-color .2s,box-shadow .2s}input::placeholder,textarea::placeholder{color:var(--text-dim)}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--pac-yellow);box-shadow:0 0 0 3px var(--pac-yellow-dim),0 0 12px #ffd70014}select{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236464aa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .875rem center;padding-right:2.5rem}textarea{min-height:200px;font-family:var(--font-mono);font-size:.85rem;resize:vertical}.form-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.5rem;margin-bottom:1.5rem}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem}.form-field,.form-field input,.form-field select{margin-bottom:0}.form-section{margin-bottom:1.5rem}.field-label{display:block;font-size:.8rem;font-weight:500;color:var(--text-muted);margin-bottom:.5rem;letter-spacing:.01em}.field-hint{display:block;font-size:.78rem;color:var(--text-dim);margin-top:.35rem;line-height:1.5}.field-error{display:block;font-size:.76rem;color:var(--error);margin-top:.45rem;line-height:1.4}.profile-grid{display:grid;grid-template-columns:1fr 1fr;gap:.625rem}.profile-card{display:flex;align-items:center;gap:.875rem;padding:.875rem 1rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;transition:all .2s;position:relative;overflow:hidden}.profile-card:hover{border-color:var(--border-hover);background:var(--bg-elevated)}.profile-card.selected{border-color:var(--pac-yellow);background:var(--pac-yellow-dim);box-shadow:0 0 12px #ffd70014}.profile-indicator{width:3px;height:28px;border-radius:2px;flex-shrink:0;opacity:.7;transition:opacity .2s}.profile-card.selected .profile-indicator{opacity:1;box-shadow:0 0 8px currentColor}.profile-info{display:flex;flex-direction:column;gap:1px}.profile-name{font-weight:600;font-size:.85rem;color:var(--text-primary)}.profile-desc{font-size:.72rem;color:var(--text-muted);line-height:1.4}button{padding:.75rem 1.5rem;border:none;border-radius:var(--radius-sm);font-family:var(--font-body);font-size:.9rem;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background:linear-gradient(135deg,var(--pac-yellow) 0%,var(--accent-hover) 100%);color:#060614;box-shadow:0 2px 8px #0000004d,0 0 16px #ffd7001f}.btn-primary:hover:not(:disabled){box-shadow:0 4px 16px #0000004d,0 0 28px #ffd70033;transform:translateY(-1px)}.btn-primary:active:not(:disabled){transform:translateY(0)}.btn-primary:disabled{opacity:.35;cursor:not-allowed;transform:none}.btn-secondary{background:var(--bg-elevated);color:var(--text-secondary);border:1px solid var(--border)}.btn-secondary:hover:not(:disabled){background:var(--border);color:var(--text-primary);border-color:var(--border-hover)}.btn-secondary:disabled{opacity:.35;cursor:not-allowed}.btn-group{display:flex;gap:.75rem;margin-top:2rem}.checkbox-group{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:1rem}.checkbox-item{display:flex;align-items:center;gap:.625rem;padding:.625rem .75rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:.875rem;color:var(--text-secondary);transition:all .2s}.checkbox-item:hover{border-color:var(--border-hover);color:var(--text-primary)}.checkbox-item input[type=checkbox]{width:16px;height:16px;margin:0;accent-color:var(--pac-yellow);flex-shrink:0}.radio-group{display:flex;gap:.75rem;margin-bottom:1.25rem}.radio-item{flex:1;padding:1.25rem 1rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;text-align:center;transition:all .2s}.radio-item:hover{border-color:var(--border-hover);background:var(--bg-elevated)}.radio-item.selected{border-color:var(--pac-yellow);background:var(--pac-yellow-dim);box-shadow:0 0 12px #ffd70014}.radio-item strong{color:var(--text-primary)}.radio-item small{color:var(--text-muted);font-size:.78rem}.integration-section-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:1.5rem}.integration-count{font-family:var(--font-pixel);font-size:.5rem;color:var(--success);background:var(--success-subtle);padding:6px 12px;border-radius:99px;letter-spacing:.02em}.recommendation-banner{display:flex;align-items:center;gap:.75rem;padding:.875rem 1.25rem;background:var(--pac-yellow-dim);border:1px solid rgba(255,215,0,.15);border-radius:var(--radius-sm);margin-bottom:1.25rem;font-size:.82rem;color:var(--pac-yellow)}.recommendation-banner .pacman-char-small{flex-shrink:0}.recommended-badge{font-family:var(--font-pixel);font-size:.42rem;color:var(--pac-yellow);background:var(--pac-yellow-dim);padding:3px 8px;border-radius:99px;letter-spacing:.04em;border:1px solid rgba(255,215,0,.2);white-space:nowrap}.folder-section{margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid var(--border)}.folder-section-title{font-family:var(--font-display);font-size:.85rem;font-weight:600;color:var(--text-primary);margin-bottom:.35rem;display:flex;align-items:center;gap:.5rem}.folder-section-desc{font-size:.8rem;color:var(--text-muted);margin-bottom:1rem;line-height:1.5}.folder-list{display:flex;flex-direction:column;gap:.5rem;margin-bottom:.75rem}.folder-row{display:grid;grid-template-columns:1fr auto 1fr auto;gap:.5rem;align-items:center;padding:.5rem .75rem;background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius-sm)}.folder-row input{margin-bottom:0;padding:.55rem .75rem;font-size:.82rem}.folder-row .btn-browse{padding:.55rem .75rem;font-size:.75rem;background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-secondary);white-space:nowrap}.folder-row .btn-browse:hover{border-color:var(--border-hover);color:var(--text-primary)}.folder-row .btn-remove{padding:.4rem .6rem;background:transparent;border:1px solid transparent;color:var(--text-dim);font-size:1rem;line-height:1}.folder-row .btn-remove:hover{color:var(--error);border-color:var(--error-subtle);background:var(--error-subtle)}.btn-add-folder{display:inline-flex;align-items:center;gap:.4rem;padding:.55rem 1rem;font-size:.8rem;font-weight:500;background:var(--bg-surface);border:1px dashed var(--border);color:var(--text-muted);border-radius:var(--radius-sm);cursor:pointer;transition:all .2s}.btn-add-folder:hover{border-color:var(--pac-yellow);color:var(--pac-yellow);background:var(--pac-yellow-dim)}.project-structure-banner{display:grid;gap:.35rem;padding:1rem 1.1rem;margin-bottom:1rem;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius)}.project-structure-banner strong{color:var(--pac-yellow);font-family:var(--font-display);font-size:.78rem;letter-spacing:.05em;text-transform:uppercase}.project-structure-banner span{color:var(--text-secondary);font-size:.82rem;line-height:1.6}.project-structure-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem;margin-bottom:1rem}.project-structure-card{display:grid;gap:.65rem;width:100%;padding:1rem;text-align:left;background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius)}.project-structure-card.active{border-color:#ffd7004d;box-shadow:0 0 0 1px #ffd70014}.project-structure-card-top{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.project-structure-card-top strong{color:var(--text-primary);font-size:.9rem;font-family:var(--font-display)}.project-structure-card-top span{color:var(--text-dim);font-size:.74rem;white-space:nowrap}.project-structure-card p{color:var(--text-secondary);font-size:.8rem;line-height:1.55}.project-structure-meta{display:flex;flex-wrap:wrap;gap:.45rem}.project-structure-chip{display:inline-flex;align-items:center;padding:.28rem .55rem;border-radius:999px;background:var(--pac-yellow-dim);color:var(--pac-yellow);font-size:.72rem;line-height:1.2}.project-structure-chip.muted{background:var(--bg-inset);color:var(--text-secondary);border:1px solid var(--border)}.template-preview{display:flex;height:520px;background:var(--bg-inset);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;margin-bottom:.5rem}.template-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;gap:1rem;color:var(--text-muted);font-family:var(--font-body)}.template-spinner{width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--pac-yellow);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.template-error{display:flex;align-items:center;justify-content:center;width:100%;color:var(--error);font-size:.9rem}.template-sidebar{width:200px;min-width:200px;background:var(--bg-input);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow-y:auto}.template-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;font-family:var(--font-display);font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--text-muted);border-bottom:1px solid var(--border)}.template-sidebar-actions{display:flex;gap:.25rem}.template-action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;border:none;border-radius:var(--radius-xs);color:var(--text-muted);cursor:pointer;transition:all .15s}.template-action-btn:hover{background:var(--border);color:var(--pac-yellow)}.template-file-tab{display:flex;align-items:center;gap:.5rem;width:100%;padding:.55rem 1rem;background:transparent;border:none;border-left:2px solid transparent;color:var(--text-secondary);font-family:var(--font-body);font-size:.8rem;cursor:pointer;text-align:left;transition:all .15s}.template-file-tab:hover{background:var(--bg-elevated)}.template-file-tab.active{background:var(--bg-inset);border-left-color:var(--pac-yellow);color:var(--text-primary)}.template-file-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:4px;font-family:var(--font-mono);font-size:.65rem;font-weight:700;flex-shrink:0}.template-file-icon.icon-m{background:var(--pac-yellow-dim);color:var(--pac-yellow)}.template-file-icon.icon-j{background:#ffab401f;color:var(--ghost-orange)}.template-file-icon.icon-y{background:#ff80ab1f;color:var(--ghost-pink)}.template-file-icon.icon-f{background:#6464aa1f;color:var(--text-muted)}.template-file-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.template-file-lines{font-family:var(--font-mono);font-size:.65rem;color:var(--text-dim);flex-shrink:0}.template-folder-group{display:flex;flex-direction:column}.template-folder-row{display:flex;align-items:center;position:relative}.template-folder-tab{display:flex;align-items:center;gap:.35rem;flex:1;padding:.55rem .6rem .55rem .7rem;background:transparent;border:none;border-left:2px solid transparent;color:var(--text-secondary);font-family:var(--font-body);font-size:.8rem;cursor:pointer;text-align:left;transition:all .15s;min-width:0}.template-folder-tab:hover{background:var(--bg-elevated)}.template-folder-arrow{font-size:.65rem;width:12px;flex-shrink:0;color:var(--text-muted)}.template-file-icon.icon-folder{background:var(--pac-yellow-dim);color:var(--pac-yellow);display:flex;align-items:center;justify-content:center}.template-folder-action{display:none;align-items:center;justify-content:center;width:22px;height:22px;font-size:.75rem;color:var(--text-muted);cursor:pointer;border-radius:3px;flex-shrink:0;transition:all .15s}.template-folder-row:hover .template-folder-action{display:flex}.template-folder-add:hover{background:var(--pac-yellow-dim);color:var(--pac-yellow)}.template-folder-children{display:flex;flex-direction:column}.template-file-tab.indented{padding-left:2.1rem}.template-folder-empty{padding:.4rem 1rem .4rem 2.1rem;font-size:.7rem;color:var(--text-dim);font-style:italic}.template-item-delete{display:none;align-items:center;justify-content:center;width:22px;height:22px;font-size:.85rem;color:var(--text-muted);cursor:pointer;border-radius:3px;flex-shrink:0;transition:all .15s}.template-file-tab:hover .template-item-delete,.template-folder-row:hover .template-item-delete{display:flex}.template-file-tab:hover .template-file-lines,.template-folder-row:hover .template-file-lines{display:none}.template-item-delete:hover{background:var(--error-subtle);color:var(--error)}.template-create-input{display:flex;align-items:center;gap:.35rem;padding:.35rem .6rem;background:var(--bg-elevated);border:1px solid var(--border);border-radius:4px;margin:.3rem .5rem}.template-create-prefix{font-family:var(--font-mono);font-size:.72rem;color:var(--text-muted);white-space:nowrap;flex-shrink:0}.template-create-field{width:100%;padding:.2rem .3rem;background:transparent;border:none;border-radius:0;color:var(--text-primary);font-family:var(--font-mono);font-size:.78rem;outline:none;margin:0}.template-create-field::placeholder{color:var(--text-dim)}.template-empty-editor{display:flex;align-items:center;justify-content:center;flex:1;color:var(--text-dim);font-size:.9rem;font-family:var(--font-body)}.template-editor{flex:1;display:flex;flex-direction:column;min-width:0}.template-editor-header{display:flex;align-items:center;gap:.75rem;padding:.6rem 1rem;background:var(--bg-input);border-bottom:1px solid var(--border);min-height:40px}.template-editor-path{font-family:var(--font-mono);font-size:.78rem;color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.template-editor-badge{font-family:var(--font-mono);font-size:.6rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;padding:.15rem .45rem;border-radius:9999px;background:var(--pac-yellow-dim);color:var(--pac-yellow);flex-shrink:0}.template-editor-body{display:flex;flex:1;overflow:auto}.template-line-numbers{display:flex;flex-direction:column;padding:.75rem 0;min-width:42px;text-align:right;font-family:var(--font-mono);font-size:.78rem;line-height:1.55;color:var(--text-dim);-webkit-user-select:none;user-select:none;border-right:1px solid var(--border);background:var(--bg-inset)}.template-line-numbers span{padding:0 .6rem}.template-textarea{flex:1;padding:.75rem;background:var(--bg-inset);border:none;border-radius:0;color:var(--text-secondary);font-family:var(--font-mono);font-size:.78rem;line-height:1.55;resize:none;outline:none;min-height:100%;margin:0;white-space:pre;overflow-wrap:normal;overflow-x:auto}.template-textarea:focus{border:none;box-shadow:none}.success{text-align:center;padding:2rem 0}.success h1{font-family:var(--font-display);font-size:1.75rem;margin-bottom:.75rem}.success h2{color:var(--success);font-family:var(--font-display);font-size:1.25rem;font-weight:600}.success-icon{width:80px;height:80px;border-radius:50%;background:var(--success-dim);display:flex;align-items:center;justify-content:center;margin:0 auto 1.5rem;animation:scaleIn .5s cubic-bezier(.34,1.56,.64,1);box-shadow:0 0 30px #00ff8826}@keyframes scaleIn{0%{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.success-icon svg{width:36px;height:36px;color:var(--success)}.pac-celebration{display:flex;align-items:center;justify-content:center;gap:10px;margin:1.5rem 0;animation:fadeIn .8s ease-out}.pac-celebration .pac-trail-dot{animation:dotPop .6s ease-out both}.pac-celebration .pac-trail-dot:nth-child(1){animation-delay:.1s}.pac-celebration .pac-trail-dot:nth-child(2){animation-delay:.2s}.pac-celebration .pac-trail-dot:nth-child(3){animation-delay:.3s}.pac-celebration .pac-trail-dot:nth-child(5){animation-delay:.3s}.pac-celebration .pac-trail-dot:nth-child(6){animation-delay:.2s}.pac-celebration .pac-trail-dot:nth-child(7){animation-delay:.1s}@keyframes dotPop{0%{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.saving-overlay{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1.5rem;padding:4rem 2rem;text-align:center;animation:fadeIn .3s ease}.saving-text{font-family:var(--font-display);font-size:.9rem;color:var(--pac-yellow);letter-spacing:.04em}.saving-dots{display:flex;gap:8px;align-items:center}.saving-dots .pac-trail-dot{animation:savingEat 1.2s ease-in-out infinite}.saving-dots .pac-trail-dot:nth-child(1){animation-delay:0s}.saving-dots .pac-trail-dot:nth-child(2){animation-delay:.2s}.saving-dots .pac-trail-dot:nth-child(3){animation-delay:.4s}.saving-dots .pac-trail-dot:nth-child(4){animation-delay:.6s}.saving-dots .pac-trail-dot:nth-child(5){animation-delay:.8s}@keyframes savingEat{0%,60%,to{opacity:1;transform:scale(1)}30%{opacity:.2;transform:scale(.4)}}.code-block{text-align:left;margin-top:1rem;background:var(--bg-surface);border:1px solid var(--border);padding:1.25rem;border-radius:var(--radius);font-size:.82rem;font-family:var(--font-mono);white-space:pre-wrap;color:var(--text-secondary);line-height:1.7}.summary{background:var(--bg-surface);border:1px solid var(--border);padding:1.25rem;border-radius:var(--radius);margin-bottom:1rem;font-family:var(--font-mono);font-size:.82rem;white-space:pre-wrap;color:var(--text-secondary);line-height:1.6;max-height:400px;overflow-y:auto}.banner-warning{background:#ffd7000a;border:1px solid rgba(255,215,0,.15);border-radius:var(--radius-sm);padding:.75rem 1rem;font-size:.82rem;color:var(--pac-yellow);line-height:1.6}.banner-info{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:1rem;font-size:.85rem;color:var(--text-secondary);line-height:1.6}.banner-success{background:var(--success-subtle);border:1px solid rgba(0,255,136,.15);border-radius:var(--radius-sm);padding:1rem}.instructions-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin:1.5rem 0;text-align:left}.instruction-card{background:var(--bg-surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.25rem;transition:border-color .2s}.instruction-card:hover{border-color:var(--border-hover)}.instruction-card-icon{width:36px;height:36px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;font-family:var(--font-pixel);font-size:.45rem;margin-bottom:.75rem;color:#fff}.instruction-card h4{font-family:var(--font-display);font-size:.78rem;font-weight:600;color:var(--text-primary);margin-bottom:.5rem;letter-spacing:.02em}.instruction-card .cmd{font-family:var(--font-mono);font-size:.75rem;color:var(--pac-yellow);background:var(--bg-inset);padding:.45rem .65rem;border-radius:var(--radius-xs);border:1px solid var(--border);display:block;margin-top:.35rem;white-space:pre-wrap;line-height:1.6}.instruction-card p{font-size:.78rem;color:var(--text-muted);line-height:1.5}.text-success{color:var(--success)}.text-error{color:var(--error)}.text-warning{color:var(--warning)}.text-muted{color:var(--text-muted)}.text-pac{color:var(--pac-yellow)}.mt-1{margin-top:.5rem}.mb-1{margin-bottom:.5rem}.neon-glow{box-shadow:0 0 5px var(--pac-yellow-glow),0 0 20px #ffd70014}.ghost-svg{transition:transform .3s}.ghost-svg:hover{transform:scale(1.1)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--bg-inset)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--border-hover)}.validation-dots{display:inline-flex;gap:4px;align-items:center}.validation-dots .pac-trail-dot{animation:validationPulse 1s ease-in-out infinite}.validation-dots .pac-trail-dot:nth-child(2){animation-delay:.2s}.validation-dots .pac-trail-dot:nth-child(3){animation-delay:.4s}@keyframes validationPulse{0%,to{opacity:.3;transform:scale(.8)}50%{opacity:1;transform:scale(1.3)}}:focus-visible{outline:2px solid var(--pac-yellow);outline-offset:2px}@media(max-width:980px){.container.container-with-sidebar{max-width:960px}.onboarding-shell.with-summary{grid-template-columns:1fr}.onboarding-summary-sidebar{display:none}.onboarding-summary-mobile-wrap{display:block}}